329 lines
14 KiB
Lua
329 lines
14 KiB
Lua
--[[
|
|
Auctioneer Addon for World of Warcraft(tm).
|
|
Version: 5.9.4961 (WhackyWallaby)
|
|
Revision: $Id: BeanCounterTidyUp.lua 4953 2010-10-17 19:37:42Z Nechckn $
|
|
URL: http://auctioneeraddon.com/
|
|
|
|
BeanCounterTidyUp - Database clean up and maintenance functions
|
|
|
|
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 it's designated purpose as per:
|
|
http://www.fsf.org/licensing/licenses/gpl-faq.html#InterpreterIncompat
|
|
]]
|
|
LibStub("LibRevision"):Set("$URL: http://svn.norganna.org/auctioneer/branches/5.9/BeanCounter/BeanCounterTidyUp.lua $","$Rev: 4953 $","5.1.DEV.", 'auctioneer', 'libs')
|
|
|
|
local lib = BeanCounter
|
|
local private = lib.Private
|
|
local private, print, get, set, _BC = lib.getLocals()
|
|
local pairs,ipairs,next,select,type = pairs,ipairs,next,select,type
|
|
local strsplit = strsplit
|
|
|
|
local function debugPrint(...)
|
|
if get("util.beancounter.debugTidyUp") then
|
|
private.debugPrint("BeanCounterTidyUp", ...)
|
|
end
|
|
end
|
|
--Sum all entries for display in window TODO:Add in check for lua key value limitations
|
|
function private.sumDatabase()
|
|
private.DBSumEntry, private.DBSumItems = 0, 0
|
|
for player, v in pairs(private.serverData) do
|
|
for DB, data in pairs(v) do
|
|
if DB == "failedBids" or DB == "failedAuctions" or DB == "completedAuctions" or DB == "completedBidsBuyouts" or DB == "failedBidsNeutral" or DB == "failedAuctionsNeutral" or DB == "completedAuctionsNeutral" or DB == "completedBidsBuyoutsNeutralNeutral" then
|
|
for itemID, value in pairs(data) do
|
|
for itemString, data in pairs(value) do
|
|
private.DBSumEntry = private.DBSumEntry +1
|
|
for index, text in pairs(data) do
|
|
private.DBSumItems = private.DBSumItems + 1
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
private.frame.DBCount:SetText("Items: "..private.DBSumEntry)
|
|
private.frame.DBItems:SetText("Entries: "..private.DBSumItems)
|
|
end
|
|
|
|
--Recreate/refresh ItemIName to ItemID array
|
|
function private.refreshItemIDArray(announce)
|
|
for player, v in pairs(private.serverData)do
|
|
for DB,data in pairs(private.serverData[player]) do
|
|
for itemID, value in pairs(data) do
|
|
for itemString, text in pairs(value) do
|
|
local key, suffix = lib.API.decodeLink(itemString)
|
|
if not BeanCounterDBNames[key..":"..suffix] then
|
|
local _, itemLink = private.getItemInfo(itemString, "itemid")
|
|
if itemLink then
|
|
debugPrint("Added to array, missing value", itemLink)
|
|
lib.API.storeItemLinkToArray(itemLink)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if announce then print("Finished refresing ItemName Array") end
|
|
end
|
|
|
|
--[[ --Possible code to use to purge itemID array links that no longer have transactions
|
|
]]
|
|
local function pruneItemNameArrayHelper(itemID)
|
|
for server, serverData in pairs(BeanCounterDB) do
|
|
for player, playerData in pairs(serverData) do
|
|
for DB, data in pairs(playerData) do
|
|
if data[itemID] then
|
|
--found a match
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function private.pruneItemNameArray()
|
|
local itemID, key
|
|
for i, link in pairs(BeanCounterDBNames) do
|
|
itemID, key = strsplit(":", i)
|
|
if not pruneItemNameArrayHelper(itemID) then
|
|
print("Never seen", itemID, link)
|
|
end
|
|
end
|
|
end
|
|
|
|
--Moves entries older than 40 days into compressed( non uniqueID) Storage
|
|
--Removes data older than X months from the DB
|
|
--Array refresh needs to run before this function
|
|
local TIME = time()--no need to call this for every loop
|
|
function private.compactDB(announce)
|
|
debugPrint("Compressing database entries older than 40 days")
|
|
TIME = time() --sets TIME for this compact operation
|
|
for DB,data in pairs(private.playerData) do -- just do current player to make process as fast as possible
|
|
if DB == "failedBids" or DB == "failedAuctions" or DB == "completedAuctions" or DB == "completedBidsBuyouts" or DB == "failedBidsNeutral" or DB == "failedAuctionsNeutral" or DB == "completedAuctionsNeutral" or DB == "completedBidsBuyoutsNeutralNeutral" then
|
|
for itemID, value in pairs(data) do
|
|
for itemString, index in pairs(value) do
|
|
local _, suffix, uniqueID = lib.API.decodeLink(itemString)
|
|
if uniqueID ~= "0" and string.len(uniqueID) > 8 then --ignore the already compacted keys, compacted keys are uniqueID of 0 or the scaling factor for negative suffix items
|
|
private.removeUniqueID(index, DB, itemString)
|
|
elseif lib.GetSetting("oldDataExpireEnabled") then
|
|
--for non unique strings we know they are already older than the compress date, So check to see if they are old enough to be pruned by the Remove Old transactions option
|
|
local months = lib.GetSetting("monthstokeepdata")
|
|
local expire = TIME - (months * 30 * 24 * 60 * 60)
|
|
private.removeOldData(index, DB, itemString, expire)
|
|
end
|
|
--remove itemStrings that are now empty, all the keys have been moved to compressed format
|
|
if #index == 0 then debugPrint("Removed empty table:", itemString) private.playerData[DB][itemID][itemString] = nil end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if announce then print("Finished compressing Databases") end
|
|
end
|
|
function private.removeUniqueID(data, DB, itemString)
|
|
local _, _, _, _, _, _, _, postTime = private.unpackString(data[1])
|
|
if data[1] and (TIME - postTime) >= 3456000 then --we have an old data entry lets process this
|
|
--debugPrint("Compressed", "|H"..itemString, data[1] )
|
|
private.databaseAdd(DB, nil, itemString, data[1], true) --store using the compress option set to true
|
|
table.remove(data, 1)
|
|
private.removeUniqueID(data, DB, itemString)
|
|
end
|
|
end
|
|
|
|
function private.removeOldData(data, DB, itemString, expire)
|
|
local _, _, _, _, _, _, _, postTime = private.unpackString(data[1])
|
|
postTime = tonumber(postTime)
|
|
if data[1] and (postTime) <= expire then --we have an old data entry lets process this
|
|
--debugPrint("Removed", "|H"..itemString, data[1] , date("%c", postTime), "Older than", date("%c", keep) )
|
|
table.remove(data, 1)
|
|
private.removeOldData(data, DB, itemString, expire)
|
|
end
|
|
end
|
|
|
|
--Sort all array entries by Date oldest to newest
|
|
--Helps make compact more efficent needs to run once per week or so
|
|
function private.sortArrayByDate(announce)
|
|
for player, v in pairs(private.serverData)do
|
|
for DB, data in pairs(private.serverData[player]) do
|
|
if DB == "failedBids" or DB == "failedAuctions" or DB == "completedAuctions" or DB == "completedBidsBuyouts" or DB == "failedBidsNeutral" or DB == "failedAuctionsNeutral" or DB == "completedAuctionsNeutral" or DB == "completedBidsBuyoutsNeutralNeutral" then
|
|
for itemID, value in pairs(data) do
|
|
for itemString, index in pairs(value) do
|
|
table.sort(index, function(a,b)
|
|
local _, _, _, _, _, _, _, postTimeA = private.unpackString(a)
|
|
local _, _, _, _, _, _, _, postTimeB = private.unpackString(b)
|
|
return postTimeA < postTimeB end)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if announce then print("Finished sorting database") end
|
|
end
|
|
--Prune Old keys from postedXXXX tables
|
|
--First we find a itemID that needs pruning then we check all other keys for that itemID and prune.
|
|
function private.prunePostedDB(announce)
|
|
--Used to clean up post DB
|
|
debugPrint("Cleaning posted Databases")
|
|
for DB,data in pairs(private.playerData) do -- just do current player to make process as fast as possible
|
|
if DB == "postedBids" or DB == "postedAuctions" then
|
|
for itemID, value in pairs(data) do
|
|
for itemString, index in pairs(value) do
|
|
--While the entrys remain 40 days old remove entry
|
|
local _, _ ,_ ,_ ,_ ,TIME
|
|
if index[1] then
|
|
_, _ ,_ ,_ ,_ ,TIME = strsplit(";", index[1])
|
|
end
|
|
while index[1] and (time() - TIME) >= 3456000 do
|
|
--debugPrint("Removed Old posted entry", itemString)
|
|
table.remove(index, 1)
|
|
if index[1] then
|
|
_, _ ,_ ,_ ,_ ,TIME = strsplit(";", index[1])
|
|
end
|
|
end
|
|
-- remove empty itemString tables
|
|
if #index == 0 then
|
|
--debugPrint("Removed empty itemString table", itemID, itemString)
|
|
private.playerData[DB][itemID][itemString] = nil
|
|
end
|
|
end
|
|
--after removing the itemStrings look to see if there are itemID's that need removing
|
|
if next (value) == nil then
|
|
debugPrint("Removed empty itemID:", itemID)
|
|
private.playerData[DB][itemID] = nil
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if announce then print("Finished pruning Posted Databases") end
|
|
end
|
|
--deletes all entries matching a itemLink from database for that server
|
|
function private.deleteExactItem(itemLink)
|
|
if not itemLink or not itemLink:match("^(|c%x+|H.+|h%[.+%])") then return end
|
|
for player, playerData in pairs(private.serverData) do
|
|
for DB, data in pairs(playerData) do
|
|
for itemID, itemIDData in pairs(data) do
|
|
for itemString, itemStringData in pairs(itemIDData) do
|
|
local _,_,_,_,_,_, _, suffix, uniqueID = strsplit(":", itemString)
|
|
local linkID, linkSuffix = lib.API.decodeLink(itemLink)
|
|
if linkID == itemID and suffix == linkSuffix then
|
|
debugPrint("matched", itemLink, itemString, linkSuffix, suffix)
|
|
itemIDData[itemString] = nil
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
--[[INTEGRITY CHECKS
|
|
Make sure the DB format is correct removing any entries that were missed by updating.
|
|
To be run after every DB update
|
|
]]--
|
|
local integrity = {} --table containing teh DB layout
|
|
integrity["completedBidsBuyouts"] = {"number", "number", "number", "number", "number", "number", "string", "number", "string", "string"}--10
|
|
integrity["completedAuctions"] = {"number", "number", "number", "number", "number", "number", "string", "number", "string", "string"}--10
|
|
integrity["failedBids"] = {"number", "number", "number", "number", "number", "number", "string", "number", "string", "string"}--10
|
|
integrity["failedAuctions"] = {"number", "number", "number", "number", "number", "number", "string", "number", "string", "string"}--10
|
|
integrity["postedBids"] = {"number", "number", "string", "string", "number", "number", "string" } --7
|
|
integrity["postedAuctions"] = {"number", "number", "number", "number", "number" ,"number", "string"} --7
|
|
|
|
integrity["completedBidsBuyoutsNeutral"] = {"number", "number", "number", "number", "number", "number", "string", "number", "string", "string"}--10
|
|
integrity["completedAuctionsNeutral"] = {"number", "number", "number", "number", "number", "number", "string", "number", "string", "string"}--10
|
|
integrity["failedBidsNeutral"] = {"number", "number", "number", "number", "number", "number", "string", "number", "string", "string"}--10
|
|
integrity["failedAuctionsNeutral"] = {"number", "number", "number", "number", "number", "number", "string", "number", "string", "string"}--10
|
|
local integrityClean, integrityCount = true, 1
|
|
function private.integrityCheck(complete, server)
|
|
if not server then server = private.realmName end
|
|
local tbl
|
|
debugPrint(integrityCount)
|
|
for player, v in pairs(BeanCounterDB[server])do
|
|
for DB, data in pairs(v) do
|
|
for itemID, value in pairs(data) do
|
|
for itemString, data in pairs(value) do
|
|
local _, itemStringLength = itemString:gsub(":", ":")
|
|
--check that the data is a string and table
|
|
if type(itemString) ~= "string" or type(data) ~= "table" then
|
|
BeanCounterDB[server][player][DB][itemID][itemString] = nil
|
|
debugPrint("Failed: Invalid format", DB, data, "", itemString)
|
|
integrityClean = false
|
|
elseif itemStringLength > 10 then --Bad itemstring purge
|
|
debugPrint("Failed: Invalid itemString", DB, data, "", itemString)
|
|
local _, link = GetItemInfo(itemString) --ask server for a good itemlink
|
|
local itemStringNew = lib.API.getItemString(link) --get NEW itemString from itemlink
|
|
if itemStringNew then
|
|
debugPrint(itemStringNew, "New link recived replacing")
|
|
BeanCounterDB[server][player][DB][itemID][itemStringNew] = data
|
|
BeanCounterDB[server][player][DB][itemID][itemString] = nil
|
|
else
|
|
debugPrint(itemString, "New link falied purging item")
|
|
BeanCounterDB[server][player][DB][itemID][itemString] = nil
|
|
end
|
|
integrityClean = false
|
|
elseif itemStringLength < 9 then
|
|
local itemStringNew = itemString..":80"
|
|
BeanCounterDB[server][player][DB][itemID][itemStringNew] = data
|
|
BeanCounterDB[server][player][DB][itemID][itemString] = nil
|
|
integrityClean = false
|
|
else
|
|
for index, text in pairs(data) do
|
|
tbl = {strsplit(";", text)}
|
|
--check entries for missing data points, skip any DB we havnt made a key for
|
|
if integrity[DB] then
|
|
if #integrity[DB] ~= #tbl then
|
|
debugPrint("Failed: Number of entries invalid", player, DB, #tbl, text)
|
|
table.remove(data, index)
|
|
integrityClean = false
|
|
elseif complete and private.IC(tbl, DB) then
|
|
--do a full check type() = check
|
|
debugPrint("Failed type() check", player, DB)
|
|
table.remove(data, index)
|
|
integrityClean = false
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
--rerun integrity 10 times or until it goes cleanly
|
|
if not integrityClean and integrityCount < 10 then
|
|
integrityCount = integrityCount + 1
|
|
integrityClean = true
|
|
private.integrityCheck(complete, server)
|
|
else
|
|
print("BeanCounter Integrity Check Completed after:",integrityCount, "passes")
|
|
integrityClean, integrityCount = true, 1
|
|
--set("util.beancounter.integrityCheckComplete", true)
|
|
--set("util.beancounter.integrityCheck", true)
|
|
end
|
|
|
|
end
|
|
--look at each value and compare to the number, string, number pattern for that specific DB
|
|
function private.IC(tbl, DB, text)
|
|
for i,v in pairs(tbl) do
|
|
if v ~= "" then --<nil> is a placeholder for string and number values, ignore
|
|
v = tonumber(v) or v
|
|
if type(v) ~= integrity[DB][i] then
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
return false
|
|
end
|