Módulo:Citar Q/Testes

Origem: Wikipédia, a enciclopédia livre.
Documentação do módulo[ver] [editar] [histórico] [purgar]

Uso[editar código-fonte]

Este módulo implementa a predefinição {{Citar Q}}, que cria o elemento de citação de um item do Wikidata.

-- Version: 2021-02-21

local p = {}

require('Module:No globals')
local wdib = require('Module:WikidataIB')
local getValue = wdib._getValue
local getPropOfProp = wdib._getPropOfProp
local followQid = wdib._followQid
local getPropertyIDs = wdib._getPropertyIDs

local i18n = {
    ["unknown-author"] = mw.wikibase.getLabel("Q4233718"):gsub("^%l", mw.ustring.upper),
    ["unknown-author-trackingcat"] = "[[Categoria:!Citar Q - autores desconhecidos]]",
    ["ordinal"] = {
        [1] = "st",
        [2] = "nd",
        [3] = "rd",
        ["default"] = "th"
    },
    ["months"] = {
        "janeiro", "fevereiro", "março", "abril", "maio", "junho",
        "julho", "agosto", "setembro", "outubro", "novembro", "dezembro"
    },
}

-------------------------------------------------------------------------------
-- makeOrdinal needs to be internationalised along with the above i18n
-- takes cardinal number as a numeric and returns the ordinal as a string
-- we need three exceptions in English for 1st, 2nd, 3rd, 21st, .. 31st, etc.
-------------------------------------------------------------------------------
p.makeOrdinal = function(cardinal)
    local card = tonumber(cardinal)
    if not card then return cardinal end
    local ordsuffix = i18n.ordinal.default
    if card % 10 == 1 then
        ordsuffix = i18n.ordinal[1]
    elseif card % 10 == 2 then
        ordsuffix = i18n.ordinal[2]
    elseif card % 10 == 3 then
        ordsuffix = i18n.ordinal[3]
    end
    -- In English, 1, 21, 31, etc. use 'st', but 11, 111, etc. use 'th'
    -- similarly for 12 and 13, etc.
    if (card % 100 == 11) or (card % 100 == 12) or (card % 100 == 13) then
        ordsuffix = i18n.ordinal.default
    end
    return card .. ordsuffix
end

-- Table of simple properties that can be fetched in roughly the same way:
-- id = PXXX
-- maxvals = maximum number of multiple values (0 for all)
-- linked = "no" suppresses linking
-- populate_from_journal = true/false determines whether to look in a journal where the source is published
-- rank = "best", "preferred", normal, etc. determines how Wikidata ranks are treated
-- others = true - the value for the property goes to "others" section
local simple_properties = {
    publisher = {id = "P123", maxvals = 1},
    oclc = {id = "P243", maxvals = 1},
    ['publication-place'] = {id = "P291", maxvals = 0, linked = 'no'}, -- publication place (don't put into |place=; is treated specially in {{citation}} if both are given)
    doi = {id = "P356", maxvals = 1}, -- take care of |doi-broken-date= (WD "reason for deprecation"/"stated as") and |doi-access= (WD "access status")?
    issue = {id = "P433", maxvals = 0, populate_from_journal = true}, -- distinguish from |number= ("P1545"?) if both are given (still blocked by {{citation}}, but will be supported in the future)
    pmid = {id = "P698", maxvals = 1},
--    gbooks = {id = "P675", maxvals = 1}, -- to be added to {{citation}}
--    ia = {id = "P724", maxvals = 1}, -- to be added to {{citation}}
    arxiv = {id = "P818", maxvals = 1},
    bibcode = {id = "P819", maxvals = 1}, -- take care of |bibcode-access=?
    jstor = {id = "P888", maxvals = 1}, -- take care of |jstor-access=?
    mr = {id = "P889", maxvals = 1},
    rfc = {id = "P892", maxvals = 1},
    zbl = {id = "P894", maxvals = 1},
    ssrn = {id = "P893", maxvals = 1},
    place = {id = "P1071", maxvals = 0, linked = 'no'}, -- written-at place
--    ['total-pages'] = {id = "P1104", maxvals = 0, linked = 'no'}, -- to be added to {{citation}} / COinS &rft.tpages=
--    coden = {id = "P1159", maxvals = 1}, -- to be added to {{citation}} / COinS &rft.coden=
--    s2cid = {id = "P8299", maxvals = 1}, -- take care of |s2cid-access=?
    pmc = {id = "P932", maxvals = 1}, -- take care of |pmc-embargo-date= (WD "reason for deprecation")?
    lccn = {id = "P1144", maxvals = 1},
    hdl = {id = "P1184", maxvals = 1}, -- take care of |hdl-access=?
    ismn = {id = "P1208", maxvals = 1},
    journal = {id = "P1433", maxvals = 1},
    citeseerx = {id = "P3784", maxvals = 1},
    osti = {id = "P3894", maxvals = 1}, -- take care of |osti-access=?
--    biorxiv = {id = "P3951", maxvals = 1},
    asin = {id = "P5749", maxvals = 1}, -- What about |asin-tld=? (WD examples resolve to .com at present, but may change)
--    ['catalog-number'] = {id = "P528", maxvals = 0}, -- to be added to {{citation}} / COinS &rft.artnum=
    isbn = {id = "P212", maxvals = 1, populate_from_journal = true}, -- ISBN 13
    issn = {id = "P236", maxvals = 1, populate_from_journal = true}, -- distinguish from |eissn= for electronic issues?
--    jfm = {id = "P?", maxvals = 1}, -- Jahrbuch über die Fortschritte der Mathematik (not Zbl)
--    sbn = {id = "P?", maxvals = 1}, -- Standard Book Number (predecessor of ISBN, not ICCU)
--    message-id = {id = "P?", maxvals = 1}, -- Usenet message ID
    chapter = {id = "P792", maxvals = 1},
    ['publication-date'] = {id = "P577", maxvals = 1, populate_from_journal = true}, -- publication date (don't use |date=; is treated specially in {{citation}} if both are given.)
    series = {id = "P179", maxvals = 1, populate_from_journal = true},
    version = {id = "P348", maxvals = 0},
    edition = {id = "P393", maxvals = 0},
    volume = {id = "P478", maxvals = 0, populate_from_journal = true},
--    part = {id = "P1545"?, maxvals = 0}, --  to be added to {{citation}} / COinS &rft.part=
    title = {id = "P1476", rank="p n"},
--    url = {id = "P953", maxvals = 1}, -- deal with this along with archive-url
    pages = {id = "P304", maxvals = 0, populate_from_journal = true},
    at = {id = "P958", maxvals = 0, populate_from_journal = true}, -- also incorporate lines (P7421) and columns (P3903) into this (cite map also supports |section=)
--    sheets = {id = "P7416", maxvals = 0, populate_from_journal = true},
--    interviewer = {id = "P?", maxvals = 0}, -- does **not** go to "others" section! Multiple interviewers should be n-enumerated
    illustrator = {id = "P110", maxvals = 10, others = true}, -- goes to "others" section
-- foreword and afterword, when contributions to another author's work, are contributions so belong in |contribution=;
-- the writer's name goes in |contributor=; requires |title= and |author=
-- However, this might need to add support for multiple contributors and their roles to {{citation}}, see Help_talk:Citation_Style_1#Others
--    foreword = {id = "P2679", maxvals = 10, others = true}, -- goes to "others" section
--    afterword = {id = "P2680", maxvals = 10, others = true}, -- goes to "others" section
    composer = {id = "P86", maxvals = 10, others = true}, -- goes to "others" section
    animator = {id = "P6942", maxvals = 10, others = true}, -- goes to "others" section
    director = {id = "P57", maxvals = 10, others = true}, -- goes to "others" section
    screenwriter = {id = "P58", maxvals = 10, others = true}, -- goes to "others" section
    signatory = {id = "P1891", maxvals = 10, others = true}, -- goes to "others" section
    presenter = {id = "P371", maxvals = 10, others = true}, -- goes to "others" section
    performer = {id = "P175", maxvals = 10, others = true}, -- goes to "others" section
}

--[[--------------------------< I S _ S E T >--------------------------------------------------------------
Returns true if argument is set; false otherwise. Argument is 'set' when it exists (not nil) or when it is not an empty string.
]]
local function is_set( var )
    return not (var == nil or var == '')
end

--[[--------------------------< I N _ A R R A Y >--------------------------------------------------------------
Whether needle is in haystack (taken from Module:Citation/CS1/Utilities)
]]
local function in_array( needle, haystack )
    if needle == nil then
        return false
    end
    for n, v in ipairs( haystack ) do
        if v == needle then
            return n
        end
    end
    return false
end


--[[--------------------------< A C C E P T _ V A L U E >-------------------------------------------------------
Accept WD value by framing in ((...)) if param_val is equal to keyword; else pass-through WD value as is.
]]
local function accept_value( param_val, wd_val )
    local val = param_val

    if val then
        if in_array (val, {'accept', '))((', ':d:'}) then
            val = '((' .. wd_val .. '))'
        elseif '((accept))' == val then
            val = 'accept'
        elseif '(())(())' == val then
            val = '))(('
        elseif '((:d:))' == val then
            val = ':d:'
        else
            val = wd_val
        end
    end

    return val
end

-- function to fetch a value to display
local function makelink(v, out, link, maxpos, wdl)
    local label
    if v.mainsnak.snaktype == "value" then
        if v.mainsnak.datatype == "wikibase-item" then
            local qnumber = v.mainsnak.datavalue.value.id
            local sitelink = mw.wikibase.getSitelink(qnumber)
            if qnumber == "Q2818964" then sitelink = nil end -- suppress link to "Various authors"
            if v.qualifiers and v.qualifiers.P1932 then
                label = v.qualifiers.P1932[1].datavalue.value
            elseif v.qualifiers and v.qualifiers.P1810 then
                label = v.qualifiers.P1810[1].datavalue.value
            else
                label = mw.wikibase.getLabel(qnumber)
                if label then
                    label = mw.text.nowiki(label)
                else
                    label = qnumber -- should add tracking category
                end
            end
            local position = maxpos + 1 -- Default to 'next' author.
            -- use P1545 (series ordinal) instead of default position.
            if v["qualifiers"] and v.qualifiers["P1545"] and v.qualifiers["P1545"][1] then
                position = tonumber(v.qualifiers["P1545"][1].datavalue.value)
            end
            maxpos = math.max(maxpos, position)
            if sitelink then
                -- just the plain name,
                -- but keep a record of the links, using the same index
                out[position] = label
                link[position] = sitelink
            else
                if wdl then
                    -- show that there's a Wikidata entry available
                    out[position] = "[[:d:Q" .. v.mainsnak.datavalue.value["numeric-id"] .. "|" .. label .. "]]&nbsp;<span title='" .. i18n["errors"]["local-article-not-found"] .. "'>[[File:Wikidata-logo.svg|16px|alt=|link=]]</span>"
                else
                    -- no Wikidata links wanted, so just give the plain label
                    out[position] = label
                end
            end
        elseif v.mainsnak.datatype == "string" then
            local position = maxpos + 1 -- Default to 'next' author.
            -- use P1545 (series ordinal) instead of default position.
            if v["qualifiers"] and v.qualifiers["P1545"] and v.qualifiers["P1545"][1] then
                position = tonumber(v.qualifiers["P1545"][1].datavalue.value)
            end
            maxpos = math.max(maxpos, position)
            out[position] = v.mainsnak.datavalue.value
        else
            -- not a wikibase-item or a string!
        end
    else
        -- code here if we want to return something when author is "unknown"
        if v.qualifiers and v.qualifiers.P1932 then
            label = v.qualifiers.P1932[1].datavalue.value
        elseif v.qualifiers and v.qualifiers.P1810 then
            label = v.qualifiers.P1810[1].datavalue.value
        else
            label = i18n["unknown-author"] .. (i18n["unknown-author-trackingcat"] or "")
        end
        maxpos = maxpos + 1
        out[maxpos] = label
    end
    return maxpos
end

--[=[-------------------------< G E T _ N A M E _ L I S T >----------------------------------------------------
get_name_list -- adapted from getAuthors code taken from Module:RexxS
arguments:
    nl_type - type of name list to fetch: nl_type = 'author' for authors; 'editor' for editors; 'translator' for translators
    args - pointer to the parameter arguments table from the template call
    qid - value from |qid= parameter; the Q-id of the source (book, etc.) in qid
    wdl - value from the |wdl= parameter; a Boolean passed to enable links to Wikidata when no article exists
returns nothing; modifies the args table
]=]

local function get_name_list (nl_type, args, qid, wdl)
    local propertyID = "P50"
    local fallbackID = "P2093" -- author name string

    if nl_type =="author" then
        propertyID = 'P50'        -- for authors
        fallbackID = 'P2093'    -- author-string
    elseif nl_type =="editor" then
        propertyID = 'P5769'    -- "editor-in-chief"
        fallbackID = 'P98'        -- for editors - So-called "fallbacks" are actually a second set of properties processed
        -- TBD. Take book series editors into account as well (if they have a separate P code as well)?
    elseif nl_type == "translator" then
        propertyID = 'P655'        -- for translators
        fallbackID = nil
--    elseif 'contributor' == nl_type then
--        f.e. author of forewords (P2679) and afterwords (P2680); requires |contribution=, |title= and |author=
--        propertyID = 'P'        -- for contributors
--        fallbackID = nil
    else
        return                    -- not specified so return
    end

    -- wdl is a Boolean passed to enable links to Wikidata when no article exists
    -- if "false" or "no" or "0" is passed set it false
    -- if nothing or an empty string is passed set it false
    if wdl and (#wdl > 0) then
        wdl = wdl:lower()
        wdl = in_array (wdl, {"false", "no", "0"})
    else
        -- wdl is empty, so
        wdl = false
    end

    local props = nil
    local fallback = nil
    if mw.wikibase.entityExists(qid) then
        props = mw.wikibase.getAllStatements(qid, propertyID)
        if props and fallbackID then
            fallback = mw.wikibase.getAllStatements(qid, fallbackID)
        end
    end

    -- Make sure it actually has at least one of the properties requested
    if not (props and props[1]) and not (fallback and fallback[1]) then
        return nil
    end

    -- So now we have something to return:
    -- table 'out' is going to store the names(s):
    -- and table 'link' will store any links to the name's article
    local out = {}
    local link = {}
    local maxpos = 0
    if props and props[1] then
        for k, v in pairs(props) do
            maxpos = makelink(v, out, link, maxpos, wdl)
        end
    end
    if fallback and fallback[1] then
        -- second properties
        for k, v in pairs(fallback) do
            maxpos = makelink(v, out, link, maxpos, wdl)
        end
    end

    -- if there's anything to return, then insert the additions in the template arguments table
    -- in the form |author1=firstname secondname |author2= ...
    -- Renumber, in case we have inconsistent numbering
    local keys = {}
    for k, v in pairs(out) do
        keys[#keys + 1] = k
    end
    table.sort(keys) -- as they might be out of order
    for i, k in ipairs(keys) do
        mw.log(i .. " " .. k .. " " .. (out[k]))
        if args[nl_type .. i] then -- name gets overwritten
            -- pull corresponding -link only if overwritten name is same as WD name
            if link[k] and (args[nl_type .. i] == out[k]) then
                args[nl_type .. '-link' .. i] = args[nl_type .. '-link' .. i] or link[k] -- author-linkn or editor-linkn
            end
        else -- name does not get overwritten, so pull name from WD
            args[nl_type .. i] = out[k]
            if link[k] then
                args[nl_type .. '-link' .. i] = args[nl_type .. '-link' .. i] or link[k] -- author-linkn or editor-linkn
            end
        end
    end
end

-- gets language codes used for a monolingual text property as a table
function p._getLangOfProp(qid, pid)
    if not pid then return {} end
    local out = {}
    local props = mw.wikibase.getAllStatements(qid, pid)
    for i, v in ipairs(props) do
        if v.mainsnak.datatype == "monolingualtext" and v.mainsnak.datavalue then
            out[#out + 1] = v.mainsnak.datavalue.value.language
        end
    end
    return out
end
function p.getLangOfProp(frame)
    local pid = frame.args.pid or mw.text.trim(frame.args[1] or "")
    if pid == "" then return end
    local qid = frame.args.qid
    if qid == "" then qid = nil end
    return table.concat(p._getLangOfProp(qid, pid), ", ")
end

-- gets the language codes of a Wikidata entry as a table
local function _lang_code(qid)
    local lc = getPropOfProp( {qid = qid, prop1 = "P407", prop2 = "P424", ps = 1} )
    if lc then return mw.text.split( lc, "[, ]+" ) end
    lc = getPropOfProp( {qid = qid, prop1 = "P407", prop2 = "P218", ps = 1} )
    if lc then return mw.text.split( lc, "[, ]+" ) end
    return p._getLangOfProp(qid, "P1476")
end
function p.lang_code(frame)
    return table.concat(_lang_code(frame.args.qid or mw.text.trim(frame.args[1] or "")), ", ")
end

-- export for debug
function p.getPropOfProp(frame)
    return getPropOfProp(frame.args)
end

-- wraps a string in nowiki unless disable flag is set
local function wrap_nowiki(str, disable)
    if disable then return str or '' end
    return mw.text.nowiki(str or '')
end

-- sort sequence table whose values are key-value pairs by key
local function comp_key(a, b)
    return a[1] < b[1]
end

-- sort sequence table whose values are key-value pairs by value
local function comp_val(a, b)
    return a[2] < b[2]
end

--[[-------------------------< C I T E _ Q >------------------------------------------------------------------
Takes standard CS1|2 template parameters and passes all to {{citation}}.  If neither of |author= and |author1=
are set, calls get_authors() to try to get an author name-list from Wikidata.  The result is passed to
{{citation}} for rendering.
--]]
function p._cite_q (citeq_args)
    local frame = mw.getCurrentFrame()

    -- parameters that don't get passed to Citation
    local expand = citeq_args.expand -- when set to anything, causes {{cite q}} to render <code><nowiki>{{citation|...}}</nowiki></code>
    local qid = citeq_args[1] or citeq_args.qid
    local wdl = citeq_args.wdl
    local template = citeq_args.template
    citeq_args.expand = nil
    citeq_args[1] = nil
    citeq_args.qid = nil
    citeq_args.wdl = nil
    citeq_args.template = nil

    -- if title supplied, flag to not read html title
    local titleforced = (citeq_args.title ~= nil)

    local oth = {}

    -- put the language codes into a sequential table langcodes[]
    local langcodes = {}
    if citeq_args.language then
        -- check these are a supported language codes
        for lc in mw.text.gsplit( citeq_args.language, "[, ]+", false ) do
            langcodes[#langcodes+1] = mw.language.isSupportedLanguage(citeq_args.language) and citeq_args.language
        end
    end
    if not langcodes[1] then
        -- try to find language of work
        langcodes = _lang_code(qid)
    end
    if not langcodes[1] then
        -- try fallback to journal's language
        local journal_qid = followQid({qid = qid, props = "P1433"})
        langcodes = journal_qid and _lang_code(journal_qid)
    end
    citeq_args.language = citeq_args.language or table.concat(langcodes, ", ")

    -- loop through list of simple properties and get their values in citeq_args
    for name, data in pairs(simple_properties) do
        citeq_args[name] = getValue( {data.id, fwd = "ALL", osd = "no", noicon = "true", qid = qid, maxvals = data.maxvals, linked = data.linked, rank = data.rank or "best", citeq_args[name] } )
        if data.populate_from_journal then
            local publishedin = getValue( {"P1433", ps = 1, qid = qid, maxvals = 0, citeq_args[name], qual = data.id, qualsonly = 'yes'} )
            citeq_args[name] = publishedin or getPropOfProp({qid = qid, prop1 = "P1433", prop2 = data.id, maxvals = data.maxvals, ps = 1})
        end
        if citeq_args[name] and citeq_args[name]:find('[[Categoria:!Artigos com informação Wikidata em falta]]', 1, true) then
            -- try fallback to work's native language
            citeq_args[name] = getValue( {data.id, ps = 1, qid = qid, maxvals = data.maxvals, linked = "no", lang = langcodes[1] } )
            if citeq_args[name]:find('^Q%d+$') then -- qid was returned
                -- try fallback to qid's native language
                local qid_languages = _lang_code(citeq_args[name])
                citeq_args[name] = getValue( {data.id, ps = 1, qid = qid, maxvals = data.maxvals, linked = "no", lang = qid_languages[1] } )
                if citeq_args[name]:find('^Q%d+$') then -- qid was returned again
                    citeq_args[name] = nil
                else
                    -- record the language found if no lang specified
                    citeq_args.language = citeq_args.language or qid_languages[1]
                end
            end
        end
        if data.others then
            oth[#oth + 1] = citeq_args[name] and (name:gsub("^%l", string.upper) .. ": " .. citeq_args[name])
            citeq_args[name] = nil
        end
    end

    citeq_args.others = citeq_args.others or table.concat(oth, ". ")
    if citeq_args.others == "" then
        citeq_args.others = nil
    end

    citeq_args.journal = citeq_args.journal and citeq_args.journal:gsub("^''", ""):gsub("''$", ""):gsub("|''", "|"):gsub("'']]", "]]")

    citeq_args.ol = (getValue( {"P648", ps = 1, qid = qid, maxvals = 1, citeq_args.ol } ) or ''):gsub("^OL(.+)$", "%1")
    if citeq_args.ol == "" then
        citeq_args.ol = nil
    end
    -- TBD. Take care of |ol-access=?

--    citeq_args.biorxiv = citeq_args.biorxiv and ("10.1101/" .. citeq_args.biorxiv)

    citeq_args.isbn = getValue( {"P957", ps = 1, qid = qid, maxvals = 1, rank="best", citeq_args.isbn } ) -- try ISBN 10 (only one value accepted)

    -- if url then see if there's an archive: citeq_args.url
    local url
    if not citeq_args.url then
        for i, pr in ipairs( {"P953", "P856", "P2699"} ) do
            url = getValue( {pr, ps = 1, qid = qid, maxvals = 1, qual="P1065" } )
            if url then
                citeq_args.url = mw.text.split( url, " (", true )[1]
                local arcurl = mw.ustring.match( url, "%((.*)%)" )
                if arcurl then
                    local arcy, arcm, arcd = arcurl:match("(20%d%d)%p?(%d%d)%p?(%d%d)")
                    if arcy and arcm and arcd then
                        citeq_args["archive-url"] = arcurl
                        citeq_args["archive-date"] = tonumber(arcd) .. " " .. i18n.months[tonumber(arcm)] .. " " .. arcy
                    end
                end
                break
            end
        end
    end

    if citeq_args.publisher == "Unknown" then -- look for "stated as" (P1932)
        local stated_as = getValue( {"P123", ps = 1, qid = qid, maxvals = 1, qual="P1932", qo="y"} )
        if stated_as then citeq_args.publisher = stated_as end
    end

    if not titleforced then
        local htmltitle = getValue( {"P1476", qual = "P6833", ps = 1, qid = qid, maxvals = 1, qo = "y"} )
        if htmltitle then
            citeq_args.title = htmltitle:gsub("</?i>", "''")
        else
            local title_display = citeq_args.title
                or mw.wikibase.getLabel(qid)
                or (langcodes[1] and mw.wikibase.getLabelByLang(qid, langcodes[1]))
                or ("No label or title -- debug: " .. qid)
            if citeq_args.url then
                citeq_args.title = wrap_nowiki(title_display)
            else
                local slink = mw.wikibase.getSitelink(qid)
                local slink_flag = false
                local wrap_title = ''
                local wslink = false
                if not slink then
                    -- See if we have wikisource
                    if not citeq_args.url then
                        local wikisource_sitelink = mw.wikibase.getSitelink(qid, "enwikisource") or nil
                        if wikisource_sitelink then
                            slink = ':s:'..wikisource_sitelink
                            wslink = true
                        end
                    end
                end
                if citeq_args.title then
                    if slink then
                        wrap_title = wrap_nowiki(citeq_args.title)
                        slink_flag = true
                    else
                        citeq_args.title = wrap_nowiki(citeq_args.title)
                    end
                else
                    if slink and not wslink then
                        if slink:lower() == title_display:lower() then
                            citeq_args.title = '[[' .. slink .. ']]'
                        else
                            wrap_title = wrap_nowiki(slink:gsub("%s%(.+%)$", ""):gsub(",.+$", ""))
                            slink_flag = true
                        end
                    elseif wslink then
                        wrap_title = wrap_nowiki(title_display)
                        slink_flag = true
                    else
                        citeq_args.title = wrap_nowiki(title_display)
                    end
                end
                if slink_flag then
                    if slink == wrap_title and not wslink then -- direct link
                        citeq_args.title = '[[' .. slink .. ']]'
                    else -- piped link
                        citeq_args.title = '[[' .. slink .. '|' .. wrap_title .. ']]'
                    end
                end
            end
        end
    end

    -- TBD: incorporate |at, |sheets= and |sheet= here as well
    -- Sort out what should happen if several of them are given at the same time
    if citeq_args.page or citeq_args.p then -- let single take precedence over multiple
        citeq_args.pages = nil
        citeq_args.pp = nil
    end
    if citeq_args.pages then
        local _, count = string.gsub(citeq_args.pages, "[,;%s]%d+", "")
        if count == 1 then
            citeq_args.page = citeq_args.pages
            citeq_args.pages = nil
        end
    end

    if is_set (qid) then
        if not is_set (citeq_args.author) and not is_set (citeq_args.author1)
            and not is_set (citeq_args.subject) and not is_set (citeq_args.subject1)
            and not is_set (citeq_args.host) and not is_set (citeq_args.host1)
            and not is_set (citeq_args.last) and not is_set (citeq_args.last1)
            and not is_set (citeq_args.surname) and not is_set (citeq_args.surname1)
            and not is_set (citeq_args['author-last']) and not is_set (citeq_args['author-last1']) and not is_set (citeq_args['author1-last'])
            and not is_set (citeq_args['author-surname']) and not is_set (citeq_args['author-surname1']) and not is_set (citeq_args['author1-surname1']) then    -- if neither are set, try to get authors from Wikidata
            get_name_list ('author', citeq_args, qid, wdl)                -- modify citeq_args table with authors from Wikidata
        end

        if not is_set (citeq_args.editor) and not is_set (citeq_args.editor1)
            and not is_set (citeq_args['editor-last']) and not is_set (citeq_args['editor-last1']) and not is_set (citeq_args['editor1-last'])
            and not is_set (citeq_args['editor-surname']) and not is_set (citeq_args['editor-surname1']) and not is_set (citeq_args['editor1-surname']) then    -- if neither are set, try to get editors from Wikidata
            get_name_list ('editor', citeq_args, qid, wdl)                -- modify citeq_args table with editors from Wikidata
        end

        if not is_set (citeq_args.translator) and not is_set (citeq_args.translator1)
            and not is_set (citeq_args['translator-last']) and not is_set (citeq_args['translator-last1']) and not is_set (citeq_args['translator1-last'])
            and not is_set (citeq_args['translator-surname']) and not is_set (citeq_args['translator-surname1']) and not is_set (citeq_args['translator1-surname']) then    -- if neither are set, try to get translators from Wikidata
            get_name_list ('translator', citeq_args, qid, wdl)            -- modify citeq_args table with translators from Wikidata
        end
    end

    for k, v in pairs(citeq_args) do
        if in_array (v, {'(())', 'unset', 'ignore'}) or 'string' ~= type(k) then -- empty accept-as-is-written (()) markup to indicate an empty/unused parameter value, other ((...)) markups are deliberately passed down to {{citation}}
            citeq_args[k] = nil
        elseif in_array (v, {'((unset))', '((ignore))'}) then -- strip off markup for free-text values clashing with local keywords
            citeq_args[k] = 'unset'
        end
    end

    local author_count = 0
    for k, v in pairs(citeq_args) do
        if k:find("^author%d+$") then
            author_count = author_count + 1
        end
    end
    if author_count > 8 then -- convention in astronomy journals, optional mode for this?
        citeq_args['display-authors'] = citeq_args['display-authors'] or 3
    end

    local editor_count = 0
    for k, v in pairs(citeq_args) do
        if k:find("^editor%d+$") then
            editor_count = editor_count + 1
        end
    end
    if editor_count > 8 then -- convention in astronomy journals, optional mode for this?
        citeq_args['display-editors'] = citeq_args['display-editors'] or 3
    end

    -- change edition to ordinal if it's set and numeric
    citeq_args.edition = citeq_args.edition and p.makeOrdinal(citeq_args.edition)

    -- code to make a guess what template to use from the supplied parameters
    -- (first draft for proof-of-concept)
    if citeq_args.isbn then
        template = template or "book"
        citeq_args.asin = nil -- suppress ASIN if ISBN exists
    elseif citeq_args.journal then
        template = template or "journal"
    elseif citeq_args.website then
        template = template or "web"
    end

    -- template is CS1 designator: journal, web, news, etc.
    if template then
        citeq_args.mode = citeq_args.mode or "cs1"
        template = "Cite " .. template
    else
        citeq_args.mode = citeq_args.mode or "cs2"
        template = "Citation"
    end

    -- |id= could hold more than one identifier pulled from Wikidata not supported by {{citation}}, right now only add our qid to the list
    local list_sep = '. '
    if citeq_args.mode ~= 'cs1' then
        list_sep = ', '
    end
    local id = '[[WDQ (identificador)|Wikidata]]&nbsp;[[:d:' .. qid .. '|' .. qid .. ']]' -- go through "WDQ (identifier)" redirect to reduce clutter in "What links here" and improve reverse lookup. Keep in sync with {{QID}}.
    local old_id = citeq_args.id
    if wdl then -- show WD logo
        id = id .. '[[File:Wikidata-logo.svg|16px|alt=|link=]]' -- possibly replace by WD edit icon?
    end
    if is_set (old_id) then
        citeq_args.id = old_id .. list_sep .. id -- append to user-specified contents
    else
        citeq_args.id = id
    end

    -- clean up any blank parameters
    for k, v in pairs(citeq_args) do
        if v == "" then citeq_args[k] = nil end
    end

    -- if |expand=<anything>, write a nowiki'd version to see what the {{citation}} template call looks like
    if expand then
        local expand_args = { "{{" .. template }        -- init with citation template
        if expand == "self" then
            citeq_args.id = old_id -- restore original |id= parameter
            expand_args = { "{{cite Q|" .. qid } -- expand to itself
        end
        -- make a sortable table and sort it by param name
        local sorttable = {}
        for param, val in pairs (citeq_args) do
            table.insert(sorttable, {param, val})
        end
        table.sort(sorttable, comp_key)
        -- add contents to expand_args
        for idx, val in ipairs(sorttable) do
            table.insert(expand_args, val[1] .. '=' .. val[2])
        end
        -- make the nowiki'd string and done
        return "<code>" .. table.concat (expand_args, ' |') .. "}}</code>"
    end

    local erratumid = getPropertyIDs( { "P2507", qid = qid, fwd = "ALL", osd = "no", rank = "best", maxvals = 1 } )
    if erratumid then
        erratumid = " [[d:" .. erratumid .. "|(erratum)]]" .. "[[Categoria:!Citar Q - cita obra com erratum]]"
    else
        erratumid = ""
    end

    local opt_cat = ''
    if getValue( {"P5824", ps = 1, qid = qid} ) then
        opt_cat = '[[Categoria:!Citar Q - cita um trabalho retratado]]<!-- retracted -->'
    end
    if getValue( {"P1366", ps = 1, qid = qid} ) then
        opt_cat = opt_cat .. '[[Categoria:!Citar Q - cita um trabalho substituído]]<!-- replaced -->'
    end
    return frame:expandTemplate{title = template, args = citeq_args} .. erratumid .. opt_cat -- render the template
end

function p.cite_q (frame)
    local args = {}
    for k, v in pairs(frame:getParent().args) do
        if v ~= "" then args[k] = v end
    end
    for k, v in pairs(frame.args) do
        if v ~= "" then args[k] = v end
    end
    args.qid = args.qid or args[1] or ""
    if args.qid == "" then return nil end
    args[1] = nil

    local citesep = (args.citesep or "")
    if citesep == "" then citesep = ", " end
    citesep = citesep:gsub('"', '') -- strip double quotes after setting default to allow |citesep="" as a blank separator
    args.citesep = nil

    local tag = args.tag or ""
    if tag == "" then tag = nil end
    args.tag = nil

    local list = args.list or ""
    if list == "" then list = nil end
    args.list = nil

    args.language = args.language or args.lang
    args.lang = nil

    local cites = {}
    for q in args.qid:gmatch("Q%d+") do
        -- make a new copy of the arguments
        local newargs = {}
        for k, v in pairs(args) do
            if k ~= "qid" then
                newargs[k] = v
            end
        end
        newargs.qid = q
        if tag == "ref" then
            cites[#cites + 1] = frame:callParserFunction{ name = "#tag:ref", args = { p._cite_q(newargs), name = q } }
            -- expand like this: args = { p._cite_q(newargs), name = 'foo', group = 'bar' }
        else
            cites[#cites + 1] = p._cite_q(newargs)
        end
    end

    if list then
        return frame:expandTemplate{ title = list, args = cites }
    else
        return table.concat(cites, citesep)
    end
end

return p