printer install-list/pc-default format=text

This commit is contained in:
2026-07-29 13:51:01 -04:00
parent 366f49c969
commit dd0865d103
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,

View File

@@ -38,6 +38,54 @@ def test_install_list_returns_network_printer_with_map(client, db, auth_headers,
assert row['mapx'] == 120 and row['mapy'] == 240
def test_install_list_vendorname_resolves_via_model(client, db, auth_headers,
printer_assettype):
"""vendorname falls back to the model's vendor when the printer has no
direct vendorid (the import sets the model, not the printer's vendor). The
Inno installer filters on this, so it must not be null on real data."""
from shopdb.core.models import Vendor, Model
hp = Vendor(vendor='HP')
db.session.add(hp)
db.session.commit()
model = Model(modelnumber='LaserJet M607', vendorid=hp.vendorid)
db.session.add(model)
db.session.commit()
client.post('/api/printers', json={
'assetnumber': 'CSF09-2022-HP', 'hostname': 'wjprn09',
'modelnumberid': model.modelnumberid,
}, headers=auth_headers)
resp = client.get('/api/printers/install-list', headers=auth_headers)
row = next((r for r in resp.get_json()['data']
if r['machinenumber'] == 'CSF09-2022-HP'), None)
assert row is not None
assert row['vendorname'] == 'HP'
def test_install_list_text_format(client, db, auth_headers, printer_assettype):
"""format=text returns one pipe-delimited line per printer (fixed field
order) so the Inno installers split() instead of parsing JSON."""
client.post('/api/printers', json={
'assetnumber': 'CSF01-HP', 'windowsname': 'CSF01-HP', 'hostname': 'wjprn01',
'mapx': 120, 'mapy': 240,
}, headers=auth_headers)
resp = client.get('/api/printers/install-list?format=text', headers=auth_headers)
assert resp.status_code == 200
assert resp.mimetype == 'text/plain'
line = next((l for l in resp.get_data(as_text=True).splitlines()
if 'CSF01-HP' in l), None)
assert line is not None
cols = line.split('|')
# printerid|windowsname|vendorname|modelnumber|hostname|ipaddress|mapx|mapy
assert len(cols) == 8
assert cols[1] == 'CSF01-HP' # windowsname (falls back to assetnumber name)
assert cols[4] == 'wjprn01' # hostname
assert cols[6] == '120' and cols[7] == '240'
def test_install_list_excludes_usb_only_printer(client, db, auth_headers,
printer_assettype):
"""A printer with no hostname and no network IP is excluded."""