printers: add format=text to install-list + pc-default; vendor via model

The Inno printer installers hand-parsed JSON in Pascal (brittle brace-counting).
Add ?format=text to install-list (one printer per line, pipe-delimited:
printerid|windowsname|vendorname|modelnumber|hostname|ipaddress|mapx|mapy) and
to pc-default (printerid|windowsname), so the installer side is a split() with
no JSON parser. The web map keeps the default JSON.

Also resolve install-list's vendorname via the model (as the batch already does),
since the import sets the model, not the printer's direct vendorid - otherwise
the installers' HP/Xerox/Brother filter drops every prod printer.
This commit is contained in:
cproudlock
2026-07-29 13:51:01 -04:00
parent cb075a278f
commit ad84c9060a
2 changed files with 87 additions and 5 deletions

View File

@@ -343,7 +343,7 @@ def printer_install_list():
'sharename': printer.sharename,
'hostname': printer.hostname,
'ipaddress': ipaddress,
'vendorname': data.get('vendorname'),
'vendorname': _printer_vendor(printer),
'modelnumber': data.get('modelname'),
'installpath': printer.installpath,
'iscsf': printer.iscsf,
@@ -352,6 +352,15 @@ def printer_install_list():
'mapy': asset.mapy,
})
# A pipe-delimited text variant for the Inno installers: one printer per
# line, fixed field order, so the Pascal side is a split() instead of a
# hand-rolled JSON parser. The web map uses the default JSON.
if request.args.get('format') == 'text':
fields = ('printerid', 'windowsname', 'vendorname', 'modelnumber',
'hostname', 'ipaddress', 'mapx', 'mapy')
lines = [_text_line(row, fields) for row in rows]
return Response('\n'.join(lines), mimetype='text/plain')
return success_response(rows)
@@ -378,6 +387,17 @@ def _batch_base_url():
return 'https://%s%s' % (host, root)
def _text_line(row, fields):
"""One pipe-delimited line for the installer text format. None -> empty;
any pipe/newline in a value is neutralized so the field count stays fixed."""
parts = []
for key in fields:
value = row.get(key)
text = '' if value is None else str(value)
parts.append(text.replace('|', ' ').replace('\r', ' ').replace('\n', ' '))
return '|'.join(parts)
def _printer_vendor(printer):
"""Vendor name for install grouping. The legacy import sets the printer's
model but not its direct vendorid, so resolve via the model's vendor (as the
@@ -560,14 +580,21 @@ def pc_default_printer():
Returns {printerid, windowsname}, or {} when the machine is unknown or has
no active default printer set.
"""
as_text = request.args.get('format') == 'text'
def _empty():
# Text variant returns an empty body (no default) so the installer's
# split yields nothing; JSON keeps the {} contract.
return Response('', mimetype='text/plain') if as_text else success_response({})
machine = (request.args.get('machine') or '').strip()
if not machine:
return success_response({})
return _empty()
pc = Asset.query.filter_by(assetnumber=machine, isactive=True).first()
dp_type = RelationshipType.query.filter_by(relationshiptype='defaultprinter').first()
if not pc or not dp_type:
return success_response({})
return _empty()
rel = AssetRelationship.query.filter_by(
sourceassetid=pc.assetid,
@@ -575,14 +602,21 @@ def pc_default_printer():
isactive=True,
).first()
if not rel:
return success_response({})
return _empty()
printer = db.session.query(Printer).join(Asset).filter(
Printer.assetid == rel.targetassetid,
Asset.isactive == True,
).first()
if not printer:
return success_response({})
return _empty()
if as_text:
return Response(
_text_line({'printerid': printer.printerid,
'windowsname': printer.windowsname},
('printerid', 'windowsname')),
mimetype='text/plain')
return success_response({
'printerid': printer.printerid,