Module:Chart

De Wikirouge
Aller à la navigation Aller à la recherche

Module Lua invoqué par le modèle {{Graph}}


-- Module:Chart

local p = {}

local function split(str, sep)
    local result = {}
    if not str or str == '' then return result end
    for part in (str .. sep):gmatch("(.-)" .. sep) do
        table.insert(result, mw.text.trim(part))
    end
    return result
end

local defaultColors = {
    '#ffbf7f','#1f77b4','#2ca02c','#d62728',
    '#9467bd','#8c564b','#e377c2','#ff7f0e'
}

local function getColor(colorList, index)
    local c = colorList[index]
    if c and c ~= '' then return mw.text.trim(c) end
    return defaultColors[((index - 1) % #defaultColors) + 1]
end

function p.chart(frame)
    local args = frame:getParent().args
    for k, v in pairs(frame.args) do
        if not args[k] or args[k] == '' then args[k] = v end
    end

    local chartType = mw.text.trim(args['type'] or 'line')
    if chartType == 'rect' then chartType = 'bar' end
    local stacked = false
    if chartType == 'stackedrect' then
        chartType = 'bar'
        stacked = true
    end

    local isSymbol = (mw.text.trim(args['type'] or '') == 'symbol')
    if isSymbol then chartType = 'scatter' end

    local indexAxis = (mw.text.trim(args['orientation'] or '') == 'horizontal') and 'y' or 'x'
    local width       = tonumber(args['width'])  or 400
    local height      = tonumber(args['height']) or 200
    local legend      = mw.text.trim(args['legend'] or '')
    local align       = mw.text.trim(args['align'] or 'center')
    local chartTitle  = mw.text.trim(args['title'] or '')
    local xTitle      = mw.text.trim(args['xAxisTitle'] or '')
    local yTitle      = mw.text.trim(args['yAxisTitle'] or '')
    local showValues  = (args['showValues'] ~= nil)
    local yScaleLog   = (mw.text.trim(args['yScaleType'] or '') == 'log')
    local tension     = (mw.text.trim(args['interpolate'] or '') == 'monotone') and 0.4 or 0
    local xAxisAngle = tonumber(args['xAxisAngle'] or 0) or 0
    local yAxisMax   = tonumber(args['yAxisMax'] or nil)

    local xLabels   = split(mw.text.trim(args['x'] or ''), ',')
    local colorList = split(mw.text.trim(args['colors'] or ''), ',')


--Construction des datasets

    local datasets = {}
	local jsDatasets = {}

	--type Symbol (ou "scatter", nuage de points)
    if isSymbol then
        -- scatter nécessite de combiner x et y en paires {x, y}
        local i = 1
        while true do
            local yRaw = mw.text.trim(args['y' .. i] or '')
            if yRaw == '' then break end
            local yVals = split(yRaw, ',')
            local points = {}
            for j, yv in ipairs(yVals) do
                local xv = xLabels[j]
                table.insert(points, {
                    x = tonumber(xv) or xv,
                    y = tonumber(yv) or 0
                })
            end
            table.insert(jsDatasets, {
                label           = mw.text.trim(args['y' .. i .. 'Title'] or args['legend' .. i] or ''),
                data            = points,
                borderColor     = getColor(colorList, i),
                backgroundColor = getColor(colorList, i),
                pointRadius     = 5,
            })
            i = i + 1
        end
    end

	--autres types (line, rect, stackedrect, pie)
    if not isSymbol then
		local ySimple = mw.text.trim(args['y'] or '')
		if ySimple ~= '' then
			local nums = {}
			for _, v in ipairs(split(ySimple, ',')) do
				table.insert(nums, tonumber(v) or 0)
			end
			table.insert(datasets, {
				label = mw.text.trim(args['yTitle'] or ''),
				data  = nums,
				color = getColor(colorList, 1),
				tension = tension,
			})
		end

		local i = 1
		while true do
			local yRaw = mw.text.trim(args['y' .. i] or '')
			if yRaw == '' then break end
			local nums = {}
			for _, v in ipairs(split(yRaw, ',')) do
				table.insert(nums, tonumber(v) or 0)
			end
			table.insert(datasets, {
				label   = mw.text.trim(args['y' .. i .. 'Title'] or ''),
				data    = nums,
				color   = getColor(colorList, i),
				tension = tension,
			})
			i = i + 1
		end
	
	
		if #datasets == 0 then
			return '<span class="error">Module:Chart — paramètre y manquant</span>'
		end

		if chartType == 'pie' then
			local bgColors = {}
			for j = 1, #datasets[1].data do
				table.insert(bgColors, getColor(colorList, j))
			end
			table.insert(jsDatasets, {
				label           = datasets[1].label,
				data            = datasets[1].data,
				backgroundColor = bgColors,
			})
		else
			for _, ds in ipairs(datasets) do
				table.insert(jsDatasets, {
					label           = ds.label,
					data            = ds.data,
					borderColor     = ds.color,
					backgroundColor = ds.color,
					tension         = ds.tension,
					fill            = false,
				})
			end
		end
	end

    local scales
    if chartType ~= 'pie' then
        local yScale = {
            type  = yScaleLog and 'logarithmic' or 'linear',
            title = { display = (yTitle ~= ''), text = yTitle },
        }
        if yAxisMax then
            yScale.max = yAxisMax
        end
        scales = {
            x = {
                title = { display = (xTitle ~= ''), text = xTitle },
                ticks = { maxRotation = -xAxisAngle, minRotation = -xAxisAngle },
            },
            y = yScale,
        }
        if indexAxis == 'y' then
            scales.x, scales.y = scales.y, scales.x
        end
    end

    local config = {
        type       = chartType,
        labels     = xLabels,
        datasets   = jsDatasets,
        showLegend = (legend ~= ''),
        showValues = showValues,
        align      = align,
        indexAxis  = indexAxis,
        scales     = scales,
        chartTitle = chartTitle,
        stacked = stacked,
    }

    local configJson = mw.text.jsonEncode(config)
    local uid = 'mwchart-' .. tostring(math.random(100000, 999999))

	--transforme en <chart> interprété par WikirougeHooks
    return frame:extensionTag{
        name    = 'chart',
        content = configJson,
        args    = {
            id     = uid,
            width  = tostring(width),
            height = tostring(height),
            align  = align,
        }
    }
end

return p