Add basic "auto-execute" logic for new batches created via filemon

this is for the sake of label batches in particular, when deploying several
copies from host to all store nodes
This commit is contained in:
Lance Edgar 2017-11-29 18:22:50 -06:00
parent 76b8856854
commit 827d8cf924
3 changed files with 44 additions and 8 deletions

View file

@ -31,6 +31,7 @@ import logging
from rattail.db import Session, model from rattail.db import Session, model
from rattail.config import parse_bool from rattail.config import parse_bool
from rattail.filemon import Action from rattail.filemon import Action
from rattail.time import make_utc
from rattail.util import load_object from rattail.util import load_object
@ -43,7 +44,9 @@ class MakeBatch(Action):
handles the simple case of creating a batch with a single input data file. handles the simple case of creating a batch with a single input data file.
""" """
def __call__(self, path, batch_type='', handler='', user='', delete_if_empty='false', **kwargs): def __call__(self, path, batch_type='', handler='', user='',
delete_if_empty='false', auto_execute_allowed='true',
**kwargs):
""" """
Make a batch from the given file path. Make a batch from the given file path.
@ -61,13 +64,20 @@ class MakeBatch(Action):
:param delete_if_empty: Set this to 'true' if you wish the batch to be :param delete_if_empty: Set this to 'true' if you wish the batch to be
auto-deleted in the event that it has no data rows, after being auto-deleted in the event that it has no data rows, after being
initially populated from the given data file. initially populated from the given data file.
:param auto_execute_allowed: Set this to 'false' if you wish to disable
auto-execution of new batches. Default behavior is for the handler
to decide whether each batch is eligible for auto-execution. Note
that it is not possible to "force" auto-execution of a batch; you
may only "allow" or "prevent" it.
""" """
if not handler: if not handler:
assert batch_type assert batch_type
handler = self.config.require('rattail.batch', '{}.handler'.format(batch_type)) handler = self.config.require('rattail.batch', '{}.handler'.format(batch_type))
handler = load_object(handler)(self.config) handler = load_object(handler)(self.config)
session = Session() session = Session()
kwargs['created_by'] = session.query(model.User).filter_by(username=user).one() user = session.query(model.User).filter_by(username=user).one()
kwargs['created_by'] = user
batch = handler.make_batch(session, **kwargs) batch = handler.make_batch(session, **kwargs)
handler.set_input_file(batch, path) handler.set_input_file(batch, path)
handler.populate(batch) handler.populate(batch)
@ -79,6 +89,12 @@ class MakeBatch(Action):
log.debug("auto-deleting empty '{}' batch: {}".format(handler.batch_key, batch)) log.debug("auto-deleting empty '{}' batch: {}".format(handler.batch_key, batch))
batch.delete_data(self.config) batch.delete_data(self.config)
session.delete(batch) session.delete(batch)
batch = None
if batch and parse_bool(auto_execute_allowed) and handler.auto_executable(batch):
handler.execute(batch, user=user)
batch.executed = make_utc()
batch.executed_by = user
session.commit() session.commit()
session.close() session.close()

View file

@ -308,6 +308,13 @@ class BatchHandler(object):
return True return True
return not bool(batch.executed) return not bool(batch.executed)
def auto_executable(self, batch):
"""
Must return a boolean indicating whether the given bath is eligible for
"automatic" execution, i.e. immediately after batch is created.
"""
return False
def execute(self, batch, progress=None, **kwargs): def execute(self, batch, progress=None, **kwargs):
""" """
Execute the given batch, with given progress and kwargs. That is an Execute the given batch, with given progress and kwargs. That is an

View file

@ -37,6 +37,7 @@ from rattail import enum
from rattail.db import model, api from rattail.db import model, api
from rattail.gpc import GPC from rattail.gpc import GPC
from rattail.batch import BatchHandler from rattail.batch import BatchHandler
from rattail.csvutil import UnicodeDictReader
from rattail.util import progress_loop from rattail.util import progress_loop
from rattail.time import make_utc from rattail.time import make_utc
from rattail.config import parse_bool from rattail.config import parse_bool
@ -67,6 +68,15 @@ class LabelBatchHandler(BatchHandler):
self.calc_check_digit = parse_bool(self.calc_check_digit) self.calc_check_digit = parse_bool(self.calc_check_digit)
return super(LabelBatchHandler, self).make_batch(session, progress, **kwargs) return super(LabelBatchHandler, self).make_batch(session, progress, **kwargs)
def auto_executable(self, batch):
"""
Must return a boolean indicating whether the given bath is eligible for
"automatic" execution, i.e. immediately after batch is created.
"""
if batch.filename and '.autoexecute.' in batch.filename:
return True
return False
def populate(self, batch, progress=None): def populate(self, batch, progress=None):
""" """
Pre-fill batch with row data from handheld batch, etc. Pre-fill batch with row data from handheld batch, etc.
@ -117,14 +127,17 @@ class LabelBatchHandler(BatchHandler):
with open(path, 'rb') as f: with open(path, 'rb') as f:
if self.skip_first_line: if self.skip_first_line:
f.readline() f.readline()
reader = csv.reader(f) reader = csv.reader(f)
data = list(reader) data = [{'upc': row[0]} for row in reader]
else:
reader = UnicodeDictReader(f)
data = list(reader)
products = [] products = []
session = orm.object_session(batch) session = orm.object_session(batch)
def append(entry, i): def append(entry, i):
upc = entry[0].strip() upc = entry['upc'].strip()
if upc: if upc:
upc = GPC(upc, calc_check_digit=self.calc_check_digit) upc = GPC(upc, calc_check_digit=self.calc_check_digit)
product = api.get_product_by_upc(session, upc) product = api.get_product_by_upc(session, upc)
@ -133,9 +146,9 @@ class LabelBatchHandler(BatchHandler):
else: else:
log.warning("product not found: {}".format(upc)) log.warning("product not found: {}".format(upc))
if self.progress_loop(append, data, progress, self.progress_loop(append, data, progress,
message="Reading data from CSV file"): message="Reading data from CSV file")
return products return products
def get_label_profile(self, session): def get_label_profile(self, session):
code = self.config.get('rattail.batch', 'labels.default_code') code = self.config.get('rattail.batch', 'labels.default_code')