Jump to content

Module:User:McBayne/Json2table

From Wikipedia, the free encyclopedia
--[[  
  __  __           _       _           _                 ____  _        _     _      
 |  \/  | ___   __| |_   _| | ___ _   | |___  ___  _ __ |___ \| |_ __ _| |__ | | ___ 
 | |\/| |/ _ \ / _` | | | | |/ _ (_)  | / __|/ _ \| '_ \  __) | __/ _` | '_ \| |/ _ \
 | |  | | (_) | (_| | |_| | |  __/| |_| \__ \ (_) | | | |/ __/| || (_| | |_) | |  __/
 |_|  |_|\___/ \__,_|\__,_|_|\___(_)___/|___/\___/|_| |_|_____|\__\__,_|_.__/|_|\___|
                                                                                     
This module is intended for converting tabular data pages on commons to wikitables.

Authors and maintainers:
* User:Jarekt - original version in response to https://phabricator.wikimedia.org/T252307
]]

require('strict')                                   -- see Q16748603
local ISOdate = require('Module:ISOdate')._ISOdate  -- see Q20962109
local getArgs = require('Module:User:McBayne/Core').getArgs      -- see Q93865060
local yesno   = require('Module:User:McBayne/Core').yesno        -- see Q93865060

-- ==================================================
-- === External functions ===========================
-- ==================================================
local p = {}

-- ideas: decimate data, linear correction

-- ========================================================================================
-- === INPUTS:                                                                          ===
-- ===  * dataset - name of a page on Commons in "Data:" namespace                      ===
-- ===  * lang    - language code, in not provided, it will default to Wiki's language  ===
-- ===            (on monolingual wikis) or the user's language (on multilingual Wikis) ===                                                    ===
-- ===  * args    - additional optional arguments:                                      ===
-- ===    * row_header - (true) should first column be in bold                          ===
-- ===    * col_header - (true) should we look up and use column names                  ===
-- ===    * caption    - (true) should we add table description as caption              ===
-- ===    * empty_cell - ([space]) value to use if "nil" value in the table             ===
-- ===    * format_table - (default class="Wikitable") change table formating           ===
-- ===    * bare       - (false) show only table's cells (needs to be placed within     ===
-- ===                    table wikitext                                                ===
-- ===    * nowiki     - (false) show table's wikicode (used for debugging)             ===
-- ========================================================================================
function p._json2table(dataset, lang, args)	
	-- make sure inputs are in the right format
--	if not lang or not mw.language.isValidCode( lang ) then
--		_, lang = mw.wikibase.getLabelWithLang( 'Q2' ) -- get user's chosen language
--	end
	
	local rowHeader = yesno(args.row_header, true ) -- row header? Or should first column be highlighted?
	rowHeader = (rowHeader and '!') or '|'
	local bare      = yesno(args.bare, false)
	local endRow = '\n|-\n'
	local useCol = {}
	local xAxisCol = 1 --hardcoded, but could be made dynamic if needed
	
	-- allow some columns to be skipped
	local useAllCol = (args.columns==nil)
	if args.columns then
		local str = args.columns.."/"
		local columns = {str:match((str:gsub("[^/]*/", "([^/]*)/")))} -- split string
		for _, col in ipairs(columns) do
			useCol[mw.ustring.lower(col)] = true
		end
	end
	
	-- make sure detaset is provided and has the right extension
	assert(dataset, 'Commons dataset variable not provided')
	if string.sub(dataset,-4) ~= '.tab' then
		dataset = dataset .. '.tab'
	end
	
	local data = mw.ext.data.get(dataset, lang)
	assert(data, 'Can not access Commons dataset [[c:Data:' .. dataset..'|'..dataset..']]')
	local dataTable, colIdx, colDate, colTable = {}, {}, {}, {}
	if yesno(args.caption, true) and not bare then -- add table caption
		local linkback = " [[File:Blue pencil.svg |frameless |text-top |10px |link=c:Data:"..dataset .. "]]"
		table.insert(dataTable, '|+'..data.description..linkback..'\n')
	end
	-- add column names
	local seriesIdx = 1
	local yTitles = {}
	local key
	
	for iCol, field in pairs(data.schema.fields) do -- go over field names
		key = mw.ustring.lower(field.name)
		if useAllCol or useCol[key] then 
			table.insert(colTable, '!'.. (field.title or field.name))
			table.insert(colIdx, iCol)
			-- if field.name hase a word "date" in it than we will trreat it as  
			-- a date and translate to project's language
			table.insert(colDate, mw.ustring.find(key, 'date')~=nil)
			if iCol ~= xAxisCol then
				yTitles['y' .. seriesIdx .. 'Title'] = (field.title or field.name)
				seriesIdx = seriesIdx + 1
			end	
		end
	end	
	local nCol = #colTable
	if yesno(args.col_header, true) then
		table.insert(dataTable, table.concat(colTable, '\n'))
	end
	--add table cells
	local xValues = {}
	local yValues = {}
	
	for i = 1,nCol do
		yValues['y'..i] = {}
	end
	
	for _, row in pairs(data.data) do -- go over rows
		colTable = {}
		seriesIdx = 1
		
		for _, i in ipairs(colIdx) do            -- go over columns
			local val = (row[i] or args.empty_cell or ' ')
--			if colDate[i] then
--				val = ISOdate(val, lang) -- convert ISO date YYYY-MM-DD to I18n date 
--			end
			table.insert(colTable, val)
			if i == xAxisCol then
				table.insert(xValues, val)	
			else
				table.insert(yValues['y' .. seriesIdx], val)
				seriesIdx = seriesIdx + 1
			end			
		end
		table.insert(dataTable, rowHeader..table.concat(colTable, '\n|'))
	end
	-- finish formating the table
	local wikitable = endRow .. table.concat(dataTable, endRow)
	if not yesno(args.bare, false) then
		local Format = args.table_format or 'class="wikitable"'
		wikitable = '{| ' .. Format .. '\n' .. wikitable .. '\n|}'
	end
	if yesno(args.nowiki, false) then
		wikitable = '<pre>'..wikitable..'</pre>'
	end
	
	--chart specific parameter
	if args.template then
		local template = args.template
		if string.sub(template,-4) ~= '.tab' then
			template = template .. '.tab'
		end
		local template_data = mw.ext.data.get(template, lang)
		assert(template_data, 'Can not access Commons template [[c:Data:' .. template..'|'..template..']]')	
		
		seriesIdx = 1
		for iCol, field in pairs(template_data.schema.fields) do -- go over field names
			if useAllCol or useCol[key] then 
				-- overwrite yTitles with template ones
				--table.insert(colDate, mw.ustring.find(key, 'date')~=nil)
				
				-- only add when not xAxis
				if iCol ~= xAxisCol then
					yTitles['y' .. seriesIdx .. 'Title'] = (field.title or field.name)
					seriesIdx = seriesIdx + 1
				end	
			end
		end	

		--if args.template_x --> overwrite xValues
		if args.template_x then
			local do_Xvalues = yesno(args.template_x, true)
			
			if do_Xvalues then
				--clear xValues
				xValues = {}
				for _, row in pairs(template_data.data) do -- go over rows
					local val = (row[xAxisCol] or args.empty_cell or ' ')
					table.insert(xValues, val)
				end			
			end
		end		
	end
	
	--default values, can be overwritten with inputs from interface
	local chartArgs =
	{
		type = "line",
		x = table.concat(xValues, ","),
--		y1 = table.concat(yValues['y1'], ","),
--		y1Title = yTitles['y1Title']
	}
	
	--fill in chart args here properly (from dataset)
	for i = 1,nCol do
		chartArgs['y'..i] = table.concat(yValues['y'..i], ",")
		chartArgs['y'.. i ..'Title'] = yTitles['y'.. i ..'Title']
	end

    -- overwrite chart parameters with inputs from interface
	for k, v in pairs(args) do
		local chartParam = string.match(k, "^chart_(.+)")
		if chartParam then 
--			if chartParam == "width" then
--				chartArgs["Breite"] = v .. "px"
--			else
				chartArgs[chartParam] = v 
--			end
		end
	end

	--todo: args = chartArgs...
	if args.output == "chart" then
		wikitable = {}
	
		local text
		wikitable = mw.getCurrentFrame():expandTemplate {
			title = 'Template:ChartDirect',
			args = chartArgs
--			args = {
--				chart = "Elo rating (automatic).chart",
--				x = chartArgs["x"],
--				y1 = chartArgs["y1"],
--				y1Title = chartArgs["y1Title"]
--			}
		}
	end

	return wikitable 
end

--[[ ===========================================================================
Call through a template with code:

{{#invoke:Json2table|json2table |lang={{{lang|xx}}} }}

where xx is a  project's language ( leave blank on multilanguage projects )

Template can be called with variety of parameters:
                                                                    
 * dataset - name of a page on Commons in "Data:" namespace                     
 * lang    - language code, in not provided, it will default to Wiki's          
			 language (on monolingual wikis) or the user's language              
			 (on multilingual Wikis)                                            
 * row_header - (true) should first column be in bold                           
 * col_header - (true) should we look up and use column names                   
 * caption    - (true) should we add table description as caption               
 * empty_cell - ([space]) value to use if "nil" value in the table              
 * format_table - (default class="Wikitable") change table formating            
 * bare       - (false) show only table's cells (needs to be placed within      
				 table wikitext                                                 
 * nowiki     - (false) show table's wikicode (used for debugging)              
]]
function p.json2table(frame)
--	local args = getArgs(frame)
	local args = frame:getParent().args
	if args.dataset then 
		return p._json2table(args.dataset, args.lang, args)	
	else
		return ''
	end
end

return p