Buildings and levels for the floor map, and make every identifier searchable
This commit is contained in:
@@ -236,13 +236,26 @@ def _search_applications(query, search_term):
|
||||
|
||||
|
||||
def _search_knowledgebase(query, search_term):
|
||||
"""Search Knowledge Base by description and keywords."""
|
||||
"""Search Knowledge Base by description and keywords.
|
||||
|
||||
An article whose topic is a RETIRED application is excluded, matching
|
||||
GET /api/knowledgebase. Filtering it out of the plugin's own listing while
|
||||
global search still returned it is not a rule at all: the article was two
|
||||
keystrokes away, and the result printed the retired application's name as its
|
||||
subject, which reads as though it were still in service.
|
||||
|
||||
A null topic still matches. Not every article is about an application.
|
||||
"""
|
||||
results = []
|
||||
try:
|
||||
_require_enabled('knowledgebase')
|
||||
from plugins.knowledgebase.models import KnowledgeBase
|
||||
retired = db.session.query(Application.appid).filter(
|
||||
Application.isactive.is_(False))
|
||||
kb_articles = KnowledgeBase.query.filter(
|
||||
KnowledgeBase.isactive == True,
|
||||
db.or_(KnowledgeBase.appid.is_(None),
|
||||
KnowledgeBase.appid.notin_(retired)),
|
||||
_word_match(query, KnowledgeBase.shortdescription,
|
||||
KnowledgeBase.keywords)
|
||||
).limit(20).all()
|
||||
@@ -339,7 +352,22 @@ def _search_employees(query, search_term):
|
||||
|
||||
|
||||
def _search_assets(query, search_term):
|
||||
"""Search unified Assets table by number, name, serial, notes."""
|
||||
"""Search unified Assets table by number, name, serial, notes and the two
|
||||
optional identifiers.
|
||||
|
||||
gaugelabreference and maintenancereference are searched for EVERY asset type
|
||||
(ADR-001). Settings lets a site enable either identifier on machines, PCs,
|
||||
printers and network devices, but only the measuring-tools searcher looked at
|
||||
gaugelabreference and nothing looked at maintenancereference at all - so a
|
||||
tag an operator was told to record was one nobody could search by. An
|
||||
identifier that can be entered has to be findable, or it is a write-only
|
||||
field.
|
||||
|
||||
The per-type `identifier_<name>_<assettype>_enabled` toggles are NOT applied
|
||||
here. They govern whether the field is SHOWN on that type; a value already in
|
||||
the row is still the tag written on the physical machine, and matching it is
|
||||
strictly better than returning nothing to someone reading it off a label.
|
||||
"""
|
||||
results = []
|
||||
try:
|
||||
assets = Asset.query.join(AssetType).options(
|
||||
@@ -348,7 +376,8 @@ def _search_assets(query, search_term):
|
||||
).filter(
|
||||
Asset.isactive == True,
|
||||
_word_match(query, Asset.assetnumber, Asset.name,
|
||||
Asset.serialnumber, Asset.notes)
|
||||
Asset.serialnumber, Asset.notes,
|
||||
Asset.gaugelabreference, Asset.maintenancereference)
|
||||
).limit(15).all()
|
||||
|
||||
for asset in assets:
|
||||
@@ -357,6 +386,10 @@ def _search_assets(query, search_term):
|
||||
relevance = 100
|
||||
elif asset.name and query.lower() == asset.name.lower():
|
||||
relevance = 90
|
||||
elif asset.gaugelabreference and query.lower() == asset.gaugelabreference.lower():
|
||||
relevance = 88
|
||||
elif asset.maintenancereference and query.lower() == asset.maintenancereference.lower():
|
||||
relevance = 86
|
||||
elif asset.serialnumber and query.lower() == asset.serialnumber.lower():
|
||||
relevance = 85
|
||||
elif asset.name and query.lower() in asset.name.lower():
|
||||
@@ -408,6 +441,104 @@ def _search_measuringtools(query, search_term):
|
||||
return results
|
||||
|
||||
|
||||
def _search_usbdevices(query, search_term):
|
||||
"""Search USB devices by serial, asset tag and product name.
|
||||
|
||||
A USB device is NOT an asset - it lives in the usb plugin's own table - so
|
||||
the generic asset search cannot see it and these records were unreachable
|
||||
from search entirely. Serial number is the field people actually have in
|
||||
hand: it is what is printed on the stick they are holding.
|
||||
|
||||
currentusername is deliberately NOT searched. It records who holds the
|
||||
device, and making search a way to list what a named person has checked out
|
||||
is a different feature from finding a device.
|
||||
"""
|
||||
results = []
|
||||
try:
|
||||
_require_enabled('usb')
|
||||
from plugins.usb.models import USBDevice
|
||||
devices = USBDevice.query.filter(
|
||||
USBDevice.isactive == True,
|
||||
_word_match(query, USBDevice.serialnumber, USBDevice.assetnumber,
|
||||
USBDevice.label, USBDevice.productname)
|
||||
).limit(10).all()
|
||||
|
||||
for device in devices:
|
||||
relevance = 20
|
||||
if query.lower() == (device.serialnumber or '').lower():
|
||||
relevance = 100
|
||||
elif query.lower() == (device.assetnumber or '').lower():
|
||||
relevance = 90
|
||||
elif query.lower() == (device.label or '').lower():
|
||||
relevance = 85
|
||||
elif query.lower() in (device.label or '').lower():
|
||||
relevance = 50
|
||||
elif query.lower() in (device.productname or '').lower():
|
||||
relevance = 40
|
||||
|
||||
results.append({
|
||||
'type': 'usb_device',
|
||||
'id': device.usbdeviceid,
|
||||
'title': device.label or device.productname or device.serialnumber,
|
||||
'subtitle': device.assetnumber or device.serialnumber,
|
||||
'url': f'/usb/{device.usbdeviceid}',
|
||||
'relevance': relevance,
|
||||
})
|
||||
except ImportError:
|
||||
pass # usb plugin absent or disabled
|
||||
except Exception as e:
|
||||
logger.error(f"USB device search failed: {e}")
|
||||
return results
|
||||
|
||||
|
||||
def _search_printeditems(query, search_term):
|
||||
"""Search printed items by bin code, gage-lab tag, name and description.
|
||||
|
||||
Printed items are their own records, not assets, so the generic asset search
|
||||
never covered them. itemcode (the bin label, e.g. 3DP-0042) and gagelabtag
|
||||
are both unique and both printed on physical labels, which makes them the
|
||||
likeliest thing anyone types into search.
|
||||
"""
|
||||
results = []
|
||||
try:
|
||||
_require_enabled('printedparts')
|
||||
from plugins.printedparts.models import PrintedItem
|
||||
items = PrintedItem.query.filter(
|
||||
PrintedItem.isactive == True,
|
||||
_word_match(query, PrintedItem.itemcode, PrintedItem.gagelabtag,
|
||||
PrintedItem.itemname, PrintedItem.itemdescription)
|
||||
).limit(10).all()
|
||||
|
||||
for item in items:
|
||||
relevance = 20
|
||||
if query.lower() == (item.itemcode or '').lower():
|
||||
relevance = 100
|
||||
elif query.lower() == (item.gagelabtag or '').lower():
|
||||
relevance = 95
|
||||
elif query.lower() == (item.itemname or '').lower():
|
||||
relevance = 90
|
||||
elif query.lower() in (item.itemname or '').lower():
|
||||
relevance = 50
|
||||
|
||||
subtitle = item.itemcode or item.gagelabtag
|
||||
if item.binlocation:
|
||||
subtitle = f'{subtitle} - {item.binlocation}' if subtitle else item.binlocation
|
||||
|
||||
results.append({
|
||||
'type': 'printed_item',
|
||||
'id': item.printeditemid,
|
||||
'title': item.itemname,
|
||||
'subtitle': subtitle,
|
||||
'url': f'/printedparts/{item.printeditemid}',
|
||||
'relevance': relevance,
|
||||
})
|
||||
except ImportError:
|
||||
pass # printedparts plugin absent or disabled
|
||||
except Exception as e:
|
||||
logger.error(f"Printed item search failed: {e}")
|
||||
return results
|
||||
|
||||
|
||||
def _search_customfields(query, search_term):
|
||||
"""Search custom-field VALUES for fields flagged searchable.
|
||||
|
||||
@@ -886,6 +1017,8 @@ def global_search():
|
||||
results.extend(_search_employees(query, search_term))
|
||||
results.extend(_search_assets(query, search_term))
|
||||
results.extend(_search_measuringtools(query, search_term))
|
||||
results.extend(_search_usbdevices(query, search_term))
|
||||
results.extend(_search_printeditems(query, search_term))
|
||||
results.extend(_search_customfields(query, search_term))
|
||||
results.extend(_search_notifications(query, search_term))
|
||||
results.extend(_search_hostnames(query, search_term))
|
||||
|
||||
Reference in New Issue
Block a user