From 3488a5d26eea4e88613272465f7791e7598cd28c Mon Sep 17 00:00:00 2001 From: DarkPhoenix Date: Fri, 24 Jun 2011 21:37:41 +0400 Subject: [PATCH] Rework number formatter to avoid silly stuff like formatting 999999 with precision of 3 to 1000k instead of 1M --- gui/builtinStatsViews/priceViewFull.py | 6 +- gui/builtinViewColumns/price.py | 4 +- gui/utils/numberFormatter.py | 142 +++++++++++++++---------- 3 files changed, 93 insertions(+), 59 deletions(-) diff --git a/gui/builtinStatsViews/priceViewFull.py b/gui/builtinStatsViews/priceViewFull.py index dd967f15b..0b44d209d 100644 --- a/gui/builtinStatsViews/priceViewFull.py +++ b/gui/builtinStatsViews/priceViewFull.py @@ -142,15 +142,15 @@ class PriceViewFull(StatsView): shipPrice = 0 modPrice = sum(map(lambda p: p.price or 0, prices[1:])) if self._cachedShip != shipPrice: - self.labelPriceShip.SetLabel("%s ISK" % formatAmount(shipPrice, 3, 3, 9)) + self.labelPriceShip.SetLabel("%s ISK" % formatAmount(shipPrice, 3, 3, 9, currency=True)) self.labelPriceShip.SetToolTip(wx.ToolTip("%.2f ISK" % shipPrice)) self._cachedShip = shipPrice if self._cachedFittings != modPrice: - self.labelPriceFittings.SetLabel("%s ISK" % formatAmount(modPrice, 3, 3, 9)) + self.labelPriceFittings.SetLabel("%s ISK" % formatAmount(modPrice, 3, 3, 9, currency=True)) self.labelPriceFittings.SetToolTip(wx.ToolTip("%.2f ISK" % modPrice)) self._cachedFittings = modPrice if self._cachedTotal != (shipPrice+modPrice): - self.labelPriceTotal.SetLabel("%s ISK" % formatAmount(shipPrice + modPrice, 3, 3, 9)) + self.labelPriceTotal.SetLabel("%s ISK" % formatAmount(shipPrice + modPrice, 3, 3, 9, currency=True)) self.labelPriceTotal.SetToolTip(wx.ToolTip("%.2f ISK" % (shipPrice + modPrice))) self._cachedTotal = shipPrice + modPrice self.panel.Layout() diff --git a/gui/builtinViewColumns/price.py b/gui/builtinViewColumns/price.py index 9025bd1bf..f459c9685 100644 --- a/gui/builtinViewColumns/price.py +++ b/gui/builtinViewColumns/price.py @@ -37,12 +37,12 @@ class Price(ViewColumn): sMarket = service.Market.getInstance() price = sMarket.getPriceNow(stuff.item.ID) - return formatAmount(price.price, 3, 3, 9) if price and price.price else False + return formatAmount(price.price, 3, 3, 9, currency=True) if price and price.price else False def delayedText(self, mod, display, colItem): def callback(requests): price = requests[0].price - colItem.SetText(formatAmount(price, 3, 3, 9) if price else "") + colItem.SetText(formatAmount(price, 3, 3, 9, currency=True) if price else "") display.SetItem(colItem) service.Market.getInstance().getPrices([mod.item.ID], callback) diff --git a/gui/utils/numberFormatter.py b/gui/utils/numberFormatter.py index 6ca18acfe..8a14ab159 100644 --- a/gui/utils/numberFormatter.py +++ b/gui/utils/numberFormatter.py @@ -1,74 +1,108 @@ import math -def formatAmount(val, prec=3, lowest=0, highest=0): +def formatAmount(val, prec=3, lowest=0, highest=0, currency=False): """ Add suffix to value, transform value to match new suffix and round it. Keyword arguments: val -- value to process prec -- precision of final number (number of significant positions to show) - lowest -- lowest order for suffixizing - highest -- highest order for suffixizing + lowest -- lowest order for suffixizing for numbers 0 < |num| < 1 + highest -- highest order for suffixizing for numbers |num| > 1 + currency -- if currency, billion suffix will be B instead of G """ if val is None: - result = "" - else: - # Separate value to mantissa and suffix - mantissa, suffix = suffixizeAmount(val, lowest, highest) - # Round mantissa and add suffix - newMantissa = processAmount(mantissa, prec) - result = u"{0}{1}".format(newMantissa, suffix) - return result - -def suffixizeAmount(val, lowest=-6, highest=9): - """ - Add suffix to value and transform value to match new suffix. - - Keyword arguments: - val -- value to process - lowest -- lowest order for suffixizing - highest -- highest order for suffixizing - - Suffixes below lowest and above highest orders won't be used. - """ - if abs(val) >= 1000 and highest >= 3: - suffixmap = {3 : "k", 6 : "M", 9 : "B"} + return "" + # Define suffix maps + posSuffixMap = {3: "k", 6: "M", 9: "B" if currency is True else "G"} + negSuffixMap = {-6: u'\u03bc', -3: "m"} + # Define tuple of the map keys + # As we're going to go from the biggest order of abs(key), sort + # them differently due to one set of values being negative + # and other positive + posOrders = tuple(sorted(posSuffixMap.iterkeys(), reverse=True)) + negOrders = tuple(sorted(negSuffixMap.iterkeys(), reverse=False)) + # Find the least abs(key) + posLowest = min(posOrders) + negHighest = max(negOrders) + # By default, mantissa takes just value and no suffix + mantissa, suffix = val, "" + # Positive suffixes + if abs(val) > 1 and highest >= posLowest: # Start from highest possible suffix - for key in sorted(suffixmap, reverse = True): + for key in posOrders: # Find first suitable suffix and check if it's not above highest order if abs(val) >= 10**key and key <= highest: - return val/float(10**key), suffixmap[key] + mantissa, suffix = val/float(10**key), posSuffixMap[key] + # Do additional step to eliminate results like 999999 => 1000k + # If we're already using our greatest order, we can't do anything useful + if posOrders.index(key) == 0: + break + else: + # Get order greater than current + prevKey = posOrders[posOrders.index(key) - 1] + # Check if the key to which we potentially can change is greater + # than our highest boundary + if prevKey > highest: + # If it is, bail - we already have acceptable results + break + # Find multiplier to get from one order to another + orderDiff = 10**(prevKey - key) + # If rounded mantissa according to our specifications is greater than + # or equal to multiplier + if roundToPrec(mantissa, prec) >= orderDiff: + # Divide mantissa and use suffix of greater order + mantissa, suffix = mantissa/orderDiff, posSuffixMap[prevKey] + # Otherwise consider current results as acceptable + break # Take numbers between 0 and 1, and matching/below highest possible negative suffix - elif abs(val) < 1 and val != 0 and lowest <= -3: - suffixmap = {-6 : u'\u03bc', -3 : "m"} + elif abs(val) < 1 and val != 0 and lowest <= negHighest: # Start from lowest possible suffix - for key in sorted(suffixmap, reverse = False): + for key in negOrders: + # Get next order + try: + nextKey = negOrders[negOrders.index(key) + 1] + except IndexError: + nextKey = 0 # Check if mantissa with next suffix is in range [1, 1000) - # Here we assume that each next order is greater than previous by 3 - if abs(val) < 10**(key+3) and key >= lowest: - return val/float(10**key), suffixmap[key] - # If no suitable suffixes are found within given order borders, or value - # is already within [1, 1000) boundaries, just return rounded value with no suffix - else: - return val, "" - -def processAmount(val, prec=3): - """ - Round number and return as string. - - Keyword arguments: - val -- value to round - prec -- precision of final number (number of significant positions to show) - - Integer numbers are not rounded, only fractional part. - """ - if val == 0: # Logarithm is not defined for zero - return "0" + if abs(val) < 10**(nextKey) and key >= lowest: + mantissa, suffix = val/float(10**key), negSuffixMap[key] + # Do additional step to eliminate results like 0.9999 => 1000m + # Check if the key we're potentially switching to is greater than our + # upper boundary + if nextKey > highest: + # If it is, leave loop with results we already have + break + # Find the multiplier between current and next order + orderDiff = 10**(nextKey - key) + # If rounded mantissa according to our specifications is greater than + # or equal to multiplier + if roundToPrec(mantissa, prec) >= orderDiff: + # Divide mantissa and use suffix of greater order + # Use special handling of zero key as it's not on the map + mantissa, suffix = mantissa/orderDiff, posSuffixMap[nextKey] if nextKey != 0 else "" + # Otherwise consider current results as acceptable + break + # Round mantissa according to our prec variable + mantissa = roundToPrec(mantissa, prec) + # Round mantissa and add suffix + result = u"{0}{1}".format(mantissa, suffix) + return result +def roundToPrec(val, prec): + # We're not rounding integers anyway + # Also make sure that we do not ask to calculate logarithm of zero + if int(val) == val: + return int(val) + # Find round factor, taking into consideration that we want to keep at least prec + # positions for fractions with zero integer part (e.g. 0.0000354 for prec=3) roundFactor = int(prec - math.ceil(math.log10(abs(val)))) # But we don't want to round integers - if roundFactor < 0: roundFactor = 0 + if roundFactor < 0: + roundFactor = 0 + # Do actual rounding val = round(val, roundFactor) - # Strip trailing zero for integers and convert to string - result = str(val)[-2:] == '.0' and str(val)[:-2] or str(val) - return result + # Make sure numbers with .0 part designating float don't get through + if int(val) == val: + val = int(val) + return val