local p = {}local lang = mw.getContentLanguage()local navbar = require("Module:Navbar")local messages = {["true"] = "Yes",["false"] = "No",null = "N/A",}local bgColors = {["true"] = "#9f9",["false"] = "#f99",null = "#ececec",}local colors = {null = "#2c2c2c",}function p._cell(args)local data = args.data or mw.ext.data.get(args[1])local rowIdx = tonumber(args.output_row)local outputFormat = args.output_formatlocal outputColumnNames = {args.output_column1 or args.output_column,}while args["output_column" .. #outputColumnNames + 1] dotable.insert(outputColumnNames, args["output_column" .. #outputColumnNames + 1])endlocal outputColumnIdxs = {}local numOutputColumnIdxs = 0for i, field in ipairs(data.schema.fields) dofor j, outputColumnName in ipairs(outputColumnNames) doif field.name == outputColumnName thenoutputColumnIdxs[outputColumnName] = inumOutputColumnIdxs = numOutputColumnIdxs + 1endendif numOutputColumnIdxs == #outputColumnNames thenbreakendendif numOutputColumnIdxs < #outputColumnNames thenfor i, outputColumnName in ipairs(outputColumnNames) doassert(outputColumnIdxs[outputColumnName],mw.ustring.format("Output column “%s” not found.", outputColumnName))endendif rowIdx > 0 thenrowIdx = (rowIdx - 1) % #data.data + 1elseif rowIdx < 0 thenrowIdx = rowIdx % #data.data + 1elseerror("0 is not a valid row index.")endlocal record = data.data[rowIdx]if record ~= nil thenif outputFormat or numOutputColumnIdxs > 1 thenlocal values = {}for i, columnName in ipairs(outputColumnNames) dolocal columnIdx = outputColumnIdxs[columnName]table.insert(values, record[columnIdx])endif outputFormat thenreturn mw.ustring.format(outputFormat, unpack(values))elsereturn mw.text.listToText(values)endelselocal columnIdx = outputColumnIdxs[outputColumnNames[1]]return record[columnIdx]endendend--- Returns the value of the cell at the given row index and column name.--- A row index of 1 refers to the first row in the table. A row index of -1--- refers to the last row in the table. It is an error to specify a row index--- of 0.--- Usage: {{#invoke:Tabular data | cell | Table name | output_row = Index of row to output | output_column = Name of column to output }}function p.cell(frame)return p._cell(frame.args)endfunction p._lookup(args)local data = args.data or mw.ext.data.get(args[1])local searchValue = args.search_valuelocal searchPattern = args.search_patternlocal searchColumnName = args.search_columnlocal searchColumnIdxfor i, field in ipairs(data.schema.fields) doif field.name == searchColumnName thensearchColumnIdx = iendif searchColumnIdx thenbreakendendassert(searchColumnIdx, mw.ustring.format("Search column “%s” not found.", searchColumnName))local occurrence = tonumber(args.occurrence) or 1local numMatchingRecords = 0for i = (occurrence < 0 and #data.data or 1),(occurrence < 0 and 1 or #data.data),(occurrence < 0 and -1 or 1) dolocal record = data.data[i]if (searchValue and record[searchColumnIdx] == searchValue) or(searchPattern and mw.ustring.match(tostring(record[searchColumnIdx]), searchPattern)) thennumMatchingRecords = numMatchingRecords + 1if numMatchingRecords == math.abs(occurrence) thenlocal args = mw.clone(args)args.data = dataargs.output_row = ireturn p._cell(args)endendendend--- Returns the value of the cell(s) in the given output column(s) of the row--- matching the search key and column.--- Reminiscent of LOOKUP() macros in popular spreadsheet applications, except--- that the search key must match exactly. (On the other hand, this means the--- table does not need to be sorted.)--- Usage: {{#invoke: Tabular data | lookup | Table name | search_value = Value to find in column | search_pattern = Pattern to find in column | search_column = Name of column to search in | occurrence = 1-based index of the matching row to output | output_column = Name of column to output | output_column2 = Name of another column to output | … | output_format = String format to output the values in }}function p.lookup(frame)return p._lookup(frame.args)endfunction p._wikitable(args)local pageName = args[1]local data = mw.ext.data.get(pageName)local datatypes = {}local htmlTable = mw.html.create("table"):addClass("wikitable sortable")htmlTable:tag("caption"):wikitext(navbar.navbar({template = ":c:Data:" .. pageName,mini = "y",style = "float: right;","view", "edit",})):wikitext(data.description)local headerRow = htmlTable:tag("tr")for i, field in ipairs(data.schema.fields) doheaderRow:tag("th"):attr("scope", "col"):attr("data-sort-type", datatypes[j] == "text" and "string" or datatypes[j]):wikitext(field.title)datatypes[i] = field.typeendfor i, record in ipairs(data.data) dolocal row = htmlTable:tag("tr")for j = 1, #data.schema.fields dolocal cell = row:tag("td")if record[j] thenlocal formattedData = record[j]if datatypes[j] == "number" thenformattedData = lang:formatNum(formattedData)cell:attr("align", "right")elseif datatypes[j] == "boolean" thencell:addClass(record[j] and "table-yes" or "table-no"):css({background = record[j] and bgColors["true"] or bgColors["false"],color = record[j] and colors["true"] or colors["false"],["vertical-align"] = "middle",["text-align"] = "center",}):wikitext(record[j] and messages["true"] or messages["false"])endcell:wikitext(formattedData)elsecell:addClass("mw-tabular-value-null"):addClass("table-na"):css({background = bgColors.null,color = colors.null,["vertical-align"] = "middle",["text-align"] = "center",}):wikitext(messages.null)endendendlocal footer = htmlTable:tag("tr"):tag("td"):addClass("sortbottom"):attr("colspan", #data.schema.fields)footer:wikitext(data.sources)footer:tag("br")local licenseText = mw.message.new("Jsonconfig-license",mw.ustring.format("[%s %s]", data.license.url, data.license.text))footer:tag("i"):wikitext(tostring(licenseText))return htmlTableend--- Returns a tabular data page as a wikitext table.--- Usage: {{#invoke:Tabular data | wikitable | Table name }}function p.wikitable(frame)return p._wikitable(frame.args)endreturn p