initial commit
This commit is contained in:
235
Core/Service/Accounting/Auctions.lua
Normal file
235
Core/Service/Accounting/Auctions.lua
Normal file
@@ -0,0 +1,235 @@
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
-- TradeSkillMaster --
|
||||
-- https://tradeskillmaster.com --
|
||||
-- All Rights Reserved - Detailed license information included with addon. --
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
|
||||
local _, TSM = ...
|
||||
local Auctions = TSM.Accounting:NewPackage("Auctions")
|
||||
local Database = TSM.Include("Util.Database")
|
||||
local CSV = TSM.Include("Util.CSV")
|
||||
local String = TSM.Include("Util.String")
|
||||
local Log = TSM.Include("Util.Log")
|
||||
local ItemString = TSM.Include("Util.ItemString")
|
||||
local CustomPrice = TSM.Include("Service.CustomPrice")
|
||||
local private = {
|
||||
db = nil,
|
||||
numExpiresQuery = nil,
|
||||
dataChanged = false,
|
||||
statsQuery = nil,
|
||||
statsTemp = {},
|
||||
}
|
||||
local COMBINE_TIME_THRESHOLD = 300 -- group expenses within 5 minutes together
|
||||
local REMOVE_OLD_THRESHOLD = 180 * 24 * 60 * 60 -- remove records over 6 months old
|
||||
local SECONDS_PER_DAY = 24 * 60 * 60
|
||||
local CSV_KEYS = { "itemString", "stackSize", "quantity", "player", "time" }
|
||||
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- Module Functions
|
||||
-- ============================================================================
|
||||
|
||||
function Auctions.OnInitialize()
|
||||
private.db = Database.NewSchema("ACCOUNTING_AUCTIONS")
|
||||
:AddStringField("baseItemString")
|
||||
:AddStringField("type")
|
||||
:AddStringField("itemString")
|
||||
:AddNumberField("stackSize")
|
||||
:AddNumberField("quantity")
|
||||
:AddStringField("player")
|
||||
:AddNumberField("time")
|
||||
:AddNumberField("saveTime")
|
||||
:AddIndex("baseItemString")
|
||||
:AddIndex("time")
|
||||
:Commit()
|
||||
private.numExpiresQuery = private.db:NewQuery()
|
||||
:Select("quantity")
|
||||
:Equal("type", "expire")
|
||||
:Equal("baseItemString", Database.BoundQueryParam())
|
||||
:GreaterThanOrEqual("time", Database.BoundQueryParam())
|
||||
private.statsQuery = private.db:NewQuery()
|
||||
:Select("type", "quantity")
|
||||
:Equal("baseItemString", Database.BoundQueryParam())
|
||||
:GreaterThanOrEqual("time", Database.BoundQueryParam())
|
||||
|
||||
private.db:BulkInsertStart()
|
||||
private.LoadData("cancel", TSM.db.realm.internalData.csvCancelled, TSM.db.realm.internalData.saveTimeCancels)
|
||||
private.LoadData("expire", TSM.db.realm.internalData.csvExpired, TSM.db.realm.internalData.saveTimeExpires)
|
||||
private.db:BulkInsertEnd()
|
||||
CustomPrice.OnSourceChange("NumExpires")
|
||||
end
|
||||
|
||||
function Auctions.OnDisable()
|
||||
if not private.dataChanged then
|
||||
-- nothing changed, so no need to save
|
||||
return
|
||||
end
|
||||
local cancelSaveTimes, expireSaveTimes = {}, {}
|
||||
local cancelEncodeContext = CSV.EncodeStart(CSV_KEYS)
|
||||
local expireEncodeContext = CSV.EncodeStart(CSV_KEYS)
|
||||
-- order by time to speed up loading
|
||||
local query = private.db:NewQuery()
|
||||
:Select("type", "itemString", "stackSize", "quantity", "player", "time", "saveTime")
|
||||
:OrderBy("time", true)
|
||||
for _, recordType, itemString, stackSize, quantity, player, timestamp, saveTime in query:Iterator() do
|
||||
local saveTimes, encodeContext = nil, nil
|
||||
if recordType == "cancel" then
|
||||
saveTimes = cancelSaveTimes
|
||||
encodeContext = cancelEncodeContext
|
||||
elseif recordType == "expire" then
|
||||
saveTimes = expireSaveTimes
|
||||
encodeContext = expireEncodeContext
|
||||
else
|
||||
error("Invalid recordType: "..tostring(recordType))
|
||||
end
|
||||
-- add the save time
|
||||
tinsert(saveTimes, saveTime ~= 0 and saveTime or time())
|
||||
-- add to our list of CSV lines
|
||||
CSV.EncodeAddRowDataRaw(encodeContext, itemString, stackSize, quantity, player, timestamp)
|
||||
end
|
||||
query:Release()
|
||||
TSM.db.realm.internalData.csvCancelled = CSV.EncodeEnd(cancelEncodeContext)
|
||||
TSM.db.realm.internalData.saveTimeCancels = table.concat(cancelSaveTimes, ",")
|
||||
TSM.db.realm.internalData.csvExpired = CSV.EncodeEnd(expireEncodeContext)
|
||||
TSM.db.realm.internalData.saveTimeExpires = table.concat(expireSaveTimes, ",")
|
||||
end
|
||||
|
||||
function Auctions.InsertCancel(itemString, stackSize, timestamp)
|
||||
private.InsertRecord("cancel", itemString, stackSize, timestamp)
|
||||
end
|
||||
|
||||
function Auctions.InsertExpire(itemString, stackSize, timestamp)
|
||||
private.InsertRecord("expire", itemString, stackSize, timestamp)
|
||||
end
|
||||
|
||||
function Auctions.GetStats(itemString, minTime)
|
||||
private.statsQuery:BindParams(ItemString.GetBase(itemString), minTime or 0)
|
||||
wipe(private.statsTemp)
|
||||
private.statsQuery:GroupedSum("type", "quantity", private.statsTemp)
|
||||
local cancel = private.statsTemp.cancel or 0
|
||||
local expire = private.statsTemp.expire or 0
|
||||
local total = cancel + expire
|
||||
return cancel, expire, total
|
||||
end
|
||||
|
||||
function Auctions.GetNumExpires(itemString, minTime)
|
||||
private.numExpiresQuery:BindParams(ItemString.GetBase(itemString), minTime or 0)
|
||||
local num = 0
|
||||
for _, quantity in private.numExpiresQuery:Iterator() do
|
||||
num = num + quantity
|
||||
end
|
||||
return num
|
||||
end
|
||||
|
||||
function Auctions.GetNumExpiresSinceSale(itemString)
|
||||
return Auctions.GetNumExpires(itemString, TSM.Accounting.Transactions.GetLastSaleTime(itemString))
|
||||
end
|
||||
|
||||
function Auctions.CreateQuery()
|
||||
return private.db:NewQuery()
|
||||
end
|
||||
|
||||
function Auctions.RemoveOldData(days)
|
||||
private.dataChanged = true
|
||||
private.db:SetQueryUpdatesPaused(true)
|
||||
local numRecords = private.db:NewQuery()
|
||||
:LessThan("time", time() - days * SECONDS_PER_DAY)
|
||||
:DeleteAndRelease()
|
||||
private.db:SetQueryUpdatesPaused(false)
|
||||
CustomPrice.OnSourceChange("NumExpires")
|
||||
return numRecords
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- Private Helper Functions
|
||||
-- ============================================================================
|
||||
|
||||
function private.LoadData(recordType, csvRecords, csvSaveTimes)
|
||||
local saveTimes = String.SafeSplit(csvSaveTimes, ",")
|
||||
if not saveTimes then
|
||||
return
|
||||
end
|
||||
|
||||
local decodeContext = CSV.DecodeStart(csvRecords, CSV_KEYS)
|
||||
if not decodeContext then
|
||||
Log.Err("Failed to decode %s records", recordType)
|
||||
private.dataChanged = true
|
||||
return
|
||||
end
|
||||
|
||||
local removeTime = time() - REMOVE_OLD_THRESHOLD
|
||||
local index = 1
|
||||
local prevTimestamp = 0
|
||||
for itemString, stackSize, quantity, player, timestamp in CSV.DecodeIterator(decodeContext) do
|
||||
itemString = ItemString.Get(itemString)
|
||||
local baseItemString = ItemString.GetBaseFast(itemString)
|
||||
local saveTime = tonumber(saveTimes[index])
|
||||
stackSize = tonumber(stackSize)
|
||||
quantity = tonumber(quantity)
|
||||
timestamp = tonumber(timestamp)
|
||||
if itemString and baseItemString and stackSize and quantity and timestamp and saveTime and timestamp > removeTime then
|
||||
local newTimestamp = floor(timestamp)
|
||||
if newTimestamp ~= timestamp then
|
||||
-- make sure all timestamps are stored as integers
|
||||
private.dataChanged = true
|
||||
timestamp = newTimestamp
|
||||
end
|
||||
if timestamp < prevTimestamp then
|
||||
-- not ordered by timestamp
|
||||
private.dataChanged = true
|
||||
end
|
||||
prevTimestamp = timestamp
|
||||
private.db:BulkInsertNewRowFast8(baseItemString, recordType, itemString, stackSize, quantity, player, timestamp, saveTime)
|
||||
else
|
||||
private.dataChanged = true
|
||||
end
|
||||
index = index + 1
|
||||
end
|
||||
|
||||
if not CSV.DecodeEnd(decodeContext) then
|
||||
Log.Err("Failed to decode %s records", recordType)
|
||||
private.dataChanged = true
|
||||
end
|
||||
|
||||
CustomPrice.OnSourceChange("NumExpires")
|
||||
end
|
||||
|
||||
function private.InsertRecord(recordType, itemString, stackSize, timestamp)
|
||||
private.dataChanged = true
|
||||
assert(itemString and stackSize and stackSize > 0 and timestamp)
|
||||
timestamp = floor(timestamp)
|
||||
local baseItemString = ItemString.GetBase(itemString)
|
||||
local matchingRow = private.db:NewQuery()
|
||||
:Equal("type", recordType)
|
||||
:Equal("baseItemString", baseItemString)
|
||||
:Equal("itemString", itemString)
|
||||
:Equal("stackSize", stackSize)
|
||||
:Equal("player", UnitName("player"))
|
||||
:GreaterThan("time", timestamp - COMBINE_TIME_THRESHOLD)
|
||||
:LessThan("time", timestamp + COMBINE_TIME_THRESHOLD)
|
||||
:Equal("saveTime", 0)
|
||||
:GetFirstResultAndRelease()
|
||||
if matchingRow then
|
||||
matchingRow:SetField("quantity", matchingRow:GetField("quantity") + stackSize)
|
||||
matchingRow:Update()
|
||||
matchingRow:Release()
|
||||
else
|
||||
private.db:NewRow()
|
||||
:SetField("baseItemString", baseItemString)
|
||||
:SetField("type", recordType)
|
||||
:SetField("itemString", itemString)
|
||||
:SetField("stackSize", stackSize)
|
||||
:SetField("quantity", stackSize)
|
||||
:SetField("player", UnitName("player"))
|
||||
:SetField("time", timestamp)
|
||||
:SetField("saveTime", 0)
|
||||
:Create()
|
||||
end
|
||||
|
||||
if recordType == "expire" then
|
||||
CustomPrice.OnSourceChange("NumExpires", itemString)
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user