search: asset lists search the type column they display
Every asset list shows a Type column (and printers a Model, machines and network a Vendor), but the search filters only looked at the asset number, name, serial and hostname. Searching a type returned zero rows: 'Part Washer' on machines, 'Standard' on PCs, 'Thermal' on printers. Extend the search on machines, computers, printers, network devices, measuring tools and the unified asset list to cover the type name plus the vendor/model where the list shows them. Joins are outer joins so an asset missing a type or vendor still matches on its own fields; the core list uses a correlated EXISTS instead, since its type-name filter already joins AssetType.
This commit is contained in:
@@ -363,14 +363,20 @@ def list_computers():
|
||||
if exactassetnumber := request.args.get('assetnumber'):
|
||||
query = query.filter(Asset.assetnumber == exactassetnumber)
|
||||
|
||||
# Search filter
|
||||
# Search filter. Covers the type name too - it is a column in the list, so
|
||||
# searching 'Standard' must find the PCs of that type. Outer join so a PC
|
||||
# with no type still matches on its own fields.
|
||||
if search := request.args.get('search'):
|
||||
query = query.filter(
|
||||
pattern = f'%{search}%'
|
||||
query = query.outerjoin(
|
||||
ComputerType, Computer.computertypeid == ComputerType.computertypeid
|
||||
).filter(
|
||||
db.or_(
|
||||
Asset.assetnumber.ilike(f'%{search}%'),
|
||||
Asset.name.ilike(f'%{search}%'),
|
||||
Asset.serialnumber.ilike(f'%{search}%'),
|
||||
Computer.hostname.ilike(f'%{search}%')
|
||||
Asset.assetnumber.ilike(pattern),
|
||||
Asset.name.ilike(pattern),
|
||||
Asset.serialnumber.ilike(pattern),
|
||||
Computer.hostname.ilike(pattern),
|
||||
ComputerType.computertype.ilike(pattern)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
from flask import Blueprint, request
|
||||
from flask_jwt_extended import jwt_required
|
||||
|
||||
from shopdb.api import db, Asset, AssetType, AuditLog, success_response, error_response, paginated_response, ErrorCodes, get_pagination_params, paginate_query, resolve_dualpath_pairs, dualpath_single_machine_enabled
|
||||
from shopdb.api import db, Asset, AssetType, AuditLog, Vendor, success_response, error_response, paginated_response, ErrorCodes, get_pagination_params, paginate_query, resolve_dualpath_pairs, dualpath_single_machine_enabled
|
||||
|
||||
from ..models import Machine, MachineType
|
||||
|
||||
@@ -174,13 +174,23 @@ def list_machines():
|
||||
if exactassetnumber := request.args.get('assetnumber'):
|
||||
query = query.filter(Asset.assetnumber == exactassetnumber)
|
||||
|
||||
# Search filter
|
||||
# Search filter. Covers what the list actually SHOWS - the asset fields plus
|
||||
# the type and vendor names, which are columns in the table. Searching a type
|
||||
# like 'Part Washer' used to return nothing. Outer joins so a machine with no
|
||||
# type or vendor still matches on its own fields.
|
||||
if search := request.args.get('search'):
|
||||
query = query.filter(
|
||||
pattern = f'%{search}%'
|
||||
query = query.outerjoin(
|
||||
MachineType, Machine.machinetypeid == MachineType.machinetypeid
|
||||
).outerjoin(
|
||||
Vendor, Machine.vendorid == Vendor.vendorid
|
||||
).filter(
|
||||
db.or_(
|
||||
Asset.assetnumber.ilike(f'%{search}%'),
|
||||
Asset.name.ilike(f'%{search}%'),
|
||||
Asset.serialnumber.ilike(f'%{search}%')
|
||||
Asset.assetnumber.ilike(pattern),
|
||||
Asset.name.ilike(pattern),
|
||||
Asset.serialnumber.ilike(pattern),
|
||||
MachineType.machinetype.ilike(pattern),
|
||||
Vendor.vendor.ilike(pattern)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -168,11 +168,18 @@ def list_tools():
|
||||
# Exact-match natural-key lookup for idempotent import (asset number).
|
||||
if exactassetnumber := request.args.get('assetnumber'):
|
||||
query = query.filter(Asset.assetnumber == exactassetnumber)
|
||||
# Type is a column in the list, so it has to be searchable. Outer join so a
|
||||
# tool with no type still matches on its own fields.
|
||||
if search := request.args.get('search'):
|
||||
query = query.filter(db.or_(
|
||||
Asset.assetnumber.ilike(f'%{search}%'),
|
||||
Asset.name.ilike(f'%{search}%'),
|
||||
Asset.serialnumber.ilike(f'%{search}%'),
|
||||
pattern = f'%{search}%'
|
||||
query = query.outerjoin(
|
||||
MeasuringToolType,
|
||||
MeasuringTool.measuringtooltypeid == MeasuringToolType.measuringtooltypeid
|
||||
).filter(db.or_(
|
||||
Asset.assetnumber.ilike(pattern),
|
||||
Asset.name.ilike(pattern),
|
||||
Asset.serialnumber.ilike(pattern),
|
||||
MeasuringToolType.name.ilike(pattern),
|
||||
))
|
||||
if type_id := request.args.get('typeid', type=int):
|
||||
query = query.filter(MeasuringTool.measuringtooltypeid == type_id)
|
||||
|
||||
@@ -201,14 +201,24 @@ def list_network_devices():
|
||||
if exactassetnumber := request.args.get('assetnumber'):
|
||||
query = query.filter(Asset.assetnumber == exactassetnumber)
|
||||
|
||||
# Search filter
|
||||
# Search filter. Type and vendor are columns in the list, so both must be
|
||||
# searchable. Outer joins so a device missing either still matches on its
|
||||
# own fields.
|
||||
if search := request.args.get('search'):
|
||||
query = query.filter(
|
||||
pattern = f'%{search}%'
|
||||
query = query.outerjoin(
|
||||
NetworkDeviceType,
|
||||
NetworkDevice.networkdevicetypeid == NetworkDeviceType.networkdevicetypeid
|
||||
).outerjoin(
|
||||
Vendor, NetworkDevice.vendorid == Vendor.vendorid
|
||||
).filter(
|
||||
db.or_(
|
||||
Asset.assetnumber.ilike(f'%{search}%'),
|
||||
Asset.name.ilike(f'%{search}%'),
|
||||
Asset.serialnumber.ilike(f'%{search}%'),
|
||||
NetworkDevice.hostname.ilike(f'%{search}%')
|
||||
Asset.assetnumber.ilike(pattern),
|
||||
Asset.name.ilike(pattern),
|
||||
Asset.serialnumber.ilike(pattern),
|
||||
NetworkDevice.hostname.ilike(pattern),
|
||||
NetworkDeviceType.networkdevicetype.ilike(pattern),
|
||||
Vendor.vendor.ilike(pattern)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -240,15 +240,24 @@ def list_printers():
|
||||
if exactassetnumber := request.args.get('assetnumber'):
|
||||
query = query.filter(Asset.assetnumber == exactassetnumber)
|
||||
|
||||
# Search filter
|
||||
# Search filter. Type and model are columns in the list, so searching
|
||||
# 'Thermal' must find the thermal printers. Outer joins so a printer missing
|
||||
# either still matches on its own fields.
|
||||
if search := request.args.get('search'):
|
||||
query = query.filter(
|
||||
pattern = f'%{search}%'
|
||||
query = query.outerjoin(
|
||||
PrinterType, Printer.printertypeid == PrinterType.printertypeid
|
||||
).outerjoin(
|
||||
Model, Printer.modelnumberid == Model.modelnumberid
|
||||
).filter(
|
||||
db.or_(
|
||||
Asset.assetnumber.ilike(f'%{search}%'),
|
||||
Asset.name.ilike(f'%{search}%'),
|
||||
Asset.serialnumber.ilike(f'%{search}%'),
|
||||
Printer.hostname.ilike(f'%{search}%'),
|
||||
Printer.windowsname.ilike(f'%{search}%')
|
||||
Asset.assetnumber.ilike(pattern),
|
||||
Asset.name.ilike(pattern),
|
||||
Asset.serialnumber.ilike(pattern),
|
||||
Printer.hostname.ilike(pattern),
|
||||
Printer.windowsname.ilike(pattern),
|
||||
PrinterType.printertype.ilike(pattern),
|
||||
Model.modelnumber.ilike(pattern)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user