Files
pyfa/service/market.py
2015-09-01 01:26:30 +03:00

796 lines
34 KiB
Python

#===============================================================================
# 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 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