Add better support for "make import batch from file" pattern
This commit is contained in:
parent
0375d66b91
commit
4fa9ab3c6e
|
@ -2,7 +2,7 @@
|
||||||
################################################################################
|
################################################################################
|
||||||
#
|
#
|
||||||
# Rattail -- Retail Software Framework
|
# Rattail -- Retail Software Framework
|
||||||
# Copyright © 2010-2017 Lance Edgar
|
# Copyright © 2010-2018 Lance Edgar
|
||||||
#
|
#
|
||||||
# This file is part of Rattail.
|
# This file is part of Rattail.
|
||||||
#
|
#
|
||||||
|
@ -28,4 +28,4 @@ from __future__ import unicode_literals, absolute_import
|
||||||
|
|
||||||
from . import types
|
from . import types
|
||||||
from . import widgets
|
from . import widgets
|
||||||
from .core import Form
|
from .core import Form, SimpleFileImport
|
||||||
|
|
|
@ -881,3 +881,21 @@ class FieldList(list):
|
||||||
def insert_after(self, field, newfield):
|
def insert_after(self, field, newfield):
|
||||||
i = self.index(field)
|
i = self.index(field)
|
||||||
self.insert(i + 1, newfield)
|
self.insert(i + 1, newfield)
|
||||||
|
|
||||||
|
|
||||||
|
@colander.deferred
|
||||||
|
def upload_widget(node, kw):
|
||||||
|
request = kw['request']
|
||||||
|
tmpstore = SessionFileUploadTempStore(request)
|
||||||
|
return dfwidget.FileUploadWidget(tmpstore)
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleFileImport(colander.Schema):
|
||||||
|
"""
|
||||||
|
Schema for simple file import. Note that you must bind your ``request``
|
||||||
|
object to this schema, i.e.::
|
||||||
|
|
||||||
|
schema = SimpleFileImport().bind(request=request)
|
||||||
|
"""
|
||||||
|
filename = colander.SchemaNode(deform.FileData(),
|
||||||
|
widget=upload_widget)
|
||||||
|
|
|
@ -79,7 +79,7 @@
|
||||||
${self.context_menu_items()}
|
${self.context_menu_items()}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
% if status_breakdown is not Undefined:
|
% if status_breakdown is not Undefined and status_breakdown is not None:
|
||||||
<div class="object-helper">
|
<div class="object-helper">
|
||||||
<h3>Row Status Breakdown</h3>
|
<h3>Row Status Breakdown</h3>
|
||||||
<div class="object-helper-content">
|
<div class="object-helper-content">
|
||||||
|
|
6
tailbone/templates/master/import_file.mako
Normal file
6
tailbone/templates/master/import_file.mako
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
## -*- coding: utf-8; -*-
|
||||||
|
<%inherit file="/master/create.mako" />
|
||||||
|
|
||||||
|
<%def name="title()">Import ${model_title_plural} from ${importer_host_title}</%def>
|
||||||
|
|
||||||
|
${parent.body()}
|
|
@ -103,15 +103,23 @@ class ImporterBatchView(BatchMasterView):
|
||||||
f.set_readonly('importer_key')
|
f.set_readonly('importer_key')
|
||||||
f.set_readonly('row_table')
|
f.set_readonly('row_table')
|
||||||
|
|
||||||
|
def make_status_breakdown(self, batch):
|
||||||
|
# TODO: should implement this, just can't use batch.data_rows apparently
|
||||||
|
pass
|
||||||
|
|
||||||
def delete_instance(self, batch):
|
def delete_instance(self, batch):
|
||||||
self.make_row_table(batch.row_table)
|
self.make_row_table(batch.row_table)
|
||||||
|
if self.current_row_table is not None:
|
||||||
self.current_row_table.drop()
|
self.current_row_table.drop()
|
||||||
super(ImporterBatchView, self).delete_instance(batch)
|
super(ImporterBatchView, self).delete_instance(batch)
|
||||||
|
|
||||||
def make_row_table(self, name):
|
def make_row_table(self, name):
|
||||||
if not hasattr(self, 'current_row_table'):
|
if not hasattr(self, 'current_row_table'):
|
||||||
metadata = sa.MetaData(schema='batch', bind=self.Session.bind)
|
metadata = sa.MetaData(schema='batch', bind=self.Session.bind)
|
||||||
|
try:
|
||||||
self.current_row_table = sa.Table(name, metadata, autoload=True)
|
self.current_row_table = sa.Table(name, metadata, autoload=True)
|
||||||
|
except sa.exc.NoSuchTableError:
|
||||||
|
self.current_row_table = None
|
||||||
|
|
||||||
def get_row_data(self, batch):
|
def get_row_data(self, batch):
|
||||||
self.make_row_table(batch.row_table)
|
self.make_row_table(batch.row_table)
|
||||||
|
|
|
@ -89,6 +89,7 @@ class MasterView(View):
|
||||||
execute_progress_template = None
|
execute_progress_template = None
|
||||||
execute_progress_initial_msg = None
|
execute_progress_initial_msg = None
|
||||||
supports_prev_next = False
|
supports_prev_next = False
|
||||||
|
supports_import_batch_from_file = False
|
||||||
|
|
||||||
supports_mobile = False
|
supports_mobile = False
|
||||||
mobile_creatable = False
|
mobile_creatable = False
|
||||||
|
@ -705,6 +706,10 @@ class MasterView(View):
|
||||||
if isinstance(node.typ, deform.FileData):
|
if isinstance(node.typ, deform.FileData):
|
||||||
if skip and node.name in skip:
|
if skip and node.name in skip:
|
||||||
continue
|
continue
|
||||||
|
# TODO: does form ever *not* have 'validated' attr here?
|
||||||
|
if hasattr(form, 'validated'):
|
||||||
|
filedict = form.validated.get(node.name)
|
||||||
|
else:
|
||||||
filedict = self.form_deserialized.get(node.name)
|
filedict = self.form_deserialized.get(node.name)
|
||||||
if filedict:
|
if filedict:
|
||||||
tempdir = tempfile.mkdtemp()
|
tempdir = tempfile.mkdtemp()
|
||||||
|
@ -722,6 +727,38 @@ class MasterView(View):
|
||||||
def process_uploads(self, obj, form, uploads):
|
def process_uploads(self, obj, form, uploads):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def import_batch_from_file(self, handler_factory, model_name,
|
||||||
|
delete=False, schema=None, importer_host_title=None):
|
||||||
|
|
||||||
|
handler = handler_factory(self.rattail_config)
|
||||||
|
|
||||||
|
if not schema:
|
||||||
|
schema = forms.SimpleFileImport().bind(request=self.request)
|
||||||
|
form = forms.Form(schema=schema, request=self.request)
|
||||||
|
form.save_label = "Upload"
|
||||||
|
form.cancel_url = self.get_index_url()
|
||||||
|
if form.validate(newstyle=True):
|
||||||
|
|
||||||
|
uploads = self.normalize_uploads(form)
|
||||||
|
filepath = uploads['filename']['temp_path']
|
||||||
|
batches = handler.make_batches(model_name,
|
||||||
|
delete=delete,
|
||||||
|
# tdc_input_path=filepath,
|
||||||
|
# source_csv_path=filepath,
|
||||||
|
source_data_path=filepath,
|
||||||
|
runas_user=self.request.user)
|
||||||
|
batch = batches[0]
|
||||||
|
return self.redirect(self.request.route_url('batch.importer.view', uuid=batch.uuid))
|
||||||
|
|
||||||
|
if not importer_host_title:
|
||||||
|
importer_host_title = handler.host_title
|
||||||
|
|
||||||
|
return self.render_to_response('import_file', {
|
||||||
|
'form': form,
|
||||||
|
'dform': form.make_deform_form(),
|
||||||
|
'importer_host_title': importer_host_title,
|
||||||
|
})
|
||||||
|
|
||||||
def render_product_key_value(self, obj):
|
def render_product_key_value(self, obj):
|
||||||
"""
|
"""
|
||||||
Render the "canonical" product key value for the given object.
|
Render the "canonical" product key value for the given object.
|
||||||
|
@ -3087,6 +3124,11 @@ class MasterView(View):
|
||||||
config.add_tailbone_permission(permission_prefix, '{0}.delete'.format(permission_prefix),
|
config.add_tailbone_permission(permission_prefix, '{0}.delete'.format(permission_prefix),
|
||||||
"Delete {0}".format(model_title))
|
"Delete {0}".format(model_title))
|
||||||
|
|
||||||
|
# import batch from file
|
||||||
|
if cls.supports_import_batch_from_file:
|
||||||
|
config.add_tailbone_permission(permission_prefix, '{}.import_file'.format(permission_prefix),
|
||||||
|
"Create a new import batch from data file")
|
||||||
|
|
||||||
### sub-rows stuff follows
|
### sub-rows stuff follows
|
||||||
|
|
||||||
# download row results as CSV
|
# download row results as CSV
|
||||||
|
|
Loading…
Reference in a new issue