searchUtil.py (16862B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\util\searchUtil.py 2 from collections import defaultdict 3 import localization 4 import evetypes 5 from eve.client.script.ui.control.entries.generic import Generic 6 from eve.client.script.ui.control.entries.item import Item 7 from eve.client.script.ui.control.entries.universe import SolarSystem, SolarSystemStructure 8 from eve.client.script.ui.control.entries.util import GetFromClass 9 from eve.client.script.ui.control.listgroup import ListGroup 10 from eve.client.script.ui.shared.userentry import AgentEntry, User 11 from eve.common.script.util.systemExclusions import IsValidDestination 12 from eve.common.lib import appConst 13 from eve.common.script.search import const as search_const 14 from eveexceptions import UserError 15 from globalConfig.getFunctions import ArePointerLinksActive 16 from localization import GetByLabel 17 from bannedwords.client import bannedwords 18 SEARCHBY_OPTIONS = ([GetByLabel('UI/Search/UniversalSearch/PartialTerms'), search_const.MatchBy.partial_terms], 19 [GetByLabel('UI/Search/UniversalSearch/ExactTerms'), search_const.MatchBy.exact_terms], 20 [GetByLabel('UI/Search/UniversalSearch/ExactPhrase'), search_const.MatchBy.exact_phrase], 21 [GetByLabel('UI/Search/UniversalSearch/OnlyExactPhrase'), search_const.MatchBy.exact_phrase_only]) 22 RESULT_ORDER = (search_const.ResultType.agent, 23 search_const.ResultType.character, 24 search_const.ResultType.faction, 25 search_const.ResultType.corporation, 26 search_const.ResultType.alliance, 27 search_const.ResultType.region, 28 search_const.ResultType.solar_system, 29 search_const.ResultType.station, 30 search_const.ResultType.structure, 31 search_const.ResultType.structure_with_inlined_data, 32 search_const.ResultType.constellation, 33 search_const.ResultType.item_type) 34 RESULT_LABELS = {search_const.ResultType.agent: 'UI/Search/UniversalSearch/Agents', 35 search_const.ResultType.character: 'UI/Search/UniversalSearch/Characters', 36 search_const.ResultType.faction: 'UI/Search/UniversalSearch/Factions', 37 search_const.ResultType.corporation: 'UI/Search/UniversalSearch/Corporations', 38 search_const.ResultType.alliance: 'UI/Search/UniversalSearch/Alliances', 39 search_const.ResultType.region: 'UI/Search/UniversalSearch/Regions', 40 search_const.ResultType.solar_system: 'UI/Search/UniversalSearch/SolarSystems', 41 search_const.ResultType.station: 'UI/Common/LocationTypes/Stations', 42 search_const.ResultType.structure: 'UI/Common/LocationTypes/Structures', 43 search_const.ResultType.structure_with_inlined_data: 'UI/Common/LocationTypes/Structures', 44 search_const.ResultType.constellation: 'UI/Search/UniversalSearch/Constellations', 45 search_const.ResultType.item_type: 'UI/Search/UniversalSearch/Types'} 46 47 def GetSearchByChoices(): 48 return SEARCHBY_OPTIONS 49 50 51 def GetResultsScrollList(searchStr, groupIDList, searchBy = search_const.MatchBy.partial_terms, searchWndName = 'mySearch', corpMemberIDs = None): 52 resultsByType = GetResultsByGroupID(searchStr, groupIDList, searchBy) 53 return _GetResultsScrollList(resultsByType, corpMemberIDs) 54 55 56 def GetResultsInNewWindow(searchStr, groupIDList, searchBy = search_const.MatchBy.partial_terms, searchWndName = 'mySearch', corpMemberIDs = None, includeUiElements = False): 57 resultsByType = GetResultsByGroupID(searchStr, groupIDList, searchBy) 58 scrolllist = _GetResultsScrollList(resultsByType, corpMemberIDs) 59 header = localization.GetByLabel('UI/Common/SearchResults') 60 numUiElements = 0 61 if includeUiElements: 62 uiElementResults, numUiElements = _GetResultsForUiElements(searchStr) 63 scrolllist += uiElementResults 64 totalResults = sum([ len(results) for results in resultsByType.values() ]) 65 if totalResults >= search_const.max_result_count: 66 top = localization.GetByLabel('UI/Search/UniversalSearch/WindowHeaderOverMax', maxNumber=search_const.max_result_count, searchStr=searchStr) 67 else: 68 totalResults += numUiElements 69 top = localization.GetByLabel('UI/Search/UniversalSearch/WindowHeaderNumResults', numResults=totalResults, searchStr=searchStr) 70 noContentHint = localization.GetByLabel('UI/Search/UniversalSearch/NoResultsReturned', searchStr=searchStr) 71 from eve.client.script.ui.control.listwindow import ListWindow 72 chosen = ListWindow.ShowList(scrolllist, 'generic', header, top, 0, isModal=0, minChoices=0, windowName=searchWndName, lstDataIsGrouped=1, unstackable=1, noContentHint=noContentHint, showCollapseBtn=True) 73 if chosen: 74 return chosen[1] 75 76 77 def _GetResultsScrollList(resultsByType, corpMemberIDs): 78 scrolllist = [] 79 for resultType in RESULT_ORDER: 80 if resultType not in resultsByType: 81 continue 82 results = resultsByType[resultType] 83 entryList = GetScrollEntryData(results, resultType, corpMemberIDs) 84 if entryList: 85 _AppendScrollGroups(entryList, resultType, scrolllist) 86 87 return scrolllist 88 89 90 def _GetResultsForUiElements(searchStr): 91 if ArePointerLinksActive(sm.GetService('machoNet')): 92 return sm.GetService('helpPointer').SearchPointers(searchStr) 93 return ([], 0) 94 95 96 def _AppendScrollGroups(entryList, resultType, scrolllist): 97 if resultType == search_const.ResultType.item_type: 98 _AppendScrollGroupsInvItems(entryList, resultType, scrolllist) 99 else: 100 _AppendScrollGroup(entryList, resultType, scrolllist) 101 102 103 def _AppendScrollGroupsInvItems(entryList, resultType, scrolllist): 104 entriesByCategory = defaultdict(list) 105 for entry in entryList: 106 categoryID = evetypes.GetCategoryID(entry['typeID']) 107 entriesByCategory[categoryID].append(entry) 108 109 for categoryID, entries in sorted(entriesByCategory.items()): 110 searchGroupID = 'inv_%s_%s' % (resultType, categoryID) 111 data = GetScrollListGroup(entries, _GetInvItemEntryType(categoryID), evetypes.GetCategoryNameByCategory(categoryID), searchGroupID) 112 scrolllist.append(GetFromClass(ListGroup, data)) 113 114 115 def _GetInvItemEntryType(categoryID): 116 return Item 117 118 119 def _AppendScrollGroup(entryList, resultType, scrolllist): 120 label = localization.GetByLabel(RESULT_LABELS[resultType]) 121 entryType = GetScrollEntryType(resultType) 122 data = GetScrollListGroup(entryList, entryType, label, str(resultType)) 123 scrolllist.append(GetFromClass(ListGroup, data)) 124 125 126 def GetScrollListGroup(entryList, entryType, label, searchGroupID): 127 sectionHeader = localization.GetByLabel('UI/Search/UniversalSearch/SectionHeader', resultType=label, numberReturned=len(entryList)) 128 data = {'GetSubContent': GetSearchSubContent, 129 'label': sectionHeader, 130 'groupItems': (entryType, entryList), 131 'id': ('search_cat', searchGroupID), 132 'sublevel': 0, 133 'showlen': 0, 134 'showicon': 'hide', 135 'state': 'locked'} 136 return data 137 138 139 def GetScrollEntryData(results, resultType, corpMemberIDs): 140 entryList = [] 141 if resultType == search_const.ResultType.agent: 142 for agentID in results: 143 agentSvc = sm.GetService('agents') 144 agentInfo = agentSvc.GetAgentByID(agentID) 145 if agentInfo is None: 146 continue 147 if IsValidDestination(agentInfo.solarsystemID): 148 data = {'charID': agentID, 149 'sublevel': 0} 150 entryList.append(data) 151 152 elif resultType in (search_const.ResultType.corporation, 153 search_const.ResultType.alliance, 154 search_const.ResultType.faction, 155 search_const.ResultType.character): 156 for ownerID in results: 157 if corpMemberIDs and ownerID not in corpMemberIDs: 158 continue 159 data = {'charID': ownerID, 160 'sublevel': 0, 161 'showinfo': True} 162 entryList.append(data) 163 164 elif resultType == search_const.ResultType.solar_system: 165 for itemID in results: 166 if IsValidDestination(itemID): 167 data = {'itemID': itemID, 168 'solarSystemID': itemID, 169 'typeID': appConst.typeSolarSystem, 170 'label': cfg.evelocations.Get(itemID).name, 171 'sublevel': 0, 172 'showinfo': True} 173 entryList.append(data) 174 175 elif resultType == search_const.ResultType.constellation: 176 for itemID in results: 177 if IsValidDestination(itemID): 178 data = {'itemID': itemID, 179 'typeID': appConst.typeConstellation, 180 'label': cfg.evelocations.Get(itemID).name, 181 'sublevel': 0, 182 'showinfo': True} 183 entryList.append(data) 184 185 elif resultType == search_const.ResultType.region: 186 for itemID in results: 187 if IsValidDestination(itemID): 188 data = {'itemID': itemID, 189 'typeID': appConst.typeRegion, 190 'label': cfg.evelocations.Get(itemID).name, 191 'sublevel': 0, 192 'showinfo': True} 193 entryList.append(data) 194 195 elif resultType == search_const.ResultType.structure: 196 for itemID in results: 197 info = sm.GetService('structureDirectory').GetStructureInfo(itemID) 198 if IsValidDestination(info.solarSystemID): 199 data = {'itemID': itemID, 200 'solarSystemID': info.solarSystemID, 201 'sublevel': 0, 202 'typeID': info.typeID, 203 'label': info.itemName, 204 'showinfo': True} 205 entryList.append(data) 206 207 elif resultType == search_const.ResultType.structure_with_inlined_data: 208 for info in results: 209 data = {'itemID': info.structureID, 210 'sublevel': 0, 211 'typeID': info.typeID, 212 'label': info.itemName} 213 entryList.append(data) 214 215 elif resultType == search_const.ResultType.station: 216 for itemID in results: 217 station = cfg.stations.Get(itemID) 218 if IsValidDestination(station.solarSystemID): 219 data = {'itemID': itemID, 220 'solarSystemID': station.solarSystemID, 221 'typeID': station.stationTypeID, 222 'label': cfg.evelocations.Get(itemID).name, 223 'sublevel': 0, 224 'showinfo': True} 225 entryList.append(data) 226 227 elif resultType == search_const.ResultType.item_type: 228 for typeID in results: 229 if not evetypes.Exists(typeID): 230 continue 231 data = {'itemID': None, 232 'typeID': typeID, 233 'getIcon': 1, 234 'label': evetypes.GetName(typeID), 235 'sublevel': 0, 236 'showinfo': True} 237 entryList.append(data) 238 239 return entryList 240 241 242 def GetScrollEntryType(resultType): 243 if resultType == search_const.ResultType.agent: 244 return AgentEntry 245 elif resultType in (search_const.ResultType.corporation, 246 search_const.ResultType.alliance, 247 search_const.ResultType.faction, 248 search_const.ResultType.character): 249 return User 250 elif resultType == search_const.ResultType.item_type: 251 return Item 252 elif resultType == search_const.ResultType.structure: 253 return SolarSystemStructure 254 elif resultType in (search_const.ResultType.solar_system, search_const.ResultType.station): 255 return SolarSystem 256 else: 257 return Generic 258 259 260 def GetResultsByGroupID(searchStr, groupIDList, searchBy = search_const.MatchBy.partial_terms): 261 searchStr = _FormatSearchInput(searchStr) 262 _ValidateSearchInput(searchStr, searchBy) 263 searchMgr = sm.ProxySvc('search') 264 resultsByType = searchMgr.Query(searchStr, groupIDList, exact=searchBy) 265 if resultsByType: 266 _PrimeResults(resultsByType) 267 _SortResults(resultsByType) 268 return resultsByType or {} 269 270 271 def _SortResults(resultsByType): 272 for resultID, results in resultsByType.iteritems(): 273 if resultID in search_const.location_result_types: 274 results.sort(key=lambda x: cfg.evelocations.Get(x).locationName) 275 elif resultID in search_const.owner_result_types: 276 results.sort(key=lambda x: cfg.eveowners.Get(x).ownerName) 277 elif resultID == search_const.ResultType.item_type: 278 results.sort(key=lambda x: evetypes.GetName(x)) 279 280 281 def GetResultsList(searchStr, groupIDList, searchBy = search_const.MatchBy.partial_terms, hideNPC = 0, onlyAltName = 0): 282 searchStr = _FormatSearchInput(searchStr) 283 _ValidateSearchInput(searchStr, searchBy) 284 searchMgr = sm.ProxySvc('search') 285 return searchMgr.QuickQuery(searchStr, groupIDList, hideNPC=hideNPC, onlyAltName=onlyAltName, exact=searchBy) 286 287 288 def _FormatSearchInput(searchStr): 289 searchStr = searchStr.replace('*', '') 290 searchStr = searchStr.strip() 291 return searchStr 292 293 294 def _ValidateSearchInput(searchStr, exact): 295 if len(searchStr) < 1: 296 raise UserError('LookupStringMinimum', {'minimum': 1}) 297 elif len(searchStr) >= 100: 298 raise UserError('CustomInfo', {'info': localization.GetByLabel('UI/Common/SearchStringTooLong')}) 299 elif exact == search_const.MatchBy.partial_terms and not localization.util.IsTextInConciseLanguage(session.languageID, searchStr): 300 if len([ x for x in searchStr.split() if len(x) >= search_const.min_wildcard_length ]) == 0: 301 raise UserError('PartialSearchLessThanMinLength', {'minChars': search_const.min_wildcard_length}) 302 bannedwords.check_search_words_allowed(searchStr) 303 304 305 def _PrimeResults(resultsByType): 306 _PrimeOwners(resultsByType) 307 _PrimeCorpTickers(resultsByType) 308 _RemoveNonExistingTypes(resultsByType) 309 310 311 def _PrimeCorpTickers(resultsByType): 312 results = resultsByType.get(search_const.ResultType.corporation, None) 313 if results: 314 cfg.corptickernames.Prime(results) 315 316 317 def _PrimeOwners(resultsByType): 318 toPrime = [] 319 for resultType in search_const.owner_result_types: 320 toPrime.extend(resultsByType.get(resultType, [])) 321 322 cfg.eveowners.Prime(toPrime) 323 324 325 def _RemoveNonExistingTypes(resultsByType): 326 if search_const.ResultType.item_type in resultsByType: 327 results = resultsByType[search_const.ResultType.item_type] 328 filteredResults = filter(evetypes.Exists, results) 329 resultsByType[search_const.ResultType.item_type] = filteredResults 330 331 332 def GetSearchSubContent(dataX, *args): 333 scrolllist = [] 334 entryType, typeList = dataX['groupItems'] 335 for data in typeList: 336 scrolllist.append(GetFromClass(entryType, data)) 337 338 return scrolllist 339 340 341 def SearchCharacters(searchStr, searchBy = search_const.MatchBy.partial_terms): 342 scrolllist = GetResultsScrollList(searchStr, [search_const.ResultType.character], searchBy=searchBy) 343 if len(scrolllist): 344 return GetSearchSubContent(scrolllist[0]) 345 return [] 346 347 348 def SearchCharactersInCorp(searchStr, corpMemberIDs): 349 scrolllist = GetResultsScrollList(searchStr, [search_const.ResultType.character], corpMemberIDs=corpMemberIDs) 350 if len(scrolllist): 351 return GetSearchSubContent(scrolllist[0]) 352 return [] 353 354 355 def SearchCorporations(searchStr, searchBy = search_const.MatchBy.partial_terms): 356 scrolllist = GetResultsScrollList(searchStr, [search_const.ResultType.corporation], searchBy=searchBy) 357 if len(scrolllist): 358 return GetSearchSubContent(scrolllist[0]) 359 return [] 360 361 362 def IsMatch(searchStr, candidate, exact): 363 if exact == search_const.MatchBy.partial_terms and not localization.util.IsTextInConciseLanguage(session.languageID, searchStr): 364 if len([ x for x in searchStr.split() if len(x) >= search_const.min_wildcard_length ]) == 0: 365 exact = search_const.MatchBy.exact_terms 366 searchStr = searchStr.lower().strip() 367 candidate = candidate.lower().strip() 368 if exact == search_const.MatchBy.partial_terms: 369 searchTermList = searchStr.split() 370 candTermList = candidate.lower().split() 371 matches = True 372 for searchTerm in searchTermList: 373 termMatches = False 374 for candTerm in candTermList: 375 if candTerm.startswith(searchTerm): 376 termMatches = True 377 break 378 379 if not termMatches: 380 matches = False 381 break 382 383 return matches 384 if exact == search_const.MatchBy.exact_terms: 385 candTermList = candidate.split() 386 return all([ s in candTermList for s in searchStr.split() ]) 387 if exact == search_const.MatchBy.exact_phrase: 388 searchTermList = searchStr.split() 389 lBound = 0 390 uBound = len(searchTermList) 391 candTermList = candidate.split() 392 for idx in range(uBound, len(candTermList) + 1): 393 if searchTermList == candTermList[lBound:idx]: 394 return True 395 lBound += 1 396 397 return False 398 if exact == search_const.MatchBy.exact_phrase_only: 399 return searchStr == candidate 400 raise RuntimeError('Unknown value passed in for exact flag')