Hoppa till innehållet

Modul:Table

Från Wikipedia

Dokumentationen för denna modul kan skapas på Modul:Table/dok

local tab = {}
local lang = mw.getContentLanguage()
local chart = require("Modul:Chart")
require("Modul:Debug")
local defColors = require "Module:Plotter/DefaultColors"

local function addTitlesToChart(htmlContent, xTitle, yTitle, chartWidth, chartHeight,iColumns)
    -- Create the HTML for the x-axis title with centered alignment
    local xTitleHtml
    if (iColumns==1) then
    	xTitleHtml= '<div style="text-align:center;font-weight:bold;position:relative;top:-50px;width:' .. chartWidth .. 'px;">' .. xTitle .. '</div>'
    	else
		xTitleHtml = '<div style="text-align:center;font-weight:bold;margin-top:20px;width:' .. chartWidth .. 'px;">' .. xTitle .. '</div>'
		end
    -- Create the HTML for the y-axis title with adjusted positioning
    lengthYtitle = string.len(yTitle)*8   --length in pixels, about 8 pixels per char
    local yTitleHtml = '<div style="transform: rotate(-90deg);transform-origin: left top 0;position:absolute;font-weight:bold;margin-top:' .. ((chartHeight-lengthYtitle)/2) .. 'px;">' .. yTitle .. '</div>'
--    local yTitleHtml = '<div style="transform: rotate(-90deg);transform-origin: left top 0;position:absolute;font-weight:bold;top:50%;">' .. yTitle .. '</div>'

    -- Find the position to insert the y-axis title (after the opening div of the chart container)
    local chartDivStart = string.find(htmlContent, 'class="chart noresize"', 1, true)
    if chartDivStart then
        -- Find the end of the opening div tag
        local divEnd = string.find(htmlContent, '>', chartDivStart)
        if divEnd then
            -- Insert the y-axis title right after the opening div tag
            htmlContent = string.sub(htmlContent, 1, divEnd) .. yTitleHtml .. string.sub(htmlContent, divEnd + 1)
        else
            print("End of opening div tag not found. Cannot insert y-axis title.")
        end
    else
        print("Chart div not found. Cannot insert y-axis title.")
    end

    -- Find the position to insert the x-axis title (before the closing div of the chart container)
    local chartDivEnd = string.find(htmlContent, '</div>', -20, true)
    if chartDivEnd then
        -- Insert the x-axis title right before the closing div tag
        htmlContent = string.sub(htmlContent, 1, chartDivEnd) .. xTitleHtml .. string.sub(htmlContent, chartDivEnd)
        htmlContent = string.gsub(htmlContent,"<<","<")
    else
        print("Chart div not found. Cannot insert x-axis title.")
    end

    return htmlContent
end

--Function that reads data and put it into a structure that other functions use to present the data
tab.data=function(myframe)
	local step = tonumber(myframe.args['step'])
    if (not step) then step = 1 end
	tab.step=step	
	columns = mw.text.split(myframe.args['columns'],"%s*,%s*")
	iTitles=(#columns)
	tab.columntypes = mw.text.split(myframe.args['columntypes'],"%s*,%s*")
	if not (#columns==#tab.columntypes) then
	    error("Number of columns and their types not matching")
	end

	local xmin = tonumber(myframe.args['xmin'])
	local xmax = tonumber(myframe.args['xmax'])

	local factor = tonumber(myframe.args['factor'])
    if (not factor) then factor = 1 end
	tab.factor=factor	

	local combination = myframe.args['combination']
	local files=mw.text.split(myframe.args['file'],",")
	local iPages=#files
	local filelabels
	if (iPages>1) then
		local strfilelabels=myframe.args['filelabels']
		if (strfilelabels) then
			filelabels= mw.text.split(strfilelabels,",")
			else
		    error("Multiple files use, but no 'filelabels' used as argument to give them labels")
			end
		if not (#filelabels==#files) then
		    error("Number of files and their labels not matching")
			end
		tab.iColumns=(iTitles-1)*iPages
		else
		tab.iColumns=(iTitles-1)*iPages
		end
	local jsonFileContent
	tab.values={}
	tab.xvalues={}
	tab.iTiles=0
	tab.titles={}
	for i, page in ipairs(files) do
		mw.log("i: " .. i)
		jsonFileContent = mw.ext.data.get(page)
		if jsonFileContent then
			if next(jsonFileContent) then
				tab.fields=jsonFileContent.fields;
				data=jsonFileContent.data;
				schema=jsonFileContent.schema;
				indices={}
				tab.xtitle=tab.titles[1]
			 	for j=1,iTitles do
					local index,selecteddata=tab.filter(schema,"fields","name",columns[j],false)
					table.insert(indices,index)
					if (j>1) then 
						if (iPages==1) then
							table.insert(tab.titles,selecteddata.title)
							else
							if (iTitles==2) then
								table.insert(tab.titles,filelabels[i])
								else
								if (combination=="filefirst") then
									table.insert(tab.titles,filelabels[i] .. " (" .. selecteddata.title .. ")" )
									end
								if (combination=="columnfirst") then
									table.insert(tab.titles,selecteddata.title .. " (" .. filelabels[i] .. ")")
									end
								end
							end
						end
			 	end
--				table.remove(tab.titles,1)
				local dataitems=#data/tab.step
				local jRowWrite=0
				for jRow=1,dataitems do
					mw.log("jRow: " .. jRow)
					local jRowRead=1+(jRow-1)*tab.step
					xvalue=data[jRowRead][1]
					minok=(not xmin) or (xvalue>=xmin)
					maxok=(not xmax) or (xvalue<=xmax)
					if (minok and maxok) then
						jRowWrite=jRowWrite+1
						if (i==1) then
							tab.values[jRowWrite]={}
							table.insert(tab.xvalues,xvalue) 
							end
						for k=2,iTitles do
							index=indices[k]
							table.insert(tab.values[jRowWrite],data[jRowRead][index]*factor)
						end
					end
				end
				tab.iDataitems=#tab.xvalues
			else
			    error("The page '" .. page .. "' contains no data")
			end
		else
		    error("The page '" .. page .. "' does not exist")
    	end
	end
	mw.log(tprint(tab))
	return tprint(tab)
end

tab.showBarChart=function(frame)	
    local myframe=frame
    local width=myframe.args["width"]
    local height=myframe.args["height"]
    local xtitle=myframe.args["xtitle"]
    local ytitle=myframe.args["ytitle"]
    local stack=myframe.args["stack"]
    local xtick=myframe.args["xtick"]
    if (not xtick) then xtick = 1 end
    myframe.args={}
    myframe.args=frame.args
	local indices={}
	local titles={}
	local types={}
    if height then
    	myframe.args["height"]=height
    	end
    if stack then
    	myframe.args["stack"]=stack
    	end
	if not width then width="1400" end 
    myframe.args["width"]=width
    myframe.args["group names"]=table.concat(tab.titles,":")
    myframe.args["colors"]=defColors[1]
    for ii=1,20,2 do
    	table.remove(defColors,ii)
	    end
	table.remove(defColors,1)

    local count = 1
    for k, v in pairs(defColors) do
	    if count < (tab.iColumns) then
	    	myframe.args["colors"]=myframe.args["colors"] .. ":" ..  v
	        count = count + 1
	    else
	        break
	    end
	end
	jfirst=true
		tab.step=2
	for j=1,tab.iDataitems do
		thisvalue=""
	    if (((j-1) % xtick) == 0) then
	    	thisvalue=tab.xvalues[j]
		    end
		if (jfirst==true) then
			myframe.args["x legends"]=thisvalue
			jfirst=false
			else
			myframe.args["x legends"]=myframe.args["x legends"] .. ":" .. thisvalue
			end

		for i=1,tab.iColumns do
			txtTarget="group " .. i
			value=tab.values[j][i]
			if (j==1) then
				myframe.args[txtTarget]=value
				else
				myframe.args[txtTarget]=myframe.args[txtTarget] .. ":" .. value
				end
		end
	end
	mw.log(tprint(myframe))
--	local html=tprint(myframe)
	local html=chart["bar chart"](myframe)
	if (xtitle or ytitle) then
		html = addTitlesToChart(html, xtitle, ytitle,width,600,1)
----		html = addTitlesToChart(html, "xtitle", "ytitle")
	end
	return html
end    

tab.showColumnTable=function(frame)	
	local types={}
	local tblTxt={}
	table.insert(tblTxt,"<table class='wikitable'>")
	table.insert(tblTxt,"<tr>")
	table.insert(tblTxt,"<th>")
	table.insert(tblTxt,tab.xtitle)
	table.insert(tblTxt,"</th>")
	for i=1,tab.iColumns do
		table.insert(tblTxt,"<th>")
		table.insert(tblTxt,tab.titles[i])
		table.insert(tblTxt,"</th>")
	end
	table.insert(tblTxt,"</tr>")
	-- Data
	for j=1,tab.iDataitems do
		table.insert(tblTxt,"<tr>")
		value=tab.xvalues[j]
		if (tab.columntypes[1]=="value") then
			table.insert(tblTxt,"<td align='right'>")
			table.insert(tblTxt,lang:formatNum( value ))
			else
			table.insert(tblTxt,"<td>")
			table.insert(tblTxt,value)
			end
			table.insert(tblTxt,"</td>")
		for i=1,tab.iColumns do
			value=tab.values[j][i]
			if (tab.columntypes[i+1]=="value") then
				table.insert(tblTxt,"<td align='right'>")
				table.insert(tblTxt,lang:formatNum( value ))
				else
				table.insert(tblTxt,"<td>")
				table.insert(tblTxt,value)
				end
			table.insert(tblTxt,"</td>")
		end
		table.insert(tblTxt,"</tr>")
	end
	
	table.insert(tblTxt,"</tr>")
	table.insert(tblTxt,"</table>")
	return table.concat(tblTxt)	
end


tab.showColumn=function(frame)	
	tabdata=tab.data(frame)
	if (frame.args["output"]=="table") then
		return tab.showColumnTable(frame)
		end
	if (frame.args["output"]=="chart") then
		return tab.showBarChart(frame)
		end
end

tab.showRow=function(frame)	
	local pageName = frame.args['file']
	local data = mw.ext.data.get(pageName)

	if (frame.args["output"]=="table") then
		return tab.showRowTable(frame,data)
		end
	if (frame.args["output"]=="chart") then
		return tab.showPieChart(frame,data)
	end
	return tprint(data.schema.fields)
--	return tprint(myframe)
end

tab.showRowTable=function(frame,data)	
	local category = frame.args['category']
	local row = frame.args['row']
	local unit = frame.args['unit']
	local xlabel = frame.args['xlabel']
	local ylabel = frame.args['ylabel']
	local index,selecteddata=tab.filter(data.schema,"fields","name",category,false)
	local index2,selecteddata2=tab.filter(data,"data",index,row,true)

	local tblTxt={}
	table.insert(tblTxt,"<table class='wikitable sortable'>")
		table.insert(tblTxt,"<tr>")
		table.insert(tblTxt,"<th>" .. xlabel .. "</td>")
		table.insert(tblTxt,"<th data-sort-type='number'>" .. ylabel .. " (" .. unit .. ")</td>")
		table.insert(tblTxt,"</tr>")
	local first=true
	for i=1,#data.schema.fields do
		if not (i==index) then
			local value=data.data[index2][i]
			table.insert(tblTxt,"<tr>")
			table.insert(tblTxt,"<td>" .. data.schema.fields[i].title .. "</td>")
			table.insert(tblTxt,"<td align='right'>" .. data.data[index2][i] .. "</td>") --lang:formatNum()
			table.insert(tblTxt,"</tr>")
		end
	end	

	table.insert(tblTxt,"</table>")
	return table.concat(tblTxt)	
end

function tochartstring(input)
return string.gsub(input, ",", ":")
end

tab.showPieChart=function(frame,data)	
	local category = frame.args['category']
	local row = frame.args['row']
	local unit = frame.args['unit']
    local radius=frame.args["radius"]
    local minimum=tonumber(frame.args["minimum"])
	local index,selecteddata=tab.filter(data.schema,"fields","name",category,false)
	local index2,selecteddata2=tab.filter(data,"data",index,row,true)

    local myframe=frame
    myframe.args={}
    myframe.args=frame.args
	myframe.args["slices"]=''
	if not radius then radius="150" end 
    myframe.args["radius"]=radius
    others=0
    txtOthers="Övriga"
	local first=true
	for i=1,#data.schema.fields do
		if not (i==index) then
			local value=data.data[index2][i]
			if (not minimum or value>minimum) then -- either not asking for values to be above a minimum value, or the values are above the minimum value
				myframe.args["slices"]=myframe.args["slices"] .. '(' ..  value .. ' : ' .. data.schema.fields[i].title  .. ')' 
				else 
				others=others+tonumber(value)			
				end
		end
	end	
	if (others>0) then
		myframe.args["slices"]=myframe.args["slices"] .. '(' ..  others .. ' : ' .. txtOthers  .. ')' 
	end		
	myframe.args['percent']=true
	myframe.args['units suffix']="_" .. unit
	return chart["pie chart"](myframe)
end

local function decodeHtmlEntities(htmlContent)
    -- Decode common HTML entities
    htmlContent = string.gsub(htmlContent, "&lt;", "<")
    htmlContent = string.gsub(htmlContent, "&gt;", ">")
    htmlContent = string.gsub(htmlContent, "&amp;", "&")
    htmlContent = string.gsub(htmlContent, "&quot;", '"')
    htmlContent = string.gsub(htmlContent, "&apos;", "'")
    return htmlContent
end



tab.filter=function(tblIn,txtProperty,txtVariable,selectedvalue,bIsnumber)
	local tblProperty=tblIn[txtProperty]							-- Get chosen property
	local iItems=#tblProperty										-- Count number of items
	local tblOut={}													-- Empty table that will be filled by the results of the filter query
	local indices={}
	for i=1,iItems do												-- Loop through all items
		if (not bIsnumber and (tblProperty[i][txtVariable]==selectedvalue)) or (bIsnumber and ((tblProperty[i][txtVariable]==selectedvalue) or (tonumber(tblProperty[i][txtVariable])==tonumber(selectedvalue)))) then		-- If the data value for the chosen variable of this item is the same as the selected value (for the filter) ...
			return i,tblProperty[i]																			-- Return the out table
		end
	end
end

-- Funktion från https://stackoverflow.com/questions/41942289/display-contents-of-tables-in-lua
function tprint (tbl, indent)
  if not indent then indent = 0 end
  local toprint = string.rep(" ", indent) .. "{\r\n"
  indent = indent + 2 
  for k, v in pairs(tbl) do
    toprint = toprint .. string.rep(" ", indent)
    if (type(k) == "number") then
      toprint = toprint .. "[" .. k .. "] = "
    elseif (type(k) == "string") then
      toprint = toprint  .. k ..  "= "   
    end
    if (type(v) == "number") then
      toprint = toprint .. v .. ",\r\n"
    elseif (type(v) == "string") then
      toprint = toprint .. "\"" .. v .. "\",\r\n"
    elseif (type(v) == "table") then
      toprint = toprint .. tprint(v, indent + 2) .. ",\r\n"
    else
      toprint = toprint .. "\"" .. tostring(v) .. "\",\r\n"
    end
  end
  toprint = toprint .. string.rep(" ", indent-2) .. "}"
  return toprint
end

return tab