Module:Skill table

From RuneRealm Wiki
Jump to navigation Jump to search
Module documentation
This documentation is transcluded from Template:No documentation/doc. [edit] [history] [purge]
This module does not have any documentation. Please consider adding documentation at Module:Skill table/doc. [edit]
Module:Skill table's function main is invoked by Template:Skill table.
Module:Skill table requires Module:Paramtest.
Module:Skill table requires Module:SCP.
Module:Skill table requires Module:Yesno.

local p = {}

local scp = require('Module:SCP')._main
local yesNo = require('Module:Yesno')
local paramTest = require('Module:Paramtest')

local trim = mw.text.trim
local split = mw.text.split
local ustring = mw.ustring
local listToText = mw.text.listToText

function plinkify(tbl)
	if tbl == nil then
		return nil
	elseif type(tbl) == 'string' then
		tbl = {tbl}
	end
	for i, v in ipairs(tbl) do
		local name = ustring.gsub(v, '[^%[]*%[%[:?([^%|]+).*', '%1')
		local fileName = ustring.match(name, '[^%#]+') -- Remove #foo from image name
		if(not mw.title.new(fileName .. '.png', 'File'):getContent()) then -- If file doesnt exist, just use name
			tbl[i] = '[[' .. name .. ']]'
		else
			tbl[i] = '[[File:' .. fileName .. '.png|link=' .. name .. '|frameless|35x35px]] [[' .. name .. ']]'
		end
	end
	return table.concat(tbl, '<br/>')
end

-- val index in arr
function indexOf(tbl, val, doSearch)
	for i, item in ipairs(tbl) do
		if item == val then
			return i
		elseif doSearch and string.find(item, val) then
			return i
		end
	end
	return 0
end

function buildRow(page, skillName, images, columns)
	local row = mw.html.create('tr')
	
	-- First cell, image/icon
	local image = getLastElement(page['Image']) or getLastElement(page['All Image']) or getLastElement(page['Parent Image'])
	local icon = getLastElement(page['Icon'])
	-- If images is set to icons, prefer icon over images. If its not icon, prefer image over icon
	local pic = images == 'icons' and (icon or image) or (image or icon)
	-- If images is set to icons, set smaller size, otherwise set larger size
	local imageSize = images == 'icons' and '35x35px' or '120x120px'

	if(pic) then
		row:tag('td'):wikitext('[[' .. pic .. '|frameless|' .. imageSize .. '|center|link=' .. page[1] .. ' ]]')
	else
		row:tag('td'):wikitext('(?)')
	end

	-- Second cell, page name + verisoning (if applicable)
	local title = split(page[1], '#')[1]
	local subtitle = split(page[1], '#')[2]
	row:tag('td'):wikitext(string.format("[[%s|%s]]%s", page[1], title, subtitle and ' <small>('..string.gsub(subtitle, '_', ' ')..')</small>' or ''))
	
	-- Third cell (optional), members
	if(columns['membersColumn']) then
		if(page['Is members only'] or page['All Is members only'] or page['Parent Members']) then
			row:tag('td'):wikitext('[[File:Member icon.png|link=|Members]]')
		else
			row:tag('td'):wikitext('[[File:Free-to-play icon.png|link=|Free-to-play]]')
		end
	end
	
	-- Fourth cell, skill level requirement
	local skillLevels = page[skillName .. ' level']
	local skillStr = ''
	if(type(skillLevels) == 'table') then
		for i, skillLevel in ipairs(skillLevels) do
			skillStr = skillStr .. scp(skillName, skillLevel, 'yes') .. '<br/>'
		end
	else
		skillStr = scp(skillName, skillLevels, 'yes')
	end
	if skillStr == '' then skillstr = '(?)' end
	row:tag('td'):wikitext(skillStr)
	
	-- Fifth cell, skill experience yield
	row:tag('td'):wikitext(concatenateTable(page[skillName..' experience']) or '(?)')
	
	-- Sixth cell, materials
	if(columns['materialsColumn']) then
		row:tag('td'):wikitext(plinkify(page['Uses material']))
	end
	
	-- Seventh cell, tools
	if(columns['toolsColumn'])then
		row:tag('td'):wikitext(plinkify(page['Uses tool']))
	end
	
	-- Eighth cell, facilities
	if(columns['facilitiesColumn']) then
		row:tag('td'):wikitext(concatenateTable(page['Uses facility']))
	end
	
	-- Ninth cell, facilities
	if type(page['Uses skill']) == 'table' then
		-- Remove primary skill information displayed in previous cells
		table.remove(page['Uses skill'], indexOf(page['Uses skill'], skillName, true))
	elseif string.find(page['Uses skill'] or '', skillName) then
		-- If the singular string is of primary skill, set to nil as there are no other skills to display
		page['Uses skill'] = nil
	else
		-- Not a table, but also not just the original skill (somehow). Convert to table to avoid errors.
		page['Uses skill'] = {page['Uses skill']}
	end
	
	-- Convert other skills information to be SCPs, these do not include levels because it would require further smw and pre-emptive knowledge otherwise grabbing all skills info
	if page['Uses skill'] then
		for i, skill in ipairs(page['Uses skill']) do
			page['Uses skill'][i] = scp(skill, nil, 'yes')
		end
	end
	
	if(columns['otherSkillsColumn']) then
		row:tag('td'):wikitext(page['Uses skill'] and table.concat(page['Uses skill'], '<br/>') or '')
	end
	
	return row
end

function createHeader(skill, columns)
	local tabl = mw.html.create('table'):addClass('wikitable sortable sticky-header align-center-1'):done()
	local header = tabl:tag('tr')
	header:tag('th'):attr('colspan', '2'):wikitext('Name')
	if(columns['membersColumn']) then
		tabl:addClass('align-center-3')
		header:tag('th'):wikitext('[[File:Member icon.png|link=|Members]]')
	end
	header:tag('th'):wikitext(skill..' level')
	header:tag('th'):wikitext(skill..' XP')
	if(columns['materialsColumn']) then
		header:tag('th'):wikitext('Materials used')
	end
	if(columns['toolsColumn']) then
		header:tag('th'):wikitext('Tools used')
	end
	if(columns['facilitiesColumn']) then
		header:tag('th'):wikitext('Facilities used')
	end
	if(columns['otherSkillsColumn']) then
		header:tag('th'):wikitext('Skills involved')
	end
	return tabl
end

function shouldExclude(property, exclusionList)
	for _, exclusion in ipairs(exclusionList) do
		if((paramTest.has_content(exclusion)) and (ustring.find(string.lower(concatenateTable(property) or ''), string.lower(trim(exclusion))))) then
			return true
		end
	end
	return false
end

function tableConcat(mainTable, newTable, exclusionList)
    for i = 1, #newTable, 1 do
    	-- Only add pages not using excluded materials
    	if(not((newTable[i]['Uses infobox'] == 'Pure') or (shouldExclude(newTable[i]['Uses material'], exclusionList)))) then
    		mainTable[#mainTable+1] = newTable[i]
    	end
    end
    return mainTable
end

function getLastElement(tbl)
	return type(tbl) == 'table' and tbl[#tbl] or tbl
end

function concatenateTable(tbl)
	return type(tbl) == 'table' and listToText(tbl, ', ', ' and ') or tbl
end

function loadData(strictExp, skillName, membersColumn, exclusionList, limit, batchSize, altQuery)
	local allData = {}
	
	local query = {
		"[[" .. skillName .. " level::+]]" .. strictExp .. "[[" .. skillName .. " experience::>>0]]",
		"?=#-",
		"?Image#-",
		"?All Image#-",
		"?Is variant of.All Image#-=Parent Image",
		"?Icon#-",
		--"?All Icon#-",
		--"?Is variant of.All Icon#-=Parent Icon",
		"?Uses material",
		"?Uses tool",
		"?Uses facility",
		"?Uses skill#-",
		"?Uses infobox",
		"?" .. skillName .. " level",
		"?" .. skillName .. " experience",
		limit = batchSize,
		offset = 0,
	}
	
	-- Only fetch members data if the members column is going to be shown
	if(membersColumn) then
		table.insert(query, "?Is members only")
		table.insert(query, "?All Is members only")
		table.insert(query, "?Is variant of.All Is members only=Parent Members")
	end
	
	-- prepend AND append, to make sure the custom query gets applied to both sides of the OR
	if(altQuery) then
		query[1] = altQuery .. query[1] .. altQuery
	end
	
	for i = 0, limit, batchSize do
		query.limit = batchSize
		query.offset = i
		
		local t1 = os.clock()
		local smwData = mw.smw.ask(query)
		local t2 = os.clock()
		if(smwData == nil) then break end
		mw.log(string.format('SMW: entries %d, time elapsed: %.3f ms.', #smwData, (t2 - t1) * 1000))
		
		tableConcat(allData, smwData, exclusionList)
	end

	if(allData == nil) then
		error("No results found for the specified query.")
	end
	return allData
end

function p._main(args)
	-- Strict requests experience >0
	local strictExp = (yesNo(args.strict) or false) and '' or ' OR '
	local skillName = trim(args.skill)
	local limit = paramTest.default_to(tonumber(args.limit), 5000) -- Acting as a real limiter
	local batchSize = paramTest.default_to(tonumber(args.batchsize), 100)
	
	--Table columns
	local columns = {
		membersColumn = yesNo(args.members or '', true),
		materialsColumn = yesNo(args.materials or '', true),
		toolsColumn = yesNo(args.tools or '', true),
		facilitiesColumn = yesNo(args.facilities or '', true),
		otherSkillsColumn = yesNo(args['other skills'] or '', true),
	}
	
	local exclusions = split(args['exclude material'] or '', ',')
	local images = string.lower(args.images or '')

	local data = loadData(strictExp, skillName, columns['membersColumn'], exclusions, limit, batchSize, args.query)

	local ret = createHeader(skillName, columns)
	
	for i, page in pairs(data) do
		ret:node(buildRow(page, skillName, images, columns))
	end
	
	return ret
end

function p.main(frame)
	local args = frame:getParent().args
	--mw.logObject(args)
	return p._main(args)
end

return p