forked from pyfa-org/Pyfa
-
Notifications
You must be signed in to change notification settings - Fork 0
/
market.py
745 lines (670 loc) · 31.6 KB
/
market.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
#===============================================================================
# Copyright (C) 2010 Diego Duclos
#
# This file is part of pyfa.
#
# pyfa is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pyfa is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
#===============================================================================
import re
import threading
import wx
import Queue
import eos.db
import eos.types
from service.settings import SettingsProvider, ProxySettings
try:
from collections import OrderedDict
except ImportError:
from gui.utils.compat import OrderedDict
# Event which tells threads dependent on Market that it's initialized
mktRdy = threading.Event()
class ShipBrowserWorkerThread(threading.Thread):
def run(self):
self.queue = Queue.Queue()
self.cache = {}
# Wait for full market initialization (otherwise there's high risky
# this thread will attempt to init Market which is already being inited)
mktRdy.wait(5)
self.processRequests()
def processRequests(self):
queue = self.queue
cache = self.cache
sMarket = Market.getInstance()
while True:
try:
callback, id = queue.get()
set = cache.get(id)
if set is None:
set = sMarket.getShipList(id)
cache[id] = set
wx.CallAfter(callback, (id, set))
except:
pass
finally:
try:
queue.task_done()
except:
pass
class PriceWorkerThread(threading.Thread):
def run(self):
self.queue = Queue.Queue()
self.processUpdates()
def processUpdates(self):
queue = self.queue
while True:
# Grab our data
callback, requests = queue.get()
# Grab prices, this is the time-consuming part
if len(requests) > 0:
proxy = ProxySettings.getInstance().getProxySettings()
if proxy is not None:
proxy = "{0}:{1}".format(*proxy)
eos.types.Price.fetchPrices(requests, proxy=proxy)
wx.CallAfter(callback)
queue.task_done()
def trigger(self, prices, callbacks):
self.queue.put((callbacks, prices))
class SearchWorkerThread(threading.Thread):
def run(self):
self.cv = threading.Condition()
self.searchRequest = None
self.processSearches()
def processSearches(self):
cv = self.cv
while True:
cv.acquire()
while self.searchRequest is None:
cv.wait()
request, callback = self.searchRequest
self.searchRequest = None
cv.release()
sMarket = Market.getInstance()
# Rely on category data provided by eos as we don't hardcode them much in service
filter = eos.types.Category.name.in_(sMarket.SEARCH_CATEGORIES)
results = eos.db.searchItems(request, where=filter,
join=(eos.types.Item.group, eos.types.Group.category),
eager=("icon", "group.category", "metaGroup", "metaGroup.parent"))
items = set()
# Return only published items, consult with Market service this time
for item in results:
if sMarket.getPublicityByItem(item):
items.add(item)
wx.CallAfter(callback, items)
def scheduleSearch(self, text, callback):
self.cv.acquire()
self.searchRequest = (text, callback)
self.cv.notify()
self.cv.release()
class Market():
instance = None
def __init__(self):
self.priceCache = {}
#Init recently used module storage
serviceMarketRecentlyUsedModules = {"pyfaMarketRecentlyUsedModules": []}
self.serviceMarketRecentlyUsedModules = SettingsProvider.getInstance().getSettings("pyfaMarketRecentlyUsedModules", serviceMarketRecentlyUsedModules)
# Start price fetcher
self.priceWorkerThread = PriceWorkerThread()
self.priceWorkerThread.daemon = True
self.priceWorkerThread.start()
# Thread which handles search
self.searchWorkerThread = SearchWorkerThread()
self.searchWorkerThread.daemon = True
self.searchWorkerThread.start()
# Ship browser helper thread
self.shipBrowserWorkerThread = ShipBrowserWorkerThread()
self.shipBrowserWorkerThread.daemon = True
self.shipBrowserWorkerThread.start()
# Items' group overrides
self.customGroups = set()
# Limited edition ships
self.les_grp = eos.types.Group()
self.les_grp.ID = -1
self.les_grp.name = "Limited Issue Ships"
self.les_grp.published = True
ships = self.getCategory("Ship")
self.les_grp.category = ships
self.les_grp.categoryID = ships.ID
self.les_grp.description = ""
self.les_grp.icon = None
self.ITEMS_FORCEGROUP = {
"Opux Luxury Yacht": self.les_grp, # One of those is wedding present at CCP fanfest, another was hijacked from ISD guy during an event
"Silver Magnate": self.les_grp, # Amarr Championship prize
"Gold Magnate": self.les_grp, # Amarr Championship prize
"Armageddon Imperial Issue": self.les_grp, # Amarr Championship prize
"Apocalypse Imperial Issue": self.les_grp, # Amarr Championship prize
"Guardian-Vexor": self.les_grp, # Illegal rewards for the Gallente Frontier Tour Lines event arc
"Megathron Federate Issue": self.les_grp, # Reward during Crielere event
"Raven State Issue": self.les_grp, # AT4 prize
"Tempest Tribal Issue": self.les_grp, # AT4 prize
"Apotheosis": self.les_grp, # 5th EVE anniversary present
"Zephyr": self.les_grp, # 2010 new year gift
"Primae": self.les_grp, # Promotion of planetary interaction
"Freki": self.les_grp, # AT7 prize
"Mimir": self.les_grp, # AT7 prize
"Utu": self.les_grp, # AT8 prize
"Adrestia": self.les_grp, # AT8 prize
"Echelon": self.les_grp, # 2011 new year gift
"Malice": self.les_grp, # AT9 prize
"Vangel": self.les_grp, # AT9 prize
"Cambion": self.les_grp, # AT10 prize
"Etana": self.les_grp, # AT10 prize
"Chremoas": self.les_grp, # AT11 prize :(
"Moracha": self.les_grp } # AT11 prize
self.ITEMS_FORCEGROUP_R = self.__makeRevDict(self.ITEMS_FORCEGROUP)
self.les_grp.addItems = list(self.getItem(itmn) for itmn in self.ITEMS_FORCEGROUP_R[self.les_grp])
self.customGroups.add(self.les_grp)
# List of items which are forcibly published or hidden
self.ITEMS_FORCEPUBLISHED = {
"Data Subverter I": False, # Not used in EVE, probably will appear with Dust link
"Ghost Heavy Missile": False, # Missile used by Sansha
"QA Cross Protocol Analyzer": False, # QA modules used by CCP internally
"QA Damage Module": False,
"QA ECCM": False,
"QA Immunity Module": False,
"QA Multiship Module - 10 Players": False,
"QA Multiship Module - 20 Players": False,
"QA Multiship Module - 40 Players": False,
"QA Multiship Module - 5 Players": False,
"QA Remote Armor Repair System - 5 Players": False,
"QA Shield Transporter - 5 Players": False,
"Aliastra Catalyst": False, # Vanity
"Inner Zone Shipping Catalyst": False, # Vanity
"Intaki Syndicate Catalyst": False, # Vanity
"InterBus Catalyst": False, # Vanity
"Quafe Catalyst": False, # Vanity
"Nefantar Thrasher": False, # Vanity
"Sarum Magnate": False, # Vanity
"Sukuuvestaa Heron": False, # Vanity
"Inner Zone Shipping Imicus": False, # Vanity
"Vherokior Probe": False, # Vanity
"Miasmos Quafe Ultra Edition": False, # Vanity
"Miasmos Quafe Ultramarine Edition": False, # Vanity
"Miasmos Amastris Edition": False, # Vanity
"Goru's Shuttle": False, # Vanity
"Guristas Shuttle": False, # Vanity
"Tash-Murkon Magnate": False, # Vanity
"Scorpion Ishukone Watch": False } # Vanity
# List of groups which are forcibly published
self.GROUPS_FORCEPUBLISHED = {
"Prototype Exploration Ship": False } # We moved the only ship from this group to other group anyway
# Dictionary of items with forced meta groups, uses following format:
# Item name: (metagroup name, parent type name)
self.ITEMS_FORCEDMETAGROUP = {
"'Habitat' Miner I": ("Storyline", "Miner I"),
"'Wild' Miner I": ("Storyline", "Miner I"),
"Medium Nano Armor Repair Unit I": ("Tech I", "Medium Armor Repairer I"),
"Large 'Reprieve' Vestment Reconstructer I": ("Storyline", "Large Armor Repairer I"),
"Khanid Navy Torpedo Launcher": ("Faction", "Torpedo Launcher I"),
"Dark Blood Tracking Disruptor": ("Faction", "Tracking Disruptor I"),
"True Sansha Tracking Disruptor": ("Faction", "Tracking Disruptor I"),
"Shadow Serpentis Remote Sensor Dampener": ("Faction", "Remote Sensor Dampener I") }
# Parent type name: set(item names)
self.ITEMS_FORCEDMETAGROUP_R = {}
for item, value in self.ITEMS_FORCEDMETAGROUP.items():
parent = value[1]
if not parent in self.ITEMS_FORCEDMETAGROUP_R:
self.ITEMS_FORCEDMETAGROUP_R[parent] = set()
self.ITEMS_FORCEDMETAGROUP_R[parent].add(item)
# Dictionary of items with forced market group (service assumes they have no
# market group assigned in db, otherwise they'll appear in both original and forced groups)
self.ITEMS_FORCEDMARKETGROUP = {
"'Alpha' Data Analyzer I": 714, # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners
"'Codex' Data Analyzer I": 714, # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners
"'Daemon' Data Analyzer I": 714, # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners
"'Libram' Data Analyzer I": 714, # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners
"Advanced Cerebral Accelerator": 977, # Implants & Boosters > Booster
"Civilian Damage Control": 760, # Ship Equipment > Civilian Modules
"Civilian EM Ward Field": 760, # Ship Equipment > Civilian Modules
"Civilian Explosive Deflection Field": 760, # Ship Equipment > Civilian Modules
"Civilian Hobgoblin": 837, # Drones > Combat Drones > Light Scout Drones
"Civilian Kinetic Deflection Field": 760, # Ship Equipment > Civilian Modules
"Civilian Light Missile Launcher": 760, # Ship Equipment > Civilian Modules
"Civilian Scourge Light Missile": 920, # Ammunition & Charges > Missiles > Light Missiles > Standard Light Missiles
"Civilian Small Remote Armor Repairer": 760, # Ship Equipment > Civilian Modules
"Civilian Small Remote Shield Booster": 760, # Ship Equipment > Civilian Modules
"Civilian Stasis Webifier": 760, # Ship Equipment > Civilian Modules
"Civilian Thermic Dissipation Field": 760, # Ship Equipment > Civilian Modules
"Civilian Warp Disruptor": 760, # Ship Equipment > Civilian Modules
"Hardwiring - Zainou 'Sharpshooter' ZMX10": 1493, # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06
"Hardwiring - Zainou 'Sharpshooter' ZMX100": 1493, # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06
"Hardwiring - Zainou 'Sharpshooter' ZMX1000": 1493, # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06
"Hardwiring - Zainou 'Sharpshooter' ZMX11": 1493, # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06
"Hardwiring - Zainou 'Sharpshooter' ZMX110": 1493, # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06
"Hardwiring - Zainou 'Sharpshooter' ZMX1100": 1493, # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06
"Nugoehuvi Synth Blue Pill Booster": 977, # Implants & Boosters > Booster
"Prototype Cerebral Accelerator": 977, # Implants & Boosters > Booster
"Prototype Iris Probe Launcher": 712, # Ship Equipment > Turrets & Bays > Scan Probe Launchers
"Shadow": 1310, # Drones > Combat Drones > Fighter Bombers
"Sleeper Data Analyzer I": 714, # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners
"Standard Cerebral Accelerator": 977, # Implants & Boosters > Booster
"Talocan Data Analyzer I": 714, # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners
"Terran Data Analyzer I": 714, # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners
"Tetrimon Data Analyzer I": 714 } # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners
self.ITEMS_FORCEDMARKETGROUP_R = self.__makeRevDict(self.ITEMS_FORCEDMARKETGROUP)
# Misc definitions
# 0 is for items w/o meta group
self.META_MAP = OrderedDict([("normal", frozenset((0, 1, 2, 14))),
("faction", frozenset((4, 3))),
("complex", frozenset((6,))),
("officer", frozenset((5,)))])
self.SEARCH_CATEGORIES = ("Drone", "Module", "Subsystem", "Charge", "Implant")
self.ROOT_MARKET_GROUPS = (9, # Modules
1111, # Rigs
157, # Drones
11, # Ammo
1112, # Subsystems
24) # Implants & Boosters
# Tell other threads that Market is at their service
mktRdy.set()
@classmethod
def getInstance(cls):
if cls.instance == None:
cls.instance = Market()
return cls.instance
def __makeRevDict(self, orig):
"""Creates reverse dictionary"""
rev = {}
for item, value in orig.items():
if not value in rev:
rev[value] = set()
rev[value].add(item)
return rev
def getItem(self, identity, *args, **kwargs):
"""Get item by its ID or name"""
if isinstance(identity, eos.types.Item):
item = identity
elif isinstance(identity, (int, basestring)):
item = eos.db.getItem(identity, *args, **kwargs)
elif isinstance(identity, float):
id = int(identity)
item = eos.db.getItem(id, *args, **kwargs)
else:
raise TypeError("Need Item object, integer, float or string as argument")
return item
def getGroup(self, identity, *args, **kwargs):
"""Get group by its ID or name"""
if isinstance(identity, eos.types.Group):
return identity
elif isinstance(identity, (int, float, basestring)):
if isinstance(identity, float):
identity = int(identity)
# Check custom groups
for cgrp in self.customGroups:
# During first comparison we need exact int, not float for matching
if cgrp.ID == identity or cgrp.name == identity:
# Return first match
return cgrp
# Return eos group if everything else returned nothing
return eos.db.getGroup(identity, *args, **kwargs)
else:
raise TypeError("Need Group object, integer, float or string as argument")
def getCategory(self, identity, *args, **kwargs):
"""Get category by its ID or name"""
if isinstance(identity, eos.types.Category):
category = identity
elif isinstance(identity, (int, basestring)):
category = eos.db.getCategory(identity, *args, **kwargs)
elif isinstance(identity, float):
id = int(identity)
category = eos.db.getCategory(id, *args, **kwargs)
else:
raise TypeError("Need Category object, integer, float or string as argument")
return category
def getMetaGroup(self, identity, *args, **kwargs):
"""Get meta group by its ID or name"""
if isinstance(identity, eos.types.MetaGroup):
metaGroup = identity
elif isinstance(identity, (int, basestring)):
metaGroup = eos.db.getMetaGroup(identity, *args, **kwargs)
elif isinstance(identity, float):
id = int(identity)
metaGroup = eos.db.getMetaGroup(id, *args, **kwargs)
else:
raise TypeError("Need MetaGroup object, integer, float or string as argument")
return metaGroup
def getMarketGroup(self, identity, *args, **kwargs):
"""Get market group by its ID"""
if isinstance(identity, eos.types.MarketGroup):
marketGroup = identity
elif isinstance(identity, (int, float)):
id = int(identity)
marketGroup = eos.db.getMarketGroup(id, *args, **kwargs)
else:
raise TypeError("Need MarketGroup object, integer or float as argument")
return marketGroup
def getGroupByItem(self, item):
"""Get group by item"""
if item.name in self.ITEMS_FORCEGROUP:
group = self.ITEMS_FORCEGROUP[item.name]
else:
group = item.group
return group
def getCategoryByItem(self, item):
"""Get category by item"""
grp = self.getGroupByItem(item)
cat = grp.category
return cat
def getMetaGroupByItem(self, item):
"""Get meta group by item"""
# Check if item is in forced metagroup map
if item.name in self.ITEMS_FORCEDMETAGROUP:
# Create meta group from scratch
metaGroup = eos.types.MetaType()
# Get meta group info object based on meta group name
metaGroupInfo = self.getMetaGroup(self.ITEMS_FORCEDMETAGROUP[item.name][0])
# Get parent item based on its name
parent = self.getItem(self.ITEMS_FORCEDMETAGROUP[item.name][1])
# Assign all required for metaGroup variables
metaGroup.info = metaGroupInfo
metaGroup.items = item
metaGroup.parent = parent
metaGroup.metaGroupID = metaGroupInfo.ID
metaGroup.parentTypeID = parent.ID
metaGroup.typeID = item.ID
# If no forced meta group is provided, try to use item's
# meta group if any
else:
metaGroup = item.metaGroup
return metaGroup
def getMetaGroupIdByItem(self, item, fallback=0):
"""Get meta group ID by item"""
id = getattr(self.getMetaGroupByItem(item), "ID", fallback)
return id
def getMarketGroupByItem(self, item, parentcheck=True):
"""Get market group by item, its ID or name"""
# Check if we force market group for given item
if item.name in self.ITEMS_FORCEDMARKETGROUP:
mgid = self.ITEMS_FORCEDMARKETGROUP[item.name]
return self.getMarketGroup(mgid)
# Check if item itself has market group
elif item.marketGroupID:
return item.marketGroup
elif parentcheck:
# If item doesn't have marketgroup, check if it has parent
# item and use its market group
parent = self.getParentItemByItem(item, selfparent=False)
if parent:
return parent.marketGroup
else:
return None
else:
return None
def getParentItemByItem(self, item, selfparent=True):
"""Get parent item by item"""
mg = self.getMetaGroupByItem(item)
if mg:
parent = mg.parent
# Consider self as parent if item has no parent in database
elif selfparent is True:
parent = item
else:
parent = None
return parent
def getVariationsByItems(self, items, alreadyparent=False):
"""Get item variations by item, its ID or name"""
# Set for IDs of parent items
parents = set()
# Set-container for variables
variations = set()
for item in items:
# Get parent item
if alreadyparent is False:
parent = self.getParentItemByItem(item)
else:
parent = item
# Combine both in the same set
parents.add(parent)
# Check for overrides and add them if any
if parent.name in self.ITEMS_FORCEDMETAGROUP_R:
for itmn in self.ITEMS_FORCEDMETAGROUP_R[parent.name]:
variations.add(self.getItem(itmn))
# Add all parents to variations set
variations.update(parents)
# Add all variations of parents to the set
parentids = tuple(item.ID for item in parents)
variations.update(eos.db.getVariations(parentids))
return variations
def getGroupsByCategory(self, cat):
"""Get groups from given category"""
groups = set(filter(lambda grp: self.getPublicityByGroup(grp), cat.groups))
return groups
def getMarketGroupChildren(self, mg):
"""Get the children marketGroups of marketGroup."""
children = set()
for child in mg.children:
children.add(child)
return children
def getItemsByGroup(self, group):
"""Get items assigned to group"""
# Return only public items; also, filter out items
# which were forcibly set to other groups
groupItems = set(group.items)
if hasattr(group, 'addItems'):
groupItems.update(group.addItems)
items = set(filter(lambda item: self.getPublicityByItem(item) and self.getGroupByItem(item) == group, groupItems))
return items
def getItemsByMarketGroup(self, mg, vars=True):
"""Get items in the given market group"""
result = set()
# Get items from eos market group
baseitms = set(mg.items)
# Add hardcoded items to set
if mg.ID in self.ITEMS_FORCEDMARKETGROUP_R:
forceditms = set(self.getItem(itmn) for itmn in self.ITEMS_FORCEDMARKETGROUP_R[mg.ID])
baseitms.update(forceditms)
if vars:
parents = set()
for item in baseitms:
# Add one of the base market group items to result
result.add(item)
parent = self.getParentItemByItem(item, selfparent=False)
# If item has no parent, it's base item (or at least should be)
if parent is None:
parents.add(item)
# Fetch variations only for parent items
variations = self.getVariationsByItems(parents, alreadyparent=True)
for variation in variations:
# Exclude items with their own explicitly defined market groups
if self.getMarketGroupByItem(variation, parentcheck=False) is None:
result.add(variation)
else:
result = baseitms
# Get rid of unpublished items
result = set(filter(lambda item: self.getPublicityByItem(item), result))
return result
def marketGroupHasTypesCheck(self, mg):
"""If market group has any items, return true"""
if mg and mg.ID in self.ITEMS_FORCEDMARKETGROUP_R:
return True
elif len(mg.items) > 0:
return True
else:
return False
def marketGroupValidityCheck(self, mg):
"""Check market group validity"""
# The only known case when group can be invalid is
# when it's declared to have types, but it doesn't contain anything
if mg.hasTypes and not self.marketGroupHasTypesCheck(mg):
return False
else:
return True
def getIconByMarketGroup(self, mg):
"""Return icon associated to marketgroup"""
if mg.icon:
return mg.icon.iconFile
else:
while mg and not mg.hasTypes:
mg = mg.parent
if not mg:
return ""
elif self.marketGroupHasTypesCheck(mg):
# Do not request variations to make process faster
# Pick random item and use its icon
items = self.getItemsByMarketGroup(mg, vars=False)
try:
item = items.pop()
except KeyError:
return ""
return item.icon.iconFile if item.icon else ""
elif self.getMarketGroupChildren(mg) > 0:
kids = self.getMarketGroupChildren(mg)
mktGroups = self.getIconByMarketGroup(kids)
size = len(mktGroups)
return mktGroups.pop() if size > 0 else ""
else:
return ""
def getPublicityByItem(self, item):
"""Return if an item is published"""
if item.name in self.ITEMS_FORCEPUBLISHED:
pub = self.ITEMS_FORCEPUBLISHED[item.name]
else:
pub = item.published
return pub
def getPublicityByGroup(self, group):
"""Return if an group is published"""
if group.name in self.GROUPS_FORCEPUBLISHED:
pub = self.GROUPS_FORCEPUBLISHED[group.name]
else:
pub = group.published
return pub
def getMarketRoot(self):
"""
Get the root of the market tree.
Returns a list, where each element is a tuple containing:
the ID, the name and the icon of the group
"""
root = set()
for id in self.ROOT_MARKET_GROUPS:
mg = self.getMarketGroup(id, eager="icon")
root.add(mg)
return root
def getShipRoot(self):
cat = self.getCategory("Ship")
root = set(self.getGroupsByCategory(cat))
return root
def getShipList(self, grpid):
"""Get ships for given group id"""
grp = self.getGroup(grpid, eager=("items", "items.group", "items.marketGroup"))
ships = self.getItemsByGroup(grp)
for ship in ships:
ship.race
return ships
def getShipListDelayed(self, id, callback):
"""Background version of getShipList"""
self.shipBrowserWorkerThread.queue.put((id, callback))
def searchShips(self, name):
"""Find ships according to given text pattern"""
results = eos.db.searchItems(name)
ships = set()
for item in results:
if self.getCategoryByItem(item).name == "Ship" and self.getPublicityByItem(item):
ships.add(item)
return ships
def searchItems(self, name, callback):
"""Find items according to given text pattern"""
self.searchWorkerThread.scheduleSearch(name, callback)
def directAttrRequest(self, items, attribs):
try:
itemIDs = tuple(map(lambda i: i.ID, items))
except TypeError:
itemIDs = (items.ID,)
try:
attrIDs = tuple(map(lambda i: i.ID, attribs))
except TypeError:
attrIDs = (attribs.ID,)
info = {}
for itemID, typeID, val in eos.db.directAttributeRequest(itemIDs, attrIDs):
info[itemID] = val
return info
def getImplantTree(self):
"""Return implant market group children"""
img = self.getMarketGroup(27)
return self.getMarketGroupChildren(img)
def filterItemsByMeta(self, items, metas):
"""Filter items by meta lvl"""
filtered = set(filter(lambda item: self.getMetaGroupIdByItem(item) in metas, items))
return filtered
def getPriceNow(self, typeID):
"""Get price for provided typeID"""
price = self.priceCache.get(typeID)
if price is None:
price = eos.db.getPrice(typeID)
if price is None:
price = eos.types.Price(typeID)
eos.db.add(price)
self.priceCache[typeID] = price
return price
def getPricesNow(self, typeIDs):
"""Return map of calls to get price against list of typeIDs"""
return map(self.getPrice, typeIDs)
def getPrices(self, typeIDs, callback):
"""Get prices for multiple typeIDs"""
requests = []
for typeID in typeIDs:
price = self.getPriceNow(typeID)
requests.append(price)
def cb():
try:
callback(requests)
except:
pass
eos.db.commit()
self.priceWorkerThread.trigger(requests, cb)
def getSystemWideEffects(self):
"""
Get dictionary with system-wide effects
"""
# Container for system-wide effects
effects = {}
# Expressions for matching when detecting effects we're looking for
validgroups = ("Black Hole Effect Beacon",
"Cataclysmic Variable Effect Beacon",
"Magnetar Effect Beacon",
"Pulsar Effect Beacon",
"Red Giant Beacon",
"Wolf Rayet Effect Beacon",
"Incursion ship attributes effects")
# Stuff we don't want to see in names
garbages = ("Effect", "Beacon", "ship attributes effects")
# Get group with all the system-wide beacons
grp = self.getGroup("Effect Beacon")
beacons = self.getItemsByGroup(grp)
# Cycle through them
for beacon in beacons:
# Check if it belongs to any valid group
for group in validgroups:
# Check beginning of the name only
if re.match(group, beacon.name):
# Get full beacon name
beaconname = beacon.name
for garbage in garbages:
beaconname = re.sub(garbage, "", beaconname)
beaconname = re.sub(" {2,}", " ", beaconname).strip()
# Get short name
shortname = re.sub(group, "", beacon.name)
for garbage in garbages:
shortname = re.sub(garbage, "", shortname)
shortname = re.sub(" {2,}", " ", shortname).strip()
# Get group name
groupname = group
for garbage in garbages:
groupname = re.sub(garbage, "", groupname)
groupname = re.sub(" {2,}", " ", groupname).strip()
# Add stuff to dictionary
if not groupname in effects:
effects[groupname] = set()
effects[groupname].add((beacon, beaconname, shortname))
# Break loop on 1st result
break
return effects