Merge branch 'pricing_v2' into development
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()))
|
||||
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -8,4 +8,5 @@ __all__ = [
|
||||
"outgoingViewMinimal",
|
||||
"targetingMiscViewMinimal",
|
||||
"priceViewFull",
|
||||
"priceViewMinimal",
|
||||
]
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
|
||||
141
gui/builtinStatsViews/priceViewMinimal.py
Normal file
141
gui/builtinStatsViews/priceViewMinimal.py
Normal 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()
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ from gui.builtinStatsViews import ( # noqa: E402, F401
|
||||
rechargeViewFull,
|
||||
targetingMiscViewMinimal,
|
||||
priceViewFull,
|
||||
priceViewMinimal,
|
||||
outgoingViewFull,
|
||||
outgoingViewMinimal,
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
123
service/price.py
123
service/price.py
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user