Merge branch 'pricing_v2' into development

This commit is contained in:
blitzmann
2017-04-10 20:46:57 -04:00
16 changed files with 402 additions and 179 deletions

View File

@@ -68,7 +68,7 @@ else:
saveddata_meta = None
# Lock controlling any changes introduced to session
sd_lock = threading.Lock()
sd_lock = threading.RLock()
# Import all the definitions for all our database stuff
# noinspection PyPep8

View File

@@ -23,6 +23,7 @@ from sqlalchemy.orm import reconstructor
import eos.db
from eqBase import EqBase
from eos.saveddata.price import Price as types_Price
try:
from collections import OrderedDict
@@ -438,6 +439,39 @@ class Item(EqBase):
return False
@property
def price(self):
try:
if not hasattr(self, "__price"):
self.__price = types_Price(self.ID)
# Get the price from the DB
price = eos.db.getPrice(self.ID)
if price:
if self.__price.time > price.time:
# The object is newer than the DB, update the DB.
eos.db.add(self.__price)
eos.db.commit()
if self.__price.time < price.time:
# DB object is newer than local object, update the local object
self.__price = price
else:
pyfalog.debug("Unable to fetch item price from database.")
return self.__price
except Exception as e:
# We want to catch our failure and log it, but don't bail out for a single missing price tag.
pyfalog.error("Failed to get price for typeID: {0}", self.ID)
pyfalog.error(e)
if not self.__price.price:
self.__price.price = 0
self.__price.failed = True
return self.__price
def __repr__(self):
return "Item(ID={}, name={}) at {}".format(
self.ID, self.name, hex(id(self))

View File

@@ -21,6 +21,9 @@
import time
from sqlalchemy.orm import reconstructor
from logbook import Logger
pyfalog = Logger(__name__)
class Price(object):
@@ -29,7 +32,6 @@ class Price(object):
self.time = 0
self.price = 0
self.failed = None
self.__item = None
@reconstructor
def init(self):

View File

@@ -43,9 +43,12 @@ class BoosterViewDrop(wx.PyDropTarget):
class BoosterView(d.Display):
DEFAULT_COLS = ["State",
"attr:boosterness",
"Base Name"]
DEFAULT_COLS = [
"State",
"attr:boosterness",
"Base Name",
"Price",
]
def __init__(self, parent):
d.Display.__init__(self, parent, style=wx.LC_SINGLE_SEL | wx.BORDER_NONE)

View File

@@ -3,7 +3,6 @@ import gui.mainFrame
# noinspection PyPackageRequirements
import wx
import gui.globalEvents as GE
from service.market import Market
from service.settings import ContextMenuSettings
@@ -22,8 +21,6 @@ class PriceClear(ContextMenu):
return "Reset Price Cache"
def activate(self, fullContext, selection, i):
sMkt = Market.getInstance()
sMkt.clearPriceCache()
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit()))

View File

@@ -9,7 +9,6 @@ import gui.globalEvents as GE
from service.settings import SettingsProvider
from service.fit import Fit
from service.price import Price
from service.market import Market
class PFGeneralPref(PreferenceView):
@@ -197,9 +196,6 @@ class PFGeneralPref(PreferenceView):
fitID = self.mainFrame.getActiveFit()
sMkt = Market.getInstance()
sMkt.clearPriceCache()
self.sFit.refreshFit(fitID)
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
event.Skip()

View File

@@ -91,8 +91,6 @@ class PFStatViewPref(PreferenceView):
rbSizerRow3 = wx.BoxSizer(wx.HORIZONTAL)
self.rbPrice = wx.RadioBox(panel, -1, "Price", wx.DefaultPosition, wx.DefaultSize, ['None', 'Minimal', 'Full'], 1, wx.RA_SPECIFY_COLS)
# Disable minimal as we don't have a view for this yet
self.rbPrice.EnableItem(1, False)
self.rbPrice.SetSelection(self.settings.get('price'))
rbSizerRow3.Add(self.rbPrice, 1, wx.TOP | wx.RIGHT, 5)
self.rbPrice.Bind(wx.EVT_RADIOBOX, self.OnPriceChange)

View File

@@ -8,4 +8,5 @@ __all__ = [
"outgoingViewMinimal",
"targetingMiscViewMinimal",
"priceViewFull",
"priceViewMinimal",
]

View File

@@ -22,7 +22,6 @@ import wx
from gui.statsView import StatsView
from gui.bitmapLoader import BitmapLoader
from gui.utils.numberFormatter import formatAmount
from service.market import Market
from service.price import Price
@@ -32,9 +31,6 @@ class PriceViewFull(StatsView):
def __init__(self, parent):
StatsView.__init__(self)
self.parent = parent
self._cachedShip = 0
self._cachedFittings = 0
self._cachedTotal = 0
def getHeaderText(self, fit):
return "Price"
@@ -51,10 +47,16 @@ class PriceViewFull(StatsView):
headerContentSizer.Add(self.labelEMStatus)
headerPanel.GetParent().AddToggleItem(self.labelEMStatus)
gridPrice = wx.GridSizer(1, 3)
gridPrice = wx.GridSizer(2, 3)
contentSizer.Add(gridPrice, 0, wx.EXPAND | wx.ALL, 0)
for type in ("ship", "fittings", "total"):
image = "%sPrice_big" % type if type != "ship" else "ship_big"
for _type in ("ship", "fittings", "drones", "cargoBay", "character", "total"):
if _type in "ship":
image = "ship_big"
elif _type in ("fittings", "total"):
image = "%sPrice_big" % _type
else:
image = "%s_big" % _type
box = wx.BoxSizer(wx.HORIZONTAL)
gridPrice.Add(box, 0, wx.ALIGN_TOP)
@@ -63,50 +65,91 @@ class PriceViewFull(StatsView):
vbox = wx.BoxSizer(wx.VERTICAL)
box.Add(vbox, 1, wx.EXPAND)
vbox.Add(wx.StaticText(contentPanel, wx.ID_ANY, type.capitalize()), 0, wx.ALIGN_LEFT)
vbox.Add(wx.StaticText(contentPanel, wx.ID_ANY, _type.capitalize()), 0, wx.ALIGN_LEFT)
hbox = wx.BoxSizer(wx.HORIZONTAL)
vbox.Add(hbox)
lbl = wx.StaticText(contentPanel, wx.ID_ANY, "0.00 ISK")
setattr(self, "labelPrice%s" % type.capitalize(), lbl)
setattr(self, "labelPrice%s" % _type.capitalize(), lbl)
hbox.Add(lbl, 0, wx.ALIGN_LEFT)
def refreshPanel(self, fit):
if fit is not None:
self.fit = fit
typeIDs = Price.fitItemsList(fit)
fit_items = Price.fitItemsList(fit)
sMkt = Market.getInstance()
sMkt.getPrices(typeIDs, self.processPrices)
sPrice = Price.getInstance()
sPrice.getPrices(fit_items, self.processPrices)
self.labelEMStatus.SetLabel("Updating prices...")
else:
self.labelEMStatus.SetLabel("")
self.labelPriceShip.SetLabel("0.0 ISK")
self.labelPriceFittings.SetLabel("0.0 ISK")
self.labelPriceTotal.SetLabel("0.0 ISK")
self._cachedFittings = self._cachedShip = self._cachedTotal = 0
self.panel.Layout()
self.refreshPanelPrices(fit)
self.panel.Layout()
def refreshPanelPrices(self, fit=None):
ship_price = 0
module_price = 0
drone_price = 0
fighter_price = 0
cargo_price = 0
booster_price = 0
implant_price = 0
if fit:
ship_price = fit.ship.item.price.price
if fit.modules:
for module in fit.modules:
if not module.isEmpty:
module_price += module.item.price.price
if fit.drones:
for drone in fit.drones:
drone_price += drone.item.price.price * drone.amount
if fit.fighters:
for fighter in fit.fighters:
fighter_price += fighter.item.price.price * fighter.amount
if fit.cargo:
for cargo in fit.cargo:
cargo_price += cargo.item.price.price * cargo.amount
if fit.boosters:
for booster in fit.boosters:
booster_price += booster.item.price.price
if fit.implants:
for implant in fit.implants:
implant_price += implant.item.price.price
fitting_price = module_price + drone_price + fighter_price + cargo_price + booster_price + implant_price
total_price = ship_price + fitting_price
self.labelPriceShip.SetLabel("%s ISK" % formatAmount(ship_price, 3, 3, 9, currency=True))
self.labelPriceShip.SetToolTip(wx.ToolTip('{:,.2f}'.format(ship_price)))
self.labelPriceFittings.SetLabel("%s ISK" % formatAmount(module_price, 3, 3, 9, currency=True))
self.labelPriceFittings.SetToolTip(wx.ToolTip('{:,.2f}'.format(module_price)))
self.labelPriceDrones.SetLabel("%s ISK" % formatAmount(drone_price + fighter_price, 3, 3, 9, currency=True))
self.labelPriceDrones.SetToolTip(wx.ToolTip('{:,.2f}'.format(drone_price + fighter_price)))
self.labelPriceCargobay.SetLabel("%s ISK" % formatAmount(cargo_price, 3, 3, 9, currency=True))
self.labelPriceCargobay.SetToolTip(wx.ToolTip('{:,.2f}'.format(cargo_price)))
self.labelPriceCharacter.SetLabel("%s ISK" % formatAmount(booster_price + implant_price, 3, 3, 9, currency=True))
self.labelPriceCharacter.SetToolTip(wx.ToolTip('{:,.2f}'.format(booster_price + implant_price)))
self.labelPriceTotal.SetLabel("%s ISK" % formatAmount(total_price, 3, 3, 9, currency=True))
self.labelPriceTotal.SetToolTip(wx.ToolTip('{:,.2f}'.format(total_price)))
def processPrices(self, prices):
shipPrice = prices[0].price
modPrice = sum(map(lambda p: p.price or 0, prices[1:]))
self.refreshPanelPrices(self.fit)
self.labelEMStatus.SetLabel("")
if self._cachedShip != shipPrice:
self.labelPriceShip.SetLabel("%s ISK" % formatAmount(shipPrice, 3, 3, 9, currency=True))
self.labelPriceShip.SetToolTip(wx.ToolTip('{:,.2f}'.format(shipPrice)))
self._cachedShip = shipPrice
if self._cachedFittings != modPrice:
self.labelPriceFittings.SetLabel("%s ISK" % formatAmount(modPrice, 3, 3, 9, currency=True))
self.labelPriceFittings.SetToolTip(wx.ToolTip('{:,.2f}'.format(modPrice)))
self._cachedFittings = modPrice
if self._cachedTotal != (shipPrice + modPrice):
self.labelPriceTotal.SetLabel("%s ISK" % formatAmount(shipPrice + modPrice, 3, 3, 9, currency=True))
self.labelPriceTotal.SetToolTip(wx.ToolTip('{:,.2f}'.format(shipPrice + modPrice)))
self._cachedTotal = shipPrice + modPrice
self.panel.Layout()

View File

@@ -0,0 +1,141 @@
# =============================================================================
# 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/>.
# =============================================================================
# noinspection PyPackageRequirements
import wx
from gui.statsView import StatsView
from gui.bitmapLoader import BitmapLoader
from gui.utils.numberFormatter import formatAmount
from service.price import Price
class PriceViewMinimal(StatsView):
name = "priceViewMinimal"
def __init__(self, parent):
StatsView.__init__(self)
self.parent = parent
def getHeaderText(self, fit):
return "Price"
def populatePanel(self, contentPanel, headerPanel):
contentSizer = contentPanel.GetSizer()
self.panel = contentPanel
self.headerPanel = headerPanel
headerContentSizer = wx.BoxSizer(wx.HORIZONTAL)
hsizer = headerPanel.GetSizer()
hsizer.Add(headerContentSizer, 0, 0, 0)
self.labelEMStatus = wx.StaticText(headerPanel, wx.ID_ANY, "")
headerContentSizer.Add(self.labelEMStatus)
headerPanel.GetParent().AddToggleItem(self.labelEMStatus)
gridPrice = wx.GridSizer(1, 3)
contentSizer.Add(gridPrice, 0, wx.EXPAND | wx.ALL, 0)
for _type in ("ship", "fittings", "total"):
image = "%sPrice_big" % _type if _type != "ship" else "ship_big"
box = wx.BoxSizer(wx.HORIZONTAL)
gridPrice.Add(box, 0, wx.ALIGN_TOP)
box.Add(BitmapLoader.getStaticBitmap(image, contentPanel, "gui"), 0, wx.ALIGN_CENTER)
vbox = wx.BoxSizer(wx.VERTICAL)
box.Add(vbox, 1, wx.EXPAND)
vbox.Add(wx.StaticText(contentPanel, wx.ID_ANY, _type.capitalize()), 0, wx.ALIGN_LEFT)
hbox = wx.BoxSizer(wx.HORIZONTAL)
vbox.Add(hbox)
lbl = wx.StaticText(contentPanel, wx.ID_ANY, "0.00 ISK")
setattr(self, "labelPrice%s" % _type.capitalize(), lbl)
hbox.Add(lbl, 0, wx.ALIGN_LEFT)
def refreshPanel(self, fit):
if fit is not None:
self.fit = fit
fit_items = Price.fitItemsList(fit)
sPrice = Price.getInstance()
sPrice.getPrices(fit_items, self.processPrices)
self.labelEMStatus.SetLabel("Updating prices...")
self.refreshPanelPrices(fit)
self.panel.Layout()
def refreshPanelPrices(self, fit=None):
ship_price = 0
module_price = 0
drone_price = 0
fighter_price = 0
cargo_price = 0
booster_price = 0
implant_price = 0
if fit:
ship_price = fit.ship.item.price.price
if fit.modules:
for module in fit.modules:
if not module.isEmpty:
module_price += module.item.price.price
if fit.drones:
for drone in fit.drones:
drone_price += drone.item.price.price * drone.amount
if fit.fighters:
for fighter in fit.fighters:
fighter_price += fighter.item.price.price * fighter.amount
if fit.cargo:
for cargo in fit.cargo:
cargo_price += cargo.item.price.price * cargo.amount
if fit.boosters:
for booster in fit.boosters:
booster_price += booster.item.price.price
if fit.implants:
for implant in fit.implants:
implant_price += implant.item.price.price
fitting_price = module_price + drone_price + fighter_price + cargo_price + booster_price + implant_price
total_price = ship_price + fitting_price
self.labelPriceShip.SetLabel("%s ISK" % formatAmount(ship_price, 3, 3, 9, currency=True))
self.labelPriceShip.SetToolTip(wx.ToolTip('{:,.2f}'.format(ship_price)))
self.labelPriceFittings.SetLabel("%s ISK" % formatAmount(fitting_price, 3, 3, 9, currency=True))
self.labelPriceFittings.SetToolTip(wx.ToolTip('{:,.2f}'.format(fitting_price)))
self.labelPriceTotal.SetLabel("%s ISK" % formatAmount(total_price, 3, 3, 9, currency=True))
self.labelPriceTotal.SetToolTip(wx.ToolTip('{:,.2f}'.format(total_price)))
def processPrices(self, prices):
self.refreshPanelPrices(self.fit)
self.labelEMStatus.SetLabel("")
self.panel.Layout()
PriceViewMinimal.register()

View File

@@ -22,7 +22,7 @@ import wx
from eos.saveddata.cargo import Cargo
from eos.saveddata.drone import Drone
from service.market import Market
from service.price import Price as ServicePrice
from gui.viewColumn import ViewColumn
from gui.bitmapLoader import BitmapLoader
from gui.utils.numberFormatter import formatAmount
@@ -41,13 +41,15 @@ class Price(ViewColumn):
if stuff.item is None or stuff.item.group.name == "Ship Modifiers":
return ""
sMkt = Market.getInstance()
price = sMkt.getPriceNow(stuff.item.ID)
if hasattr(stuff, "isEmpty"):
if stuff.isEmpty:
return ""
if not price or not price.price or not price.isValid:
return False
sPrice = ServicePrice.getInstance()
price = sPrice.getPriceNow(stuff.item)
price = price.price # Set new price variable with what we need
if not price:
return ""
if isinstance(stuff, Drone) or isinstance(stuff, Cargo):
price *= stuff.amount
@@ -55,10 +57,10 @@ class Price(ViewColumn):
return formatAmount(price, 3, 3, 9, currency=True)
def delayedText(self, mod, display, colItem):
sMkt = Market.getInstance()
sPrice = ServicePrice.getInstance()
def callback(item):
price = sMkt.getPriceNow(item.ID)
price = sPrice.getPriceNow(item.ID)
text = formatAmount(price.price, 3, 3, 9, currency=True) if price.price else ""
if price.failed:
text += " (!)"
@@ -66,7 +68,7 @@ class Price(ViewColumn):
display.SetItem(colItem)
sMkt.waitForPrice(mod.item, callback)
sPrice.getPrices([mod.item], callback, True)
def getImageId(self, mod):
return -1

View File

@@ -79,10 +79,13 @@ class ImplantView(wx.Panel):
class ImplantDisplay(d.Display):
DEFAULT_COLS = ["State",
"attr:implantness",
"Base Icon",
"Base Name"]
DEFAULT_COLS = [
"State",
"attr:implantness",
"Base Icon",
"Base Name",
"Price",
]
def __init__(self, parent):
d.Display.__init__(self, parent, style=wx.LC_SINGLE_SEL | wx.BORDER_NONE)

View File

@@ -43,6 +43,7 @@ from eos.saveddata.citadel import Citadel
from eos.saveddata.fit import Fit
from service.market import Market
from service.attribute import Attribute
from service.price import Price as ServicePrice
import gui.mainFrame
from gui.bitmapLoader import BitmapLoader
from gui.utils.numberFormatter import formatAmount
@@ -623,7 +624,7 @@ class ItemCompare(wx.Panel):
def processPrices(self, prices):
for i, price in enumerate(prices):
self.paramList.SetStringItem(i, len(self.attrs) + 1, formatAmount(price.price, 3, 3, 9, currency=True))
self.paramList.SetStringItem(i, len(self.attrs) + 1, formatAmount(price.value, 3, 3, 9, currency=True))
def PopulateList(self, sort=None):
@@ -660,9 +661,6 @@ class ItemCompare(wx.Panel):
self.paramList.InsertColumn(len(self.attrs) + 1, "Price")
self.paramList.SetColumnWidth(len(self.attrs) + 1, 60)
sMkt = Market.getInstance()
sMkt.getPrices([x.ID for x in self.items], self.processPrices)
for item in self.items:
i = self.paramList.InsertStringItem(sys.maxint, item.name)
for x, attr in enumerate(self.attrs.keys()):
@@ -678,6 +676,10 @@ class ItemCompare(wx.Panel):
self.paramList.SetStringItem(i, x + 1, valueUnit)
# Add prices
sPrice = ServicePrice.getInstance()
self.paramList.SetStringItem(i, len(self.attrs) + 1, formatAmount(sPrice.getPriceNow(item), 3, 3, 9, currency=True))
self.paramList.RefreshRows()
self.Layout()

View File

@@ -52,6 +52,7 @@ from gui.builtinStatsViews import ( # noqa: E402, F401
rechargeViewFull,
targetingMiscViewMinimal,
priceViewFull,
priceViewMinimal,
outgoingViewFull,
outgoingViewMinimal,
)

View File

@@ -30,11 +30,10 @@ import config
import eos.db
from service import conversions
from service.settings import SettingsProvider
from service.price import Price
from eos.gamedata import Category as types_Category, Group as types_Group, Item as types_Item, MarketGroup as types_MarketGroup, \
MetaGroup as types_MetaGroup, MetaType as types_MetaType
from eos.saveddata.price import Price as types_Price
try:
from collections import OrderedDict
@@ -85,48 +84,6 @@ class ShipBrowserWorkerThread(threading.Thread):
pyfalog.critical(e)
class PriceWorkerThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.name = "PriceWorker"
pyfalog.debug("Initialize PriceWorkerThread.")
def run(self):
pyfalog.debug("Run start")
self.queue = Queue.Queue()
self.wait = {}
self.processUpdates()
pyfalog.debug("Run end")
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:
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 __init__(self):
threading.Thread.__init__(self)
@@ -180,7 +137,6 @@ class Market(object):
instance = None
def __init__(self):
self.priceCache = {}
# Init recently used module storage
serviceMarketRecentlyUsedModules = {"pyfaMarketRecentlyUsedModules": []}
@@ -188,11 +144,6 @@ class Market(object):
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
@@ -855,60 +806,6 @@ class Market(object):
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 = 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 Exception as e:
pyfalog.critical("Callback failed.")
pyfalog.critical(e)
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 Exception as e:
pyfalog.critical("Callback failed.")
pyfalog.critical(e)
self.priceWorkerThread.setToWait(item.ID, cb)
def clearPriceCache(self):
self.priceCache.clear()
eos.db.clearPrices()
def getSystemWideEffects(self):
"""
Get dictionary with system-wide effects

View File

@@ -19,12 +19,17 @@
import time
import threading
import Queue
from xml.dom import minidom
from logbook import Logger
import wx
from eos import db
from service.network import Network, TimeoutError
from service.fit import Fit
from logbook import Logger
from service.market import Market
pyfalog = Logger(__name__)
@@ -35,6 +40,8 @@ TIMEOUT = 15 * 60 # Network timeout delay for connection issues, 15 minutes
class Price(object):
instance = None
systemsList = {
"Jita": 30000142,
"Amarr": 30002187,
@@ -43,10 +50,17 @@ class Price(object):
"Hek": 30002053
}
def __init__(self):
# Start price fetcher
self.priceWorkerThread = PriceWorkerThread()
self.priceWorkerThread.daemon = True
self.priceWorkerThread.start()
@classmethod
def invalidPrices(cls, prices):
for price in prices:
price.time = 0
def getInstance(cls):
if cls.instance is None:
cls.instance = Price()
return cls.instance
@classmethod
def fetchPrices(cls, prices):
@@ -113,6 +127,10 @@ class Price(object):
priceobj.time = time.time() + VALIDITY
priceobj.failed = None
# Update the DB.
db.add(priceobj)
db.commit()
# delete price from working dict
del priceMap[typeID]
@@ -124,6 +142,11 @@ class Price(object):
priceobj = priceMap[typeID]
priceobj.time = time.time() + TIMEOUT
priceobj.failed = True
# Update the DB.
db.add(priceobj)
db.commit()
del priceMap[typeID]
except:
# all other errors will pass and continue onward to the REREQUEST delay
@@ -136,22 +159,102 @@ class Price(object):
priceobj.time = time.time() + REREQUEST
priceobj.failed = True
# Update the DB.
db.add(priceobj)
db.commit()
@classmethod
def fitItemsList(cls, fit):
# Compose a list of all the data we need & request it
typeIDs = [fit.ship.item.ID]
fit_items = [fit.ship.item]
for mod in fit.modules:
if not mod.isEmpty:
typeIDs.append(mod.itemID)
fit_items.append(mod.item)
for drone in fit.drones:
typeIDs.append(drone.itemID)
fit_items.append(drone.item)
for fighter in fit.fighters:
typeIDs.append(fighter.itemID)
fit_items.append(fighter.item)
for cargo in fit.cargo:
typeIDs.append(cargo.itemID)
fit_items.append(cargo.item)
return typeIDs
for boosters in fit.boosters:
fit_items.append(boosters.item)
for implants in fit.implants:
fit_items.append(implants.item)
return list(set(fit_items))
def getPriceNow(self, objitem):
"""Get price for provided typeID"""
sMkt = Market.getInstance()
item = sMkt.getItem(objitem)
return item.price.price
def getPrices(self, objitems, callback, waitforthread=False):
"""Get prices for multiple typeIDs"""
requests = []
for objitem in objitems:
sMkt = Market.getInstance()
item = sMkt.getItem(objitem)
requests.append(item.price)
def cb():
try:
callback(requests)
except Exception as e:
pyfalog.critical("Callback failed.")
pyfalog.critical(e)
db.commit()
if waitforthread:
self.priceWorkerThread.setToWait(requests, cb)
else:
self.priceWorkerThread.trigger(requests, cb)
class PriceWorkerThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.name = "PriceWorker"
pyfalog.debug("Initialize PriceWorkerThread.")
def run(self):
pyfalog.debug("Run start")
self.queue = Queue.Queue()
self.wait = {}
self.processUpdates()
pyfalog.debug("Run end")
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:
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)