Jump to content

Module:Video game reviews/sandbox

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Izno (talk | contribs) at 22:25, 9 December 2020 (extract the rendermainheading function, move aggregators above reviewers in supermode table, remove dependency on reviewercount in aggregators in prep to extract to its own table). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
require('Module:No globals')

local p = {}
local data = require('Module:Video game reviews/data/sandbox')
local yesno = require('Module:Yesno')
local vgwd = require('Module:Video game wikidata')
local getArgs

local function getActiveSystems(args)
	local activeSystems = {}
	for k, v in pairs(args) do
		if data.systems[k] and yesno(v) then
			table.insert(activeSystems, k)
		end
	end
	table.sort(activeSystems, function(a, b)
		return data.systems[a].sortkey < data.systems[b].sortkey
	end)
	return activeSystems
end

local function getArgKeyTables(args)
	local reviewers, aggregators, awards = {}, {}, {}
	for k in pairs(args) do
		if string.match(k, data.i18n.pattern.reviewer) then
			table.insert(reviewers, k)
		elseif string.match(k, data.i18n.pattern.aggregator) then
			table.insert(aggregators, k)
		elseif string.match(k, data.i18n.pattern.award) then
			table.insert(awards, k)
		end
	end
	local function comparator(a, b)
		return tonumber(a:match('%d+')) < tonumber(b:match('%d+'))
	end
	table.sort(reviewers, comparator)
	table.sort(aggregators, comparator)
	table.sort(awards, comparator)
	return reviewers, aggregators, awards
end

local function getProvidedReviewersAndAggregators(args, usePlatforms)
	local providedReviewers, providedAggregators = {}, {}
	if usePlatforms then
		local seen = {}
		for k in pairs(args) do
			local splitPos = string.find(k, '_')
			if splitPos then
				local halfarg = string.sub(k, 1, splitPos - 1)
				if not seen[halfarg] then
					seen[halfarg] = true
					if data.reviewers[halfarg] then
						table.insert(providedReviewers, halfarg)
					elseif data.aggregators[halfarg] then
						table.insert(providedAggregators, halfarg)
					end
				end
			end
		end
	else
		for k in pairs(args) do
			if not string.find(k, '_') then
				if data.reviewers[k] then
					table.insert(providedReviewers, k)
				elseif data.aggregators[k] then
					table.insert(providedAggregators, k)
				end
			end
		end
	end
	table.sort(providedReviewers, function(a, b)
		return data.reviewers[a].sortkey < data.reviewers[b].sortkey
	end)
	table.sort(providedAggregators, function(a, b)
		return data.aggregators[a].sortkey < data.aggregators[b].sortkey
	end)
	return providedReviewers, providedAggregators
end

local function renderMainHeading(builder, colspan, headingText)
	builder:tag('tr')
		:addClass('vgr-main-heading')
		:tag('th')
			:attr('colspan', colspan)
			:attr('scope', colspan == 1 and 'col' or 'colgroup')
			:wikitext(headingText)
			:done()
end

local function renderHeadingRowWithSystems(builder, mainHeading, activeSystems)
	builder:tag('tr')
		:addClass('vgr-systems')
		:tag('th')
			:attr('scope', 'col')
			:attr('rowspan', '2')
			:wikitext(data.i18n.publication)
			:done()
		:tag('th')
			:attr('scope', 'colgroup')
			:attr('colspan', #activeSystems)
			:wikitext(data.i18n.score)
			:done()
	builder = builder:tag('tr')
	for _, v in ipairs(activeSystems) do
		builder:tag('th')
			:wikitext(data.systems[v].name)
			:attr('scope', 'col')
			:done()
	end
end

local function renderHeadingRow(builder, nameHeading)
	builder:tag('tr')
		:addClass('vgr-hrow')
		:tag('th')
			:attr('scope', 'col')
			:wikitext(nameHeading)
			:done()
		:tag('th')
			:attr('scope', 'col')
			:wikitext(data.i18n.score)
			:done()
end

local function renderRatingsBySystem(builder, code, name, activeSystems, args, na)
	builder = builder:tag('tr')
	builder:tag('td')
		:wikitext(name)

	for _, v in ipairs(activeSystems) do
		local combinedCode = code .. '_' .. v
		local cell = builder:tag('td')
		if args[combinedCode] then
			cell
				:wikitext(args[combinedCode])
				:done()
		elseif na then
			cell
				:addClass('table-na')
				:wikitext(data.i18n.na)
				:done()
		end
	end
end

local function renderRating(builder, name, rating)
	builder:tag('tr')
		:tag('td')
			:addClass('vgr-center')
			:wikitext(name)
			:done()
		:tag('td')
			:wikitext(rating)
			:done()
end

local function renderReviews(builder, providedReviewers, providedAggregators,
	activeSystems, customAggregatorKeys, customReviewerKeys, args)
	builder = builder:tag('table')
		:addClass('vgr-reviews')
	if args.state then builder:addClass('mw-collapsible-content') end

	local reviewerCount = #providedReviewers + #customReviewerKeys
	local aggregatorCount = #providedAggregators + #customAggregatorKeys
	local reviewScore = data.i18n[reviewerCount == 1 and 'reviewScore' or 'reviewScores']
	local aggregateScore = data.i18n[aggregatorCount == 1 and 'aggregateScore' or 'aggregateScores']
	if #activeSystems ~= 0 then
		builder:wikitext(data.i18n.multiplatformCategory)
		local na = yesno(args.na)
		local showplatforms = #activeSystems ~= 1 or yesno(args.showplatforms)
		if aggregatorCount ~= 0 then
			if showplatforms then
				renderMainHeading(builder, #activeSystems + 1, aggregateScore)
				renderHeadingRowWithSystems(builder, activeSystems)
			else
				renderMainHeading(builder, 2, aggregateScore)
				renderHeadingRow(builder, data.i18n.aggregator)
			end

			for _, v in ipairs(providedAggregators) do
				renderRatingsBySystem(builder, v, data.aggregators[v].name, activeSystems, args, na)
			end
			for _, v in ipairs(customAggregatorKeys) do
				renderRatingsBySystem(builder, v, args[v], activeSystems, args, na)
			end
		end
		if reviewerCount ~= 0 then
			if showplatforms then
				renderMainHeading(builder, #activeSystems + 1, reviewScore)
				renderHeadingRowWithSystems(builder, activeSystems)
			else
				renderMainHeading(builder, 2, reviewScore)
				renderHeadingRow(builder, data.i18n.publication)
			end

			for _, v in ipairs(providedReviewers) do
				renderRatingsBySystem(builder, v, data.reviewers[v].name, activeSystems, args, na)
			end
			for _, v in ipairs(customReviewerKeys) do
				renderRatingsBySystem(builder, v, args[v], activeSystems, args, na)
			end
		end
	else
		builder:wikitext(data.i18n.singleplatformCategory)
		if aggregatorCount ~= 0 then
			renderMainHeading(builder, 2, aggregateScore)
			renderHeadingRow(builder, data.i18n.aggregator)
			for _, v in ipairs(providedAggregators) do
				renderRating(builder, data.aggregators[v].name, args[v])
			end
			for _, v in ipairs(customAggregatorKeys) do
				renderRating(builder, args[v], args[v .. 'Score'])
			end
		end
		if reviewerCount ~= 0 then
			renderMainHeading(builder, 2, reviewScore)
			renderHeadingRow(builder, data.i18n.publication)
			for _, v in ipairs(providedReviewers) do
				renderRating(builder, data.reviewers[v].name, args[v])
			end
			for _, v in ipairs(customReviewerKeys) do
				renderRating(builder, args[v], args[v .. 'Score'])
			end
		end
	end
end

local function renderAwards(builder, args, awardKeys, stackedClass)
	builder = builder:tag('table')
		:addClass('vgr-awards')
	if args.state then builder:addClass('mw-collapsible-content') end
	
	builder:tag('caption')
		:wikitext(data.i18n[#awardKeys == 1 and 'award' or 'awards'])
		:addClass(stackedClass)
	
	builder:tag('tr')
		:tag('th')
			:attr('scope', 'col')
			:wikitext(data.i18n.publication)
			:done()
		:tag('th')
			:attr('scope', 'col')
			:wikitext(data.i18n.award)
			:done()

	for _, v in ipairs(awardKeys) do
		builder:tag('tr')
			:tag('td')
				:wikitext(args[v .. 'Pub'])
				:done()
			:tag('td')
				:wikitext(args[v])
				:done()
	end
	builder:done()
	builder:done()
end

local function render(providedReviewers, providedAggregators, awardKeys,
	activeSystems, customAggregatorKeys, customReviewerKeys, args, wikidata)
	local div = mw.html.create('div')
		:addClass('video-game-reviews')
	
	if args.align then
		if args.align == 'left' then
			div:addClass('vgr-left')
		elseif args.align == 'none' then
			div:addClass('vgr-none')
		end
	end
	
	if #activeSystems == 0 then
		div:addClass('vgr-single')
			:css('width', args.width or nil)
	end

	if args.title and args.state and (args.state == 'autocollapse'
			or args.state == 'collapsed' or args.state == 'expanded') then
		div
			:addClass('mw-collapsible')
		if args.state == 'collapsed' then
			div:addClass('mw-collapsed')
		elseif args.state == 'autocollapse' or args.state == 'expanded' then
			div:addClass(args.state)
		end
	end

	div:tag('div')
		:addClass('vgr-title')
		:wikitext(args.title or data.i18n.reception)
		:done()

	if args.subtitle then
		div:tag('div')
			:addClass('vgr-subtitle')
			 -- The only reason to use the subtitle is collapsible content
			 -- So always add the related class.
			:addClass('mw-collapsible-content')
			:wikitext(args.subtitle)
			:done()
	end

	renderReviews(
		div,
		providedReviewers,
		providedAggregators,
		activeSystems,
		customAggregatorKeys,
		customReviewerKeys,
		args
	)
	if #awardKeys ~= 0 then
		local stackedClass =
			(#customAggregatorKeys ~= 0 or
				#customReviewerKeys ~= 0 or
				#providedAggregators ~= 0 or
				#providedReviewers ~= 0
			) and 'vgr-stacked' or nil
		renderAwards(
			div,
			args,
			awardKeys,
			stackedClass
		)
	end

	if wikidata == true then
		div:tag('div')
			:addClass('vgr-edit-on-wikidata')
			-- TODO: I think getUpdateLink can be made const in the other module.
			:wikitext(data.i18n.editOnWikidata .. vgwd.getUpdateLink('nosub'))
			
		if args.state then div:addClass('mw-collapsible-content') end
	end

	return div
end

local function checkForWikidata(frame, args, activeSystems, providedAggregators)
	local wikidata = false

	vgwd.setDateFormat(args["df"])
	vgwd.setGame(args["qid"])
	vgwd.setSystem(nil)
	vgwd.setGenerateReferences(true)
	vgwd.setShowUpdateLink(false)
	vgwd.setUpdateLinkStyle("pen")
	vgwd.setSystemFormat(args["systemFormat"])

	-- Loop through aggregators if we have any.
	if #providedAggregators ~= 0 then
		for _, aggr in ipairs(providedAggregators) do
			-- Check if vgwd knows this aggregator.
			if vgwd.setReviewer(aggr) == nil then
				-- Loop through active systems
				if #activeSystems ~= 0 then
					for _, sys in ipairs(activeSystems) do
						local combinedCode = aggr .. '_' .. sys
						if args[combinedCode] == 'wikidata' then
							vgwd.setSystem(sys)
							vgwd.setShowSystem(false)
							local vgwdScore = vgwd.printReviewScores(frame)
							if vgwdScore then
								args[combinedCode] = vgwdScore
							end
							wikidata = true
						end
					end
				else
					vgwd.setShowSystem(true)
					if args[aggr] == 'wikidata' then
						local vgwdScore = vgwd.printReviewScores(frame)
						if vgwdScore then
							args[aggr] = vgwdScore
						end
						wikidata = true
					end
				end
			end
		end
	end

	return wikidata
end

function p._reviewbox(frame, args)
	local activeSystems = getActiveSystems(args)
	local customReviewerKeys, customAggregatorKeys, awardKeys = getArgKeyTables(args)
	local providedReviewers, providedAggregators = getProvidedReviewersAndAggregators(args, #activeSystems ~= 0)
	local wikidata = checkForWikidata(frame, args, activeSystems, providedAggregators)
	if #customAggregatorKeys ~= 0 or #customReviewerKeys ~= 0 or
		#providedAggregators ~= 0 or #providedReviewers ~= 0 or #awardKeys ~= 0 then
		return frame:extensionTag{
			name='templatestyles', args = { src = data.i18n.templatestyles }
		} ..  tostring(render(
				providedReviewers,
				providedAggregators,
				awardKeys,
				activeSystems,
				customAggregatorKeys,
				customReviewerKeys,
				args,
				wikidata
		))
	elseif mw.title.getCurrentTitle().namespace == 0 then
		return data.i18n.emptyCategory
	end
end

function p.reviewbox(frame)
	if not getArgs then
		getArgs = require('Module:Arguments').getArgs
	end
	return p._reviewbox(frame, getArgs(frame, { wrappers = data.i18n.wrapper, trim = false, translate = data.argi18n }))
end

return p