- Added an experimental way to clean stale prices in the database. Use it at your own risk, as it has not been debugged yet.

Yaron Kfir [06-03-14 - 06:33]
 - Added an experimental way to clean stale prices in the database.  Use it at your own risk, as it has not been debugged yet.
 - Suggested price shows which guild it was found in, if possible.
 - Reformatted the price table
 - Minor improvements to code quality
Filename
Changelog
MathUtils.lua
PriceTracker.lua
PriceTrackerMenu.lua
diff --git a/Changelog b/Changelog
index a7d478d..dc588a3 100644
--- a/Changelog
+++ b/Changelog
@@ -1,8 +1,14 @@
 Changelog

+v0.7
+ - Added an experimental way to clean stale prices in the database.  Use it at your own risk, as it has not been debugged yet.
+ - Suggested price shows which guild it was found in, if possible.
+ - Reformatted the price table
+ - Minor improvements to code quality
+
 v0.6
  - Removed unnecessary debug information
-
+
 v0.5
  - Fixed loot window error

diff --git a/MathUtils.lua b/MathUtils.lua
index 4abc55a..b99681d 100644
--- a/MathUtils.lua
+++ b/MathUtils.lua
@@ -6,12 +6,8 @@ local MathUtils = {}
 PriceTracker.mathUtils = MathUtils

 function MathUtils:GetSortedPriceTable(itemTable)
-	local prices = {}
-	for i = 1, #itemTable do
-		table.insert(prices, math.floor(itemTable[i].purchasePrice / itemTable[i].stackCount))
-	end
-	table.sort(prices)
-	return prices
+	table.sort(itemTable, function(a, b) return (a.purchasePrice / a.stackCount) < (b.purchasePrice / b.stackCount) end)
+	return itemTable
 end

 function MathUtils:WeightedAverage(itemTable)
@@ -21,27 +17,35 @@ function MathUtils:WeightedAverage(itemTable)
 		sum = sum + itemTable[i].purchasePrice
 		weight = weight + itemTable[i].stackCount
 	end
-	return math.floor(sum / weight)
+	item = {
+		price = math.floor(sun / weight)
+	}
+	return item
 end

 function MathUtils:Median(itemTable)
-	local prices = self:GetSortedPriceTable(itemTable)
-	local index = (#prices + 1) / 2
-	if (#prices / 2) == (math.floor(#prices / 2)) then
-		return math.floor((prices[index] + prices[index + 1]) / 2)
+	local itemTable = self:GetSortedPriceTable(itemTable)
+	local index = (#itemTable + 1) / 2
+	if (#itemTable / 2) == (math.floor(#itemTable / 2)) then
+		local item = {
+			price = math.floor((itemTable[index].price + itemtTable[index + 1]).price / 2)
+		}
+		return
 	else
-		return prices[index]
+		return itemTable[index]
 	end
 end

 function MathUtils:Mode(itemTable)
-	local prices = self:GetSortedPriceTable(itemTable)
-	local number = prices[1]
-	local mode = number	local count = 1
+	local itemTable = self:GetSortedPriceTable(itemTable)
+	PriceTracker.itemTable = itemTable
+	local number = itemTable[1]
+	local mode = number
+	local count = 1
 	local countMode = 1

-	for i = 2, #prices do
-		if prices[i] == number then
+	for i = 2, #itemTable do
+		if itemTable[i].price == number.price then
 			count = count + 1
 		else
 			if count > countMode then
@@ -57,22 +61,26 @@ end

 function MathUtils:Max(itemTable)
 	local price = math.floor(itemTable[1].purchasePrice / itemTable[1].stackCount)
-	for i = 1, #itemTable do
+	local item = itemTable[1]
+	for i = 2, #itemTable do
 		local newPrice = math.floor(itemTable[i].purchasePrice / itemTable[i].stackCount)
 		if newPrice > price then
 			price = newPrice
+			item = itemTable[i]
 		end
 	end
-	return price
+	return item
 end

 function MathUtils:Min(itemTable)
 	local price = math.floor(itemTable[1].purchasePrice / itemTable[1].stackCount)
-	for i = 1, #itemTable do
+	local item = itemTable[1]
+	for i = 2, #itemTable do
 		local newPrice = math.floor(itemTable[i].purchasePrice / itemTable[i].stackCount)
 		if newPrice < price then
 			price = newPrice
+			item = itemTable[i]
 		end
 	end
-	return price
+	return item
 end
diff --git a/PriceTracker.lua b/PriceTracker.lua
index f6eb9a0..d3abea0 100644
--- a/PriceTracker.lua
+++ b/PriceTracker.lua
@@ -14,9 +14,7 @@ local PriceTracker = PriceTracker

 -- Addon initialization
 function PriceTracker:OnLoad(eventCode, addOnName)
-	if(addOnName ~= "PriceTracker") then
-		return
-	end
+	if(addOnName ~= "PriceTracker") then return end

 	EVENT_MANAGER:RegisterForEvent("OnSearchResultsReceived", EVENT_TRADING_HOUSE_SEARCH_RESULTS_RECEIVED, function(...) self:OnSearchResultsReceived(...) end)
 	EVENT_MANAGER:RegisterForEvent("OnTradingHouseClosed", EVENT_CLOSE_TRADING_HOUSE, function(...) self:OnTradingHouseClosed(...) end)
@@ -43,7 +41,6 @@ function PriceTracker:OnLoad(eventCode, addOnName)
 	self.button:SetWidth(ZO_TradingHouseLeftPaneBrowseItemsCommonQuality:GetWidth())

 	self.menu:InitAddonMenu(addOnName)
-
 end

 -- Handle slash commands
@@ -57,6 +54,12 @@ function PriceTracker:CommandHandler(text)
 		self.settings.itemList = {}
 		return
 	end
+
+	if text == "clean" then
+		self:CleanItemList()
+		return
+	end
+
 end

 function PriceTracker:ShowHelp()
@@ -64,40 +67,35 @@ function PriceTracker:ShowHelp()
 	d(" ")
 	d("/pt help - Show this help")
 	d("/pt clear - Clear stored price values")
+	d("pt clean - Remove stale items (experimental)")
 end

 function PriceTracker:OnUpdateTooltip(item)
-	if not item or not item.dataEntry or not item.dataEntry.data or self.selectedItem == item then
-		return
-	end
-
+	if not item or not item.dataEntry or not item.dataEntry.data or self.selectedItem == item then return end
 	self.selectedItem = item
 	local stackCount = item.dataEntry.data.stackCount or item.dataEntry.data.stack
-	if not stackCount then
-		return
-	end
+	if not stackCount then return end

 	local matches = self:GetMatches(item.dataEntry.data.name)
-	if not matches then
-		return
-	end
+	if not matches then return end

-	local price = self:SuggestPrice(matches)
-	if not price then
-		return
-	end
+	local item = self:SuggestPrice(matches)
+	if not item then return end

 	ZO_Tooltip_AddDivider(ItemTooltip)
 	ItemTooltip:AddLine("Price Tracker", "ZoFontHeader2")
 	local r, g, b = ZO_TOOLTIP_DEFAULT_COLOR:UnpackRGB()
-	ItemTooltip:AddLine("Suggested Price: |r", "ZoFontGame", r, g, b, TOPLEFT, MODIFY_TEXT_TYPE_NONE, LEFT, false)
-	ItemTooltip:AddLine(self:FormatTooltipLine("Item price: ", price, stackCount), "ZoFontGame", r, g, b, CENTER, MODIFY_TEXT_TYPE_NONE, CENTER, false)
+	ItemTooltip:AddLine(self:FormatTooltipLine("Suggested price:", math.floor(item.purchasePrice / item.stackCount)), "ZoFontGame", r, g, b, CENTER, MODIFY_TEXT_TYPE_NONE, CENTER, true)
 	if stackCount > 1 then
-		ItemTooltip:AddLine(self:FormatTooltipLine("Stack price: ", price * stackCount), "ZoFontGame", r, g, b, CENTER, MODIFY_TEXT_TYPE_NONE, CENTER, false)
+		ItemTooltip:AddLine(self:FormatTooltipLine("Stack price:", math.floor(item.purchasePrice / item.stackCount * stackCount)), "ZoFontGame", r, g, b, CENTER, MODIFY_TEXT_TYPE_NONE, CENTER, true)
 	end
 	if self.settings.showMinMax then
-		ItemTooltip:AddLine(self:FormatTooltipLine("Min / Max: ", self.mathUtils:Min(matches) .. " / " .. self.mathUtils:Max(matches)), "ZoFontGame", r, g, b, CENTER, MODIFY_TEXT_TYPE_NONE, CENTER, false)
-		ItemTooltip:AddLine(self:FormatTooltipLine("Min / Max (stack): ", self.mathUtils:Min(matches) * stackCount .. " / " .. self.mathUtils:Max(matches) * stackCount), "ZoFontGame", r, g, b, CENTER, MODIFY_TEXT_TYPE_NONE, CENTER, false)
+		local minItem = self.mathUtils:Min(matches)
+		local maxItem = self.mathUtils:Max(matches)
+		local minPrice = math.floor(minItem.purchasePrice / minItem.stackCount)
+		local maxPrice = math.floor(maxItem.purchasePrice / maxItem.stackCount)
+		ItemTooltip:AddLine(self:FormatTooltipLine("Min (each / stack):", minPrice, minPrice * stackCount, minItem.guildName), "ZoFontGame", r, g, b, CENTER, MODIFY_TEXT_TYPE_NONE, CENTER, true)
+		ItemTooltip:AddLine(self:FormatTooltipLine("Max (each / stack):", maxPrice, maxPrice * stackCount, maxItem.guildName), "ZoFontGame", r, g, b, CENTER, MODIFY_TEXT_TYPE_NONE, CENTER, true)
 	end
 	if self.settings.showSeen then
 		ItemTooltip:AddLine("Seen " .. #matches .. " times", "ZoFontGame", r, g, b, CENTER, MODIFY_TEXT_TYPE_NONE, CENTER, false)
@@ -109,9 +107,7 @@ function PriceTracker:OnHideTooltip()
 end

 function PriceTracker:OnScanPrices()
-	if self.isSearching then
-		return
-	end
+	if self.isSearching then return end

 	self.button:SetEnabled(false)
 	self.isSearching = true
@@ -125,9 +121,7 @@ function PriceTracker:OnScanPrices()
 end

 function PriceTracker:OnSearchResultsReceived(eventId, guildId, numItemsOnPage, currentPage, hasMorePages)
-	if not self.isSearching then
-		return
-	end
+	if not self.isSearching then return end

 	for i = 1, numItemsOnPage do
 		self:AddItem(GetTradingHouseSearchResultItemInfo(i))
@@ -161,6 +155,7 @@ function PriceTracker:AddItem(icon, itemName, quality, stackCount, sellerName, t
 	item.stackCount = stackCount
 	item.sellerName = sellerName
 	item.purchasePrice = purchasePrice
+	item.eachPrice = purchasePrice / stackCount
 	item.guildId = self.currentGuild
 	item.guildName = GetGuildName(item.guildId)

@@ -174,24 +169,27 @@ function PriceTracker:AddItem(icon, itemName, quality, stackCount, sellerName, t
 	end
 end

+function PriceTracker:CleanItemList()
+	local timestamp = GetTimeStamp()
+	for k, v in pairs(PriceTracker.settings.itemList) do
+		for itemK, itemV in pairs(v) do
+			if itemV.expiry > timestamp then
+				table.remove(v, itemK)
+			end
+		end
+	end
+end
+
 function PriceTracker:GetMatches(itemName)
 	local normalizedName = self:NormalizeName(itemName)
 	if not self.settings.itemList or not self.settings.itemList[normalizedName] then
 		return nil
 	end

-	local index = next(self.settings.itemList[normalizedName])
-	if index == nil then
-		return nil
-	end
-
 	local matches = {}
-	while index do
-		table.insert(matches, self.settings.itemList[normalizedName][index])
-		index = next(self.settings.itemList[normalizedName], index)
+	for k, v in pairs(self.settings.itemList[normalizedName]) do
+		table.insert(matches, v)
 	end
-
-	self.matches = matches
 	return matches
 end

@@ -213,8 +211,18 @@ function PriceTracker:SuggestPrice(matches)
 	return nil
 end

-function PriceTracker:FormatTooltipLine(title, price, stackCount)
-	return string.format("%-30s %7s%s", title, price, zo_iconFormat(self.icons.gold, 16, 16))
+function PriceTracker:FormatTooltipLine(title, price1, price2, guild)
+	if price2 then
+		price1 = price1 .. " / " .. price2
+	end
+	local str
+	if guild then
+		str = "%-20.20s  (%-10.10s)"
+	else
+		str = "%-40.40s  %1.1s"
+	end
+	str = str .. " %12.12s%s"
+	return string.format(str, title, guild or "", price1, zo_iconFormat(self.icons.gold, 16, 16))
 end

 function PriceTracker:NormalizeName(name)
diff --git a/PriceTrackerMenu.lua b/PriceTrackerMenu.lua
index c3c6846..793de94 100644
--- a/PriceTrackerMenu.lua
+++ b/PriceTrackerMenu.lua
@@ -6,12 +6,11 @@ local PriceTrackerMenu = {}
 PriceTracker.menu = PriceTrackerMenu

 PriceTrackerMenu.algorithmTable = {
-		"Weighted Average",
-		"Median",
-		"Most Frequently Used"
+	"Weighted Average",
+	"Median",
+	"Most Frequently Used"
 }

-
 function PriceTrackerMenu:InitAddonMenu(addOnName)
 	local LAM = LibStub:GetLibrary("LibAddonMenu-1.0")
 	local addOnMenu = LAM:CreateControlPanel("PriceTrackerMenu", addOnName)