diff --git a/tailbone/forms2/core.py b/tailbone/forms2/core.py index dc44e7f4..2832d0aa 100644 --- a/tailbone/forms2/core.py +++ b/tailbone/forms2/core.py @@ -326,7 +326,9 @@ class Form(object): widgets={}, defaults={}, validators={}, required={}, helptext={}, action_url=None, cancel_url=None): - self.fields = FieldList(fields) if fields is not None else None + self.fields = None + if fields is not None: + self.set_fields(fields) self.schema = schema self.request = request self.readonly = readonly @@ -336,7 +338,7 @@ class Form(object): if self.model_instance and not self.model_class: self.model_class = type(self.model_instance) if self.model_class and self.fields is None: - self.fields = self.make_fields() + self.set_fields(self.make_fields()) self.nodes = nodes or {} self.enums = enums or {} self.labels = labels or {} @@ -352,6 +354,9 @@ class Form(object): self.action_url = action_url self.cancel_url = cancel_url + def set_fields(self, fields): + self.fields = FieldList(fields) + def make_fields(self): """ Return a default list of fields, based on :attr:`model_class`. diff --git a/tailbone/views/batch/core3.py b/tailbone/views/batch/core3.py index 69694f4e..6f37e648 100644 --- a/tailbone/views/batch/core3.py +++ b/tailbone/views/batch/core3.py @@ -27,9 +27,13 @@ Base views for maintaining batches from __future__ import unicode_literals, absolute_import import os +import tempfile import six - +import colander +import deform +from deform import widget as dfwidget +from pyramid_deform import SessionFileUploadTempStore from webhelpers2.html import tags from tailbone.views import MasterView3 @@ -46,8 +50,7 @@ class BatchMasterView3(MasterView3, BatchMasterView2): 'created', 'created_by', 'rowcount', - 'cognized', - 'cognized_by', + 'status_code', 'executed', 'executed_by', 'purge', @@ -113,7 +116,8 @@ class BatchMasterView3(MasterView3, BatchMasterView2): def save_create_form(self, form): self.before_create(form) - with self.Session.no_autoflush: + session = self.Session() + with session.no_autoflush: # transfer form data to batch instance batch = self.objectify(form, self.form_deserialized) @@ -121,23 +125,35 @@ class BatchMasterView3(MasterView3, BatchMasterView2): # current user is batch creator batch.created_by = self.request.user or self.late_login_user() + # obtain kwargs for making batch via handler, below + kwargs = self.get_batch_kwargs(batch) + + # TODO: this needs work yet surely... + filedict = kwargs.pop('filename', None) + filepath = None + if filedict: + kwargs['filename'] = '' # null not allowed + tempdir = tempfile.mkdtemp() + filepath = os.path.join(tempdir, filedict['filename']) + tmpinfo = form.deform_form['filename'].widget.tmpstore.get(filedict['uid']) + tmpdata = tmpinfo['fp'].read() + with open(filepath, 'wb') as f: + f.write(tmpdata) + # TODO: is this still necessary with colander? # destroy initial batch and re-make using handler - kwargs = self.get_batch_kwargs(batch) # if batch in self.Session: # self.Session.expunge(batch) - batch = self.handler.make_batch(self.Session(), **kwargs) + batch = self.handler.make_batch(session, **kwargs) self.Session.flush() # TODO: this needs work yet surely... # if batch has input data file, let handler properly establish that - filename = getattr(batch, 'filename', None) - if filename: - path = os.path.join(self.upload_dir, filename) - if os.path.exists(path): - self.handler.set_input_file(batch, path) - os.remove(path) + if filedict: + self.handler.set_input_file(batch, filepath) + os.remove(filepath) + os.rmdir(tempdir) return batch @@ -175,11 +191,11 @@ class FileBatchMasterView3(BatchMasterView3, FileBatchMasterView2): if self.editing: f.set_readonly('filename') - # if creating, let filename be our only field by default if self.creating: - f.fields = [ - 'filename', - ] + if 'filename' not in f.fields: + f.fields.insert(0, 'filename') + tmpstore = SessionFileUploadTempStore(self.request) + f.set_node('filename', colander.SchemaNode(deform.FileData(), widget=dfwidget.FileUploadWidget(tmpstore))) def render_filename(self, batch, field): path = batch.filepath(self.rattail_config, filename=batch.filename) diff --git a/tailbone/views/handheld.py b/tailbone/views/handheld.py index 4487c6a3..bea1c378 100644 --- a/tailbone/views/handheld.py +++ b/tailbone/views/handheld.py @@ -2,7 +2,7 @@ ################################################################################ # # Rattail -- Retail Software Framework -# Copyright © 2010-2017 Lance Edgar +# Copyright © 2010-2018 Lance Edgar # # This file is part of Rattail. # @@ -28,17 +28,15 @@ from __future__ import unicode_literals, absolute_import import os -from rattail import enum from rattail.db import model from rattail.util import OrderedDict -import formalchemy as fa import formencode as fe from webhelpers2.html import tags from tailbone import forms from tailbone.db import Session -from tailbone.views.batch import FileBatchMasterView2 as FileBatchMasterView +from tailbone.views.batch import FileBatchMasterView3 as FileBatchMasterView ACTION_OPTIONS = OrderedDict([ @@ -53,21 +51,6 @@ class ExecutionOptions(fe.Schema): action = fe.validators.OneOf(ACTION_OPTIONS) -class InventoryBatchFieldRenderer(fa.FieldRenderer): - """ - Renderer for handheld batch's "inventory batch" field. - """ - - def render_readonly(self, **kwargs): - batch = self.raw_value - if batch: - return tags.link_to( - batch.id_str, - self.request.route_url('batch.inventory.view', uuid=batch.uuid)) - return '' - - - class HandheldBatchView(FileBatchMasterView): """ Master view for handheld batches. @@ -96,6 +79,19 @@ class HandheldBatchView(FileBatchMasterView): 'executed', ] + form_fields = [ + 'id', + 'device_type', + 'device_name', + 'filename', + 'created', + 'created_by', + 'rowcount', + 'status_code', + 'executed', + 'executed_by', + ] + row_grid_columns = [ 'sequence', 'upc', @@ -117,38 +113,35 @@ class HandheldBatchView(FileBatchMasterView): if batch.status_code is not None and batch.status_code != batch.STATUS_OK: return 'notice' - def _preconfigure_fieldset(self, fs): - super(HandheldBatchView, self)._preconfigure_fieldset(fs) + def configure_form(self, f): + super(HandheldBatchView, self).configure_form(f) + batch = f.model_instance + + # device_type device_types = OrderedDict(sorted(self.enum.HANDHELD_DEVICE_TYPE.items(), key=lambda item: item[1])) - fs.device_type.set(renderer=forms.renderers.EnumFieldRenderer(device_types)) + f.set_enum('device_type', device_types) + f.widgets['device_type'].values.insert(0, ('', "(none)")) - def configure_fieldset(self, fs): if self.creating: - fs.configure( - include=[ - fs.filename, - fs.device_type, - fs.device_name, - ]) + f.set_fields([ + 'filename', + 'device_type', + 'device_name', + ]) - else: - fs.configure( - include=[ - fs.id, - fs.device_type, - fs.device_name, - fs.filename, - fs.created, - fs.created_by, - fs.rowcount, - fs.status_code, - fs.executed, - fs.executed_by, - ]) + if self.viewing: + if batch.inventory_batch: + f.append('inventory_batch') + f.set_renderer('inventory_batch', self.render_inventory_batch) - if self.viewing and fs.model.inventory_batch: - fs.append(fa.Field('inventory_batch', value=fs.model.inventory_batch, renderer=InventoryBatchFieldRenderer)) + def render_inventory_batch(self, handheld_batch, field): + batch = handheld_batch.inventory_batch + if not batch: + return "" + text = batch.id_str + url = self.request.route_url('batch.inventory.view', uuid=batch.uuid) + return tags.link_to(text, url) def get_batch_kwargs(self, batch): kwargs = super(HandheldBatchView, self).get_batch_kwargs(batch)