AuctioneerSuite/BeanCounter/BeanCounterTidyUp.lua
2026-04-13 17:48:13 -04:00

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