Module:CharacterTier/Dev
Jump to navigation
Jump to search
Documentation for this module may be created at Module:CharacterTier/Dev/doc
local p = {}
local ROW_GRANULARITY = 0.5
local LIST_COLUMNS = {race=1, weapon=1, obtain=1}
local getArgs = require('Module:Arguments').getArgs
function p.render(frame)
mw.log('Module:CharacterTier: render start: ' .. os.clock())
-- make a list of filters
local filter = {}
for ix,fil in pairs({'rarity','element','type','race','rating'}) do
if frame.args[fil] then
filter[fil] = frame.args[fil]
end
end
-- fetch character ratings
mw.log('Module:CharacterTier: fetch characters start: ' .. os.clock())
local data = p.fetchCharacters(frame, filter)
mw.log('Module:CharacterTier: fetch characters end: ' .. os.clock())
-- do we have multiple elements?
local elements = {}
local elementcount = 0
for ix,character in pairs(data) do
if (character.element ~= nil) and not (elements[character.element]) then
elements[character.element] = true
elementcount = elementcount + 1
end
end
local result = {}
if frame.args.template ~= nil then
for key,character in pairs(data) do
table.insert(result, frame:expandTemplate{title = frame.args.template, args = character})
end
else
if elementcount > 1 then
table.insert(result, '<table class="wikitable tierlist tierlist-multi"><tr><th>Rating</th><th data-filter-element="fire">Fire</th><th data-filter-element="water">Water</th><th data-filter-element="earth">Earth</th><th data-filter-element="wind">Wind</th><th data-filter-element="light">Light</th><th data-filter-element="dark">Dark</th><th data-filter-element="any">Any</th></tr>')
table.insert(result, p.makeRatingTable(data, 'elements', 70))
table.insert(result, '</table>')
else
table.insert(result, '<table class="wikitable tierlist tierlist-single">')
table.insert(result, '<tr><th>Rating</th><th>Characters</th></tr>')
table.insert(result, p.makeRatingTable(data, nil, 100))
table.insert(result, '</table>')
end
table.insert(result, '<table class="wikitable tierlist-details" style="max-width: 725px;"><tr><th>Icon</th><th>Name</th><th>Type</th><th>Rating</th><th style="min-width: 475px">Remarks</th></tr>')
table.insert(result, p.makeRemarksTable(data))
table.insert(result, '</table>')
end
mw.log('Module:CharacterTier: render end: ' .. os.clock())
return table.concat(result, '')
end
function p.renderCharacterTemplate(frame)
local args = getArgs(frame)
local id = mw.text.trim(args['id'] or '')
local template = frame.args.template
if template == nil then
return 'Error: template undefined'
end
if string.len(id) > 0 then
local characters = p.fetchCharacters(frame, {id=id})
local character = characters and characters[1]
if character ~= nil then
return frame:expandTemplate{title = template, args = character}
end
end
local name = frame.args.name
local link = frame.args.link
if (name == nil) and (link == nil) then
return 'Error: name undefined'
end
if link == nil then
link = name
end
if name == nil then
local pos = link:find('(', 1, true)
if pos ~= nil then
name = string.sub(link, 1, pos-1)
else
name = link
end
end
local character = p.fetchCharacter(frame, name, link)
if character == nil then
return 'Error: Character "'..link..'" not found!'
end
return frame:expandTemplate{title = template, args = character}
end
function p.fetchCharacter(f, name, link)
local res = p.fetchCharacters(f, {link=link, name=(not link) and name or nil})
return res and res[1]
end
function p.fetchCharacters(f, filter)
mw.log('Module:CharacterTier: fetchCharacters start: ' .. os.clock())
local queryFields = 'characters.id=id,characters._pageName=link,rarity,type,element,race,name,type,race,weapon,gw_rating=rating,gw_reasons=reasons,gw_last_update=last_update,gw_preliminary=preliminary'
local queryArgs = {limit = 1000, join = 'characters.id=character_ratings.id'}
if type(filter) == "table" then
local where = nil
for _, k in pairs({'id','element','type','race','rarity','link','name'}) do
local v = filter[k]
if type(v) == "string" and not v:match('["\\]') then
where = where or {}
if k == 'id' then
where[#where+1] = ('character_ratings.id=%s'):format(v)
else
where[#where+1] = ('%s %s "%s"'):format(k == "link" and "characters._pageName" or k, LIST_COLUMNS[k] and "HOLDS" or "=", v)
end
end
end
queryArgs.where = where and table.concat(where, " AND ") or nil
end
local result = {}
for _, row in ipairs(mw.ext.cargo.query('characters,character_ratings', queryFields, queryArgs)) do
result[#result+1] = row
-- General housekeeping
local args = row
args.ix = #result
if args.race == nil then
args.race = 'none'
end
if args.link == nil then
args.link = args.name
end
if (args.preliminary ~= nil) and (string.len(args.preliminary) > 0) then
local year, month, day = args.preliminary:match("(%d%d%d%d)%-(%d%d)%-(%d%d)")
local difftime = year and os.difftime(os.time(), os.time({year=year+0, month=month+0, day=day+0})) or math.huge
-- ratings are preliminary for 10 days
args.preliminary = (difftime < (10 * 24 * 60 * 60))
else
args.preliminary = false
end
args.element = (args.element or ""):lower()
args.weapon = args.weapon or ''
args.shortid = args.id and args.id:sub(3,3) .. args.id:sub(5,7) or ''
end
mw.log('Module:Character.fetchCharacters end: ' .. os.clock())
return result
end
function p.makeRatingTable(data, columns, iconsize)
local result = {}
local ratings = {}
local elements = {}
if columns == 'elements' then
elements = {'fire','water','earth','wind','light','dark','any'}
elseif #data > 0 then
elements = {data[1].element}
end
-- index data by rating
local rating_keys = {}
for _, charInfo in pairs(data) do
local realRating = tonumber(charInfo.rating)
if realRating then
local rating
if #elements > 1 then
rating = realRating - realRating % ROW_GRANULARITY
else
rating = realRating
end
if ratings[rating] == nil then
ratings[rating] = {}
rating_keys[#rating_keys + 1] = rating
end
charInfo.numRating = realRating
table.insert(ratings[rating], charInfo)
end
end
table.sort(rating_keys)
-- print each rating row
for i=#rating_keys, 1, -1 do
local characters = ratings[rating_keys[i]]
table.sort(characters, function(a,b)
if a.numRating ~= b.numRating then
return a.numRating > b.numRating
end
return a.name < b.name
end)
table.insert(result, '<tr style="vertical-align: top;">')
local label, hasRatingRange = "?", false
if characters[1].rating ~= characters[#characters].rating then
label, hasRatingRange = characters[1].rating .. " - " .. characters[#characters].rating, true
elseif (characters[1].rating or 0) ~= 0 then
label = characters[1].rating
end
table.insert(result, '<td style="font-weight: bold;vertical-align: middle;text-align: center;">' .. label .. '</td>')
for ix2,element in pairs(elements) do
table.insert(result, '<td data-filter-element="' .. element .. '">')
local lastRating = false
for key,character in pairs(characters) do
if character.element == element then
if hasRatingRange and lastRating ~= character.numRating then
if lastRating then
table.insert(result, '<hr class="filterable-divider" title="' .. character.rating .. '">')
end
lastRating = character.numRating
end
table.insert(result, '<span data-short-id="' .. character.shortid .. '" data-filter-rarity="' .. character.rarity:lower() .. '">[[File:' .. character.link .. ' iconA.jpg|'..iconsize..'px|link=#ref' .. character.ix .. '|' .. character.name .. ']]</span>')
end
end
table.insert(result, '</td>\n')
end
table.insert(result, '</tr>\n')
end
return table.concat(result, '')
end
function p.makeRemarksTable(data)
table.sort(data, function(a,b)
if a.link == nil then
return 1
elseif b.link == nil then
return -1
else
return a.link < b.link
end
end)
local result = {}
local frame = mw.getCurrentFrame()
local weaponsCache = {}
for ix,character in pairs(data) do
if (character ~= nil) and (character.name ~= nil) then
local name = character.name
local link = character.link
local remarks = '-'
if character.reasons ~= nil then
remarks = character.reasons
end
if character.preliminary then
remarks = '<span style="color: red; font-weight: bold;">Preliminary rating.</span> Please allow a few days for it to settle.<br />' .. remarks
end
local race = character.race:gsub("^%l", string.upper)
local typ = character.type:gsub("^%l", string.upper)
local weapons = weaponsCache[character.weapon]
if not weapons then
weapons = frame:expandTemplate{ title = 'CharacterTierWeapons', args = { character.weapon } }
weaponsCache[character.weapon] = weapons
end
table.insert(result, '<tr style="text-align: center;" data-filter-element="' .. character.element .. '">')
table.insert(result, '<td><div id="ref'..character.ix..'">[[File:' .. link .. ' iconA.jpg|80px|link=' .. link .. ']]</div></td>')
table.insert(result, '<td style="white-space: nowrap;">[['..link..'|'..name..']]</td>')
table.insert(result, '<td>[[File:Label_Type_'..typ..'.png|100px|link=]]<br />[[File:Label_Race_'..race..'.png|100px|link=]]<br />'..weapons..'</td>')
table.insert(result, '<td>'..character.rating..'</td>')
table.insert(result, '<td style="text-align: left; vertical-align: top; min-width: 475px;">\n'..remarks..'\n</td>')
table.insert(result, '</tr>')
end
end
return table.concat(result, '\n')
end
function p.testElements()
local frame = mw.getCurrentFrame()
return p.render(frame)
end
function p.testOneElement()
local frame = mw.getCurrentFrame()
frame.args.element = 'fire'
frame.args.rarity = 'ssr'
return p.render(frame)
end
function p.testSingle()
local frame = mw.getCurrentFrame()
local character = p.fetchCharacter(frame, 'Lucio', 'Lucio')
if character ~= nil then
return 'Character: ' .. character.name .. ', link: ' .. character.link .. ', rating: ' .. character.rating .. ', prel: ' .. tostring(character.preliminary)
else
return 'Character not found'
end
end
function p.testMultiTemplate()
local frame = mw.getCurrentFrame()
frame.args.template = 'CharacterTier'
return p.render(frame)
end
function p.testSingleTemplate()
local frame = mw.getCurrentFrame()
frame.args.link = 'Lucius'
frame.args.template = 'CharacterTier'
return p.renderCharacterTemplate(frame)
end
function p.testSingleTemplateMini()
local frame = mw.getCurrentFrame()
frame.args.id = '3040231000'
frame.args.template = 'CharacterTierMini'
return p.renderCharacterTemplate(frame)
end
function p.testRender()
local frame = mw.getCurrentFrame()
return p.render(frame)
end
return p