Refactor products view(s) per new master pattern.

Finally!
This commit is contained in:
Lance Edgar 2016-02-12 20:44:41 -06:00
parent 254c68034a
commit 583548cad5
12 changed files with 421 additions and 394 deletions

View file

@ -330,20 +330,18 @@ class MasterView(View):
"""
return getattr(cls, 'grid_key', '{0}s'.format(cls.get_normalized_model_name()))
def make_grid_kwargs(self):
def make_grid_kwargs(self, **kwargs):
"""
Return a dictionary of kwargs to be passed to the factory when creating
new grid instances.
"""
return {
defaults = {
'width': 'full',
'filterable': self.filterable,
'sortable': True,
'default_sortkey': getattr(self, 'default_sortkey', None),
'sortdir': getattr(self, 'sortdir', 'asc'),
'pageable': self.pageable,
'main_actions': self.get_main_actions(),
'more_actions': self.get_more_actions(),
'checkboxes': self.checkboxes,
'checked': self.checked,
'row_attrs': self.get_row_attrs,
@ -353,6 +351,15 @@ class MasterView(View):
'permission_prefix': self.get_permission_prefix(),
'route_prefix': self.get_route_prefix(),
}
if 'main_actions' not in kwargs and 'more_actions' not in kwargs:
main, more = self.get_grid_actions()
defaults['main_actions'] = main
defaults['more_actions'] = more
defaults.update(kwargs)
return defaults
def get_grid_actions(self):
return self.get_main_actions(), self.get_more_actions()
def get_row_attrs(self, row, i):
"""
@ -379,7 +386,8 @@ class MasterView(View):
Return a list of 'main' actions for the grid.
"""
actions = []
if self.viewable:
prefix = self.get_permission_prefix()
if self.viewable and self.request.has_perm('{}.view'.format(prefix)):
actions.append(self.make_action('view', icon='zoomin'))
return actions
@ -388,9 +396,10 @@ class MasterView(View):
Return a list of 'more' actions for the grid.
"""
actions = []
if self.editable:
prefix = self.get_permission_prefix()
if self.editable and self.request.has_perm('{}.edit'.format(prefix)):
actions.append(self.make_action('edit', icon='pencil'))
if self.deletable:
if self.deletable and self.request.has_perm('{}.delete'.format(prefix)):
actions.append(self.make_action('delete', icon='trash', url=self.default_delete_url))
return actions
@ -421,14 +430,14 @@ class MasterView(View):
values = [getattr(row, k) for k in keys]
return dict(zip(keys, values))
def make_grid(self):
def make_grid(self, **kwargs):
"""
Make and return a new (configured) grid instance.
"""
factory = self.get_grid_factory()
key = self.get_grid_key()
data = self.get_data()
kwargs = self.make_grid_kwargs()
data = self.get_data(session=kwargs.get('session'))
kwargs = self.make_grid_kwargs(**kwargs)
grid = factory(key, self.request, data=data, model_class=self.get_model_class(error=False), **kwargs)
self.configure_grid(grid)
grid.load_settings()

View file

@ -106,8 +106,12 @@ class TerseRecipientsFieldRenderer(formalchemy.FieldRenderer):
recipients = self.raw_value
if not recipients:
return ''
recips = filter(lambda r: r.recipient is not self.request.user, recipients)
message = self.field.parent.model
recips = [r for r in recipients if r.recipient is not self.request.user]
recips = sorted([r.recipient.display_name for r in recips])
if len(recips) < len(recipients) and (
message.sender is not self.request.user or not recips):
recips.insert(0, 'you')
if len(recips) < 5:
return ', '.join(recips)
return "{}, ...".format(', '.join(recips[:4]))

View file

@ -2,7 +2,7 @@
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2015 Lance Edgar
# Copyright © 2010-2016 Lance Edgar
#
# This file is part of Rattail.
#
@ -32,158 +32,126 @@ import re
import sqlalchemy as sa
from sqlalchemy import orm
from rattail import enum, pod, sil, batches
from rattail.db import model, api, auth, Session as RattailSession
from rattail.gpc import GPC
from rattail.threads import Thread
from rattail.exceptions import LabelPrintingError
import formalchemy
from pyramid.httpexceptions import HTTPFound
from pyramid import httpexceptions
from pyramid.renderers import render_to_response
from webhelpers.html import tags
import rattail.labels
from rattail import enum
from rattail import sil
from rattail import batches
from rattail.threads import Thread
from rattail.exceptions import LabelPrintingError
from rattail.db import model
from rattail.db.model import (
Product, ProductPrice, ProductCost, ProductCode,
Brand, Vendor, Department, Subdepartment, LabelProfile)
from rattail.gpc import GPC
from rattail.db.api import get_product_by_upc
from rattail.db.util import configure_session
from rattail.pod import get_image_url, get_image_path
from tailbone.views import SearchableAlchemyGridView, CrudView, AutocompleteView
from tailbone.views.continuum import VersionView, version_defaults
from tailbone.forms import EnumFieldRenderer, DateTimeFieldRenderer
from tailbone import forms
from tailbone.db import Session
from tailbone.forms import GPCFieldRenderer, BrandFieldRenderer, PriceFieldRenderer
from tailbone.forms.renderers import products as forms
from tailbone.views import MasterView, SearchableAlchemyGridView, AutocompleteView
from tailbone.views.continuum import VersionView, version_defaults
from tailbone.forms.renderers import products as products_forms
from tailbone.newgrids import GridAction
from tailbone.progress import SessionProgress
class ProductsGrid(SearchableAlchemyGridView):
class ProductsView(MasterView):
"""
Master view for the Product class.
"""
model_class = model.Product
mapped_class = Product
config_prefix = 'products'
sort = 'description'
# child_version_classes = [
# (model.ProductCode, 'product_uuid'),
# (model.ProductCost, 'product_uuid'),
# (model.ProductPrice, 'product_uuid'),
# ]
# These aliases enable the grid queries to filter products which may be
# purchased from *any* vendor, and yet sort by only the "preferred" vendor
# (since that's what shows up in the grid column).
ProductCostAny = orm.aliased(ProductCost)
VendorAny = orm.aliased(Vendor)
ProductCostAny = orm.aliased(model.ProductCost)
VendorAny = orm.aliased(model.Vendor)
def join_map(self):
def __init__(self, request):
self.request = request
self.print_labels = request.rattail_config.getbool('tailbone', 'products.print_labels', default=False)
def query(self, session):
user = self.request.user
if user and user not in session:
user = session.merge(user)
query = session.query(model.Product)
if not auth.has_permission(session, user, 'products.view_deleted'):
query = query.filter(model.Product.deleted == False)
# TODO: This used to be a good idea I thought...but in dev it didn't
# seem to make much difference, except with a larger (50K) data set it
# totally bogged things down instead of helping...
# query = query\
# .options(orm.joinedload(model.Product.brand))\
# .options(orm.joinedload(model.Product.department))\
# .options(orm.joinedload(model.Product.subdepartment))\
# .options(orm.joinedload(model.Product.regular_price))\
# .options(orm.joinedload(model.Product.current_price))\
# .options(orm.joinedload(model.Product.vendor))
return query
def configure_grid(self, g):
def join_vendor(q):
q = q.outerjoin(
ProductCost,
sa.and_(
ProductCost.product_uuid == Product.uuid,
ProductCost.preference == 1,
))
q = q.outerjoin(Vendor)
return q
return q.outerjoin(model.ProductCost,
sa.and_(
model.ProductCost.product_uuid == model.Product.uuid,
model.ProductCost.preference == 1))\
.outerjoin(model.Vendor)
def join_vendor_any(q):
q = q.outerjoin(
self.ProductCostAny,
self.ProductCostAny.product_uuid == Product.uuid)
q = q.outerjoin(
self.VendorAny,
self.VendorAny.uuid == self.ProductCostAny.vendor_uuid)
return q
return q.outerjoin(self.ProductCostAny,
self.ProductCostAny.product_uuid == model.Product.uuid)\
.outerjoin(self.VendorAny,
self.VendorAny.uuid == self.ProductCostAny.vendor_uuid)
return {
'brand':
lambda q: q.outerjoin(Brand),
'family':
lambda q: q.outerjoin(model.Family),
'department':
lambda q: q.outerjoin(Department,
Department.uuid == Product.department_uuid),
'subdepartment':
lambda q: q.outerjoin(Subdepartment,
Subdepartment.uuid == Product.subdepartment_uuid),
u'report_code':
lambda q: q.outerjoin(model.ReportCode),
'regular_price':
lambda q: q.outerjoin(ProductPrice,
ProductPrice.uuid == Product.regular_price_uuid),
'current_price':
lambda q: q.outerjoin(ProductPrice,
ProductPrice.uuid == Product.current_price_uuid),
'vendor':
join_vendor,
'vendor_any':
join_vendor_any,
'code':
lambda q: q.outerjoin(ProductCode),
}
g.joiners['brand'] = lambda q: q.outerjoin(model.Brand)
g.joiners['family'] = lambda q: q.outerjoin(model.Family)
g.joiners['department'] = lambda q: q.outerjoin(model.Department,
model.Department.uuid == model.Product.department_uuid)
g.joiners['subdepartment'] = lambda q: q.outerjoin(model.Subdepartment,
model.Subdepartment.uuid == model.Product.subdepartment_uuid)
g.joiners['report_code'] = lambda q: q.outerjoin(model.ReportCode)
g.joiners['regular_price'] = lambda q: q.outerjoin(model.ProductPrice,
model.ProductPrice.uuid == model.Product.regular_price_uuid)
g.joiners['current_price'] = lambda q: q.outerjoin(model.ProductPrice,
model.ProductPrice.uuid == model.Product.current_price_uuid)
g.joiners['vendor'] = join_vendor
g.joiners['vendor_any'] = join_vendor_any
g.joiners['code'] = lambda q: q.outerjoin(model.ProductCode)
def filter_map(self):
return self.make_filter_map(
ilike=['description', 'size'],
upc=self.filter_gpc(model.Product.upc),
brand=self.filter_ilike(Brand.name),
family=self.filter_ilike(model.Family.name),
department=self.filter_ilike(Department.name),
report_code=self.filter_ilike(model.ReportCode.name),
subdepartment=self.filter_ilike(Subdepartment.name),
vendor=self.filter_ilike(Vendor.name),
vendor_any=self.filter_ilike(self.VendorAny.name),
code=self.filter_ilike(ProductCode.code))
g.sorters['brand'] = g.make_sorter(model.Brand.name)
g.sorters['department'] = g.make_sorter(model.Department.name)
g.sorters['subdepartment'] = g.make_sorter(model.Subdepartment.name)
g.sorters['vendor'] = g.make_sorter(model.Vendor.name)
def filter_config(self):
return self.make_filter_config(
include_filter_upc=True,
filter_type_upc='is',
filter_label_upc="UPC",
include_filter_brand=True,
filter_type_brand='lk',
include_filter_description=True,
filter_type_description='lk',
include_filter_department=True,
filter_type_department='lk',
filter_label_vendor="Vendor (preferred)",
include_filter_vendor_any=True,
filter_label_vendor_any="Vendor (any)",
filter_type_vendor_any='lk')
g.filters['upc'].default_active = True
g.filters['upc'].default_verb = 'equal'
g.filters['upc'].label = "UPC"
g.filters['description'].default_active = True
g.filters['description'].default_verb = 'contains'
g.filters['brand'] = g.make_filter('brand', model.Brand.name,
default_active=True, default_verb='contains')
g.filters['family'] = g.make_filter('family', model.Family.name)
g.filters['department'] = g.make_filter('department', model.Department.name,
default_active=True, default_verb='contains')
g.filters['subdepartment'] = g.make_filter('subdepartment', model.Subdepartment.name)
g.filters['report_code'] = g.make_filter('report_code', model.ReportCode.name)
g.filters['vendor'] = g.make_filter('vendor', model.Vendor.name, label="Vendor (preferred)")
g.filters['vendor_any'] = g.make_filter('vendor_any', self.VendorAny.name, label="Vendor (any)")
g.filters['code'] = g.make_filter('code', model.ProductCode.code)
def sort_map(self):
return self.make_sort_map(
'upc', 'description', 'size',
brand=self.sorter(Brand.name),
department=self.sorter(Department.name),
subdepartment=self.sorter(Subdepartment.name),
regular_price=self.sorter(ProductPrice.price),
current_price=self.sorter(ProductPrice.price),
vendor=self.sorter(Vendor.name))
g.default_sortkey = 'description'
def query(self):
q = self.make_query()
if not self.request.has_perm('products.view_deleted'):
q = q.filter(model.Product.deleted == False)
q = q.options(orm.joinedload(Product.brand))
q = q.options(orm.joinedload(Product.department))
q = q.options(orm.joinedload(Product.subdepartment))
q = q.options(orm.joinedload(Product.regular_price))
q = q.options(orm.joinedload(Product.current_price))
q = q.options(orm.joinedload(Product.vendor))
return q
def grid(self):
def extra_row_class(row, i):
cls = []
if row.not_for_sale:
cls.append('not-for-sale')
if row.deleted:
cls.append('deleted')
return ' '.join(cls) if cls else None
g = self.make_grid(extra_row_class=extra_row_class)
g.upc.set(renderer=GPCFieldRenderer)
g.regular_price.set(renderer=PriceFieldRenderer)
g.current_price.set(renderer=PriceFieldRenderer)
g.upc.set(renderer=forms.renderers.GPCFieldRenderer)
g.regular_price.set(renderer=forms.renderers.PriceFieldRenderer)
g.current_price.set(renderer=forms.renderers.PriceFieldRenderer)
g.configure(
include=[
g.upc.label("UPC"),
@ -197,80 +165,68 @@ class ProductsGrid(SearchableAlchemyGridView):
],
readonly=True)
if self.request.has_perm('products.read'):
g.viewable = True
g.view_route_name = 'product.read'
if self.request.has_perm('products.update'):
g.editable = True
g.edit_route_name = 'product.update'
if self.request.has_perm('products.delete'):
g.deletable = True
g.delete_route_name = 'product.delete'
# TODO: need to check for 'print labels' permission here also
if self.print_labels:
g.more_actions.append(GridAction('print_label', icon='print'))
# Maybe add Print Label column.
if self.rattail_config.getbool('tailbone', 'products.print_labels', default=True):
q = Session.query(LabelProfile)
if q.count():
def labels(row):
return tags.link_to("Print", '#', class_='print-label')
g.add_column('labels', "Labels", labels)
def template_kwargs_index(self, **kwargs):
if self.print_labels:
kwargs['label_profiles'] = Session.query(model.LabelProfile)\
.filter(model.LabelProfile.visible == True)\
.order_by(model.LabelProfile.ordinal)\
.all()
return kwargs
return g
def row_attrs(self, row, i):
def render_kwargs(self):
q = Session.query(LabelProfile)
q = q.filter(LabelProfile.visible == True)
q = q.order_by(LabelProfile.ordinal)
return {'label_profiles': q.all()}
attrs = {'uuid': row.uuid}
classes = []
if row.not_for_sale:
classes.append('not-for-sale')
if row.deleted:
classes.append('deleted')
if classes:
attrs['class_'] = ' '.join(classes)
class ProductCrud(CrudView):
"""
Product CRUD view class.
"""
mapped_class = Product
home_route = 'products'
child_version_classes = [
(model.ProductCode, 'product_uuid'),
(model.ProductCost, 'product_uuid'),
(model.ProductPrice, 'product_uuid'),
]
return attrs
def get_model(self, key):
model = super(ProductCrud, self).get_model(key)
if model:
return model
model = Session.query(ProductPrice).get(key)
if model:
return model.product
return None
def get_instance(self):
key = self.request.matchdict['uuid']
product = Session.query(model.Product).get(key)
if product:
return product
price = Session.query(model.ProductPrice).get(key)
if price:
return price.product
raise httpexceptions.HTTPNotFound()
def fieldset(self, model):
fs = self.make_fieldset(model)
fs.upc.set(renderer=GPCFieldRenderer)
def configure_fieldset(self, fs):
fs.upc.set(renderer=forms.renderers.GPCFieldRenderer)
fs.brand.set(options=[])
fs.unit_of_measure.set(renderer=EnumFieldRenderer(enum.UNIT_OF_MEASURE))
fs.regular_price.set(renderer=PriceFieldRenderer)
fs.current_price.set(renderer=PriceFieldRenderer)
fs.unit_of_measure.set(renderer=forms.renderers.EnumFieldRenderer(enum.UNIT_OF_MEASURE))
fs.regular_price.set(renderer=forms.renderers.PriceFieldRenderer)
fs.current_price.set(renderer=forms.renderers.PriceFieldRenderer)
fs.last_sold.set(renderer=forms.renderers.DateTimeFieldRenderer(self.rattail_config))
fs.append(formalchemy.Field('current_price_ends'))
fs.current_price_ends.set(value=lambda p: p.current_price.ends if p.current_price else None)
fs.current_price_ends.set(renderer=DateTimeFieldRenderer(self.request.rattail_config))
fs.append(formalchemy.Field('current_price_ends',
value=lambda p: p.current_price.ends if p.current_price else None,
renderer=forms.renderers.DateTimeFieldRenderer(self.rattail_config)))
fs.last_sold.set(renderer=DateTimeFieldRenderer(self.request.rattail_config))
fs.configure(
include=[
fs.upc.label("UPC"),
fs.brand.with_renderer(BrandFieldRenderer),
fs.brand.with_renderer(forms.renderers.BrandFieldRenderer),
fs.description,
fs.unit_size,
fs.unit_of_measure.label("Unit of Measure"),
fs.size,
fs.weighed,
fs.case_pack,
fs.department.with_renderer(forms.DepartmentFieldRenderer),
fs.subdepartment.with_renderer(forms.SubdepartmentFieldRenderer),
fs.category.with_renderer(forms.CategoryFieldRenderer),
fs.department.with_renderer(products_forms.DepartmentFieldRenderer),
fs.subdepartment.with_renderer(products_forms.SubdepartmentFieldRenderer),
fs.category.with_renderer(products_forms.CategoryFieldRenderer),
fs.family,
fs.report_code,
fs.regular_price,
@ -285,33 +241,119 @@ class ProductCrud(CrudView):
fs.deleted,
fs.last_sold,
])
if not self.readonly:
if not self.viewing:
del fs.regular_price
del fs.current_price
if not self.request.has_perm('products.view_deleted'):
del fs.deleted
return fs
def pre_crud(self, product):
self.product_deleted = not self.creating and product.deleted
def post_crud(self, product, form):
if self.product_deleted:
self.request.session.flash("This product is marked as deleted.", 'error')
def template_kwargs(self, form):
kwargs = super(ProductCrud, self).template_kwargs(form)
def template_kwargs_view(self, **kwargs):
kwargs['image'] = False
product = form.fieldset.model
product = kwargs['instance']
if product.upc:
kwargs['image_url'] = get_image_url(
self.request.rattail_config, product.upc)
kwargs['image_path'] = get_image_path(
self.request.rattail_config, product.upc)
kwargs['image_url'] = pod.get_image_url(self.rattail_config, product.upc)
kwargs['image_path'] = pod.get_image_path(self.rattail_config, product.upc)
if os.path.exists(kwargs['image_path']):
kwargs['image'] = True
return kwargs
def edit(self):
# TODO: Should add some more/better hooks, so don't have to duplicate
# so much code here.
self.editing = True
instance = self.get_instance()
form = self.make_form(instance)
product_deleted = instance.deleted
if self.request.method == 'POST':
if form.validate():
self.save_form(form)
self.after_edit(instance)
self.request.session.flash("{} {} has been updated.".format(
self.get_model_title(), self.get_instance_title(instance)))
return self.redirect(self.get_action_url('view', instance))
if product_deleted:
self.request.session.flash("This product is marked as deleted.", 'error')
return self.render_to_response('edit', {'instance': instance,
'instance_title': self.get_instance_title(instance),
'form': form})
def make_batch(self):
if self.request.method == 'POST':
provider = self.request.POST.get('provider')
if provider:
provider = batches.get_provider(self.rattail_config, provider)
if provider:
if self.request.POST.get('params') == 'True':
provider.set_params(Session(), **self.request.POST)
else:
try:
url = self.request.route_url('batch_params.{}'.format(provider.name))
except KeyError:
pass
else:
self.request.session['referer'] = self.request.current_route_url()
return httpexceptions.HTTPFound(location=url)
progress = SessionProgress(self.request, 'products.batch')
thread = Thread(target=self.make_batch_thread, args=(provider, progress))
thread.start()
kwargs = {
'key': 'products.batch',
'cancel_url': self.request.route_url('products'),
'cancel_msg': "Batch creation was canceled.",
}
return render_to_response('/progress.mako', kwargs, request=self.request)
enabled = self.rattail_config.get('rattail.pyramid', 'batches.providers')
if enabled:
enabled = enabled.split()
providers = []
for provider in batches.iter_providers():
if not enabled or provider.name in enabled:
providers.append((provider.name, provider.description))
return {'providers': providers}
def make_batch_thread(self, provider, progress):
"""
Threat target for making a batch from current products query.
"""
session = RattailSession()
grid = self.make_grid(session=session, pageable=False,
main_actions=[], more_actions=[])
products = grid._fa_grid.rows
batch = provider.make_batch(session, products, progress)
if not batch:
session.rollback()
session.close()
return
session.commit()
session.refresh(batch)
session.close()
progress.session.load()
progress.session['complete'] = True
progress.session['success_url'] = self.request.route_url('batch.read', uuid=batch.uuid)
progress.session['success_msg'] = 'Batch "{}" has been created.'.format(batch.description)
progress.session.save()
@classmethod
def defaults(cls, config):
# make batch from product query
config.add_route('products.create_batch', '/products/batch')
config.add_view(cls, attr='make_batch', route_name='products.create_batch',
renderer='/products/batch.mako',
permission='batches.create')
cls._defaults(config)
class ProductVersionView(VersionView):
"""
@ -331,7 +373,7 @@ class ProductVersionView(VersionView):
"""
uuid = self.request.matchdict['uuid']
product = Session.query(model.Product).get(uuid)
assert product, "No product found for UUID: {0}".format(repr(uuid))
assert product, "No product found for UUID: {}".format(repr(uuid))
if product.deleted:
self.request.session.flash("This product is marked as deleted.", 'error')
@ -354,8 +396,8 @@ class ProductsAutocomplete(AutocompleteView):
def query(self, term):
q = Session.query(model.Product).outerjoin(model.Brand)
q = q.filter(sa.or_(
model.Brand.name.ilike('%{0}%'.format(term)),
model.Product.description.ilike('%{0}%'.format(term))))
model.Brand.name.ilike('%{}%'.format(term)),
model.Product.description.ilike('%{}%'.format(term))))
if not self.request.has_perm('products.view_deleted'):
q = q.filter(model.Product.deleted == False)
q = q.order_by(model.Brand.name, model.Product.description)
@ -377,11 +419,11 @@ def products_search(request):
upc = request.GET.get('upc', '').strip()
upc = re.sub(r'\D', '', upc)
if upc:
product = get_product_by_upc(Session, upc)
product = api.get_product_by_upc(Session(), upc)
if not product:
# Try again, assuming caller did not include check digit.
upc = GPC(upc, calc_check_digit='upc')
product = get_product_by_upc(Session, upc)
product = api.get_product_by_upc(Session(), upc)
if product:
if product.deleted and not request.has_perm('products.view_deleted'):
product = None
@ -396,12 +438,12 @@ def products_search(request):
def print_labels(request):
profile = request.params.get('profile')
profile = Session.query(LabelProfile).get(profile) if profile else None
profile = Session.query(model.LabelProfile).get(profile) if profile else None
if not profile:
return {'error': "Label profile not found"}
product = request.params.get('product')
product = Session.query(Product).get(product) if product else None
product = Session.query(model.Product).get(product) if product else None
if not product:
return {'error': "Product not found"}
@ -421,114 +463,19 @@ def print_labels(request):
return {}
class CreateProductsBatch(ProductsGrid):
def make_batch(self, provider, progress):
from rattail.db import Session
session = Session()
configure_session(self.request.rattail_config, session)
self._filter_config = self.filter_config()
self._sort_config = self.sort_config()
products = self.make_query(session)
batch = provider.make_batch(session, products, progress)
if not batch:
session.rollback()
session.close()
return
session.commit()
session.refresh(batch)
session.close()
progress.session.load()
progress.session['complete'] = True
progress.session['success_url'] = self.request.route_url('batch.read', uuid=batch.uuid)
progress.session['success_msg'] = "Batch \"%s\" has been created." % batch.description
progress.session.save()
def __call__(self):
if self.request.POST:
provider = self.request.POST.get('provider')
if provider:
provider = batches.get_provider(self.request.rattail_config, provider)
if provider:
if self.request.POST.get('params') == 'True':
provider.set_params(Session(), **self.request.POST)
else:
try:
url = self.request.route_url('batch_params.%s' % provider.name)
except KeyError:
pass
else:
self.request.session['referer'] = self.request.current_route_url()
return HTTPFound(location=url)
progress = SessionProgress(self.request, 'products.batch')
thread = Thread(target=self.make_batch, args=(provider, progress))
thread.start()
kwargs = {
'key': 'products.batch',
'cancel_url': self.request.route_url('products'),
'cancel_msg': "Batch creation was canceled.",
}
return render_to_response('/progress.mako', kwargs, request=self.request)
enabled = self.request.rattail_config.get('rattail.pyramid', 'batches.providers')
if enabled:
enabled = enabled.split()
providers = []
for provider in batches.iter_providers():
if not enabled or provider.name in enabled:
providers.append((provider.name, provider.description))
return {'providers': providers}
def add_routes(config):
config.add_route('products', '/products')
config.add_route('products.autocomplete', '/products/autocomplete')
config.add_route('products.search', '/products/search')
config.add_route('products.print_labels', '/products/labels')
config.add_route('products.create_batch', '/products/batch')
config.add_route('product.create', '/products/new')
config.add_route('product.read', '/products/{uuid}')
config.add_route('product.update', '/products/{uuid}/edit')
config.add_route('product.delete', '/products/{uuid}/delete')
def includeme(config):
add_routes(config)
config.add_view(ProductsGrid, route_name='products',
renderer='/products/index.mako',
permission='products.list')
config.add_route('products.autocomplete', '/products/autocomplete')
config.add_view(ProductsAutocomplete, route_name='products.autocomplete',
renderer='json',
permission='products.list')
renderer='json', permission='products.list')
config.add_route('products.print_labels', '/products/labels')
config.add_view(print_labels, route_name='products.print_labels',
renderer='json', permission='products.print_labels')
config.add_view(CreateProductsBatch, route_name='products.create_batch',
renderer='/products/batch.mako',
permission='batches.create')
config.add_view(ProductCrud, attr='create', route_name='product.create',
renderer='/products/crud.mako',
permission='products.create')
config.add_view(ProductCrud, attr='read', route_name='product.read',
renderer='/products/read.mako',
permission='products.read')
config.add_view(ProductCrud, attr='update', route_name='product.update',
renderer='/products/crud.mako',
permission='products.update')
config.add_view(ProductCrud, attr='delete', route_name='product.delete',
permission='products.delete')
config.add_route('products.search', '/products/search')
config.add_view(products_search, route_name='products.search',
renderer='json', permission='products.list')
ProductsView.defaults(config)
version_defaults(config, ProductVersionView, 'product')