diff --git a/eos/db/migrations/upgrade30.py b/eos/db/migrations/upgrade30.py new file mode 100644 index 000000000..7954f2d37 --- /dev/null +++ b/eos/db/migrations/upgrade30.py @@ -0,0 +1,17 @@ +""" +Migration 30 + +- changes to prices table +""" + + +import sqlalchemy + + +def upgrade(saveddata_engine): + try: + saveddata_engine.execute("SELECT status FROM prices LIMIT 1") + except sqlalchemy.exc.DatabaseError: + # Just drop table, table will be re-created by sqlalchemy and + # data will be re-fetched + saveddata_engine.execute("DROP TABLE prices;") diff --git a/eos/db/saveddata/price.py b/eos/db/saveddata/price.py index 8be7f6519..8abd07132 100644 --- a/eos/db/saveddata/price.py +++ b/eos/db/saveddata/price.py @@ -17,17 +17,20 @@ # along with eos. If not, see . # =============================================================================== + from sqlalchemy import Table, Column, Float, Integer from sqlalchemy.orm import mapper from eos.db import saveddata_meta from eos.saveddata.price import Price + prices_table = Table("prices", saveddata_meta, Column("typeID", Integer, primary_key=True), Column("price", Float, default=0.0), Column("time", Integer, nullable=False), - Column("failed", Integer)) + Column("status", Integer, nullable=False)) + mapper(Price, prices_table, properties={ "_Price__price": prices_table.c.price, diff --git a/eos/gamedata.py b/eos/gamedata.py index d32edf6e9..0243eec0f 100644 --- a/eos/gamedata.py +++ b/eos/gamedata.py @@ -446,17 +446,6 @@ class Item(EqBase): @property def price(self): - priceObj = self.priceObj - if not priceObj: - return 0 - else: - return priceObj.price - - @property - def priceObj(self): - if not self.marketGroupID: - return None - # todo: use `from sqlalchemy import inspect` instead (mac-deprecated doesn't have inspect(), was imp[lemented in 0.8) if self.__priceObj is not None and getattr(self.__priceObj, '_sa_instance_state', None) and self.__priceObj._sa_instance_state.deleted: pyfalog.debug("Price data for {} was deleted (probably from a cache reset), resetting object".format(self.ID)) @@ -469,10 +458,7 @@ class Item(EqBase): pyfalog.debug("Creating a price for {}".format(self.ID)) self.__priceObj = types_Price(self.ID) eos.db.add(self.__priceObj) - # Commented out by DarkPhoenix: it caused issues when opening stats for item with many - # variations, as each commit takes ~50 ms, for items with 30 variations time to open stats - # window could reach 2 seconds. Hopefully just adding it is sufficient. - # eos.db.commit() + eos.db.commit() else: self.__priceObj = db_price diff --git a/eos/modifiedAttributeDict.py b/eos/modifiedAttributeDict.py index d660dd38b..756a9f806 100644 --- a/eos/modifiedAttributeDict.py +++ b/eos/modifiedAttributeDict.py @@ -215,6 +215,8 @@ class ModifiedAttributeDict(collections.MutableMapping): if force is not None: if cappingValue is not None: force = min(force, cappingValue) + if key in (50, 30, 48, 11): + force = round(force, 2) return force # Grab our values if they're there, otherwise we'll take default values preIncrease = self.__preIncreases.get(key, 0) @@ -268,7 +270,8 @@ class ModifiedAttributeDict(collections.MutableMapping): # Cap value if we have cap defined if cappingValue is not None: val = min(val, cappingValue) - + if key in (50, 30, 48, 11): + val = round(val, 2) return val def __handleSkill(self, skillName): diff --git a/eos/saveddata/fit.py b/eos/saveddata/fit.py index 4373bcdd0..f33a78651 100644 --- a/eos/saveddata/fit.py +++ b/eos/saveddata/fit.py @@ -1016,11 +1016,11 @@ class Fit(object): @property def pgUsed(self): - return self.getItemAttrOnlineSum(self.modules, "power") + return round(self.getItemAttrOnlineSum(self.modules, "power"), 2) @property def cpuUsed(self): - return self.getItemAttrOnlineSum(self.modules, "cpu") + return round(self.getItemAttrOnlineSum(self.modules, "cpu"), 2) @property def droneBandwidthUsed(self): diff --git a/eos/saveddata/price.py b/eos/saveddata/price.py index 6618119d0..ee575b70c 100644 --- a/eos/saveddata/price.py +++ b/eos/saveddata/price.py @@ -18,24 +18,30 @@ # along with eos. If not, see . # =============================================================================== -import time -from sqlalchemy.orm import reconstructor +import time +from enum import IntEnum, unique + from logbook import Logger + pyfalog = Logger(__name__) +@unique +class PriceStatus(IntEnum): + notFetched = 0 + success = 1 + fail = 2 + notSupported = 3 + + class Price(object): def __init__(self, typeID): self.typeID = typeID self.time = 0 self.__price = 0 - self.failed = None - - @reconstructor - def init(self): - self.__item = None + self.status = PriceStatus.notFetched @property def isValid(self): @@ -43,7 +49,10 @@ class Price(object): @property def price(self): - return self.__price or 0.0 + if self.status in (PriceStatus.fail, PriceStatus.notSupported): + return 0 + else: + return self.__price or 0 @price.setter def price(self, price): diff --git a/gui/builtinItemStatsViews/itemCompare.py b/gui/builtinItemStatsViews/itemCompare.py index 3981265f1..97a4b953d 100644 --- a/gui/builtinItemStatsViews/itemCompare.py +++ b/gui/builtinItemStatsViews/itemCompare.py @@ -133,7 +133,7 @@ class ItemCompare(wx.Panel): except IndexError: # Price if sort == len(self.attrs) + 1: - func = lambda i: i.price if i.price != 0 else float("Inf") + func = lambda i: i.price.price if i.price.price != 0 else float("Inf") # Something else else: self.sortReverse = False @@ -168,7 +168,7 @@ class ItemCompare(wx.Panel): self.paramList.SetItem(i, x + 1, valueUnit) # Add prices - self.paramList.SetItem(i, len(self.attrs) + 1, formatAmount(item.price, 3, 3, 9, currency=True) if item.price else "") + self.paramList.SetItem(i, len(self.attrs) + 1, formatAmount(item.price.price, 3, 3, 9, currency=True) if item.price.price else "") self.paramList.RefreshRows() self.Layout() diff --git a/gui/builtinStatsViews/priceViewFull.py b/gui/builtinStatsViews/priceViewFull.py index c2cb05f7d..767ad958f 100644 --- a/gui/builtinStatsViews/priceViewFull.py +++ b/gui/builtinStatsViews/priceViewFull.py @@ -100,32 +100,32 @@ class PriceViewFull(StatsView): implant_price = 0 if fit: - ship_price = fit.ship.item.price + ship_price = fit.ship.item.price.price if fit.modules: for module in fit.modules: if not module.isEmpty: - module_price += module.item.price + module_price += module.item.price.price if fit.drones: for drone in fit.drones: - drone_price += drone.item.price * drone.amount + drone_price += drone.item.price.price * drone.amount if fit.fighters: for fighter in fit.fighters: - fighter_price += fighter.item.price * fighter.amountActive + fighter_price += fighter.item.price.price * fighter.amountActive if fit.cargo: for cargo in fit.cargo: - cargo_price += cargo.item.price * cargo.amount + cargo_price += cargo.item.price.price * cargo.amount if fit.boosters: for booster in fit.boosters: - booster_price += booster.item.price + booster_price += booster.item.price.price if fit.implants: for implant in fit.implants: - implant_price += implant.item.price + implant_price += implant.item.price.price total_price = 0 diff --git a/gui/builtinStatsViews/priceViewMinimal.py b/gui/builtinStatsViews/priceViewMinimal.py index fb9877690..8d6a5ce83 100644 --- a/gui/builtinStatsViews/priceViewMinimal.py +++ b/gui/builtinStatsViews/priceViewMinimal.py @@ -94,32 +94,32 @@ class PriceViewMinimal(StatsView): implant_price = 0 if fit: - ship_price = fit.ship.item.price + ship_price = fit.ship.item.price.price if fit.modules: for module in fit.modules: if not module.isEmpty: - module_price += module.item.price + module_price += module.item.price.price if fit.drones: for drone in fit.drones: - drone_price += drone.item.price * drone.amount + drone_price += drone.item.price.price * drone.amount if fit.fighters: for fighter in fit.fighters: - fighter_price += fighter.item.price * fighter.amountActive + fighter_price += fighter.item.price.price * fighter.amountActive if fit.cargo: for cargo in fit.cargo: - cargo_price += cargo.item.price * cargo.amount + cargo_price += cargo.item.price.price * cargo.amount if fit.boosters: for booster in fit.boosters: - booster_price += booster.item.price + booster_price += booster.item.price.price if fit.implants: for implant in fit.implants: - implant_price += implant.item.price + implant_price += implant.item.price.price fitting_price = module_price diff --git a/gui/builtinViewColumns/price.py b/gui/builtinViewColumns/price.py index 65ebbfb36..56901c172 100644 --- a/gui/builtinViewColumns/price.py +++ b/gui/builtinViewColumns/price.py @@ -22,6 +22,7 @@ import wx from eos.saveddata.cargo import Cargo from eos.saveddata.drone import Drone +from eos.saveddata.price import PriceStatus from service.price import Price as ServicePrice from gui.viewColumn import ViewColumn from gui.bitmap_loader import BitmapLoader @@ -45,10 +46,7 @@ class Price(ViewColumn): if stuff.isEmpty: return "" - priceObj = stuff.item.priceObj - - if not priceObj: - return "" + priceObj = stuff.item.price if not priceObj.isValid: return False @@ -69,10 +67,12 @@ class Price(ViewColumn): def callback(item): price = item[0] - text = formatAmount(price.price, 3, 3, 9, currency=True) if price.price else "" - if price.failed: - text += " (!)" - colItem.SetText(text) + textItems = [] + if price.price: + textItems.append(formatAmount(price.price, 3, 3, 9, currency=True)) + if price.status == PriceStatus.fail: + textItems.append("(!)") + colItem.SetText(" ".join(textItems)) display.SetItem(colItem) diff --git a/service/price.py b/service/price.py index 7adb5120a..3028b74ad 100644 --- a/service/price.py +++ b/service/price.py @@ -26,6 +26,7 @@ import wx from logbook import Logger from eos import db +from eos.saveddata.price import PriceStatus from service.fit import Fit from service.market import Market from service.network import TimeoutError @@ -85,13 +86,18 @@ class Price(object): toRequest = set() # Compose list of items we're going to request - for typeID in priceMap: + for typeID in tuple(priceMap): # Get item object item = db.getItem(typeID) # We're not going to request items only with market group, as eve-central # doesn't provide any data for items not on the market - if item is not None and item.marketGroupID: - toRequest.add(typeID) + if item is None: + continue + if not item.marketGroupID: + priceMap[typeID].status = PriceStatus.notSupported + del priceMap[typeID] + continue + toRequest.add(typeID) # Do not waste our time if all items are not on the market if len(toRequest) == 0: @@ -117,11 +123,10 @@ class Price(object): except TimeoutError: # Timeout error deserves special treatment pyfalog.warning("Price fetch timout") - for typeID in priceMap.keys(): + for typeID in tuple(priceMap): priceobj = priceMap[typeID] priceobj.time = time.time() + TIMEOUT - priceobj.failed = True - + priceobj.status = PriceStatus.fail del priceMap[typeID] except Exception as ex: # something happened, try another source @@ -134,7 +139,7 @@ class Price(object): for typeID in priceMap.keys(): priceobj = priceMap[typeID] priceobj.time = time.time() + REREQUEST - priceobj.failed = True + priceobj.status = PriceStatus.fail @classmethod def fitItemsList(cls, fit): @@ -167,16 +172,15 @@ class Price(object): sMkt = Market.getInstance() item = sMkt.getItem(objitem) - return item.price + return item.price.price def getPrices(self, objitems, callback, waitforthread=False): """Get prices for multiple typeIDs""" requests = [] sMkt = Market.getInstance() for objitem in objitems: - priceobj = sMkt.getItem(objitem).priceObj - if priceobj: - requests.append(priceobj) + item = sMkt.getItem(objitem) + requests.append(item.price) def cb(): try: