Module:CargoQuery

From Caves of Qud Wiki
Jump to navigation Jump to search

This module is taken from Leaguepedia's CargoQuery module.

This module lets you get around the |no html bug that Cargo has by avoiding |format=template. This module implicitly assumed you ARE using named args in your template (corresponding to |named_args=yes; you do not need to specify this.)

Unlike |format=template, this wrapper will NOT rename parameters with underscores in them to use spaces instead.

Parameters

  • You may specify all parameters the same as if they were parameters in #cargo_query.

One-To-Many

This template allows for one-to-many support, allowing you to specify a field to treat equivalently to a List of ... field. Syntax is as follows:

  • |one_to_many=, a list of fields you want concatenated together.
  • |one_to_many_group=, the thing you want our one-to-many values grouped by. UNLIKE the |group by= parameter, this should be the FINAL name of a field, so something on the RIGHT SIDE of the equals sign in your |fields= parameter.
  • |one_to_many_sep=, defaults to comma. Currently you must pick the same separator for all of your one-to-many fields.

Example

{{#invoke:CargoQuery|main
 |table=Teams,ListplayerCurrent=LPC
 |join on=Teams._pageName=LPC._pageName
 |where=Teams.Region="North America" AND LPC._pageName IS NOT NULL
 |fields=Teams._pageName=Page
 |one_to_many=LPC.ID=IDs
 |one_to_many_group=Page
 |one_to_many_sep=,
 |template=<template>
 |intro={{(!}} class="wikitable"
 |outro={{!)}}
 |limit=10
}}

Here, the one_to_many is a list of players on the team in question. For each ONE team, there are MANY players, so the field IDs that's sent to the template is a comma-separated list of players, grouped by team page.

Compound Queries

You can also run compound queries using compound. It has the same syntax as #cargo_compound_query but with a few exceptions:

  • default for each individual query is disabled; default must be specified outside as an additional argument, which is then applied if no queries return results.
  • Each query must be numbered as in |1=(query1)|2=(query2)|3=..., etc.
  • Because of how compound query uses ; as a separator, strings with this character cannot be used. This can be worked around by specifying metaseparator to be another symbol, and splitting each query's arguments using that delimiter instead.

Example

{{#invoke:CargoQuery|compound
|1=table=Food=F
;fields=F._pageName=Page
;where=F.CookEffect HOLDS 'heat and fire-based' AND F._pageNamespace = '0'
|2=table=Liquids=L
;fields=L._pageName=Page
;where=L.CookEffect HOLDS 'simple heat and fire-based' AND L._pageNamespace = '0'
|default=No Results!
|template=favilink page
|delimiter = <br>
|metaseparator = ;
}}

local PARAM_LOOKUP = {
	['order by'] = 'orderBy',
	['join on'] = 'join',
	['group by'] = 'groupBy',
	table = 'tables',
}

local h = {}

local p = {}
function p.main(frame)
	local args = h.merge()
	
	local intro = frame:preprocess(args.intro or '')
	local outro = frame:preprocess(args.outro or '')
	local delimiter = args.delimiter or ''
	
	local colcount = args.columns or nil
	local format = args.format or nil
	
	result = p.query(args)
	if (result == args.default or result == '') then
		return result
	end
	local tbl = {}
	if format == "table" then
		return (intro .. tostring(h.makeTable(result)) .. outro)
	else
		for i, row in ipairs(result) do
			row.index = i
			tbl[#tbl+1] = frame:expandTemplate{ title = args.template, args = row }
		end
		if colcount ~= nil then
			intro = (intro .. '<div style="-webkit-column-count:' .. 
			     colcount .. '; -moz-column-count:' .. 
				 colcount .. '; column-count:' .. colcount .. ';">')
			outro = '</div>' ..outro
		end
		if format == "ul" then
			intro = intro .. "<ul><li>"
			outro = "</ul>" .. outro
			delimiter = "<li>"
		end
		return intro .. table.concat(tbl, delimiter) .. outro
	end
end

function p.query(args)
	local frame = mw.getCurrentFrame()
	local query = {}
	for k, v in pairs(args) do
		if string.sub(k, 0, 2) == 'q?' then
			local key = string.sub(k, 3)
			query[PARAM_LOOKUP[key] or key] = v
		elseif PARAM_LOOKUP[k] then
			query[PARAM_LOOKUP[k]] = v
		else
			query[k] = v
		end
	end
	
	if args.one_to_many then
		query.fields = query.fields .. ',' .. query.one_to_many
	end
	
	local result = mw.ext.cargo.query(query.tables, query.fields, query)
	if #result == 0 then
		return frame:preprocess(args.default or '')
	end
	if args.one_to_many then
		result = h.groupOneToManyFields(result, h.getOneToManyTableFromArgs(args))
		h.concatOneToManyFieldsInEachRow(result, args.one_to_many_sep or ',')
	end
	return result
end

-- This function on Leaguepedia is part of Module:ArgsUtil but is copied here to avoid dependencies
function h.merge(mergeParent)
    mergeParent = mergeParent or true
	local f = mw.getCurrentFrame()
	local origArgs = f.args

	local args = {}
	if mergeParent then
		local parentArgs = f:getParent().args
	    for k, v in pairs(parentArgs) do
			v = mw.text.trim(tostring(v))
			if v ~= '' then
				args[k] = v
			end
	    end
	end
	for k, v in pairs(origArgs) do
		v = mw.text.trim(v)
		if v ~= '' then
			args[k] = v
		end
	end
	
	return args
end

function h.getOneToManyTableFromArgs(args)
	local oneToMany = {
		fields = mw.text.split(args.one_to_many, '%s*,%s*'),
		groupBy = { args.one_to_many_group },
	}
	return oneToMany
end

-- These functions on Leaguepedia are part of Module:CargoUtil but are copied here to avoide dependencies
-- Some code is updated to avoid further dependencies
function h.groupOneToManyFields(result, oneToMany)
	if not oneToMany then return result end
	local currentKey
	local groupedResult = {}
	local fields = h.parseFieldsForKeys(oneToMany.fields)
	for _, row in ipairs(result) do
		local newKey = h.getNewKey(row, oneToMany.groupBy)
		if newKey == currentKey then
			h.addRowToExistingGroup(groupedResult, row, fields)
		else
			h.addRowToNewGroup(groupedResult, row, fields)
			currentKey = newKey
		end
	end
	return groupedResult
end

function h.parseFieldsForKeys(fields)
	for i, v in ipairs(fields) do
		fields[i] = h.parseOneFieldForKey(v)
	end
	return fields
end

function h.getNewKey(row, groupBy)
	local toConcat = {}
	for _, v in ipairs(groupBy) do
		toConcat[#toConcat+1] = row[v]
	end
	return table.concat(toConcat)
end

function h.parseOneFieldForKey(str)
	if not str:find('=') then return str end
	return str:match('=(.+)')
end

function h.addRowToExistingGroup(groupedResult, row, fields)
	for _, v in ipairs(fields) do
		local parentRowValues = groupedResult[#groupedResult][v]
		parentRowValues[#parentRowValues+1] = row[v]
	end
end

function h.addRowToNewGroup(groupedResult, row, fields)
	for _, v in ipairs(fields) do
		row[v] = { row[v] }
	end
	groupedResult[#groupedResult+1] = row
end

function h.concatOneToManyFieldsInEachRow(result, sep)
	for _, row in ipairs(result) do
		for k, v in pairs(row) do
			if type(v) == 'table' then
				row[k] = table.concat(v, sep)
			end
		end
	end
end

function p.compound(frame)
	local util_args = require('Module:Args Utility')
	local util_table = require('Module:Table Utility')

	local args = h.merge(false)

	template = frame:preprocess(args['template'] or '')
	if template == nil or template == '' then
    		error('A template must be specified!')
	end
	delimiter = frame:preprocess(args['delimiter'] or '')
	metasep = frame:preprocess(args['metaseparator'] or ';')

	local splitargs = {}
	for a, b in ipairs(args) do
    		splitargs[a] = util_args.splitNamedArgs(frame:preprocess(b), '%s*' .. metasep .. '%s*')
	end
	
	splitargs['default'] = ''

	local result = {}
	for a, b in ipairs(splitargs) do
    		tempresult = p.query(b)
    		if tempresult ~= nil and tempresult ~= '' then
        		result[#result+1] = tempresult
    		end
	end
	if result == args.default or result == '' then
		return result
	end
	
	resulttbl = util_table.mergeArrays(nil, unpack(result))
	local tbl = {}
	for i, row in ipairs(resulttbl) do
		row.index = i
		tbl[#tbl+1] = frame:expandTemplate{title = args['template'], args = row}
	end
        if #tbl == 0 then
           return frame:preprocess(args['default'] or 'no results')
        end
	local intro = frame:preprocess(args['intro'] or '')
	local outro = frame:preprocess(args['outro'] or '')
	return intro  .. table.concat(tbl, delimiter) .. outro
end

function h.getColNames(row)
	cols = {}
	for colname, _ in pairs(row)do
		table.insert(cols, colname)
	end
	return cols
end

function h.makeTable(result)
	local util_html = require'Module:HTML Utility'
    local tbl = mw.html.create('table')
        :addClass('wikitable')
    colNames = h.getColNames(result[1])
    util_html.printHeader(tbl, colNames)
    util_html.printRowsByList(tbl, result, colNames)
    return tbl
end

return p