Module:Sandbox/User:Wolaznik/Uses tool list

From RuneRealm Wiki
Jump to navigation Jump to search
Module documentation
This documentation is transcluded from Template:Module sandbox/doc. [edit] [history] [purge]
Module:Sandbox/User:Wolaznik/Uses tool list requires Module:Addcommas.
Module:Sandbox/User:Wolaznik/Uses tool list requires Module:SCP.
Module:Sandbox/User:Wolaznik/Uses tool list requires Module:Yesno.

This module is a sandbox for Wolaznik. It can be used to test changes to existing modules, prototype new modules, or just experimenting with lua features.

Invocations of this sandbox should be kept in userspace; if the module is intended for use in other namespaces, it should be moved out of the sandbox into a normal module and template.

This default documentation can be overridden by creating the /doc subpage of this module, as normal.

local p = {}

local commas = require('Module:Addcommas')
local skillPic = require('Module:SCP')._main
local yesNo = require('Module:Yesno')
local lang = mw.getContentLanguage()
local trim = mw.text.trim
local split = mw.text.split
local jsonDecode = mw.text.jsonDecode

function buildRow(recipe, tool)
	local ret = mw.html.create('tr')
	
	-- Outputs
	ret:tag('td'):wikitext(recipe.output.image)
	ret:tag('td'):attr('data-sort-value', recipe.output.name):wikitext(commas._add(recipe.output.quantity) .. ' × [[' .. recipe.output.name .. ']]' .. (recipe.output.subtxt ~= nil and '<br/><small>(' .. recipe.output.subtxt .. ')</small>' or ''))

	-- Members
	if(yesNo(recipe.members)) then
		ret:tag('td'):wikitext("[[File:Member icon.png|center|link=Members|alt=Members]]")
	elseif(not yesNo(recipe.members)) then
		ret:tag('td'):wikitext("[[File:Free-to-play icon.png|center|link=Free-to-play|alt=Free-to-play]]")
	end
	
	--Tools
	local toolList = mw.html.create('ul'):addClass('products-materials')
	local toolSortValue = nil
	local tools = split(recipe.tools, ",")
	for i, toolV in ipairs(tools) do
		toolList:tag('li'):attr('data-sort-value', i == 1 and tooV or ''):wikitext(toolV) -- For highlighting _all_ tools --- :addClass('production-selected')
	end
	ret:tag('td'):addClass('plainlist'):node(toolList)
	
	-- Skills (level)
	-- Skills (xp)
	local skillList = mw.html.create('ul'):addClass('skills-list')
	local xpList = mw.html.create('ul'):addClass('skills-list')
	
	if(#recipe.skills == 0) then
		skillList:tag('li'):wikitext('None')
		xpList:tag('li'):wikitext('None')
		ret:tag('td'):addClass('table-na plainlist'):node(skillList)
		ret:tag('td'):addClass('table-na plainlist'):node(xpList)
	else
		for i, skill in ipairs(recipe.skills) do
			skillList:tag('li'):attr('data-sort-value', i == 1 and skill.level or ''):wikitext(skillPic(lang:ucfirst(skill.name), skill.level))
			xpList:tag('li'):attr('data-sort-value', i == 1 and skill.experience or ''):wikitext(commas._strip(skill.experience) ~= nil and skillPic(lang:ucfirst(skill.name), skill.experience) or skill.experience)
		end
		ret:tag('td'):addClass('plainlist'):node(skillList)
		ret:tag('td'):addClass('plainlist'):node(xpList)
	end

	-- Inputs
	local matList = mw.html.create('ul')
	local materialSortValue = nil
	for _, mat in ipairs(recipe.materials) do
		local quantity = string.gsub(mat.quantity, '%-', '–')
		if(materialSortValue == nil) then
			materialSortValue = quantity
		end
		matList:tag('li'):wikitext(string.format('%s × [[%s]]', commas._add(quantity), mat.name))
	end
	ret:tag('td'):addClass('plainlist'):attr('data-sort-value', materialSortValue):node(matList)
	
	return ret
end

function createHeader()
	local header = mw.html.create('table'):addClass('wikitable sortable products-list align-center-1 align-left-2 align-center-3'):done()
		header:tag('tr'):tag('th'):attr('colspan', '2'):wikitext('Product'):done()
			:tag('th'):wikitext('Members'):done()
			:tag('th'):wikitext('Tool'):done()
			:tag('th'):attr('data-sort-type', 'number'):wikitext('Skills'):done()
			:tag('th'):attr('data-sort-type', 'number'):wikitext('XP'):done()
			:tag('th'):wikitext('Materials'):done()
	return header
end

-- If both variables contain the same element as part of a list (or single string)
function bothContain(argOne, argTwo)
	if(argOne == nil) or (argOne == '') or (argTwo == nil) or (argTwo == '') then
		return false
	elseif((type(argOne) == 'string') and (string.find(argOne, ',') == nil)) and ((type(argTwo) == 'string') and (string.find(argTwo, ',') == nil)) then
		return trim(argOne) == trim(argTwo)
	else
		if(type(argOne) == 'string') then
			argOne = split(argOne, "%s*,%s*")
		end
		if(type(argTwo) == 'string') then
			argTwo = split(argTwo, "%s*,%s*")
		end
		for i, v in ipairs(argOne) do
			for j, w in ipairs(argTwo) do
				if(trim(w) == trim(v)) then
					return true
				end
			end
		end
		return false
	end
end

function p.loadData(tools, limit, offset)
	local query = {
		'[[Uses tool::'.. table.concat(tools, '||') ..']]',
		'[[Production JSON::+]]',
		'?=#-',
		'?Production JSON = json',
		limit = limit or 500,
		offset = offset or 0,
	}
	local t1 = os.clock()
	local smwData = mw.smw.ask(query)
	local t2 = os.clock()

	if(smwData == nil) then
		return nil
	end
	mw.log(string.format('SMW: entries %d, time elapsed: %.3f ms.', #smwData, (t2 - t1) * 1000))

	data = {}
	for i, v in ipairs(smwData) do
		if type(v['json']) == 'string' then
			table.insert(data, jsonDecode(v['json']))
		elseif type(v['json']) == 'table' then
			for _, w in ipairs(v['json']) do
				table.insert(data, jsonDecode(w))
			end
		end
	end

	-- Remove recipes that do not use at least one of the input tools
	-- Iterate in reverse so pops do not interrupt iteration
	for i = #data, 1, -1 do
		if(not bothContain(tools, data[i].tools)) then
			table.remove(data, i)
		end
	end
	
	-- Sort table values by order of tool input
	if((#tools == 1)) then -- or (#data == 1)
		return data
	else
		local ret = {}
		-- This tracks the current position of where inserts should occur
		local toolCountOffset = {}
		for i = 1, #tools, 1 do
			table.insert(toolCountOffset, 0)
		end
		for i, recipe in ipairs(data) do
			local pos = 0
			for j, tool in ipairs(tools) do
				pos = pos + toolCountOffset[j]
				if(bothContain(recipe.tools, tool)) then
					table.insert(ret, pos + 1, recipe)
					toolCountOffset[j] = toolCountOffset[j] + 1
					break
				end
			end
		end
		return ret
	end
end

function p._main(args)
	local tool = args ~= nil and args[1] or mw.title.getCurrentTitle().text

	local tools = {}
	if(string.find(tool, ',') == nil) then
		table.insert(tools, trim(tool))
	else
		for _, toolV in ipairs(split(tool, ",")) do
			table.insert(tools, trim(toolV))
		end
	end
	
	data = p.loadData(tools, args.limit, args.offset)
	
	if(data == nil) then
		return 'Failed to find products using that tool - ensure it is spelled correctly. (ERR: no results from SMW)[[Category:Empty drop lists]]'
	end
	
	local ret = createHeader()
	for _, recipe in ipairs(data) do
		ret:node(buildRow(recipe, tools))
	end
	
	return tostring(ret)
end

--[[ DEBUG
= p._main({'Hammer'})
= p._main({'Hammer, Pickaxe'})
--]]

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

return p