Send tape jobs paced, and carry the manual grouping through

Print, preview, PDF and the redirect back all honour a groups= spec, so
a hand-built label survives printing instead of reverting to drawers.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vunz4VLxAYDBnU5tLciLDC
This commit is contained in:
jessikittyandClaude Opus 5 committed 2026-10-13 10:28:09 +11:00
1 parent deb009b80e
commit fb59530f0e
1 file changed
+30 -8
+30 -8
View File
@@ -46,7 +46,15 @@ def printer_config():
}
def _options_query(options, labels):
def _group_spec():
"""
The manual grouping from the page: "1,2|3" means parts 1 and 2 share a
label and part 3 is on its own. Empty means "work it out from drawers".
"""
return (request.values.get('groups') or '').strip()
def _options_query(options, labels, group_spec=None):
"""
The chosen options as query args, so a redirect back to the tape page
comes back exactly as the user left it. Without this, printing bounced
@@ -65,6 +73,8 @@ def _options_query(options, labels):
'align': options.align,
'fields': options.fields,
}
if group_spec:
query['groups'] = group_spec
# Checkboxes: only present when on, which is what the parser expects.
if options.rotate:
query['rotate'] = '1'
@@ -115,13 +125,16 @@ def index():
source, rotate_default=config['rotate'],
size_default=config['size_mode'], tape_default=config['tape'])
plan = tape_printer.plan_groups(labels, options)
group_spec = _group_spec()
plan = tape_printer.plan_groups(labels, options, group_spec)
tape = tape_printer.get_tape(options.tape)
return render_template(
'admin/labels/tape.html',
labels=labels,
plan=plan,
group_spec=tape_printer.spec_from_groups(
[item['labels'] for item in plan]),
ids=','.join(str(l.id) for l in labels),
options=options,
tapes=tape_printer.TAPE_PRESETS,
@@ -235,15 +248,18 @@ def print_tape():
except ipp_client.IPPError as exc:
flash(f'Could not reach the printer: {exc}', 'danger')
return redirect(url_for('tape.index',
**_options_query(options, labels)))
**_options_query(options, labels,
_group_spec())))
if not mime_type:
flash('The printer did not offer a format LIM can produce. Use '
'Download PDF instead.', 'warning')
return redirect(url_for('tape.index',
**_options_query(options, labels)))
**_options_query(options, labels,
_group_spec())))
upload_folder = current_app.config['UPLOAD_FOLDER']
plan = tape_printer.plan_groups(labels, options)
group_spec = _group_spec()
plan = tape_printer.plan_groups(labels, options, group_spec)
if options.is_fixed and not tape_printer.tape_fits_label(options.tape):
flash(f'{tape_printer.LABEL_WIDTH_MM:.0f}mm labels need 50mm tape - '
@@ -261,8 +277,13 @@ def print_tape():
document = tape_printer.image_to_bytes(image, mime_type)
# copies is handled by repeating the job rather than the IPP
# copies attribute: tape printers vary wildly in honouring it.
#
# Paced rather than fired in a burst: the VC-500W takes one job
# at a time and answers "busy" to anything arriving while the
# tape is still moving, so each job waits for the printer to go
# idle and retries while it is still working.
for _ in range(options.copies):
ipp_client.print_job(
ipp_client.print_job_paced(
host, document, mime_type,
job_name=f'LIM: {name}'[:120],
port=config['port'], path=config['path'])
@@ -279,7 +300,8 @@ def print_tape():
for problem in failures[:5]:
flash(problem, 'danger')
return redirect(url_for('tape.index', **_options_query(options, labels)))
return redirect(url_for('tape.index',
**_options_query(options, labels, group_spec)))
@tape_bp.route('/download.pdf', methods=['GET', 'POST'])
@@ -298,7 +320,7 @@ def download_pdf():
source, rotate_default=config['rotate'],
size_default=config['size_mode'], tape_default=config['tape'])
buffer = tape_printer.render_batch_pdf(
labels, options, current_app.config['UPLOAD_FOLDER'])
labels, options, current_app.config['UPLOAD_FOLDER'], _group_spec())
if buffer is None:
flash('Nothing to render.', 'warning')
return redirect(url_for('labels.index'))