Modulo:Demografia

Versione del 26 ago 2015 alle 16:59 di Moroboshi (discussione | contributi) (fix recupero dati da wikidata)
local mWikidata = require('Modulo:Wikidata')

local p = {}

local function dump(t, ...)
    local args = {...}
    for _, s in ipairs(args) do
        table.insert(t, s)
    end
end

-- =============================================================
-- Funzioni copiate da Modulo:Wikidata
-- =============================================================

-- Messaggi di errore
local i18n = {
    ["errors"] = {
        ["entityid-param-not-provided"] = "Parametro ''entityid'' non fornito",
        ["property-param-not-provided"] = "Parametro ''property'' non fornito",
        ["qualifier-param-not-provided"] = "Parametro ''qualifier'' non fornito",
        ["value-param-not-provided"] = "Parametro ''valore'' da ricercare non fornito",
        ["entity-not-found"] = "Entità non trovata",
        ["unknown-claim-type"] = "Tipo asserzione sconosciuta",
        ["unknown-snak-type"] = "Tipo di snak sconosciuto",
        ["unknown-datavalue-type"] = "Tipo di dato sconosciuto",
        ["unknown-entity-type"] = "Tipo di entità sconosciuta"
    },
    ["somevalue"] = "''valore sconosciuto''",
    ["novalue"] = "''nessun valore''"
}

-------------------------------------------------------------------------------
--                             Formatters
-------------------------------------------------------------------------------

local function errhandler(msg)
    local cat = mw.title.getCurrentTitle().namespace == 0 and errorCategory or ''
    return string.format('<span class="error">%s</span>%s', msg, cat)
end

local function formatExtLink(url)
    local protocols = { ftp = true, http = true, https = true }

    local success, uri = pcall(function() return mw.uri.new(url) end)
    if success and uri.protocol and protocols[uri.protocol] then
        local dest = tostring(uri)
        return string.format('[%s %s]', dest, dest:gsub(uri.protocol .. '://', ''))
    else
        return url
    end
end

local function formatEntityId(entityId)
    local label = mw.wikibase.label(entityId)
    local link = mw.wikibase.sitelink(entityId)
    if link then
        if label then
            return '[[' .. link .. '|' .. label .. ']]'
        else
            return '[[' .. link .. ']]'
        end
    else
        return label or ''
    end
end

local function formatTime(value, args)
    local year, month, day
    local ret = ''
 
    year, month, day = value.time:match('(%d+)%-(%d%d)%-(%d%d).+')
    if value.precision == 9 then
        ret = tonumber(year)
    elseif value.precision == 10 then
        ret = mw.getLanguage('it'):formatDate('F Y', tonumber(year) .. '-' .. month)
    elseif value.precision == 11 then
        ret = mw.getLanguage('it'):formatDate('j F Y', tonumber(year) .. '-' .. month .. '-' .. day)
        ret = ret:gsub('^1%s', '1º ')
    end
    if value.precision >= 9 and value.precision <= 11 then
        ret = ret .. (value.time:sub(1, 1) == '-' and ' a.C.' or '')
    end

    return ret
end

local function formatGlobecoordinate(value, args)
    local ret
    if args.formatting == 'latitude' then
        ret = value.latitude
    elseif args.formatting == 'longitude' then
        ret = value.longitude
    else
        ret = value.latitude .. ', ' .. value.longitude
    end
    return ret
end

local function formatFromPattern(str, args)
    local pattern = args.pattern
    pattern = mw.ustring.gsub(pattern, '\\{', '{')
    pattern = mw.ustring.gsub(pattern, '\\}', '}')
    return mw.getCurrentFrame():preprocess(mw.message.newRawMessage(pattern, str):plain())
end

local function getEntityIdFromValue(value)
    local prefix = ''
    if value['entity-type'] == 'item' then
        prefix = 'Q'
    elseif value['entity-type'] == 'property' then
        prefix = 'P'
    else
        error(i18n.errors['unknown-entity-type'])
    end
    return prefix .. value['numeric-id']
end

local function formatDatavalue(datavalue, args)
    local ret

    --Default formatters
    if datavalue.type == 'wikibase-entityid' then
        if args.formatting == 'raw' then
            ret = getEntityIdFromValue(datavalue.value)
        else
            ret = formatEntityId(getEntityIdFromValue(datavalue.value))
        end
    elseif datavalue.type == 'string' then
        ret = datavalue.value
        if args.formatting == 'extlink' then
            ret = formatExtLink(ret)
        end
    elseif datavalue.type == 'monolingualtext' then
        ret = datavalue.value.text
    elseif datavalue.type == 'time' then
        if args.formatting == 'raw' then
            ret = datavalue.value.time
        else
            ret = formatTime(datavalue.value, args)
        end
    elseif datavalue.type == 'globecoordinate' then
        ret = formatGlobecoordinate(datavalue.value, args)
    elseif datavalue.type == 'quantity' then
        ret = tonumber(datavalue.value.amount)
    else
        error(i18n.errors['unknown-datavalue-type'])
    end

    return ret
end

local function formatSnak(snak, args)
    if snak.snaktype == 'somevalue' then
        return i18n['somevalue']
    elseif snak.snaktype == 'novalue' then
        return i18n['novalue']
    elseif snak.snaktype == 'value' then
        return formatDatavalue(snak.datavalue, args)
    else
        error(i18n.errors['unknown-snak-type'])
    end
end

-- =============================================================
-- Fine Funzioni copiate da Modulo:Wikidata
-- =============================================================
local function max_array(array)
    local max = 0
    for _,p in ipairs(array) do
        if p > max then max = p end
    end
    return max
end

-- Converte una stringa di valori numerici separati da "," in una lista
-- Eventuali valori non numerici sono ignorati
local function read_array(args)
    local years = {}
    local populations =  {}
    if args.anni then
        local list = mw.text.split(string.gsub(args.anni, "%s", ""), ",")
        for _,s in ipairs(list) do
            years[#years+1] = tonumber(s) or 0
        end
        if args.popolazione then
            local list = mw.text.split(string.gsub(args.popolazione, "%s", ""), ",")
            for _,s in ipairs(list) do
                populations[#populations+1] = tonumber(s) or 0
            end
        end
    else -- vecchio metodo di inserimento
        for i=1,20 do
            local index = tonumber(i)
            local year = tonumber(args['a' .. index])
            if year then
                years[#years+1] = year
                populations[#populations+1] = tonumber(args['p' .. index]) or 0
            end
        end
    end
    return years, populations
end

local function get_wikidata(from)
    local P585 = 'P585'
    local P1082 = 'P1082'
    local entity = mw.wikibase.getEntityObject(from)
    local claims
    if entity.claims and entity.claims.P1082 and #entity.claims.P1082 > 0 then
        claims = entity.claims.P1082
    else
        return nil
    end
    --if true then return mw.text.jsonEncode(claims[1],mw.text.JSON_PRETTY) end
    local years = {}
    local populations = {}
    for _,claim in ipairs(claims) do
        if claim.qualifiers and claim.qualifiers.P585 and (claim.rank == 'preferred' or  claim.rank == 'normal') then
            --if true then return claim end
            years[#years+1] = formatSnak(claim.qualifiers.P585[1] , {})
            populations[#populations+1] = formatSnak(claim.mainsnak, {})
        end
    end
    for i = 1, math.floor(#populations/2) do
        local j=#populations-i+1
        years[i], years[j] = years[j], years[i]
        populations[i], populations[j] = populations[j], populations[i]
    end
    return years, populations
end


local function default_table(popmax, n)
    local default = {}
    local log_p = math.ceil(math.log10(popmax))
    if log_p == 0 then log_p = 1 end
    local first_digit = math.floor(popmax / 10 ^ (log_p - 1))
    if first_digit == 9 then
        default.popmax = 10 ^ (log_p)
    else
        default.popmax = (first_digit +1 ) * 10 ^ (log_p-1)
    end
    if log_p < 2 then log_p = 2 end
    if first_digit <=5 then
        default.passo1 = (10 ^ (log_p-1)) / 2
        default.passo2 = 10 ^ (log_p-2)
    else
        default.passo1 = (10 ^ (log_p-1))
        default.passo2 = 2 * 10 ^ (log_p-2)     
    end
    default.dimx = 488
    if n > 15 then default.dimx = 488 + (n-15) * 20 end
    return default
end

function p._demografia(args)
    local years,populations = read_array(args)
    local from_wikidata = false
    if #years == 0 then
        years,populations = get_wikidata(args.from)
        from_wikidata = true
    end
    --if true then return mw.text.jsonEncode(years) end
    if #years == 0 then return '' end
    local popmax = max_array(populations)
    local is_thousand = false
    if from_wikidata and popmax >= 900000 then
        popmax = popmax / 1000
        for index,population in ipairs(populations) do
            populations[index] = population / 1000
            is_thousand = true
        end
    end
    -- calcolo default per la popolazione
    local default = default_table(popmax, #populations)
    -- generazione del grafico
    local graph = {}
    dump(graph, 'Colors=\n id:lightgrey value:gray(0.9)\n id:darkgrey  value:gray(0.7)\n id:sfondo value:rgb(1,1,1)\n id:barra value:rgb(0.6,0.7,0.8)\n\n')
    dump(graph, 'ImageSize = width:', args.dimx or tostring(default.dimx), ' height:', args.dmy or '373', '\n')
    dump(graph, 'PlotArea   = left:50 bottom:50 top:30 right:30\nDateFormat = x.y\n')
    dump(graph, 'Period = from:0 till:', args.popmax or tostring(default.popmax), '\n')
    dump(graph, 'TimeAxis   = orientation:vertical\nAlignBars  = justify\n')
    dump(graph, 'ScaleMajor = gridcolor:darkgrey increment:', args.passo1 or tostring(default.passo1), ' start:0\n' )
    dump(graph, 'ScaleMinor = gridcolor:lightgrey increment:', args.passo2 or tostring(default.passo2), ' start:0\n')
    dump(graph, 'BackgroundColors = canvas:sfondo\n')
    dump(graph, 'BarData=\n')
    for _,year in ipairs(years) do
        dump(graph, string.format(' bar: %d text:%d\n', year, year))
    end
    dump(graph, '\n')
    dump(graph, 'PlotData=\n color:barra width:20 align:left\n\n')
    for i =1,#years do
        dump(graph, string.format(' bar:%d from:0 till:%d\n', years[i], populations[i]))
    end
    dump(graph, '\n')
    local lang = mw.language.new('it')
    dump(graph, 'PlotData=\n')
    for i=1,#years do
        local p_formatted = lang:formatNum(math.floor(tonumber(populations[i]) + 0,5) )
        dump(graph, string.format(' bar: %d at: %d fontsize:S text: %s shift:(-10,5)|n.d. shift:(-8,5)\n',
                                    years[i], populations[i], p_formatted))
    end
    dump(graph, '\n')
    if args.fonte then
        dump(graph, 'TextData=\n fontsize:S pos:(20,20)\n text:fonte ', args.fonte, '\n')
    end
    local draw_graph
    if args.raw then
        draw_graph = '<nowiki>'.. table.concat(graph) .. '</nowiki>'
    else
        draw_graph = mw.getCurrentFrame():extensionTag('timeline', table.concat(graph))
    end
    local titolo = "''Abitanti censiti''\n"
    if is_thousand then
        titolo =  "''Abitanti censiti (migliaia)''\n"
    end
    if args.titolo then
        titolo = "''" .. args.titolo .. "''\n"
    end
    return titolo .. draw_graph
end

function p.demografia(frame)
    local args = require('Modulo:Arguments').getArgs(frame)
    return p._demografia(args)
end

return p