🇮🇷 Iran Proxy | https://www.wikipedia.org/wiki/Module:Settlement_Wikidata/sandbox
Jump to content

Module:Settlement Wikidata/sandbox

From Wikipedia, the free encyclopedia
require('strict')

local p = {}

-- List of preferred census dates, per country
local asOf = {
    austria = '2018-01-01',
    france = '2022-01-01',
    romania = '2021-12-01',
    spain = '2024',
    switzerland = '2018-12-31',
}

-- Is the string a valid QID?
local function validQid(qid)
    return qid and mw.ustring.find(qid,"^[Qq]%d+$")
end

-- lookup nestedIndex in outer table
-- Example:  fetchNested(foo,{'bar','baz','quux'}) will return
--     foo.bar and foo.bar.baz and foo.bar.baz.quux
local function fetchNested(outer,nestedIndex)
    if not outer or type(outer) ~= 'table' or not nestedIndex or type(nestedIndex) ~= 'table' then
        return nil
    end
    local current = outer
    for _, indx in ipairs(nestedIndex) do
        current = current[indx]
        if not current then
            return current
        end
    end
    return current
end

-- Mapping from wikidata properties (of a reference or "stated in" entity) to citation parameters
local pid2param = {
    P50 = "author",
    P123 = "publisher",
    P407 = "language",
    P577 = "date",
    P813 = "access-date",
    P854 = "url",
    P856 = "url",
    P953 = "url",
    P1476 = "title",
    P2699 = "url",
}

-- scan through snaks, loading citation args with correct values
local function scanSnaks(args, snaks)
    for pid, data in pairs(snaks) do
        data = fetchNested(data,{1,'mainsnak'}) or data[1] -- handle the case where the snak is buried in the claims
        local param = pid2param[pid]
        if pid == "P248" then  --- "stated in": must look up different entity for data
            local statedId = fetchNested(data,{'datavalue','value','id'})
            local statedEntity = statedId and mw.wikibase.getEntity(statedId)
            local statedClaims = statedEntity and statedEntity.claims
            if statedClaims then
                scanSnaks(args, statedClaims)
            end
            local statedLabel = fetchNested(statedEntity,{'labels','en','value'})
            args.title = args.title or statedLabel
        elseif param then
            local renderedData = data and mw.wikibase.renderSnak(data)
            if param == "title" then
                -- set language from title if not otherwise specified
                args.language = args.language or fetchNested(data,{'datavalue','value','language'})
            end
            if pid == "P856" then --- use official URL only if URL is missing
                args[param] = args[param] or renderedData
            else
                args[param] = renderedData
            end
        end
    end
end

-- function to assemble wikicode to create citation, given reference property
local function citeSource(snaks)
    local args = {}
    args.mode = "cs1"
    scanSnaks(args, snaks)
    -- citation templates are unhappy without a title, so return a wikilink
    if not args.title then  
        return args.url and "["..args.url.."]"
    end
    if not args.url then
        args["access-date"] = nil  -- don't allow access date without url
    end
    local result = '{{'..(args.url and 'cite web' or 'citation')
    for k,v in pairs(args) do
        result = result..'|'..k..'='..v
    end
    result = result..'}}'
    return result
end

-- Core function that looks up value, reference, and data for property
-- Arguments:
--   qid = entity id in Wikidata
--   pid = property id on that entity
--   unit = if non-nil, desired unit for value (expressed as a qid for that unit)
--   asOf = date or partial date string to prefer. If not given, find the latest version
-- Returns table only if Wikidata property is source externally
--   amount = value that is looked up (or nil)
--   source = wiki markup for reference
--   asOf = date that value is valid (if given)
function p._getValueAndRef(qid,pid,unit,asOf)
    local results = nil
    local latest = nil
    local statements = mw.wikibase.getAllStatements(qid, pid ) 
    for _, statement in pairs(statements) do
        local amount
        local wrongDate = true
        local value = fetchNested(statement,{'mainsnak','datavalue','value'})
        if value then
            amount = value.amount
        end
        -- check if unit is correct, if given
        if unit then
            local storedUnit = value.unit
            if not storedUnit or not mw.ustring.find(value.unit or '',unit,1,true) then
                amount = nil
            end
        end
        -- check if asOf matches stored date
        -- if asOf not specified, remember stored date
        local storedAsOf = fetchNested(statement,{'qualifiers','P585',1,'datavalue','value','time'})
        local date
        if storedAsOf then
            date = mw.ustring.match(storedAsOf,'^%+(%d+%-%d+%-%d+)')
            local justYear = mw.ustring.match(date,'^(%d+)%-0+%-0+$')
            date = justYear or date
            if asOf then
                wrongDate = date and not mw.ustring.find(date,asOf,1,true)
            end
        end
        -- check to see if this statement is adequately sourced (filter out "imported from" refs)
        local source
        local refs = statement['references']
        if refs then
            for _, ref in pairs(refs) do
                local renderedRef = ref.snaks and mw.wikibase.renderSnaks(ref.snaks)
                if renderedRef and not mw.ustring.find(renderedRef,'Wiki') then
                    source = citeSource(ref.snaks)
                end
            end
        end
        if amount and source then -- yes, it is adequately sourced,
            local theseResults = {}
            theseResults.amount = tonumber(amount)
            theseResults.source = source
            theseResults.asOf = date
            -- if we haven't found the preferred date
            if wrongDate then
                if date and (not latest or date > latest) then -- store latest if dated
                    results = theseResults
                    latest = date
                elseif not date then -- store first if undated
                    results = results or theseResults
                end
            -- here, we found the correct date, so return it
            else
                results = theseResults
                break
            end
        end
    end
    return results
end

-- for a qid, find the entity's population
-- if asOf is specified, prefer that population value
function p._population(qid,asOf)
    return p._getValueAndRef(qid,"P1082",nil,asOf)
end

-- for a qid, find the area of the entity in square kilometers
function p._area(qid)
    return p._getValueAndRef(qid,"P2046","Q712226",nil) -- look for square kilometers only
end

-- main entry point
-- Arguments:
--   args[1] = name of infobox parameter to lookup in Wikidata (e.g., "area_total_km2" or "population_footnotes")
--   args[2] = date format for returned date
--   country = name of country which contains entity
--   qid = qid for entity (for testing, by default, use qid of current page)
-- Returns (depending on args[1]):
--   the value of the population or area, or
--   a reference for the value, or
--   the date the value is valid
function p._main(args)
    local country = args.country
    local param = args[1]
    local qid = validQid(args.qid) and args.qid or mw.wikibase.getEntityIdForCurrentPage()
    if not param or not qid then
        return nil
    end
    qid = qid:upper()
    country = country and string.lower(country)
    param = string.lower(param)
    --- France infobox is narrow, so display only year (unless otherwise specified)
    args[2] = args[2] or country == "france" and 'y'
    local results
    -- currently supports area and population: check for substring in parameter
    local wantArea = mw.ustring.find(param,"area",1,true)
    local wantPop = mw.ustring.find(param,"population",1,true)
    if wantArea then
        results = p._area(qid)
    elseif wantPop then
        results = p._population(qid,asOf[country])
    end
    if not results then
        return nil
    elseif mw.ustring.find(param,"as_of",1,true) then
        -- return date of validity
        if not results.asOf then
            return nil
        end
        --- if asOf has only year, return it
        if mw.ustring.find(results.asOf,'^%d+$') then
            return results.asOf
        end
        local lang = mw.getContentLanguage()
        if args[2] == "ymd" then
            return lang:formatDate("Y-m-d",results.asOf)
        elseif args[2] == "dmy" then
            return lang:formatDate("j F Y",results.asOf)
        elseif args[2] == "mdy" then
            return lang:formatDate("F j, Y",results.asOf)
        elseif args[2] == "y" then
            return lang:formatDate("Y",results.asOf)
        else
            return results.asOf
        end
    elseif mw.ustring.find(param,"footnotes",1,true) then
        if not results.source then
            return nil
        end
        -- run the pre-processor exactly once if returning a reference, because otherwise you get multiple (unused) reference
        local frame = mw.getCurrentFrame()
        return frame:preprocess("<ref>"..results.source.."</ref>")
    elseif wantArea and mw.ustring.find(param,"km2",1,true) then
        -- if parameter is for area in km2, return value
        return results.amount
    elseif wantPop then
        return results.amount
    end
    -- either parameter unrecognized or something bad happened
    return nil
end

-- template-facing entry point: do the usual argument processing
function p.main(frame)
    local getArgs = require('Module:Arguments').getArgs
    local args = getArgs(frame)
    return p._main(args) or ""
end

return p