Moduł:Demografia

Z Wikipedii, wolnej encyklopedii
 Dokumentacja modułu [zobacz] [edytuj] [historia] [odśwież]

Moduł do generowania tabelki z danymi demograficznymi.

Raport[edytuj kod]

Funkcja do generowania wierszy tabelki z danymi demograficznymi.

Uwaga funkcja nie generuje początku ani końca tabelki. W szczególności nagłówek tabelki trzeba podawać przed każdym wywołaniem funkcji.

parametry[edytuj kod]

Pole Do czego służy? Jak wypełnić?
liczba-kolumn Liczba kolumn podawanych danych demograficznych z przedziału od 2 do 99. Wartość domyślna to 2. np. 4
liczba-grup Liczba nadrzędnych grup z przedziału 1 do 99. Wartość domyślna to 1.

Uwaga! liczba kolumn musi być podzielna przez liczbę grup.

np. 2
populacja Nazwa dla wiersza podsumowującego wszystkie dane. Wartość domyślna to Populacja. np. nie podawać
miejsce-populacji Pozycja wiersza podsumowującego wszystkie dane w tabelce.
przed
Jest to pierwszy wiersz.
za
Jest to ostatni wiersz.

Wartość domyślna to przed.

np. nie podawać
miejsce-ogółem Pozycja kolumn podsumowujących wiersz.
przed
Jest to druga kolumna (tuż po opisie)
za
Jest to ostatnia kolumna

Wartość domyślna to przed.

np. nie podawać
styl-ogółem-wartość Styl dla komórki z sumą danych. Jeśli nie podano to zwykła komórka tabeli. Jeśli podano ! to komórka nagłówka tabeli. Jeśli podano inną wartość to styl CSS zaaplikowany do zwykłej komórki tabeli.
styl-ogółem-procent Styl dla komórki z procentową sumą danych. Jeśli nie podano to zwykła komórka tabeli. Jeśli podano ! to komórka nagłówka tabeli. Jeśli podano inną wartość to styl CSS zaaplikowany do zwykłej komórki tabeli.
styl-dana-wartość Styl dla komórki ze źródłową wartością absolutną. Jeśli nie podano to zwykła komórka tabeli. Jeśli podano ! to komórka nagłówka tabeli. Jeśli podano inną wartość to styl CSS zaaplikowany do zwykłej komórki tabeli.
styl-data-procent Styl dla komórki ze źródłową wartością procentową. Jeśli nie podano to zwykła komórka tabeli. Jeśli podano ! to komórka nagłówka tabeli. Jeśli podano inną wartość to styl CSS zaaplikowany do zwykłej komórki tabeli.
styl-opisu Styl komórki opisu wiersza. Jeśli nie podano to zwykła komórka tabeli. Jeśli podano ! to komórka nagłówka tabeli. Jeśli podano inną wartość to styl CSS zaaplikowany do zwykłej komórki tabeli.
ogółem-szczegóły Stopień szczegółów sum cząstkowych
kolumny
dodaje do grupy ogółem sumy odpowiednich kolumn z grup
grupy
dodaje sumę w każdej grupie
kolumny i grupy
dodaje do grupy ogółem sumy odpowiednich kolumn z grup oraz sumę w każdej grupie

Jeśli parametru nie podano to szczegóły nie są dodawane do wynikowej tabeli.

typ-procentu Sposób wyznaczania wartości procentowych:
globalny
Wartości są obliczane globalnie dla całej tabelki względem sumy wszystkich składników z wszystkich wierszy.
lokalny
Wartości są obliczanie w obrębie każdego wiersza oddzielnie.

Jeśli parametru nie podano to wartości procentowe obliczane są metodą globalną.

1..N Dane dla raportu. Należy podawać kolejno dane dla wszystkich wierszy. Pierwszy parametr wiersza to jego opis. Za nim podaje się tyle liczb ile kolumn zostało zadeklarowanych w parametrze liczba-kolumn. Kolejne wiersze podaje się analogicznie. Parametry można podać bezpośrednio w wywołaniu funkcji. Jeśli lista parametrów indeksowanych jest pusta, to funkcja przegląda parametry indeksowane wywołanego szablonu.

przykłady[edytuj kod]

Wywołanie:

{| border="1" cellpadding="4" cellspacing="0" style="margin: 0 0 1em 1em; background: #f9f9f9; border: 1px #aaaaaa solid; border-collapse: collapse; text-align:center;"
! rowspan=3 | opis
! rowspan=2 colspan=2 | ogółem
! colspan=4 | grupa A
! colspan=4 | grupa B
|-
! colspan=2 | K
! colspan=2 | M
! colspan=2 | K
! colspan=2 | M
|-
! osób
! %
! osób
! %
! osób
! %
! osób
! %
! osób
! %
{{#invoke:Demografia|Raport|liczba-kolumn=4|liczba-grup=2|styl-ogółem-wartość=font-weight:bold;text-align:right;|styl-ogółem-procent=font-weight:bold;|styl-dana-wartość=text-align:right;|styl-dana-procent=font-weight:bold;|test|10|20|30|40|dane|100|200|300|400|x|a|b|c|d|duże|10000|20000|30000|40000}}
|}

Efekt:

opis ogółem grupa A grupa B
K M K M
osób % osób % osób % osób % osób %
Populacja 101 100 100 10 110 10,00 20 220 20,00 30 330 30,00 40 440 40,00
test 100 0,10 10 0,01 20 0,02 30 0,03 40 0,04
dane 1000 0,99 100 0,10 200 0,20 300 0,30 400 0,40
x 0 0 a b c d
duże 100 000 98,91 10 000 9,89 20 000 19,78 30 000 29,67 40 000 39,56

Wywołanie 2:

{| border="1" cellpadding="4" cellspacing="0" style="margin: 0 0 1em 1em; background: #f9f9f9; border: 1px #aaaaaa solid; border-collapse: collapse; text-align:center;"
! rowspan=3 | opis
! colspan=6 | ogółem
! colspan=6 | grupa A
! colspan=6 | grupa B
|-
! colspan=2 | Razem
! colspan=2 | K
! colspan=2 | M
! colspan=2 | Razem
! colspan=2 | K
! colspan=2 | M
! colspan=2 | Razem
! colspan=2 | K
! colspan=2 | M
|-
! osób
! %
! osób
! %
! osób
! %
! osób
! %
! osób
! %
! osób
! %
! osób
! %
! osób
! %
! osób
! %
{{#invoke:Demografia|Raport|liczba-kolumn=4|liczba-grup=2|ogółem-szczegóły=kolumny i grupy|styl-ogółem-wartość=font-weight:bold;text-align:right;|styl-ogółem-procent=font-weight:bold;|styl-dana-wartość=text-align:right;|styl-dana-procent=font-weight:bold;|test|10|20|30|40|dane|100|200|300|400|x|a|b|c|d|duże|10000|20000|30000|40000}}
|}

Efekt

opis ogółem grupa A grupa B
Razem K M Razem K M Razem K M
osób % osób % osób % osób % osób % osób % osób % osób % osób %
Populacja 101 100 100 40 440 40,00 60 660 60,00 30 330 30,00 10 110 10,00 20 220 20,00 70 770 70,00 30 330 30,00 40 440 40,00
test 100 0,10 40 0,04 60 0,06 30 0,03 10 0,01 20 0,02 70 0,07 30 0,03 40 0,04
dane 1000 0,99 400 0,40 600 0,59 300 0,30 100 0,10 200 0,20 700 0,69 300 0,30 400 0,40
x 0 0 0 0 0 0 0 0 a b 0 0 c d
duże 100 000 98,91 40 000 39,56 60 000 59,35 30 000 29,67 10 000 9,89 20 000 19,78 70 000 69,24 30 000 29,67 40 000 39,56
local m = {}

local demographicsData = {
    apiName                  = "Raport",
    argNumberOfDataColumns   = "liczba-kolumn",
    argNumberOfGroups        = "liczba-grup",
    argSummaryRowTitle       = "populacja",
    argSummaryRowLocation    = "miejsce-populacji",
    argSummaryColumnLocation = "miejsce-ogółem",
    argSummaryValueStyle     = "styl-ogółem-wartość",
    argSummaryPercentStyle   = "styl-ogółem-procent",
    argDataValueStyle        = "styl-dana-wartość",
    argDataPercentStyle      = "styl-dana-procent",
    argTitleStyle            = "styl-opisu",
    argSummaryDetails        = "ogółem-szczegóły",
    argPercentMode           = "typ-procentu",
    
    defaultSummaryTitle      = "Populacja",
    locationBefore           = "przed",
    locationAfter            = "za",
    summaryDetailsNone       = "brak",
    summaryDetailsColumns    = "kolumny",
    summaryDetailsGroups     = "grupy",
    summaryDetailsBoth       = "kolumny i grupy",
    percentGlobal            = "globalny",
    percentLocal             = "lokalny",
}

local function loadDemographicControlData(frame)
    local result = {}
    local numberOfDataColumns = frame.args[demographicsData.argNumberOfDataColumns] or "2"
    local numberOfGroups = frame.args[demographicsData.argNumberOfGroups] or "1"
    if numberOfDataColumns:match"^%d%d?$" then
        result.numberOfDataColumns = tonumber(numberOfDataColumns)
    else
        return nil -- invalid argument
    end
    
    if numberOfGroups:match"^%d%d?$" then
        result.numberOfGroups = tonumber(numberOfGroups)
    else
        return nil -- invalid argument
    end
    
    if (result.numberOfDataColumns < 2) or (result.numberOfGroups < 1) then
        return nil -- invalid arguments
    end
    
    local numberOfColumnsInGroup = result.numberOfDataColumns / result.numberOfGroups
    if (numberOfColumnsInGroup < 2) or (math.floor(numberOfColumnsInGroup) ~= numberOfColumnsInGroup) then
        return nil -- invalid arguments
    end
    
    result.numberOfColumnsInGroup = numberOfColumnsInGroup
    
    result.summaryRowTitle = frame.args[demographicsData.argSummaryRowTitle] or demographicsData.defaultSummaryTitle

    local location = frame.args[demographicsData.argSummaryRowLocation]
    if location then
        if location == demographicsData.locationBefore then
            result.summaryRowLocation = false
        elseif location == demographicsData.locationAfter then
            result.summaryRowLocation = true
        else
            return nil
        end
    else
        result.summaryRowLocation = false
    end
    
    location = frame.args[demographicsData.argSummaryColumnLocation]
    if location then
        if location == demographicsData.locationBefore then
            result.summaryColumnLocation = false
        elseif location == demographicsData.locationAfter then
            result.summaryColumnLocation = true
        else
            return nil
        end
    else
        result.summaryColumnLocation = false
    end
    
    local style = frame.args[demographicsData.argTitleStyle]
    if style and #style > 0 then
        if style:match'"' then
            return nil
        else
            result.titleStyle = style
        end
    end
    
    style = frame.args[demographicsData.argSummaryValueStyle]
    if style and #style > 0 then
        if style:match'"' then
            return nil
        else
            result.summaryValueStyle = style
        end
    end
    
    style = frame.args[demographicsData.argSummaryPercentStyle]
    if style and #style > 0 then
        if style:match'"' then
            return nil
        else
            result.summaryPercentStyle = style
        end
    end
    
    local style = frame.args[demographicsData.argDataValueStyle]
    if style and #style > 0 then
        if style:match'"' then
            return nil
        else
            result.dataValueStyle = style
        end
    end
    
    local style = frame.args[demographicsData.argDataPercentStyle]
    if style and #style > 0 then
        if style:match'"' then
            return nil
        else
            result.dataPercentStyle = style
        end
    end

    local summaryDetails = frame.args[demographicsData.argSummaryDetails]
    if summaryDetails then
        if summaryDetails == demographicsData.summaryDetailsColumns then
            result.showColumnsSummary = true
        elseif summaryDetails == demographicsData.summaryDetailsGroups then
            result.showGroupsSummary = true
        elseif  summaryDetails == demographicsData.summaryDetailsBoth then
            result.showColumnsSummary = true
            result.showGroupsSummary = true
        else
            return nil
        end
    end

    local percentMode = frame.args[demographicsData.argPercentMode]
    if percentMode then
        if percentMode == demographicsData.percentGlobal then
            result.localPercent = false
        elseif percentMode == demographicsData.percentLocal then
            result.localPercent = true
        else
            return nil
        end
    else
        result.localPercent = false
    end
    
    return result
end

local function trim(s)
  return s:match'^()%s*$' and '' or s:match'^%s*(.*%S)'
end

local function loadIndexedArguments(frame)
    local args = {}
    for i, v in ipairs(frame.args) do
        -- copy direct arguments available by index
        -- they overrides template arguments
        table.insert(args, trim(v))
    end
    if #args == 0 then
        -- if no indexed arguments are provided
        -- use arguments from called template
        local parentFrame = frame:getParent()
        if parentFrame then
            for i, v in ipairs(parentFrame.args) do
                -- copy template arguments available by index
                table.insert(args, trim(v))
            end
        end
    end

    return args
end

local function createReportRow(control)
    local function createGroup(columns)
        local result = {
            total = 0,
        }
        for i = 1, columns do
            result[i] = 0
        end
        return result
    end
    
    local result = {
        total = createGroup(control.numberOfColumnsInGroup)
    }
    for i = 1, control.numberOfGroups do
        result[i] = createGroup(control.numberOfColumnsInGroup)
    end
    
    return result
end

local function prepareReportData(control, data, rows)
    local totalRow = createReportRow(control)
    totalRow.title = control.summaryRowTitle
    local result = {
        total = totalRow,
    }
    for row = 1, rows do
        local baseIndex = (row - 1) * (control.numberOfDataColumns + 1) + 1
        local currentRow = createReportRow(control)
        table.insert(result, currentRow)
        currentRow.title = data[baseIndex]
        for column = 1, control.numberOfDataColumns do
            local groupNo = math.floor((column - 1) / control.numberOfColumnsInGroup) + 1
            local colInGroup = ((column - 1) % control.numberOfColumnsInGroup) + 1
            local valueText = data[baseIndex+column]
            if valueText:match"^%d%d?%d?%d?%d?%d?%d?%d?%d?%d?%d?$" then
                local value = tonumber(data[baseIndex+column])
                currentRow[groupNo][colInGroup] = value
                -- calculate aggregated values
                currentRow[groupNo].total       = currentRow[groupNo].total + value
                currentRow.total[colInGroup]    = currentRow.total[colInGroup] + value
                currentRow.total.total          = currentRow.total.total + value
                totalRow[groupNo][colInGroup]   = totalRow[groupNo][colInGroup] + value
                totalRow[groupNo].total         = totalRow[groupNo].total + value
                totalRow.total[colInGroup]      = totalRow.total[colInGroup] + value
                totalRow.total.total            = totalRow.total.total + value
            else
                currentRow[groupNo][colInGroup] = valueText
            end
        end
    end

    return result
end

local function printReportData(control, data, numberOfRows)
    local total = data.total.total.total
    local lang = mw.getContentLanguage()
    local percent = function(value)
        if (total <= 0) or (value < 0) then
            return "&nbsp;"
        elseif value == 0 then
            return "0"
        elseif value == total then
            return "100"
        else
            local textValue = string.format("%02.2f", 100 * value/total)
            local result, _ = string.gsub(textValue,"%.",",",1)
            return result
        end
    end
    local function cellFormat(style, value)
        if style == "!" then
            return "! " .. value
        elseif style then
            return "|style=\"" .. style .. "\"| " .. value
        else
            return "| " .. value
        end
    end
    local function printRow(result, control, rawData)
        -- new table row with title in first column
        table.insert(result, "|-")
        table.insert(result, cellFormat(control.titleStyle, rawData.title))
        -- leading summary columns
        if not control.summaryColumnLocation then
            table.insert(result, cellFormat(control.summaryValueStyle, lang:formatNum(rawData.total.total)))
            table.insert(result, cellFormat(control.summaryPercentStyle, percent(rawData.total.total)))
            if control.showColumnsSummary and (control.numberOfGroups > 1) then
                for i = 1, control.numberOfColumnsInGroup do
                    table.insert(result, cellFormat(control.summaryValueStyle, lang:formatNum(rawData.total[i])))
                    table.insert(result, cellFormat(control.summaryPercentStyle, percent(rawData.total[i])))
                end
            end
        end
        -- data groups
        for i = 1, control.numberOfGroups do
            local groupData = rawData[i]
            -- leading summary in group
            if control.showGroupsSummary and (control.numberOfGroups > 1) and not control.summaryColumnLocation then
                table.insert(result, cellFormat(control.summaryValueStyle, lang:formatNum(groupData.total)))
                table.insert(result, cellFormat(control.summaryPercentStyle, percent(groupData.total)))
            end
            -- actual data
            for j = 1, control.numberOfColumnsInGroup do
                local value = groupData[j]
                if type(value) == "number" then
                    table.insert(result, cellFormat(control.dataValueStyle, lang:formatNum(value)))
                    table.insert(result, cellFormat(control.dataPercentStyle, percent(value)))
                else
                    table.insert(result, cellFormat(control.dataValueStyle, value))
                    table.insert(result, cellFormat(control.dataPercentStyle, "—"))
                end
            end
            -- remaining summary in group
            if control.showGroupsSummary and (control.numberOfGroups > 1) and control.summaryColumnLocation then
                table.insert(result, cellFormat(control.summaryValueStyle, lang:formatNum(groupData.total)))
                table.insert(result, cellFormat(control.summaryPercentStyle, percent(groupData.total)))
            end
        end
        -- remaining summary columns
        if control.summaryColumnLocation then
            if control.showColumnsSummary and (control.numberOfGroups > 1) then
                for i = 1, control.numberOfColumnsInGroup do
                    table.insert(result, cellFormat(control.summaryValueStyle, lang:formatNum(rawData.total[i])))
                    table.insert(result, cellFormat(control.summaryPercentStyle, percent(rawData.total[i])))
                end
            end
            table.insert(result, cellFormat(control.summaryValueStyle, lang:formatNum(rawData.total.total)))
            table.insert(result, cellFormat(control.summaryPercentStyle, percent(rawData.total.total)))
        end
    end

    local result = {}
    if numberOfRows > 1 and not control.summaryRowLocation then
        if control.localPercent then
            total = data.total.total.total
        end
        printRow(result, control, data.total )
    end
    for i = 1, numberOfRows do
        if control.localPercent then
            total = data[i].total.total
        end
        printRow(result, control, data[i])
    end
    if numberOfRows > 1 and control.summaryRowLocation then
        if control.localPercent then
            total = data.total.total.total
        end
        printRow(result, control, data.total )
    end
    return table.concat(result,"\n")
end

local function DemographicsReport(frame)
    local controlData = loadDemographicControlData(frame)
    if controlData == nil then
        return nil
    end
    
    local actualData = loadIndexedArguments(frame)
    local numberOfRows = #actualData / (controlData.numberOfDataColumns + 1)
    if (numberOfRows < 1) or (math.floor(numberOfRows) ~= numberOfRows) then
        return nil -- invalid number of expected data arguments
    end

    local reportData = prepareReportData(controlData, actualData, numberOfRows)
    
    return printReportData(controlData, reportData, numberOfRows)
end

m[demographicsData.apiName] = DemographicsReport

return m