Jump to content

Module:DynamicCapiunto

Documentation for this module may be created at Module:DynamicCapiunto/doc

local capiunto = require 'capiunto'
local p = {}

function p.main(frame)
    local args = frame:getParent().args
    local qid = args.qid or args[1]

    if not qid then
        return '<span class="error">Error: No Wikidata QID provided</span>'
    end

    -- Register the entity association
    frame:callParserFunction('#unlinkedwikibase', 'id=' .. qid)

    -- Get entity data from UnlinkedWikibase
    local entity = mw.ext.UnlinkedWikibase.getEntity(qid)

    if not entity then
        return '<span class="error">Error: Entity ' .. qid ..
                   ' not found</span>'
    end

    -- Helper function to get property value from entity
    local function getPropertyValue(pid)
        if not entity.claims or not entity.claims[pid] then return nil end

        local claims = entity.claims[pid]
        if not claims or #claims == 0 then return nil end

        local claim = claims[1]
        if not claim.mainsnak or not claim.mainsnak.datavalue then
            return nil
        end

        return claim.mainsnak.datavalue.value
    end

    -- Helper function to format different value types
    local function formatValue(value, valueType)
        if not value then return nil end

        if type(value) == 'table' then
            -- Handle time values
            if value.time then
                -- Extract year-month-day from +2023-01-15T00:00:00Z format
                local dateStr = value.time
                local year, month, day =
                    dateStr:match('^[+-]?(%d+)-(%d+)-(%d+)')
                if year then
                    local y = tonumber(year)
                    local m = tonumber(month) or 0
                    local d = tonumber(day) or 0
                    -- If month or day are zero/unknown, show only the year
                    if m == 0 or d == 0 then
                        return string.format('%04d', y)
                    end
                    return string.format('%04d-%02d-%02d', y, m, d)
                end
                return value.time
                -- Handle entity references (Q-IDs)
            elseif value.id then
                local linkedEntity = mw.ext.UnlinkedWikibase.getEntity(value.id)
                if linkedEntity then
                    local label = (linkedEntity.labels and
                                      linkedEntity.labels.en) and
                                      linkedEntity.labels.en.value or value.id
                    -- Then prefer an en.wikipedia sitelink (external link)
                    if linkedEntity.sitelinks and linkedEntity.sitelinks.enwiki and
                        linkedEntity.sitelinks.enwiki.url then
                        return
                            '[' .. linkedEntity.sitelinks.enwiki.url .. ' ' ..
                                label .. ']'
                    end
                    -- Fallback to the English label if available
                    if linkedEntity.labels and linkedEntity.labels.en then
                        return label
                    end
                end
                return value.id
                -- Handle monolingual text
            elseif value.text then
                return value.text
            end
        end

        return tostring(value)
    end

    -- Helper to get claim entity id (if the claim is an entity)
    local function getClaimEntityId(pid)
        local v = getPropertyValue(pid)
        if type(v) == 'table' and v.id then return v.id end
        return nil
    end

    -- Helper to get the first claim object for a property
    local function getFirstClaimObject(pid)
        if not entity.claims or not entity.claims[pid] then return nil end
        local claims = entity.claims[pid]
        if not claims or #claims == 0 then return nil end
        return claims[1]
    end

    -- Helper to get a qualifier value (prefer English monolingual text for P2096)
    local function getQualifierValue(claim, qualPid)
        if not claim or not claim.qualifiers or not claim.qualifiers[qualPid] then
            return nil
        end
        local qsnaks = claim.qualifiers[qualPid]
        if not qsnaks or #qsnaks == 0 then return nil end
        for i, q in ipairs(qsnaks) do
            -- Check for valid snak with datavalue
            if q and q.snaktype == 'value' and q.datavalue and q.datavalue.value then
                local v = q.datavalue.value
                if type(v) == 'table' then
                    -- For monolingual text, prefer English
                    if v.language and v.language == 'en' and v.text then
                        return v.text
                    end
                else
                    return tostring(v)
                end
            end
        end
        return nil
    end

    -- Decide which infobox to render based on instance (P31)
    local instanceId = getClaimEntityId('P31')
    local infobox

    if instanceId == 'Q5' then
        -- Person infobox
        local personTitle = qid
        if entity.labels and entity.labels.en then
            personTitle = entity.labels.en.value
        end

        infobox = capiunto.create({
            title = personTitle,
            bodyClass = 'infobox vevent',
            bodyStyle = 'width: 300px;'
        })

        -- Add image (P18) with media legend (P2096) as caption
        local imageClaim = getFirstClaimObject('P18')
        if imageClaim then
            local imageVal = imageClaim.mainsnak and
                                 imageClaim.mainsnak.datavalue and
                                 imageClaim.mainsnak.datavalue.value
            if imageVal then
                local imageStr = tostring(imageVal)
                local caption = getQualifierValue(imageClaim, 'P2096')
                if caption and caption ~= '' then
                    infobox:addImage('[[File:' .. imageStr .. '|300px]]',
                                     caption)
                else
                    infobox:addImage('[[File:' .. imageStr .. '|300px]]')
                end
            end
        end

        -- Birthplace (P19)
        local birthplace = getPropertyValue('P19')
        if birthplace then
            infobox:addRow('Birthplace', formatValue(birthplace))
        end

        -- Birthday (P569)
        local birthday = getPropertyValue('P569')
        if birthday then
            infobox:addRow('Birthday', formatValue(birthday))
        end

        -- Death (P570)
        local death = getPropertyValue('P570')
        if death then infobox:addRow('Death', formatValue(death)) end

        -- Occupation (P106)
        local occupation = getPropertyValue('P106')
        if occupation then
            infobox:addRow('Occupation', formatValue(occupation))
        end

        return tostring(infobox) .. '\n[[Category:People]]'
    else
        -- Default: organisation/troupe infobox (existing behavior)
        local title = qid
        if entity.labels and entity.labels.en then
            title = entity.labels.en.value
        end

        infobox = capiunto.create({
            title = title,
            bodyClass = 'infobox vevent',
            bodyStyle = 'width: 300px;'
        })

        -- Add official name (P1448)
        local officialName = getPropertyValue('P1448')
        if officialName then
            infobox:addRow('Official Name', formatValue(officialName))
        end
    end

    -- Add logo (P154)
    local logo = getPropertyValue('P154')
    if logo then
        local logoStr = formatValue(logo)
        if logoStr then
            infobox:addImage('[[File:' .. logoStr .. '|150px]]', 'Logo')
        end
    end

    -- Add image (P18) with media legend (P2096) as caption
    local imageClaim = getFirstClaimObject('P18')
    if imageClaim then
        local imageVal =
            imageClaim.mainsnak and imageClaim.mainsnak.datavalue and
                imageClaim.mainsnak.datavalue.value
        if imageVal then
            local imageStr = tostring(imageVal)
            local caption = getQualifierValue(imageClaim, 'P2096')
            if caption and caption ~= '' then
                infobox:addImage('[[File:' .. imageStr .. '|300px]]', caption)
            else
                infobox:addImage('[[File:' .. imageStr .. '|300px]]')
            end
        end
    end

    -- Add headquarters (P159)
    local headquarters = getPropertyValue('P159')
    if headquarters then
        infobox:addRow('Headquarters', formatValue(headquarters))
    end

    -- Add founding date (P571)
    local foundingDate = getPropertyValue('P571')
    if foundingDate then
        infobox:addRow('Founding Date', formatValue(foundingDate))
    end

    -- Add dissolution date (P576)
    local dissolutionDate = getPropertyValue('P576')
    if dissolutionDate then
        infobox:addRow('Dissolution Date', formatValue(dissolutionDate))
    end

    -- Add country (P17)
    local country = getPropertyValue('P17')
    if country then infobox:addRow('Country', formatValue(country)) end

    -- Add parent organization (P749)
    local parentOrg = getPropertyValue('P749')
    if parentOrg then
        infobox:addRow('Parent Organization', formatValue(parentOrg))
    end

    -- Add official website (P856) as full-width row at bottom
    local website = getPropertyValue('P856')
    if website then
        local websiteStr = formatValue(website)
        if websiteStr then
            -- Strip http:// or https:// from display text
            local displayUrl = websiteStr:gsub('^https?://', '')
            infobox:addWikitext(
                '<div style="width:100%; text-align:center;">[' .. websiteStr ..
                    ' ' .. displayUrl .. ']</div>')
        end
    end

    return tostring(infobox) ..
               '\n[[Category:Clubs, Societies and Institutions]]'
end

return p