Modulo:Wikidata/sandbox

Versione del 21 feb 2014 alle 00:43 di Rotpunkt (discussione | contributi) (fix getClaims, più filtri insieme)
--[[
* Modulo per accedere a Wikidata in modo più avanzato rispetto a {{#property}}.

* Il modulo è stato importato inizialmente da:
* http://test2.wikipedia.org/w/index.php?title=Module:Wikidata&oldid=52322
* Nota per chi sviluppa: entity.claims[property] è una sequence Lua ma inizia da 0 invece che da 1, quindi:
* la dimensione è #entity.claims[property] + 1 ed è vuota quando next(entity.claims[property]) == nil. 
]]

local i18n = {
    ["errors"] = {
        ["property-param-not-provided"] = "Parametro ''property'' 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.",
        ["unknown-value-module"] = "Devi impostare entrambi i parametri: ''value-module'' e ''value-function''.",
        ["value-module-not-found"] = "Modulo indicato da ''value-module'' non trovato.",
        ["value-function-not-found"] = "Funzione indicata da ''value-function'' non trovata."
    },
    ["somevalue"] = "''valore sconosciuto''",
    ["novalue"] = "''nessun valore''"
}
 
function getEntityFromId( id )
    if id then
        return mw.wikibase.getEntity( id )
    end
    return mw.wikibase.getEntity()
end
 
function getEntityIdFromValue( value )
    local prefix = ''
    if value['entity-type'] == 'item' then
        prefix = 'Q'
    elseif value['entity-type'] == 'property' then
        prefix = 'P'
    else
        return formatError( 'unknown-entity-type' )
    end
    return prefix .. value['numeric-id']
end
 
function formatError( key )
    return '<span class="error">' .. i18n.errors[key] .. '</span>'
end

local function hasQualifierValue( statement, options )
    local ret = false
    for i, qualifier in pairs( statement.qualifiers[options.qualifier] ) do	
        if formatSnak( qualifier, options ) == options.qualifiervalue then
            ret = true
            break
        end
    end
    return ret
end

local function getClaims( options )
    local claims, filteredClaims

    if not options.property then
        return formatError( 'property-param-not-provided' )
    end

    local property = string.lower( options.property )

    --Get entity
    local entity = getEntityFromId( options.entityId )
    if not entity then
        return -- formatError( 'entity-not-found' )
    end

    if entity.claims == nil or
       entity.claims[property] == nil or
       type(entity.claims[property]) ~= 'table' or
       next(entity.claims[property]) == nil then
        return '' --TODO error?
    end

    claims = entity.claims[property]

    -- statements filtrati per rank
    if options.rank then
        filteredClaims = {}
        for i, claim in pairs(claims) do
            if claim.rank == options.rank then
                table.insert( filteredClaims, claim )
            end
        end
        claims = filteredClaims
    end

    -- statements filtrati per qualifier
    if options.qualifier then
        filteredClaims = {}
        for i, claim in pairs(claims) do
            if claim.qualifiers and claim.qualifiers[options.qualifier] then
                if options.qualifiervalue then
                    if hasQualifierValue( claim, options ) then
                        table.insert( filteredClaims, claim )
                    end
                else
                    table.insert( filteredClaims, claim )
                end
            end
        end
        claims = filteredClaims
    end

    -- eventualmente ritorna solo l'n-esimo elemento
    if options.n then
        local n = tonumber( options.n )
        if filteredClaims then
            claims = (n and n <= #claims) and { claims[n] } or {}
        else
            claims = (n and n <= #claims + 1) and { claims[n - 1] } or {}
        end
    end

    return claims
end
 
function formatStatements( claims, options )
    --Format statement and concat them cleanly
    local formattedStatements = {}
    local list_end, formattedStatement
    if options.list or options.orderedlist then
        if options.list then 
            formattedStatements[1] = '<ul><li>'
            list_end = '</li></ul>'
        else
            formattedStatements[1] = '<ol><li>'
            list_end = '</li></ol>'
        end
        options.separator = '</li><li>'
        options.conjunction = options.separator
    end
    
    for i = 1, #claims do
    	formattedStatement = formatStatement( claims[i], options )
    	-- eventuale pattern
    	if options.pattern then
            formattedStatement = formatFromPattern( formattedStatement, options )
    	end
    	table.insert( formattedStatements, formattedStatement )
    end

    if list_end then table.insert( formattedStatements, list_end ) end
    return mw.text.listToText( formattedStatements, options.separator, options.conjunction )
end
 
function formatStatement( statement, options )
    if not statement.type or statement.type ~= 'statement' then
        return formatError( 'unknown-claim-type' )
    end
 
    return formatSnak( statement.mainsnak, options )
end

function formatSnak( snak, options )
    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, options )
    else
        return formatError( 'unknown-snak-type' )
    end
end

function formatDatavalue( datavalue, options )
    local ret

    --Use the customize handler if provided
    if options['value-module'] or options['value-function'] then
        if not options['value-module'] or not options['value-function'] then
            return formatError( 'unknown-value-module' )
        end
        local formatter = require ('Module:' .. options['value-module'])
        if formatter == nil then
            return formatError( 'value-module-not-found' )
        end
        local fun = formatter[options['value-function']]
        if fun == nil then
            return formatError( 'value-function-not-found' )
        end
        return fun( datavalue.value, options )
    end
 
    --Default formatters
    if datavalue.type == 'wikibase-entityid' then
        ret = formatEntityId( getEntityIdFromValue( datavalue.value ), options )
    elseif datavalue.type == 'string' then
        ret = datavalue.value
    elseif datavalue.type == 'time' then
        ret = formatTime( datavalue.value, options )
    elseif datavalue.type == 'globecoordinate' then
        ret = formatGlobecoordinate( datavalue.value, options )
    elseif datavalue.type == 'quantity' then
    	ret = datavalue.value.amount
    else
        ret = formatError( 'unknown-datavalue-type' )
    end

    return ret
end
 
function formatEntityId( entityId, options )
    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

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

    return ret
end

function formatGlobecoordinate( value, options )
    return value.globe == 'http://www.wikidata.org/entity/Q2' and
           (value.latitude .. ', ' .. value.longitude) or ''
end

function formatFromPattern( str, options )
    -- la parentesi () extra serve per non ritornare anche il gsub.count 
    return ( mw.ustring.gsub( options.pattern, '$1', str ) )
end

-- Ritorna gli argomenti passati al modulo, scartando quelli valorizzati a stringhe vuote
local function getArgs(frame)
    local args = {}
 
    for k, v in pairs(frame.args) do
        if v ~= '' then
            args[k] = v
        end
    end
 
    return args
end

local p = {}

function p.formatStatements( frame )
    local args = getArgs(frame)
 
    --If parameter value is already set, use it
    return args.value and
           (args.pattern and formatFromPattern( args.value, args ) or args.value) or
           formatStatements( getClaims(args), args )
end
 
function p.N(frame) 
    local entity, property, count

    property = string.lower(frame.args[1])
    entity = mw.wikibase.getEntity()
    if entity and entity.claims and entity.claims[property] and
       type(entity.claims[property]) == 'table' and
       next(entity.claims[property]) then
       	-- "+ 1" perché entity.claims[property] è una sequence ma inizia da 0
        count = #entity.claims[property] + 1
    end

    return count or 0
end
 
return p