Module:Store locations list

Revision as of 21:23, 4 November 2021 by Admin (talk | contribs) (1 revision imported)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Documentation for this module may be created at Module:Store locations list/doc

-- <nowiki>
local p = {}

local commas = require("Module:Addcommas")._add
local yesno = require("Module:Yesno")
local is_empty = require("Module:Paramtest").is_empty
local has_content = require("Module:Paramtest").has_content
local currency_image = require("Module:Currency Image")
local purge = require("Module:Purge")._purge

local p2pIcon = '[[File:P2P icon.png|25px|frameless|link=Pay-to-play]]'
local f2pIcon = '[[File:F2P icon.png|25px|frameless|link=Free-to-play]]'
local packIcon = '  [[File:Empty sack pack.png|25px|frameless|link=Item pack|alt=Item pack]]'

function p.main(frame)
	local args = frame:getParent().args
	local item = args[1] and mw.text.trim(args[1]) or mw.title.getCurrentTitle().text
	mw.log(string.format('Searching for shops that sell: %s', item))
	
	local historical = yesno(args['Historical'],false)

	-- Get parsed smw data
	local data = getData(item,args)

	-- Create the header of the output
	local headerText = "<div class=\"seealso\">This is an auto-generated list (" .. purge('res', 'update now', 'span') .. "). For help, see [[Template:Store locations list/FAQ|the FAQ]].</div>\n"
	if historical then
		headerText = "<div class=\"seealso\">Note: As this item is no longer sold, this is a list of stores that previously sold it.</div>\n"..headerText
	end
	
	local restbl = mw.html.create('table')
	if historical then
		restbl:addClass('wikitable sortable storeloc-hist align-right-3 align-right-4 align-right-5 align-center-6 sticky-header')
	else
		restbl:addClass('wikitable sortable align-right-3 align-right-4 align-right-5 align-center-6 sticky-header')
	end
	restbl:tag('tr')
			:tag('th'):wikitext('Seller'):done()
			:tag('th'):wikitext('Location'):done()
			:tag('th'):wikitext('Cost'):attr('data-sort-type', 'number'):done()
			:tag('th'):wikitext('Currency'):done()
			:tag('th'):wikitext('Base stock'):attr('data-sort-type', 'number'):done()
			:tag('th'):wikitext('Members?'):done()
		:done()

	-- Create the rows for the output table
	local hasHistoric = false
	for _, shop in ipairs(data) do
		local row = restbl:tag('tr')
		if shop.historic then
			hasHistoric = true
			local seller = shop.seller .. '<span title="Was previously sold by" style="cursor:help;"><i><b> †</b></i></span>'
			row	:addClass('store-hist')
				:tag('td'):wikitext(seller):done()
		else
			row:tag('td'):wikitext(shop.seller):done()
		end
		row	:tag('td'):wikitext(shop.location):done()
			:tag('td')
				:attr({
					['data-sort-value']=shop.costSortValue,
					['title']=shop.costTitle
				})
				:css('cursor','help')
				:wikitext(shop.cost)
			:done()
			:tag('td')
				:attr({
					['data-sort-value']=shop.currSortValue,
					['title']=shop.currTitle
				})
				:wikitext(shop.currency)
			:done()
			:tag('td')
				:attr({
					['data-sort-value']=shop.stockSortValue,
					['title']=shop.stockTitle
				})
				:css('cursor','help')
				:wikitext(shop.stock)
			:done()
			:tag('td'):wikitext(shop.members):done()
		:done()
	end
	
	if hasHistoric then
		local caption = '<i>† Denotes discontinued stores that stocked [[' .. item .. ']]. For help, see [[Template:Store locations list/FAQ|the FAQ]].</i>'
		restbl:tag('caption')
			:css('caption-side', 'bottom')
			:wikitext(caption)
		:done()
	end

	return headerText .. tostring(restbl)
end

function getData(itemName,args)
	local historical = yesno(args['Historical'] or args[2],false)
	local itemIsMembers = false
	mw.log('Querying for item info for '..itemName)
	local itemData = mw.smw.ask({
		'[['..itemName..']]',
		'?Is members only'
	})
	if itemData then
		itemIsMembers = itemData[1]['Is members only'] == true
		-- == true to exclude Is members only being a table
	end
	mw.log('Found that item membership is: '..tostring(itemIsMembers))
		
	
	-- Query smw historical
	local qh = {
		'[[Sold item hist::'..itemName..']]',
		'?Sold by hist#-=Sold by',
		'?Store stock',
		'?Store stock infinite',
		'?Store sell price',
		'?Store currency',
		'?Sold by hist.Is members only=Store members',
		'?Sold by hist.Store location=Store location',
		limit = args.limit or 100,
		sort = args.sort,
		order = args.order
	}
	-- Query for packs historical
	local qph = {
		'[[Pack of hist::'..itemName..']]',
		'?Pack amount',
		'?Sold by hist#-=Sold by',
		'?Store stock',
		'?Store stock infinite',
		'?Store sell price',
		'?Store currency',
		'?Sold by hist.Is members only=Store members',
		'?Sold by hist.Store location=Store location',
		'?Sold item.Is members only=Item members',
		limit = args.limit or 100,
		sort = args.sort,
		order = args.order
	}
	-- Query smw
	local q = {
		'[[Sold item::'..itemName..']]',
		'?Sold by#-',
		'?Store stock',
		'?Store stock infinite',
		'?Store sell price',
		'?Store currency',
		'?Sold by.Is members only=Store members',
		'?Sold by.Store location=Store location',
		limit = args.limit or 100,
		sort = args.sort,
		order = args.order
	}
	-- Query for packs
	local qp = {
		'[[Pack of::'..itemName..']]',
		'?Pack amount',
		'?Sold by#-',
		'?Store stock',
		'?Store stock infinite',
		'?Store sell price',
		'?Store currency',
		'?Sold by.Is members only=Store members',
		'?Sold by.Store location=Store location',
		'?Sold item.Is members only=Item members',
		limit = args.limit or 100,
		sort = args.sort,
		order = args.order
	}
	
	local smwdata = {}
	local smwdataP = {}
	-- Historical pages
	if historical then
		smwdata = mw.smw.ask(qh)
		smwdataP = mw.smw.ask(qph)
	
	else -- Regular pages (show both)
		smwdata = mw.smw.ask(q)
		smwdataP = mw.smw.ask(qp)
		-- Historic shops
		local smwdataH = mw.smw.ask(qh)
		if smwdataH then
			for _,item in ipairs(smwdataH) do
				item.Historic = true
				table.insert(smwdata,item)
			end
		end
		local smwdataPH = mw.smw.ask(qph)
		if smwdataPH then
			for _,item in ipairs(smwdataPH) do
				item.Historic = true
				table.insert(smwdataP,item)
			end
		end
	end

	-- Query packs
	if smwdataP then
		for _,item in ipairs(smwdataP) do
			item['Is pack'] = true
			table.insert(smwdata,item)
		end
	end

	local data = {}

	if smwdata == nil and historical then
		error('The item "' .. itemName .. '" was never sold in any shop, please check for typos[[Category:Empty store lists]]', 0)
	elseif smwdata == nil then
		error('The item "' .. itemName .. '" is not, and was never, sold in any shop, please check for typos[[Category:Empty store lists]]', 0)
	end

	-- Loop over array of subobjects (items sold by shops)
	for _, item in ipairs(smwdata) do
		local soldby = item['Sold by'] or item['Sold by hist'] or ""
		local seller = '[['..soldby..']]'
		if item['Is pack'] then
			seller = seller..packIcon
		end
		local editbtn = editbutton(item['Sold by'] or item['Sold by hist'] or "")

		-- Retrieve shop info
		local smwdataStore = {
			['Is members only'] = itemIsMembers or item['Item members'] or item['Store members'],
			['Store location'] = item['Store location']
		}
		-- Loop over smw return items
		local members, location = {}, {}
		if type(smwdataStore['Is members only']) == 'string' or type(smwdataStore['Is members only']) == 'boolean' then
			table.insert(members,smwdataStore['Is members only'])
		elseif type(smwdataStore['Is members only']) == 'table' then
			for _, v in ipairs(smwdataStore['Is members only']) do
				table.insert(members,v)
			end
		end
		if type(smwdataStore['Store location']) == 'string' then
			table.insert(location,smwdataStore['Store location'])
		elseif type(smwdataStore['Store location']) == 'table' then
			for _, v in ipairs(smwdataStore['Store location']) do
				table.insert(location,v)
			end
		end
		if #members == 1 then
			item['Store Is members only'] = members[1]
		else
			item['Store Is members only'] = members
		end
		if #location == 1 then
			item['Store location'] = location[1]
		else
			item['Store location'] = location
		end

		-- Process the item and add it to the final data table
		local dataline = processData(item, seller, editbtn)
		table.insert(data, dataline)
	end

	return data
end

function processData(item, seller, editbtn)
	-- location
	local location = item['Store location'] or editbtn
	if type(location) == 'table' and #location > 0 then
		location = table.concat(location, ', ')
	elseif type(location) == 'table' then
		location = editbtn
	end
	
	-- Historic?
	local historic = false
	if item.Historic then
		historic = true
	end

	-- members only?
	local members = item['Store Is members only']
	if type(members) == 'table' then
		-- contains yes and no
		local hasYes, hasNo = false, false
		for _, value in ipairs(members) do
			value = yesno(value)
			if value == true then
				hasYes = true
			elseif value == false then
				hasNo = true
			end
		end
		if hasYes and hasNo then
			members = f2pIcon.."/"..p2pIcon
		elseif hasYes then
			members = p2pIcon
		elseif hasNo then
			members = f2pIcon
		else
			members = editbtn
		end
	elseif yesno(members) == true then
		members = p2pIcon
	elseif yesno(members) == false then
		members = f2pIcon
	-- Unsupported type for yesno, default to editbtn
	else
		members = editbtn
	end

	-- base amount in stock
	local stock = item['Store stock'] or ''
	local stockSortValue = 0
	local stockTtl = ''
	stock = string.lower(string.gsub(stock, ',', ''))
	if item['Store stock infinite'] then
		if item['Is pack'] then
			stockTtl = '<span style="font-size:120%;">∞</span>'
		else
			stockTtl = 'Infinite available'
		end
		stock = '<span style="font-size:120%;">∞</span>'
		if historic then
			stockSortValue = '-10e99'
		else
			stockSortValue = '10e99'
		end
	else
		stock = tonumber(stock)
		if stock then
			if item['Is pack'] then
				local packNum = tonumber(item['Pack amount'])
				local total = stock * packNum
				stockSortValue = total
				stockTtl = commas(stock)..' packs of '..commas(packNum)
				stock = commas(total)
			else
				stockSortValue = stock
				stockTtl = commas(stock)
				stock = commas(stock)
			end
			if historic then
				stockSortValue = stockSortValue * -1
			end
		else
			stock = editbtn		-- If stock can't be converted to a number it will default to the edit button
		end
	end

	-- item cost
	local cost = item['Store sell price'] or ''
	local costSortValue = 0
	local rawCost = 0
	local costTtl
	cost = string.lower(string.gsub(cost, ',', ''))
	if cost == 'free' or cost == '0' or cost == 'sample' then
		cost = 'Free sample'
		costTtl = 'Free sample'
	else
		cost = tonumber(cost)
		if cost then
			if item['Is pack'] then
				local packNum = tonumber(item['Pack amount'])
				local each = cost / packNum
				costSortValue = each
				costTtl = 'Pack of '..commas(packNum)..' costs '..commas(cost)
				cost = commas(each)
			else
				costSortValue = cost
				costTtl = commas(cost)
				cost = commas(cost)
			end
			rawCost = costSortValue
			if historic then
				costSortValue = costSortValue * 1000
			end
		else
			cost = editbtn
		end
	end

	-- currency
	local currency = item['Store currency']
	local currSortValue = currency
	local currTitle = currency
	if is_empty(currency) then
		currency = editbtn
	elseif cost == 'Free sample' then
		currency = "-"
	else
		local displayVal = currency:gsub("^%l", string.upper) -- first letter uppercase
		currTitle = displayVal
		local imageName = currency_image(currency, rawCost) or ''
		if(has_content(imageName)) then
			currency = string.format('<span class="inventory-image">[[File:%s|x22px|link=]]</span> %s', imageName, displayVal)
		else
			currency = displayVal
		end
	end
	if historic then
		currSortValue = 'zz ' .. currSortValue
	end

	return {
		seller = seller,
		location = location,
		members = members,
		stock = stock,
		stockTitle = stockTtl,
		stockSortValue = stockSortValue,
		cost = cost,
		costTitle = costTtl,
		costSortValue = costSortValue,
		currency = currency,
		currTitle = currTitle,
		currSortValue = currSortValue,
		historic = historic
	}
end

function editbutton(title)
	if (title=="") then
		return ""
	end
	local link = string.gsub(mw.title.getCurrentTitle():fullUrl("action=edit"), mw.title.getCurrentTitle().fullText, title)
	link = string.gsub(link, ' ', '_')
	link = string.format("<span class='plainlinks'>[%s '''?''' (edit)]</span>", link)
	return link
end

return p
-- </nowiki>