676 lines
26 KiB
Lua
676 lines
26 KiB
Lua
|
|
--[[
|
||
|
|
Auctioneer - StatSimple
|
||
|
|
Version: 5.7.4568 (KillerKoala)
|
||
|
|
Revision: $Id: StatSimple.lua 4840 2010-08-04 21:44:00Z Nechckn $
|
||
|
|
URL: http://auctioneeraddon.com/
|
||
|
|
|
||
|
|
This is an addon for World of Warcraft that adds statistical history to the auction data that is collected
|
||
|
|
when the auction is scanned, so that you can easily determine what price
|
||
|
|
you will be able to sell an item for at auction or at a vendor whenever you
|
||
|
|
mouse-over an item in the game
|
||
|
|
|
||
|
|
License:
|
||
|
|
This program 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 2
|
||
|
|
of the License, or (at your option) any later version.
|
||
|
|
|
||
|
|
This program 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 this program(see GPL.txt); if not, write to the Free Software
|
||
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||
|
|
|
||
|
|
Note:
|
||
|
|
This AddOn's source code is specifically designed to work with
|
||
|
|
World of Warcraft's interpreted AddOn system.
|
||
|
|
You have an implicit license to use this AddOn with these facilities
|
||
|
|
since that is its designated purpose as per:
|
||
|
|
http://www.fsf.org/licensing/licenses/gpl-faq.html#InterpreterIncompat
|
||
|
|
--]]
|
||
|
|
if not AucAdvanced then return end
|
||
|
|
|
||
|
|
local libType, libName = "Stat", "Simple"
|
||
|
|
local lib,parent,private = AucAdvanced.NewModule(libType, libName)
|
||
|
|
if not lib then return end
|
||
|
|
|
||
|
|
local aucPrint,decode,_,_,replicate,_,get,set,default,debugPrint,fill, _TRANS = AucAdvanced.GetModuleLocals()
|
||
|
|
|
||
|
|
local GetFaction = AucAdvanced.GetFaction
|
||
|
|
|
||
|
|
-- Eliminate some global lookups
|
||
|
|
local select = select
|
||
|
|
local sqrt = sqrt
|
||
|
|
local ipairs = ipairs
|
||
|
|
local unpack = unpack
|
||
|
|
local tinsert = table.insert
|
||
|
|
local assert = assert
|
||
|
|
local tonumber = tonumber
|
||
|
|
local pairs = pairs
|
||
|
|
local type,time,wipe,ceil = type,time,wipe,ceil
|
||
|
|
local concat=table.concat
|
||
|
|
local strsplit,strfind=strsplit,strfind
|
||
|
|
-- GLOBALS: AucAdvancedStatSimpleData
|
||
|
|
|
||
|
|
|
||
|
|
-- local reference to our saved stats table
|
||
|
|
local SSRealmData
|
||
|
|
|
||
|
|
function lib.CommandHandler(command, ...)
|
||
|
|
local serverKey = GetFaction()
|
||
|
|
local _,_,keyText = AucAdvanced.SplitServerKey(serverKey)
|
||
|
|
if (command == "help") then
|
||
|
|
aucPrint(_TRANS('SIMP_Help_SlashHelp1') ) --Help for Auctioneer Advanced - Simple
|
||
|
|
local line = AucAdvanced.Config.GetCommandLead(libType, libName)
|
||
|
|
aucPrint(line, "help}} - ".._TRANS('SIMP_Help_SlashHelp2') ) --this Simple help
|
||
|
|
aucPrint(line, "clear}} - ".._TRANS('SIMP_Help_SlashHelp3'):format(keyText) ) --clear current %s Simple price database
|
||
|
|
aucPrint(line, "push}} - ".._TRANS('SIMP_Help_SlashHelp4'):format(keyText) ) --force the %s Simple daily stats to archive (start a new day)
|
||
|
|
elseif (command == "clear") then
|
||
|
|
lib.ClearData(serverKey)
|
||
|
|
elseif (command == "push") then
|
||
|
|
aucPrint(_TRANS('SIMP_Help_SlashHelp6'):format(keyText) ) --Archiving {{%s}} daily stats and starting a new day
|
||
|
|
private.PushStats(serverKey)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function lib.Processor(callbackType, ...)
|
||
|
|
if (callbackType == "tooltip") then
|
||
|
|
private.ProcessTooltip(...)
|
||
|
|
elseif (callbackType == "config") then
|
||
|
|
--Called when you should build your Configator tab.
|
||
|
|
private.SetupConfigGui(...)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
lib.Processors = {}
|
||
|
|
lib.Processors.tooltip = lib.Processor
|
||
|
|
lib.Processors.config = lib.Processor
|
||
|
|
|
||
|
|
lib.ScanProcessors = {}
|
||
|
|
function lib.ScanProcessors.create(operation, itemData, oldData)
|
||
|
|
if not get("stat.simple.enable") then return end
|
||
|
|
-- This function is responsible for processing and storing the stats after each scan
|
||
|
|
-- Note: itemData gets reused over and over again, so do not make changes to it, or use
|
||
|
|
-- it in places where you rely on it. Make a deep copy of it if you need it after this
|
||
|
|
-- function returns.
|
||
|
|
|
||
|
|
-- We're only interested in items with buyouts.
|
||
|
|
local buyout = itemData.buyoutPrice
|
||
|
|
if not buyout or buyout == 0 then return end
|
||
|
|
local buyoutper = ceil(buyout/itemData.stackSize)
|
||
|
|
|
||
|
|
-- In this case, we're only interested in the initial create, other
|
||
|
|
-- Get the signature of this item and find it's stats.
|
||
|
|
local itemType, itemId, property, factor = AucAdvanced.DecodeLink(itemData.link)
|
||
|
|
if (factor ~= 0) then property = property.."x"..factor end
|
||
|
|
|
||
|
|
local data = private.GetPriceData(GetFaction())
|
||
|
|
if not data.daily[itemId] then data.daily[itemId] = "" end
|
||
|
|
local stats = private.UnpackStats(data.daily[itemId])
|
||
|
|
if not stats[property] then stats[property] = { 0, 0 , buyoutper } end
|
||
|
|
if not stats[property][3] then stats[property][3] = buyoutper end
|
||
|
|
stats[property][1] = stats[property][1] + buyout
|
||
|
|
stats[property][2] = stats[property][2] + itemData.stackSize
|
||
|
|
if stats[property][3] > buyoutper then stats[property][3] = buyoutper end
|
||
|
|
data.daily[itemId] = private.PackStats(stats)
|
||
|
|
end
|
||
|
|
|
||
|
|
|
||
|
|
local dataset = {}
|
||
|
|
|
||
|
|
function lib.GetPrice(hyperlink, serverKey)
|
||
|
|
if not get("stat.simple.enable") then return end
|
||
|
|
|
||
|
|
local linkType,itemId,property,factor = AucAdvanced.DecodeLink(hyperlink)
|
||
|
|
if (linkType ~= "item") then return end
|
||
|
|
if (factor ~= 0) then property = property.."x"..factor end
|
||
|
|
serverKey = serverKey or GetFaction()
|
||
|
|
local data = private.GetPriceData(serverKey)
|
||
|
|
|
||
|
|
local dayTotal, dayCount, dayAverage, minBuyout = 0,0,0,0
|
||
|
|
local seenDays, seenCount, avg3, avg7, avg14, avgmins = 0,0,0,0,0,0
|
||
|
|
-- Stddev calculations for market price
|
||
|
|
local count=0 -- index into dataset[] (living static outside the function)
|
||
|
|
local daysUsed = 0 -- used to keep running track of which daily averages we have
|
||
|
|
|
||
|
|
|
||
|
|
if data.daily[itemId] then
|
||
|
|
local stats = private.UnpackStats(data.daily[itemId])
|
||
|
|
if stats[property] then
|
||
|
|
dayTotal, dayCount, minBuyout = unpack(stats[property])
|
||
|
|
dayAverage = dayTotal/dayCount
|
||
|
|
if not minBuyout then minBuyout = 0 end
|
||
|
|
-- Stddev calculations for market price
|
||
|
|
count=count+1
|
||
|
|
dataset[count] = dayAverage
|
||
|
|
daysUsed = 1
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if data.means[itemId] then
|
||
|
|
local stats = private.UnpackStats(data.means[itemId])
|
||
|
|
if stats[property] then
|
||
|
|
seenDays, seenCount, avg3, avg7, avg14, avgmins = unpack(stats[property])
|
||
|
|
if not avgmins then avgmins = 0 end
|
||
|
|
-- Stddev calculations for market price
|
||
|
|
if seenDays >= 3 then
|
||
|
|
for n = 1, 3-daysUsed do
|
||
|
|
count=count+1
|
||
|
|
dataset[count] = avg3
|
||
|
|
end
|
||
|
|
daysUsed = 3
|
||
|
|
end
|
||
|
|
if seenDays >= 7 then
|
||
|
|
for n = 1, 7-daysUsed do
|
||
|
|
count=count+1
|
||
|
|
dataset[count] = avg7
|
||
|
|
end
|
||
|
|
daysUsed = 7
|
||
|
|
end
|
||
|
|
if seenDays >= 14 then
|
||
|
|
for n = 1, 14-daysUsed do
|
||
|
|
count=count+1
|
||
|
|
dataset[count] = avg14
|
||
|
|
end
|
||
|
|
daysUsed = 14
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
local mean = 0
|
||
|
|
if count > 0 then
|
||
|
|
for n=1,count do
|
||
|
|
mean=mean+dataset[n]
|
||
|
|
end
|
||
|
|
mean = mean/count
|
||
|
|
end
|
||
|
|
local variance = 0
|
||
|
|
if count == 1 then
|
||
|
|
variance = 0
|
||
|
|
else
|
||
|
|
for n=1,count do
|
||
|
|
variance = variance + (mean - dataset[n])^2;
|
||
|
|
end
|
||
|
|
variance = sqrt(variance/(count-1))
|
||
|
|
end
|
||
|
|
|
||
|
|
return dayAverage, avg3, avg7, avg14, minBuyout, avgmins, false, dayTotal, dayCount, seenDays, seenCount, mean, variance
|
||
|
|
end
|
||
|
|
|
||
|
|
function lib.GetPriceColumns()
|
||
|
|
return "Daily Avg", "3 Day Avg", "7 Day Avg", "14 Day Avg", "Min BO", "Avg MBO", false, "Daily Total", "Daily Count", "Seen Days", "Seen Count", "Mean", "StdDev"
|
||
|
|
end
|
||
|
|
|
||
|
|
local array = {}
|
||
|
|
function lib.GetPriceArray(hyperlink, serverKey)
|
||
|
|
if not get("stat.simple.enable") then return end
|
||
|
|
-- Clean out the old array
|
||
|
|
wipe(array)
|
||
|
|
|
||
|
|
-- Get our statistics
|
||
|
|
local dayAverage, avg3, avg7, avg14, minBuyout, avgmins, _, dayTotal, dayCount, seenDays, seenCount, mean, stddev = lib.GetPrice(hyperlink, serverKey)
|
||
|
|
|
||
|
|
--if nothing is returned, return nil
|
||
|
|
if not dayCount then return end
|
||
|
|
|
||
|
|
-- If reportsafe is on use the mean of all 14 day samples. Else use the "traditional" Simple values.
|
||
|
|
if not get("stat.simple.reportsafe") then
|
||
|
|
if (avg3 and seenDays > 3) or dayCount == 0 then
|
||
|
|
array.price = avg3
|
||
|
|
elseif dayCount > 0 then
|
||
|
|
array.price = dayAverage
|
||
|
|
end
|
||
|
|
else
|
||
|
|
array.price = mean
|
||
|
|
end
|
||
|
|
array.stddev = stddev
|
||
|
|
array.seen = seenCount
|
||
|
|
array.avgday = dayAverage
|
||
|
|
array.avg3 = avg3
|
||
|
|
array.avg7 = avg7
|
||
|
|
array.avg14 = avg14
|
||
|
|
array.mbo = minBuyout
|
||
|
|
array.avgmins = avgmins
|
||
|
|
array.daytotal = dayTotal
|
||
|
|
array.daycount = dayCount
|
||
|
|
array.seendays = seenDays
|
||
|
|
|
||
|
|
-- Return a temporary array. Data in this array is
|
||
|
|
-- only valid until this function is called again.
|
||
|
|
return array
|
||
|
|
end
|
||
|
|
|
||
|
|
local bellCurve = AucAdvanced.API.GenerateBellCurve();
|
||
|
|
-- Gets the PDF curve for a given item. This curve indicates
|
||
|
|
-- the probability of an item's mean price. Uses an estimation
|
||
|
|
-- of the normally distributed bell curve by performing
|
||
|
|
-- calculations on the daily, 3-day, 7-day, and 14-day averages
|
||
|
|
-- stored by SIMP
|
||
|
|
-- @param hyperlink The item to generate the PDF curve for
|
||
|
|
-- @param serverKey The realm-faction key from which to look up the data
|
||
|
|
-- @return The PDF for the requested item, or nil if no data is available
|
||
|
|
-- @return The lower limit of meaningful data for the PDF (determined
|
||
|
|
-- as the mean minus 5 standard deviations)
|
||
|
|
-- @return The upper limit of meaningful data for the PDF (determined
|
||
|
|
-- as the mean plus 5 standard deviations)
|
||
|
|
function lib.GetItemPDF(hyperlink, serverKey)
|
||
|
|
-- TODO: This is an estimate. Can we touch this up later? Especially the stddev==0 case
|
||
|
|
|
||
|
|
if not get("stat.simple.enable") then return end
|
||
|
|
-- Calculate the SE estimated standard deviation & mean
|
||
|
|
local dayAverage, avg3, avg7, avg14, minBuyout, avgmins, _, dayTotal, dayCount, seenDays, seenCount, mean, stddev = lib.GetPrice(hyperlink, serverKey)
|
||
|
|
|
||
|
|
if seenCount == 0 or stddev ~= stddev or mean ~= mean or not mean or mean == 0 then
|
||
|
|
return ; -- No available data or cannot estimate
|
||
|
|
end
|
||
|
|
|
||
|
|
-- If the standard deviation is zero, we'll have some issues, so we'll estimate it by saying
|
||
|
|
-- the std dev is 100% of the mean divided by square root of number of views
|
||
|
|
if stddev == 0 then stddev = mean / sqrt(seenCount); end
|
||
|
|
|
||
|
|
-- Calculate the lower and upper bounds as +/- 3 standard deviations
|
||
|
|
local lower, upper = mean - 3*stddev, mean + 3*stddev;
|
||
|
|
|
||
|
|
bellCurve:SetParameters(mean, stddev);
|
||
|
|
return bellCurve, lower, upper;
|
||
|
|
end
|
||
|
|
|
||
|
|
function lib.OnLoad(addon)
|
||
|
|
if SSRealmData then return end
|
||
|
|
|
||
|
|
-- Set defaults
|
||
|
|
default("stat.simple.tooltip", false)
|
||
|
|
default("stat.simple.avg3", false)
|
||
|
|
default("stat.simple.avg7", false)
|
||
|
|
default("stat.simple.avg14", false)
|
||
|
|
default("stat.simple.minbuyout", true)
|
||
|
|
default("stat.simple.avgmins", true)
|
||
|
|
default("stat.simple.quantmul", true)
|
||
|
|
default("stat.simple.enable", true)
|
||
|
|
default("stat.simple.reportsafe", false)
|
||
|
|
|
||
|
|
-- Load and check data
|
||
|
|
private.InitData()
|
||
|
|
end
|
||
|
|
|
||
|
|
function lib.ClearItem(hyperlink, serverKey)
|
||
|
|
local linkType, itemID, property, factor = AucAdvanced.DecodeLink(hyperlink)
|
||
|
|
if linkType ~= "item" then
|
||
|
|
return
|
||
|
|
end
|
||
|
|
if (factor ~= 0) then property = property.."x"..factor end
|
||
|
|
|
||
|
|
serverKey = serverKey or GetFaction ()
|
||
|
|
local data = private.GetPriceData (serverKey)
|
||
|
|
|
||
|
|
local cleareditem = false
|
||
|
|
|
||
|
|
if data.daily[itemID] then
|
||
|
|
local stats = private.UnpackStats (data.daily[itemID])
|
||
|
|
if stats[property] then
|
||
|
|
stats[property] = nil
|
||
|
|
cleareditem = true
|
||
|
|
data.daily[itemID] = private.PackStats (stats)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if data.means[itemID] then
|
||
|
|
local stats = private.UnpackStats (data.means[itemID])
|
||
|
|
if stats[property] then
|
||
|
|
stats[property] = nil
|
||
|
|
cleareditem = true
|
||
|
|
data.means[itemID] = private.PackStats (stats)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if cleareditem then
|
||
|
|
local _, _, keyText = AucAdvanced.SplitServerKey(serverKey)
|
||
|
|
aucPrint(_TRANS('SIMP_Help_SlashHelpClearingData'):format(libType, hyperlink, keyText)) --%s - Simple: clearing data for %s for {{%s}}
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function private.SetupConfigGui(gui)
|
||
|
|
local id = gui:AddTab(lib.libName, lib.libType.." Modules" )
|
||
|
|
|
||
|
|
gui:AddHelp(id, "what simple stats",
|
||
|
|
_TRANS('SIMP_Help_SimpleStats') ,--What are simple stats?
|
||
|
|
_TRANS('SIMP_Help_SimpleStatsAnswer')
|
||
|
|
)--Simple stats are the numbers that are generated by the Simple module, the Simple module averages all of the prices for items that it sees and provides moving 3, 7, and 14 day averages. It also provides daily minimum buyout along with a running average minimum buyout within 10% variance.
|
||
|
|
|
||
|
|
--all options in here will be duplicated in the tooltip frame
|
||
|
|
function private.addTooltipControls(id)
|
||
|
|
gui:AddHelp(id, "what moving day average",
|
||
|
|
_TRANS('SIMP_Help_MovingAverage') , --What does \'moving day average\' mean?
|
||
|
|
_TRANS('SIMP_Help_MovingAverageAnswer') --Moving average means that it places more value on yesterday\'s moving averagethan today\'s average. The determined amount is then used for tomorrow\'s moving average calculation.
|
||
|
|
)
|
||
|
|
|
||
|
|
gui:AddHelp(id, "how day average calculated",
|
||
|
|
_TRANS('SIMP_Help_HowAveragesCalculated') , --How is the moving day averages calculated exactly?
|
||
|
|
_TRANS('SIMP_Help_HowAveragesCalculatedAnswer') --Todays Moving Average is ((X-1)*YesterdaysMovingAverage + TodaysAverage) / X, where X is the number of days (3,7, or 14).
|
||
|
|
)
|
||
|
|
|
||
|
|
gui:AddHelp(id, "no day saved",
|
||
|
|
_TRANS('SIMP_Help_NoDaySaved') ,--So you aren't saving a day-to-day average?
|
||
|
|
_TRANS('SIMP_Help_NoDaySavedAnswer') )--No, that would not only take up space, but heavy calculations on each auction house scan, and this is only a simple model.
|
||
|
|
|
||
|
|
gui:AddHelp(id, "minimum buyout",
|
||
|
|
_TRANS('SIMP_Help_MinimumBuyout') ,--Why do I need to know minimum buyout?
|
||
|
|
_TRANS('SIMP_Help_MinimumBuyoutAnswer')--While some items will sell very well at average within 2 days, others may sell only if it is the lowest price listed. This was an easy calculation to do, so it was put in this module.
|
||
|
|
)
|
||
|
|
|
||
|
|
gui:AddHelp(id, "average minimum buyout",
|
||
|
|
_TRANS('SIMP_Help_AverageMinimumBuyout') ,--What's the point in an average minimum buyout?
|
||
|
|
_TRANS('SIMP_Help_AverageMinimumBuyoutAnswer')--This way you know how good a market is dealing. If the MBO (minimum buyout) is bigger than the average MBO, then it\'s usually a good time to sell, and if the average MBO is greater than the MBO, then it\'s a good time to buy.
|
||
|
|
)
|
||
|
|
|
||
|
|
gui:AddHelp(id, "average minimum buyout variance",
|
||
|
|
_TRANS('SIMP_Help_MinimumBuyoutVariance') ,--What\'s the \'10% variance\' mentioned earlier for?
|
||
|
|
_TRANS('SIMP_Help_MinimumBuyoutVarianceAnswer')--If the current MBO is inside a 10% range of the running average, the current MBO is averaged in to the running average at 50% (normal). If the current MBO is outside the 10% range, the current MBO will only be averaged in at a 12.5% rate.
|
||
|
|
)
|
||
|
|
|
||
|
|
gui:AddHelp(id, "why have variance",
|
||
|
|
_TRANS('SIMP_Help_WhyVariance') ,--What\'s the point of a variance on minimum buyout?
|
||
|
|
_TRANS('SIMP_Help_WhyVarianceAnswer') --Because some people put their items on the market for rediculous price (too low or too high), so this helps keep the average from getting out of hand.
|
||
|
|
)
|
||
|
|
|
||
|
|
gui:AddHelp(id, "why multiply stack size simple",
|
||
|
|
_TRANS('SIMP_Help_WhyMultiplyStack') ,--Why have the option to multiply stack size?
|
||
|
|
_TRANS('SIMP_Help_WhyMultiplyStackAnswer') --The original Stat-Simple multiplied by the stack size of the item, but some like dealing on a per-item basis.
|
||
|
|
)
|
||
|
|
|
||
|
|
gui:AddControl(id, "Header", 0, _TRANS('SIMP_Interface_SimpleOptions') )--Simple options'
|
||
|
|
gui:AddControl(id, "Note", 0, 1, nil, nil, " ")
|
||
|
|
gui:AddControl(id, "Checkbox", 0, 1, "stat.simple.enable", _TRANS('SIMP_Interface_EnableSimpleStats') )--Enable Simple Stats
|
||
|
|
gui:AddTip(id, _TRANS('SIMP_HelpTooltip_EnableSimpleStats') )--Allow Simple Stats to gather and return price data
|
||
|
|
gui:AddControl(id, "Note", 0, 1, nil, nil, " ")
|
||
|
|
|
||
|
|
gui:AddControl(id, "Checkbox", 0, 4, "stat.simple.tooltip", _TRANS('SIMP_Interface_Show') )--Show simple stats in the tooltips?
|
||
|
|
gui:AddTip(id, _TRANS('SIMP_HelpTooltip_Show') )--Toggle display of stats from the Simple module on or off
|
||
|
|
gui:AddControl(id, "Checkbox", 0, 6, "stat.simple.avg3", _TRANS('SIMP_Interface_Toggle3Day') )--Display Moving 3 Day Average
|
||
|
|
gui:AddTip(id, _TRANS('SIMP_HelpTooltip_Toggle3Day') )--Toggle display of 3-Day average from the Simple module on or off
|
||
|
|
gui:AddControl(id, "Checkbox", 0, 6, "stat.simple.avg7", _TRANS('SIMP_Interface_Toggle7Day') )--Display Moving 7 Day Average
|
||
|
|
gui:AddTip(id, _TRANS('SIMP_HelpTooltip_Toggle7Day') )--Toggle display of 7-Day average from the Simple module on or off
|
||
|
|
gui:AddControl(id, "Checkbox", 0, 6, "stat.simple.avg14", _TRANS('SIMP_Interface_Toggle14Day') )--Display Moving 14 Day Average
|
||
|
|
gui:AddTip(id,_TRANS( 'SIMP_HelpTooltip_Toggle14Day') )--Toggle display of 14-Day average from the Simple module on or off
|
||
|
|
gui:AddControl(id, "Checkbox", 0, 6, "stat.simple.minbuyout", _TRANS('SIMP_Interface_MinBuyout') )--Display Daily Minimum Buyout
|
||
|
|
gui:AddTip(id, _TRANS('SIMP_HelpTooltip_MinBuyout') )--Toggle display of Minimum Buyout from the Simple module on or offMultiplies by current stack size if on
|
||
|
|
gui:AddControl(id, "Checkbox", 0, 6, "stat.simple.avgmins", _TRANS('SIMP_Interface_MinBuyoutAverage') )--Display Average of Daily Minimum Buyouts
|
||
|
|
gui:AddTip(id,_TRANS( 'SIMP_HelpTooltip_MinBuyoutAverage') )--Toggle display of Minimum Buyout average from the Simple module on or off
|
||
|
|
gui:AddControl(id, "Note", 0, 1, nil, nil, " ")
|
||
|
|
gui:AddControl(id, "Checkbox", 0, 4, "stat.simple.quantmul", _TRANS('SIMP_Interface_MultiplyStack') )--Multiply by stack size
|
||
|
|
gui:AddTip(id, _TRANS('SIMP_HelpTooltip_MultiplyStack') )--Multiplies by current stack size if on
|
||
|
|
gui:AddControl(id, "Checkbox", 0, 4, "stat.simple.reportsafe", _TRANS('SIMP_Interface_LongerAverage') )--Report safer prices for low volume items
|
||
|
|
gui:AddTip(id, _TRANS('SIMP_HelpTooltip_LongerAverage') )--Returns longer averages (7-day, or even 14-day) for low-volume items
|
||
|
|
gui:AddControl(id, "Note", 0, 1, nil, nil, " ")
|
||
|
|
end
|
||
|
|
--This is the Tooltip tab provided by aucadvnced so all tooltip configuration is in one place
|
||
|
|
local tooltipID = AucAdvanced.Settings.Gui.tooltipID
|
||
|
|
|
||
|
|
--now we create a duplicate of these in the tooltip frame
|
||
|
|
private.addTooltipControls(id)
|
||
|
|
if tooltipID then private.addTooltipControls(tooltipID) end
|
||
|
|
end
|
||
|
|
|
||
|
|
--[[ Local functions ]]--
|
||
|
|
|
||
|
|
function private.ProcessTooltip(tooltip, name, hyperlink, quality, quantity, cost)
|
||
|
|
-- In this function, you are afforded the opportunity to add data to the tooltip should you so
|
||
|
|
-- desire. You are passed a hyperlink, and it's up to you to determine whether or what you should
|
||
|
|
-- display in the tooltip.
|
||
|
|
|
||
|
|
if not get("stat.simple.tooltip") then return end
|
||
|
|
|
||
|
|
if not quantity or quantity < 1 then quantity = 1 end
|
||
|
|
if not get("stat.simple.quantmul") then quantity = 1 end
|
||
|
|
|
||
|
|
local serverKey, realm, faction = GetFaction () -- realm/faction requested for anticipated changes to add cross-faction tooltips
|
||
|
|
local dayAverage, avg3, avg7, avg14, minBuyout, avgmins, _, dayTotal, dayCount, seenDays, seenCount = lib.GetPrice(hyperlink, serverKey)
|
||
|
|
local dispAvg3 = get("stat.simple.avg3")
|
||
|
|
local dispAvg7 = get("stat.simple.avg7")
|
||
|
|
local dispAvg14 = get("stat.simple.avg14")
|
||
|
|
local dispMinB = get("stat.simple.minbuyout")
|
||
|
|
local dispAvgMBO = get("stat.simple.avgmins")
|
||
|
|
if (not dayAverage) then return end
|
||
|
|
|
||
|
|
if (seenDays + dayCount > 0) then
|
||
|
|
tooltip:AddLine(_TRANS('SIMP_Tooltip_SimplePrices') )--Simple prices:
|
||
|
|
|
||
|
|
if (seenDays > 0) then
|
||
|
|
if (dayCount>0) then seenDays = seenDays + 1 end
|
||
|
|
tooltip:AddLine(" ".._TRANS('SIMP_Tooltip_SeenNumberDays'):format(seenCount+dayCount, seenDays) ) --Seen {{%s}} over {{%s}} days:
|
||
|
|
|
||
|
|
end
|
||
|
|
if (seenDays > 6) and dispAvg14 then
|
||
|
|
tooltip:AddLine(" ".._TRANS('SIMP_Tooltip_14DayAverage') , avg14*quantity)-- 14 day average
|
||
|
|
end
|
||
|
|
if (seenDays > 2) and dispAvg7 then
|
||
|
|
tooltip:AddLine(" ".._TRANS('SIMP_Tooltip_7DayAverage') , avg7*quantity) -- 7 day average
|
||
|
|
end
|
||
|
|
if (seenDays > 0) and dispAvg3 then
|
||
|
|
tooltip:AddLine(" ".._TRANS('SIMP_Tooltip_3DayAverage') , avg3*quantity)-- 3 day average
|
||
|
|
end
|
||
|
|
if (seenDays > 0) and (avgmins > 0) and dispAvgMBO then
|
||
|
|
tooltip:AddLine(" ".._TRANS('SIMP_Tooltip_AverageMBO') , avgmins*quantity)-- Average MBO
|
||
|
|
end
|
||
|
|
if (dayCount > 0) then
|
||
|
|
tooltip:AddLine(" ".._TRANS('SIMP_Tooltip_SeenToday'):format(dayCount) , dayAverage*quantity) --Seen {{%s}} today:
|
||
|
|
end
|
||
|
|
if (dayCount > 0) and (minBuyout > 0) and dispMinB then
|
||
|
|
tooltip:AddLine(" ".._TRANS('SIMP_Tooltip_TodaysMBO') , minBuyout*quantity)-- Today's Min BO
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
-- This is a function which migrates the data from a daily average to the
|
||
|
|
-- Exponential Moving Averages over the 3, 7 and 14 day ranges.
|
||
|
|
function private.PushStats(serverKey)
|
||
|
|
local dailyAvg
|
||
|
|
|
||
|
|
local data = private.GetPriceData(serverKey)
|
||
|
|
|
||
|
|
local pdata, fdata, temp
|
||
|
|
for itemId, stats in pairs(data.daily) do
|
||
|
|
if (itemId ~= "created") then
|
||
|
|
pdata = private.UnpackStats(stats)
|
||
|
|
fdata = private.UnpackStats(data.means[itemId] or "")
|
||
|
|
for property, info in pairs(pdata) do
|
||
|
|
dailyAvg = info[1] / info[2]
|
||
|
|
if not info[3] then info[3] = 0 end
|
||
|
|
if not fdata[property] then
|
||
|
|
fdata[property] = {
|
||
|
|
1,
|
||
|
|
info[2],
|
||
|
|
("%0.01f"):format(dailyAvg),
|
||
|
|
("%0.01f"):format(dailyAvg),
|
||
|
|
("%0.01f"):format(dailyAvg),
|
||
|
|
("%0.01f"):format(info[3])
|
||
|
|
}
|
||
|
|
else
|
||
|
|
fdata[property][1] = fdata[property][1] + 1
|
||
|
|
fdata[property][2] = fdata[property][2] + info[2]
|
||
|
|
fdata[property][3] = ("%0.01f"):format(((fdata[property][3] * 2) + dailyAvg)/3)
|
||
|
|
fdata[property][4] = ("%0.01f"):format(((fdata[property][4] * 6) + dailyAvg)/7)
|
||
|
|
fdata[property][5] = ("%0.01f"):format(((fdata[property][5] * 13) + dailyAvg)/14)
|
||
|
|
if not fdata[property][6] then fdata[property][6] = 0 end
|
||
|
|
temp = fdata[property][6]
|
||
|
|
if temp < 1 then
|
||
|
|
fdata[property][6] = info[3]
|
||
|
|
else
|
||
|
|
if info[3] ~= 0 then
|
||
|
|
if temp < info[3] then
|
||
|
|
if (temp*10/info[3]) < 9 then
|
||
|
|
fdata[property][6] = ("%0.01f"):format((temp+info[3])/2)
|
||
|
|
else
|
||
|
|
fdata[property][6] = ("%0.01f"):format((temp*7+info[3])/8)
|
||
|
|
end
|
||
|
|
else
|
||
|
|
if (info[3]*10/temp) < 9 then
|
||
|
|
fdata[property][6] = ("%0.01f"):format((temp+info[3])/2)
|
||
|
|
else
|
||
|
|
fdata[property][6] = ("%0.01f"):format((temp*7+info[3])/8)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
data.means[itemId] = private.PackStats(fdata)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
data.daily = {created = time()}
|
||
|
|
end
|
||
|
|
|
||
|
|
function private.UnpackStatIter(data, ...)
|
||
|
|
local c = select("#", ...)
|
||
|
|
local v
|
||
|
|
for i = 1, c do
|
||
|
|
v = select(i, ...)
|
||
|
|
local property, info = strsplit(":", v)
|
||
|
|
property = tonumber(property) or property
|
||
|
|
if (property and info) then
|
||
|
|
data[property] = {strsplit(";", info)}
|
||
|
|
local item
|
||
|
|
for i=1, #data[property] do
|
||
|
|
item = data[property][i]
|
||
|
|
data[property][i] = tonumber(item) or item
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function private.UnpackStats(dataItem)
|
||
|
|
local data = {}
|
||
|
|
private.UnpackStatIter(data, strsplit(",", dataItem))
|
||
|
|
return data
|
||
|
|
end
|
||
|
|
|
||
|
|
local tmp={}
|
||
|
|
function private.PackStats(data)
|
||
|
|
local n=0
|
||
|
|
for property, info in pairs(data) do
|
||
|
|
n=n+1
|
||
|
|
tmp[n]=property..":"..concat(info, ";")
|
||
|
|
end
|
||
|
|
return concat(tmp,",",1,n)
|
||
|
|
end
|
||
|
|
|
||
|
|
-- The following Functions are the routines used to access the permanent store data
|
||
|
|
|
||
|
|
function private.UpgradeDb()
|
||
|
|
private.UpgradeDb = nil
|
||
|
|
if type(AucAdvancedStatSimpleData) == "table" and AucAdvancedStatSimpleData.Version == "2.0" then return end
|
||
|
|
|
||
|
|
local newSave = {Version = "2.0", RealmData = {}}
|
||
|
|
|
||
|
|
-- Will only be run once per user account; however must run smoothly every time
|
||
|
|
-- Can afford to perform extra type-checking for safety
|
||
|
|
if type(AucAdvancedStatSimpleData) == "table" and AucAdvancedStatSimpleData.Version == "1.0" then
|
||
|
|
-- perform upgrade from "1.0" to "2.0"
|
||
|
|
for realm, realmData in pairs (AucAdvancedStatSimpleData.RealmData) do
|
||
|
|
if type (realm) == "string" and type (realmData) == "table" then
|
||
|
|
-- valid stats will only be stored in serverKeys which match realm
|
||
|
|
local realmPattern = realm.."%-%u%l"
|
||
|
|
for serverKey, data in pairs (realmData) do
|
||
|
|
if type (serverKey) == "string" and type (data) == "table" and strfind (serverKey, realmPattern) then
|
||
|
|
-- found a valid serverKey
|
||
|
|
-- ensure all required subtables are present
|
||
|
|
if type (data.means) ~= "table" then
|
||
|
|
data.means = {}
|
||
|
|
end
|
||
|
|
if type (data.daily) ~= "table" then
|
||
|
|
data.daily = {created = time()}
|
||
|
|
elseif type (data.daily.created) ~= "number" then
|
||
|
|
data.daily.created = time()
|
||
|
|
end
|
||
|
|
newSave.RealmData[serverKey] = data
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
AucAdvancedStatSimpleData = newSave
|
||
|
|
end
|
||
|
|
|
||
|
|
function lib.ClearData(serverKey)
|
||
|
|
serverKey = serverKey or GetFaction()
|
||
|
|
if AucAdvanced.API.IsKeyword(serverKey, "ALL") then
|
||
|
|
wipe(SSRealmData)
|
||
|
|
aucPrint(_TRANS('SIMP_Interface_ClearingSimple').." {{".._TRANS("ADV_Interface_AllRealms").."}}") --Clearing Simple stats for // All realms
|
||
|
|
elseif SSRealmData[serverKey] then
|
||
|
|
local _,_,keyText = AucAdvanced.SplitServerKey(serverKey)
|
||
|
|
keyText = keyText or tostring(serverKey) -- avoid display error if database entry is not a valid serverKey (due to minor database corruption)
|
||
|
|
SSRealmData[serverKey] = nil
|
||
|
|
aucPrint(_TRANS('SIMP_Interface_ClearingSimple').." {{"..keyText.."}}") --Clearing Simple stats for
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function private.GetPriceData(serverKey)
|
||
|
|
local data = SSRealmData[serverKey]
|
||
|
|
if not data then
|
||
|
|
if not AucAdvanced.SplitServerKey(serverKey) then
|
||
|
|
error("Invalid serverKey passed to Stat-Simple")
|
||
|
|
end
|
||
|
|
data = {means = {}, daily = {created = time ()}}
|
||
|
|
SSRealmData[serverKey] = data
|
||
|
|
end
|
||
|
|
return data
|
||
|
|
end
|
||
|
|
|
||
|
|
function private.InitData()
|
||
|
|
private.InitData = nil
|
||
|
|
|
||
|
|
-- Load data
|
||
|
|
private.UpgradeDb()
|
||
|
|
SSRealmData = AucAdvancedStatSimpleData.RealmData
|
||
|
|
if not SSRealmData then
|
||
|
|
SSRealmData = {} -- dummy value to avoid more errors - will not get saved
|
||
|
|
error("Error loading or creating StatSimple database")
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Note: database errors can occur if user tries to run an older version of StatSimple after the database is upgraded.
|
||
|
|
for serverKey, data in pairs (SSRealmData) do
|
||
|
|
if type(serverKey) ~= "string" or not strfind (serverKey, ".%-%u%l") then
|
||
|
|
-- not a valid serverKey - remove it
|
||
|
|
SSRealmData[serverKey] = nil
|
||
|
|
else
|
||
|
|
-- aggressive checks to strip out any data that is the wrong type
|
||
|
|
for key, _ in pairs (data) do
|
||
|
|
if key ~= "means" and key ~= "daily" then
|
||
|
|
data[key] = nil
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if type(data.means) == "table" then
|
||
|
|
for id, packed in pairs (data.means) do
|
||
|
|
if type(id) ~= "number" or type(packed) ~= "string" then
|
||
|
|
data.means[id] = nil
|
||
|
|
end
|
||
|
|
end
|
||
|
|
else
|
||
|
|
data.means = {}
|
||
|
|
end
|
||
|
|
if type(data.daily) == "table" then
|
||
|
|
for id, packed in pairs (data.daily) do
|
||
|
|
if id ~= "created" and (type(id) ~= "number" or type(packed) ~= "string") then
|
||
|
|
data.daily[id] = nil
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if type(data.daily.created) ~= "number" then
|
||
|
|
data.daily.created = time ()
|
||
|
|
end
|
||
|
|
else
|
||
|
|
data.daily = {created = time()}
|
||
|
|
end
|
||
|
|
|
||
|
|
-- database maintenance
|
||
|
|
if time() - data.daily.created > 3600*16 then
|
||
|
|
-- This data is more than 16 hours old, we classify this as "yesterday's data"
|
||
|
|
private.PushStats(serverKey)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
|
||
|
|
AucAdvanced.RegisterRevision("$URL: http://svn.norganna.org/auctioneer/branches/5.9/Auc-Stat-Simple/StatSimple.lua $", "$Rev: 4840 $")
|