#=============================================================================== # 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 . #=============================================================================== import re import threading import wx import Queue import config import eos.db import eos.types from service.settings import SettingsProvider, NetworkSettings import service import service.conversions as conversions try: from collections import OrderedDict except ImportError: from 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 sMkt = Market.getInstance() while True: try: id, callback = queue.get() set = cache.get(id) if set is None: set = sMkt.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.wait = {} 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: service.Price.fetchPrices(requests) wx.CallAfter(callback) queue.task_done() # After we fetch prices, go through the list of waiting items and call their callbacks for price in requests: callbacks = self.wait.pop(price.typeID, None) if callbacks: for callback in callbacks: wx.CallAfter(callback) def trigger(self, prices, callbacks): self.queue.put((callbacks, prices)) def setToWait(self, itemID, callback): if itemID not in self.wait: self.wait[itemID] = [] self.wait[itemID].append(callback) 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() sMkt = Market.getInstance() # Rely on category data provided by eos as we don't hardcode them much in service filter = eos.types.Category.name.in_(sMkt.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 sMkt.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 "Stratios Emergency Responder": self.les_grp, # Issued for Somer Blink lottery "Miasmos Quafe Ultra Edition": self.les_grp, # Gift to people who purchased FF HD stream "InterBus Shuttle": self.les_grp, "Leopard": self.les_grp, # 2013 new year gift "Whiptail": self.les_grp, # AT12 prize "Chameleon": self.les_grp, # AT12 prize "Victorieux Luxury Yacht": self.les_grp, # Worlds Collide prize \o/ chinese getting owned "Imp": self.les_grp, # AT13 prize "Fiend": self.les_grp, # AT13 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 "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, "Goru's Shuttle": False, "Guristas Shuttle": False, "Mobile Decoy Unit": False, # Seems to be left over test mod for deployables "Tournament Micro Jump Unit": False, # Normally seen only on tournament arenas "Council Diplomatic Shuttle": False, # CSM X celebration] } # do not publish ships that we convert for name in conversions.packs['skinnedShips']: self.ITEMS_FORCEPUBLISHED[name] = False if config.debug: # Publish Tactical Dessy Modes if in debug # Cannot use GROUPS_FORCEPUBLISHED as this does not force items # within group to be published, but rather for the group itself # to show up on ship list group = self.getGroup("Ship Modifiers", eager="items") for item in group.items: self.ITEMS_FORCEPUBLISHED[item.name] = True # 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"), "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": 615, # Ship Equipment > Hull & Armor > Damage Controls "Civilian EM Ward Field": 1695, # Ship Equipment > Shield > Shield Hardeners > EM Shield Hardeners "Civilian Explosive Deflection Field": 1694, # Ship Equipment > Shield > Shield Hardeners > Explosive Shield Hardeners "Civilian Hobgoblin": 837, # Drones > Combat Drones > Light Scout Drones "Civilian Kinetic Deflection Field": 1693, # Ship Equipment > Shield > Shield Hardeners > Kinetic Shield Hardeners "Civilian Light Missile Launcher": 640, # Ship Equipment > Turrets & Bays > Missile Launchers > Light Missile Launchers "Civilian Scourge Light Missile": 920, # Ammunition & Charges > Missiles > Light Missiles > Standard Light Missiles "Civilian Small Remote Armor Repairer": 1059, # Ship Equipment > Hull & Armor > Remote Armor Repairers > Small "Civilian Small Remote Shield Booster": 603, # Ship Equipment > Shield > Remote Shield Boosters > Small "Civilian Stasis Webifier": 683, # Ship Equipment > Electronic Warfare > Stasis Webifiers "Civilian Thermic Dissipation Field": 1692, # Ship Equipment > Shield > Shield Hardeners > Thermal Shield Hardeners "Civilian Warp Disruptor": 1935, # Ship Equipment > Electronic Warfare > Warp Disruptors "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", "Deployable") self.ROOT_MARKET_GROUPS = (9, # Modules 1111, # Rigs 157, # Drones 11, # Ammo 1112, # Subsystems 24, # Implants & Boosters 404) # Deployables # 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): item = eos.db.getItem(identity, *args, **kwargs) elif isinstance(identity, basestring): # We normally lookup with string when we are using import/export # features. Check against overrides identity = conversions.all.get(identity, identity) 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""" filter = eos.types.Category.name.in_(["Ship"]) results = eos.db.searchItems(name, where=filter, join=(eos.types.Item.group, eos.types.Group.category), eager=("icon", "group.category", "metaGroup", "metaGroup.parent")) ships = set() for item in results: if 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 waitForPrice(self, item, callback): """ Wait for prices to be fetched and callback when finished. This is used with the column prices for modules. Instead of calling them individually, we set them to wait until the entire fit price is called and calculated (see GH #290) """ def cb(): try: callback(item) except: pass self.priceWorkerThread.setToWait(item.ID, cb) def clearPriceCache(self): self.priceCache.clear() deleted_rows = eos.db.clearPrices() 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