A documentação para este módulo pode ser criada em Módulo:Infobox/Wikidata/doc
--script that retrieves basic data stored in Wikidata, for the datamodel, see https://www.mediawiki.org/wiki/Extension:Wikibase_Client/Lua local wd = {} local modules = { } local modulesNames = { reference = 'Módulo:Wikidata/Referências', i18n = 'Módulo:Wikidata/i18n', globes = 'Módulo:Wikidata/Globes', langcodes = 'Módulo:Dicionário Wikidata/Códigos língua', -- big, infrequently used data invertedlangcodes = 'Módulo:Dicionário Wikidata/Códigos língua/inverso', linguistic = 'Módulo:Linguística', formatDate = 'Módulo:Data complexa', formatNum = 'Módulo:Conversão', langmodule = 'Módulo:Língua', cite = 'Módulo:Citação/CS1', weblink = 'Módulo:Weblink' } local function loadModule( t, key ) if modulesNames[key] then local m = require( modulesNames[key] ) t[key] = m return m end end setmetatable( modules, { __index = loadModule } ) local datequalifiers = {'P585', 'P571', 'P580', 'P582', 'P1319', 'P1326'} -- === I18n === local defaultlang = mw.getContentLanguage():getCode() function wd.translate(str, rep1, rep2) str = modules.i18n[str] or str if rep1 and (type (rep1) == 'string') then str = str:gsub('$1', rep1) end if rep2 and (type (rep2) == 'string')then str = str:gsub('$2', rep2) end return str end local function addCat(cat, sortkey) if sortkey then return '[[Category:' .. cat .. '|' .. sortkey .. ']]' end return '[[Category:' .. cat .. ']]' end local function formatError( key , category, debug) if debug then return error(modules.i18n[key] or key) end if category then return addCat(category, key) -- else -- return addCat('cat-unsorted-issue', key) end end -- function wd.isSpecial(snak) return (snak.snaktype ~= 'value') end function wd.getId(snak) if (snak.snaktype == 'value') then return 'Q' .. snak.datavalue.value['numeric-id'] end end function wd.getNumericId(snak) if (snak.snaktype == 'value') then return snak.datavalue.value['numeric-id'] end end function wd.getMainId(claim) return wd.getId(claim.mainsnak) end function wd.entityId(entity) if type(entity) == 'string' then return entity end return entity.id end --[[ This is used to return an image legend from Wikidata image is property P18 image legend is property P2096 Call as {{#invoke:Infobox/Wikidata |getImageLegend | <PARAMETER> | lang=<ISO-639code> |id=<QID>}} Returns PARAMETER, unless it is equal to "FETCH_WIKIDATA", from Item QID (expensive call) If QID is omitted or blank, the current article is used (not an expensive call) If lang is omitted, it uses the local wiki language, otherwise it uses the provided ISO-639 language code ISO-639: https://docs.oracle.com/cd/E13214_01/wli/docs92/xref/xqisocodes.html#wp1252447 Ranks are: 'preferred' > 'normal' This returns the label from the first image with 'preferred' rank Or the label from the first image with 'normal' rank if preferred returns nothing Ranks: https://www.mediawiki.org/wiki/Extension:Wikibase_Client/Lua ]] wd.getImageLegend = function(frame) -- look for named parameter id; if it's blank make it nil local id = frame.args.id if id and (#id == 0) then id = nil end -- look for named parameter lang -- it should contain a two-character ISO-639 language code -- if it's blank fetch the language of the local wiki local lang = frame.args.lang if (not lang) or (#lang < 2) then lang = mw.language.getContentLanguage().code end -- first unnamed parameter is the local parameter, if supplied local input_parm = mw.text.trim(frame.args[1] or "") if input_parm == "FETCH_WIKIDATA" then local ent = mw.wikibase.getEntityObject(id) local imgs if ent and ent.claims then imgs = ent.claims.P18 end local imglbl if imgs then -- look for an image with 'preferred' rank for k1, v1 in pairs(imgs) do if v1.rank == "preferred" and v1.qualifiers and v1.qualifiers.P2096 then local imglbls = v1.qualifiers.P2096 for k2, v2 in pairs(imglbls) do if v2.datavalue.value.language == lang then imglbl = v2.datavalue.value.text break end end end end -- if we don't find one, look for an image with 'normal' rank if (not imglbl) then for k1, v1 in pairs(imgs) do if v1.rank == "normal" and v1.qualifiers and v1.qualifiers.P2096 then local imglbls = v1.qualifiers.P2096 for k2, v2 in pairs(imglbls) do if v2.datavalue.value.language == lang then imglbl = v2.datavalue.value.text break end end end end end end return imglbl else return input_parm end end function wd.getEntityIdForCurrentPage() return mw.wikibase.getEntityIdForCurrentPage() end -- function that returns true if the "qid" parameter is the qid -- of the item that is linked to the calling page function wd.isPageOfQId(qid) local entity_on_its_page = false local self_entity = mw.wikibase.getEntity() if self_entity ~= nil and qid == wd.entityId(self_entity) then entity_on_its_page = true end return entity_on_its_page end function wd.getEntity( val ) if type(val) == 'table' then return val end if val == '-' then return nil end if val == '' then val = nil end return mw.wikibase.getEntity(val) end function wd.splitStr(val) -- converte em strings strings do Wikitexto que usam vírgulas de separação if type(val) == 'string' then val = mw.text.split(val, ",") end return val end function wd.isHere(searchset, val) for i, j in pairs(searchset) do if val == j then return true end end return false end local function wikidataLink(entity) local name =':d:' if type(entity) == 'string' then if entity:match("P[0-9+]") then entity = "Property:" .. entity end return name .. entity elseif type(entity) == 'table' then if entity["type"] == "property" then name = ":d:Property:" end return name .. entity.id elseif type(entity) == nil then return formatError('entity-not-found') end end function wd.siteLink(entity, project, lang) -- returns 3 values: a sitelink (with the relevant prefix) a project name and a language lang = lang or defaultlang if (type(project) ~= 'string') then project = 'wiki' end project = project:lower() if project == 'wikipedia' then project = 'wiki' end if type(entity) == 'string' and (project == 'wiki') and ( (not lang or lang == defaultlang) ) then -- evite carregar o elemento inteiro return mw.wikibase.sitelink(entity), 'wiki', defaultlang end if project == 'wikidata' then return wikidataLink(entity), 'wikidata' end local projects = { -- nome = {prefixo no Wikidata, prefixo de links na Wikipedia, adicionar prefixo de idioma} wiki = {'wiki', nil, true}, -- wikipedia commons = {'commonswiki', 'commons', false}, commonswiki = {'commonswiki', 'commons', false}, wikiquote = {'wikiquote', 'q', true}, wikivoyage = {'wikivoyage', 'voy', true}, wikibooks = {'wikibooks', 'b', true}, wikinews = {'wikinews', 'n', true}, wikiversity = {'wikiversity', 'v', true}, wikisource = {'wikisource', 's', true}, wiktionary = {'wiktionary', 'wikt', true}, specieswiki = {'specieswiki', 'species', false}, metawiki = {'metawiki', 'm', false}, incubator = {'incubator', 'incubator', false}, outreach = {'outreach', 'outreach', false}, mediawiki = {'mediawiki', 'mw', false} } entity = wd.getEntity(entity) if not entity then return nil end local projectdata = projects[project:lower()] if not projectdata then -- defaultlink might be in the form "dewiki" rather than "project: 'wiki', lang: 'de' " for k, v in pairs(projects) do if project:match( k .. '$' ) and mw.language.isKnownLanguageTag(project:sub(1, #project-#k)) then lang = project:sub(1, #project-#k) project = project:sub(#lang + 1, #project) projectdata = projects[project] break end end if not mw.language.isKnownLanguageTag(lang) then return --formatError('invalid-project-code', projet or 'nil') end end if not projectdata then return -- formatError('invalid-project-code', projet or 'nil') end local linkcode = projectdata[1] local prefix = projectdata[2] local multiversion = projectdata[3] if multiversion then linkcode = lang .. linkcode end local link = entity:getSitelink(linkcode) if not link then return nil end if prefix then link = prefix .. ':' .. link end if multiversion then link = ':' .. lang .. ':' .. link end return link, project, lang end -- add new values to a list, avoiding duplicates function wd.addNewValues(olditems, newitems, maxnum, stopval) if not newitems then return olditems end for _, qid in pairs(newitems) do if stopval and (qid == stopval) then table.insert(olditems, qid) return olditems end if maxnum and (#olditems >= maxnum) then return olditems end if not wd.isHere(olditems, qid) then table.insert(olditems, qid) end end return olditems end --=== FILTER CLAIMS ACCORDING TO VARIOUS CRITERIA : FUNCTION GETCLAIMS et alii === local function notSpecial(claim) local snack = claim.mainsnak or snack return snack.snaktype == 'value' end local function hasTargetValue(claim, targets) -- retorna verdadeiro se o valor estiver na lista de destino ou se for um valor especial filtrado separadamente por excluiespecial local id = wd.getMainId(claim) local targets = wd.splitStr(targets) return wd.isHere(targets, id) or wd.isSpecial(claim.mainsnak) end local function excludeValues(claim, values) -- se o valor não estiver na lista ou se for um valor especial (filtrado separadamente por excludeespecial) return wd.isSpecial(claim.mainsnak) or not ( hasTargetValue(claim, values) ) end local function bestRanked(claims) if not claims then return nil end local preferred, normal = {}, {} for i, j in pairs(claims) do if j.rank == 'preferred' then table.insert(preferred, j) elseif j.rank == 'normal' then table.insert(normal, j) end end if #preferred > 0 then return preferred else return normal end end local function withRank(claims, target) if target == 'best' then return bestRanked(claims) end local newclaims = {} for pos, claim in pairs(claims) do if target == 'valid' then if claim.rank ~= 'deprecated' then table.insert(newclaims, claim) end elseif claim.rank == target then table.insert(newclaims, claim) end end return newclaims end function wd.hasQualifier(claim, acceptedqualifs, acceptedvals, excludequalifiervalues) local claimqualifs = claim.qualifiers if (not claimqualifs) then return false end acceptedqualifs = wd.splitStr(acceptedqualifs) acceptedvals = wd.splitStr( acceptedvals) local function ok(qualif) -- verificação para um qualificador individual if not claimqualifs[qualif] then return false end if not (acceptedvals) then -- se nenhum valor específico for solicitado, OK return true end for i, wanted in pairs(acceptedvals) do for j, actual in pairs(claimqualifs[qualif]) do if wd.getId(actual) == wanted then return true end end end end for i, qualif in pairs(acceptedqualifs) do if ok(qualif) then return true end end return false end local function hasSource(claim, targetsource, sourceproperty) sourceproperty = sourceproperty or 'P248' if targetsource == "-" then return true end if (not claim.references) then return false end local candidates = claim.references[1].snaks[sourceproperty] -- snaks usando a propriedade solicitada if (not candidates) then return false end if (targetsource == "any") then -- se algum valor for aceite, desde que use a propriedade solicitada return true end targetsource = wd.splitStr(targetsource) for _, source in pairs(candidates) do local s = wd.getId(source) for i, target in pairs(targetsource) do if s == target then return true end end end return false end local function excludeQualifier(claim, qualifier, qualifiervalues) return not wd.hasQualifier(claim, qualifier, qualifiervalues) end local function hasDate(claim) local claimqualifs = claims.qualifiers if not claimqualifs then return false end for _, qualif in pairs(claimqualifs) do if claimsqualifs[qualif] and claimsqualifs[qualif][1].snaktype == 'value' then return true end end return false end local function hasLink(claim, site, lang) if (claim.mainsnak.snaktype ~= 'value') then -- não excluir valores especiais, há uma função dedicada para isso return true end local id = wd.getMainId(claim) local link = wd.siteLink(id, site, lang) if link then return true end end local function isInLanguage(claim, lang) -- funciona apenas para texto monolingual / estender para outros tipos usando qualificadores? if type(lang) == 'table' then -- se é uma tabela de idiomas separada por vírgulas, aceitamos todas for i, l in pairs(lang) do local v = isInLanguage(claim, l) if v then return true end end end if type(lang) ~= ('string') then return --? end if (lang == '-') then return true end if (lang == 'locallang') then lang = mw.getContentLanguage():getCode() end -- pour les monolingual text local snak = claim.mainsnak or claim if snak.snaktype == 'value' and snak.datavalue.type == 'monolingualtext' then if snak.datavalue.value.language == lang then return true end return false end -- para outros tipos de dados: pesquisa em qualificadores if (lang == 'pt') then lang = 'Q5146' elseif (lang == 'en') then lang = 'Q1860' else lang = invertedlangcodes[lang] end if claim.qualifiers and claim.qualifiers.P407 then if wd.hasQualifier(claim, {'P407'}, {lang}) then return true else return false end end return true -- se não conhecemos a língua, condicção que é bom end local function firstVals(claims, numval) -- retornar os primeiros valores numéricos da tabela de declarações local numval = tonumber(numval) or 0 -- raise a error if numval is not a positive integer ? if not claims then return nil end while (#claims > numval) do table.remove(claims) end return claims end local function timeFromQualifs(claim, qualifs) local claimqualifs = claim.qualifiers if not claimqualifs then return nil end for i, qualif in pairs(qualifs or timequalifiers) do local vals = claimqualifs[qualif] if vals and (vals[1].snaktype == 'value') then return vals[1].datavalue.value.time end end end local function atDate(claim, mydate) if mydate == "today" then mydate = os.date("!%Y-%m-%dT%TZ") end local newclaims = {} local mindate = timeFromQualifs(claim, {'P580'}) local maxdate = timeFromQualifs(claim, {'P582'}) if modules.formatDate.before(mydate, mindate) and modules.formatDate.before(maxdate, mydate) then return true end end local function check(claim, condition) if type(condition) == 'function' then -- cas standard return condition(claim) end return formatError('invalid type', 'function', type(condition)) end local function minPrecision(claim, minprecision) local snack if claim.qualifiers then -- se uma data é dada como um qualificador, é aquela que é usada preferencialmente em handsnak for i, j in ipairs(datequalifiers) do if claim.qualifiers[j] then snack = claim.qualifiers[j][1] break end end end if not snack then snack = claim.mainsnak or claim end if (snack.snaktype == 'value') and (snack.datatype == 'time') and (snack.datavalue.value.precision < minprecision) then return false end return true end function wd.sortClaims(claims, sorttype) if not claims then return nil end if sorttype == 'chronological' then return wd.chronoSort(claims) elseif sorttype == 'inverted' then return wd.chronoSort(claims, true) elseif sorttype == 'order' then return wd.chronoSort(claims) elseif type(sorttype) == 'function' then table.sort(claims, sorttype) return claims end return claims end local alreadyFiltered = false function wd.filterClaims(claims, args) --remova das tabelas de reclamação aquelas removidas por um dos filtros na tabela de filtros local function filter(condition, filterfunction, funargs) if not args[condition] then return end for i = #claims, 1, -1 do if not( filterfunction(claims[i], args[funargs[1]], args[funargs[2]], args[funargs[3]]) ) then table.remove(claims, i) end end end filter('isinlang', isInLanguage, {'isinlang'} ) filter('excludespecial', notSpecial, {} ) filter('condition', check, {'condition'} ) if claims[1] and claims[1].mainsnak then filter('targetvalue', hasTargetValue, {'targetvalue'} ) filter('atdate', atDate, {'atdate'} ) filter('qualifier', wd.hasQualifier, {'qualifier', 'qualifiervalue'} ) filter('excludequalifier', excludeQualifier, {'excludequalifier', 'excludequalifiervalue'} ) filter('withsource', hasSource, {'withsource', 'sourceproperty'} ) filter('withdate', hasDate, {} ) filter('excludevalues', excludeValues, {'excludevalues'}) filter('withlink', hasLink, {'withlink', 'linklang'} ) filter('minprecision', minPrecision, {'minprecision'} ) claims = withRank(claims, args.rank or 'best') end if args.sorttype then claims = wd.sortClaims(claims, args.sorttype) end if #claims == 0 then return nil end if args.numval then claims = firstVals(claims, args.numval) end return claims end function wd.loadEntity(entity, cache) if type(entity) ~= 'table' then if cache then if not cache[entity] then cache[entity] = mw.wikibase.getEntity(entity) mw.log("cached") end return cache[entity] else if entity == '' or (entity == '-') then entity = nil end return mw.wikibase.getEntity(entity) end else return entity end end function wd.getClaims( args ) -- returns a table of the claims matching some conditions given in args if args.claims then -- if claims have already been set, return them return args.claims end local properties = args.property if type(properties) == 'string' then properties = wd.splitStr(string.upper(args.property)) end if not properties then return formatError( 'property-param-not-provided' ) end --Get entity local entity = args.entity if type(entity) == 'string' then if entity == '' then entity = nil end elseif type(entity) == 'table' then entity = entity.id end if (not entity) then entity = mw.wikibase.getEntityIdForCurrentPage() end if (not entity) or (entity == '-') then return nil end local claims = {} if #properties == 1 then claims = mw.wikibase.getAllStatements(entity, properties[1]) -- do not use mw.wikibase.getBestStatements at this stage, as it may remove the best ranked values that match other criteria in the query else for i, prop in ipairs(properties) do local newclaims = mw.wikibase.getAllStatements(entity, prop) if newclaims and #newclaims > 0 then for j, claim in ipairs(newclaims) do table.insert(claims, claim) end end end end if (not claims) or (#claims == 0) then return nil end return wd.filterClaims(claims, args) end --=== ENTITY FORMATTING === function wd.getLabel(entity, lang, labelformat) if (not entity) then return nil -- ou option de gestion des erreurs ? end lang = lang or defaultlang if type(labelformat) == 'function' then return labelformat(entity) end if (type(entity) == 'string') and (lang == defaultlang) then -- o mais economico local str = mw.wikibase.label(entity) if str then -- mw.wikibase.label() ne fonctionne pas avec les redirect https://phabricator.wikimedia.org/T157868 return str end end if type(entity) == 'string' then entity = mw.wikibase.getEntity(entity) end if entity and entity.labels and entity.labels[lang] then return entity.labels[lang].value, true end end function wd.formatEntity( entity, params ) if (not entity) then return nil --formatError('entity-not-found') end local id = entity if type(id) == 'table' then id = id.id end params = params or {} local lang = params.lang or params.idioma or defaultlang local speciallabels = params.speciallabels local displayformat = params.displayformat local labelformat = params.labelformat local defaultlabel = params.defaultlabel or id local linktype = params.link local defaultlink = params.defaultlink local defaultlinkquery = params.defaultlinkquery if speciallabels and speciallabels[id] then --speciallabels override the standard label + link combination return speciallabels[id] end if params.displayformat == 'raw' then return id end local link, label local str = '' -- o texto completo a devolver label = wd.getLabel(entity, lang, labelformat) -- determinar se o item está ou não sendo renderizado na página de seu artigo local rendering_entity_on_its_page = wd.isPageOfQId(id) if not label then if (defaultlabel == '-') then return nil end link = wd.siteLink(id, 'wikidata') return str .. '[[' .. link .. '|' .. id .. ']]' .. addCat(modules.i18n['to translate']) -- se não houver rótulo, colocamos um link para o Wikidata para entender o que ele se refere end if (linktype == '-') or rendering_entity_on_its_page then return str .. label end local link = wd.siteLink(entity, linktype, lang) -- defaultlinkquery will try to link to another page on this Wiki if (not link) and defaultlinkquery then if type(defaultlinkquery) == 'string' then defaultlinkquery = {property = defaultlinkquery} end defaultlinkquery.excludespecial = true defaultlinkquery.entity = entity local claims = wd.getClaims(defaultlinkquery) if claims then for i, j in pairs(claims) do local id = wd.getMainId(j) link = wd.siteLink(id, linktype, lang) if link then break end end end end if link then return str .. '[[' .. link .. '|' .. label .. ']]' end -- if not link, you can use defaultlink: a sidelink to another Wikimedia project if (not defaultlink) then defaultlink = {'enwiki'} end if defaultlink and (defaultlink ~= '-') then local linktype local sidelink, site, langcode if type(defaultlink) == 'string' then defaultlink = {defaultlink} end for i, j in ipairs(defaultlink) do sidelink, site, langcode = wd.siteLink(entity, j, lang) if sidelink then break end end if not sidelink then sidelink, site = wd.siteLink(entity, 'wikidata') end local icon, class, title = site, nil, nil -- le texte affiché du lien if site == 'wiki' then icon, class, title = langcode, "indicateur-langue", wd.translate('see-another-language', mw.language.fetchLanguageName(langcode, defaultlang)) elseif site == 'wikidata' then icon, class, title = 'd', "indicateur-langue", wd.translate('see-wikidata') else title = wd.translate('see-another-project', site) end local val = '[[' .. sidelink .. '|' .. '<span class = "' .. (class or '').. '" title = "' .. (title or '') .. '">' .. icon .. '</span>]]' return str .. label .. '<small> (' .. val .. ')</small>' end return str .. label end function wd.addTrackingCat(prop, cat) -- às vezes deve ser chamado por outros módulos if type(prop) == 'table' then prop = prop[1] -- deve logicamente adicioná-los todos end if not prop and not cat then return formatError("property-param-not-provided") end if not cat then cat = wd.translate('trackingcat', prop or 'P??') end return addCat(cat ) end local function unknownValue(snak, label) local str = label if type(str) == "function" then str = str(snak) end if (not str) then if snak.datatype == 'time' then str = wd.translate('sometime') else str = wd.translate('somevalue') end end if type(str) ~= "string" then return formatError(snak.datatype) end return str end local function noValue(displayformat) if not displayformat then return wd.translate('novalue') end if type(displayformat) == 'string' then return displayformat end return formatError() end local function getLangCode(entityid) return modules.langcodes[tonumber(entityid:sub(2))] end local function showLang(statement) -- retorna o código do idioma entre parênteses antes do valor (por exemplo, para bibliotecas e links externos) local mainsnak = statement.mainsnak if mainsnak.snaktype ~= 'value' then return nil end local langlist = {} if mainsnak.datavalue.type == 'monolingualtext' then langlist = {mainsnak.datavalue.value.language} elseif (not statement.qualifiers) or (not statement.qualifiers.P407) then return else for i, j in pairs( statement.qualifiers.P407 ) do if j.snaktype == 'value' then local langentity = wd.getId(j) local langcode = getLangCode(langentity) table.insert(langlist, langcode) end end end if (#langlist > 1) or (#langlist == 1 and langlist[1] ~= defaultlang) then -- se é em português, não precisa dizer return modules.langmodule.indicationMultilingue(langlist) end end -- === DATE HANDLING === function wd.addStandardQualifs(str, statement) if (not statement) or (not statement.qualifiers) then return str end if not str then return error()-- what's that ? end if statement.qualifiers.P1480 then for i, j in pairs(statement.qualifiers.P1480) do local v = wd.getId(j) if (v == "Q21818619") then str = wd.translate('approximate-place', str) elseif (v == "Q18122778") or (v == "Q18912752") then str = wd.translate('uncertain-information', str) elseif (v == "Q5727902") then if (statement.mainsnak.datatype == 'time') then str = modules.formatDate.fuzzydate(str) else str = wd.translate('approximate-value', str) end end end end return str end local function rangeObject(begin, ending, params) --[[ objeto com um timestamp para a classificação cronológica e dois objetos date (inicial e final) ]]-- local timestamp if begin then timestamp = begin.timestamp else timestamp = ending.timestamp end return {begin = begin, ending = ending, timestamp = timestamp, type = 'rangeobject'} end local function dateObject(orig, params) --[[ transforma um snak em um novo objeto utilizável pelo módulo: data complexa {type = 'dateobject', timestamp = str, era = '+' ou '-', year = number, month = number, day = number, calendar = calendar} ]]-- if not params then params = {} end local newobj = modules.formatDate.splitDate(orig.time, orig.calendarmodel) newobj.precision = params.precision or orig.precision newobj.type = 'dateobject' return newobj end local function objectToText(obj, params) if obj.type == 'dateobject' then return modules.formatDate.simplestring(obj, params) elseif obj.type == 'rangeobject' then return modules.formatDate.daterange(obj.begin, obj.ending, params) end end local function getDateFromQualif(statement, qualif) if (not statement) or (not statement.qualifiers) or not (statement.qualifiers[qualif]) then return nil end local v = statement.qualifiers[qualif][1] if v.snaktype ~= 'value' then -- que fazer neste caso ? return nil end return dateObject(v.datavalue.value) end function wd.getDate(statement) local period = getDateFromQualif(statement, 'P585') -- devolver um dateobject if period then return period end local begin, ending = getDateFromQualif(statement, 'P580'), getDateFromQualif(statement, 'P582') if begin or ending then return rangeObject(begin, ending) -- retourne un rangeobject fait de deux dateobject end end function wd.getFormattedDate(statement, params) if not statement then return nil end local str --procure a data com os qualificadores P580/P582 local datetable = wd.getDate(statement) if datetable then str = objectToText(datetable, params) end -- se estão no limite interno / superior if not str then local start, ending = getDateFromQualif(statement, 'P1319'), getDateFromQualif(statement, 'P1326') str = modules.formatDate.between(start, ending, params) end -- caso contrário, o handsnak, para os dados do tipo de tempo if (not str) and (statement.mainsnak.datatype == 'time') then local mainsnak = statement.mainsnak if (mainsnak.snaktype == 'value') or (mainsnak.snaktype == 'somevalue') then str = wd.formatSnak(mainsnak, params) end end if str and params and (params.addstandardqualifs ~= '-') then str = wd.addStandardQualifs(str, statement) end return str end -- Função que ordena as reivindicações de tempo em ordem cronológica -- Uma chave de classificação chamada "dateSortKey" é adicionada a cada declaração. -- Se as chaves de classificação desse nome já existirem, elas serão usadas sem modificação. function wd.chronoSort( claims, inverted ) for _, claim in ipairs( claims ) do if not claim.dateSortKey then local snack = claim.mainsnak or claim local iso if (snack.snaktype == 'value') and (snack.datatype == 'time') then iso = snack.datavalue.value.time else iso = timeFromQualifs(claim, datequalifiers) or '0' end -- transformação em número (indicação da base porque gsub retorna dois valores) iso = tonumber( iso:gsub( '(%d)%D', '%1' ), 10 ) claim.dateSortKey = iso end end table.sort( claims, function ( c1, c2 ) if inverted then return c2.dateSortKey < c1.dateSortKey end return c1.dateSortKey < c2.dateSortKey end ) return claims end -- =================== function wd.getReferences(statement) local refdata = statement.references if not refdata then return nil end local refs = {} for i, ref in pairs(refdata) do local s local function hasValue(prop) -- checks that the prop is here with valid value if ref.snaks[prop] and ref.snaks[prop][1].snaktype == 'value' then return true end return false end if ref.snaks.P248 then for j, source in pairs(ref.snaks.P248) do if source.snaktype == 'value' then local page, accessdate if hasValue('P304') then page = wd.formatSnak(ref.snaks.P304[1]) end if hasValue('P813') then accessdate = wd.formatSnak(ref.snaks.P813[1]) end s = modules.reference.citeitem(wd.getId(source), {['page'] = page, ['accessdate'] = accessdate}) table.insert(refs, s) end end elseif hasValue('P854') then -- P854: reference url local url, title, author, publisher, accessdate, publishdate, publishlang url = wd.formatSnak(ref.snaks.P854[1], {text = "-"}) if hasValue('P1476') then title = wd.formatSnak(ref.snaks.P1476[1]) else title = mw.ustring.gsub(url, '^[Hh][Tt][Tt][Pp]([Ss]?):(/?)([^/])', 'http%1://%3') end --todo : handle multiple values for author, etc. if hasValue('P813') then accessdate = wd.formatSnak(ref.snaks.P813[1]) end if hasValue('P50') then -- author (item type) author = wd.formatSnak(ref.snaks.P50[1]) elseif hasValue('P2093') then -- author (string type) author = wd.formatSnak(ref.snaks.P2093[1]) end if hasValue('P123') then publisher = wd.formatSnak(ref.snaks.P123[1]) end -- publishdate avec P577 ? (ne semble pas vraiment correspondre) if hasValue('P407') then local id = wd.getId(ref.snaks.P407[1]) publishlang = getLangCode(id) end s = modules.cite.citarweb{titulo = title, url = url, autor = author, editor = publisher, idioma = publishlang, ['data'] = publishdate, ['acesso-data'] = accessdate} table.insert(refs, s) elseif ref.snaks.P854 and ref.snaks.P854[1].snaktype == 'value' then s = wd.formatSnak(ref.snaks.P854[1], {text = "-"}) table.insert(refs, s) end end if #refs > 0 then return refs end end function wd.sourceStr(sources) if not sources or (#sources == 0) then return nil end for i, j in pairs(sources) do sources[i] = mw.getCurrentFrame():extensionTag( "ref", j) end return table.concat(sources, '<sup class="reference" style= "padding-left: 0; padding-right: 1px;">,</sup>') end function wd.getDataValue(snak, params) if not params then params = {} end local speciallabels = params.speciallabels -- às vezes precisamos fazer uma lista de elementos para os quais o rótulo deve ser alterado, não é muito prático usar uma função para isso if snak.snaktype ~= 'value' then return nil end local datatype = snak.datatype local value = snak.datavalue.value local displayformat = params.displayformat if type(displayformat) == 'function' then return displayformat(snak, params) end if datatype == 'wikibase-item' then return wd.formatEntity(wd.getId(snak), params) end if datatype == 'url' then return modules.weblink.makelink(value, params.text) end if datatype == 'math' then return mw.getCurrentFrame():extensionTag( "math", value) end if (datatype == 'string') or (datatype == 'external-id') or (datatype == 'commonsMedia') then -- todos os dados da string, exceto "math" if params.urlpattern then local urlpattern = params.urlpattern if type(urlpattern) == 'function' then urlpattern = urlpattern(value) end local url = mw.ustring.gsub(urlpattern, '$1', (value:gsub('%%', '%%%%'))):gsub(' ', '%%20') value = '[' .. url .. ' ' .. (params.texto or value) .. ']' end return value end if datatype == 'time' then -- format example: +00000001809-02-12T00:00:00Z if displayformat == 'raw' then return value.time else local dateobject = dateObject(value, {precision = params.precision}) return objectToText(dateobject, params) end end if datatype == 'globe-coordinate' then -- retorna uma tabela com as chaves latitude, longitude, precisão e globo para formatar por outro módulo (para alterar?) if displayformat == 'latitude' then return value.latitude elseif displayformat == 'longitude' then return value.longitude else local coordvalue = mw.clone( value ) coordvalue.globe = modules.globes[value.globe] -- transforma o globo ID em nome inglês utilizável por geohack return coordvalue --nota : As coordenadas do Wikidata podem ser usadas em Módulo:Coordenadas. Devemos também permitir chamar Módulo:Coordenadores aqui? end end if datatype == 'quantity' then -- todo : gerenciar os parâmetros precisão local amount, unit = value.amount, value.unit if unit then unit = unit:match('Q%d+') end local raw if displayformat == "raw" then raw = true end return modules.formatNum.displayvalue(amount, unit, {targetunit = params.targetunit, raw = raw, rounding = params.rounding, showunit = params.showunit or 'short', showlink = params.showlink} ) end if datatype == 'monolingualtext' then if value.language == defaultlang then return value.text else return modules.langmodule.lingua({value.language, value.text}) end end return formatError('unknown-datavalue-type' ) end function wd.stringTable(args) -- like getClaims, but get a list of string rather than a list of snaks, for easier manipulation local claims = args.claims if not claims then claims = wd.getClaims(args) end if not claims or claims == {} then return nil end local props = {} -- lista de propriedades associadas a cada string para categorização e linkback for i, j in pairs(claims) do claims[i] = wd.formatStatement(j, args) table.insert(props, j.mainsnak.property) end if args.removedupes and (args.removedupes ~= '-') then claims = wd.addNewValues({}, claims) -- também deve remover dos adereços aqueles que não são usados end return claims, props end function wd.getQualifiers(statement, qualifs, params) if not statement.qualifiers then return nil end local vals = {} if type(qualifs) == 'string' then qualifs = wd.splitStr(qualifs) end for i, j in pairs(qualifs) do if statement.qualifiers[j] then for k, l in pairs(statement.qualifiers[j]) do table.insert(vals, l) end end end if #vals == 0 then return nil end return vals end function wd.getFormattedQualifiers(statement, qualifs, params) if not params then params = {} end local qualiftable = wd.getQualifiers(statement, qualifs) if not qualiftable then return nil end qualiftable = wd.filterClaims(qualiftable, params) or {} for i, j in pairs(qualiftable) do qualiftable[i] = wd.formatSnak(j, params) end return modules.linguistic.conj(qualiftable, params.conjtype) end function wd.showQualifiers(str, statement, args) local qualifs = args.showqualifiers if not qualifs then return str -- or error ? end if type(qualifs) == 'string' then qualifs = wd.splitStr(qualifs) end local qualifargs = args.qualifargs or {} -- qualificadores de formatação = args começando com "qual", ou alternativamente, o mesmo que para o valor principal qualifargs.displayformat = args.qualifdisplayformat or args.displayformat qualifargs.labelformat = args.qualiflabelformat or args.labelformat qualifargs.link = args.qualiflink or args.link qualifargs.conjtype = args.qualifconjtype local formattedqualifs = wd.getFormattedQualifiers(statement, qualifs, qualifargs) if formattedqualifs and str then str = str .. modules.linguistic.inparentheses(formattedqualifs, defaultlang) end return str end function wd.formatSnak( snak, params ) if not params then params = {} end -- para facilitar a chamada de outros módulos if snak.snaktype == 'somevalue' then return unknownValue(snak, params.unknownlabel) elseif snak.snaktype == 'novalue' then return noValue(params.novaluelabel) elseif snak.snaktype == 'value' then return wd.getDataValue( snak, params) else return formatError( 'unknown-snak-type' ) end end function wd.formatStatement( statement, args ) if not args then args = {} end if not statement.type or statement.type ~= 'statement' then return formatError( 'unknown-claim-type' ) end local prop = statement.mainsnak.property local str -- special displayformat f if args.statementformat and (type(args.statementformat) == 'function') then str = args.statementformat(statement, args) elseif (statement.mainsnak.datatype == 'time') and (statement.mainsnak.dateformat ~= '-') then if args.displayformat == 'raw' and statement.mainsnak.snaktype == 'value' then str = statement.mainsnak.datavalue.value.time else str = wd.getFormattedDate(statement, args) end elseif args.showonlyqualifier and (args.showonlyqualifier ~= '') then str = wd.getFormattedQualifiers(statement, args.showonlyqualifier, args) if not str then return nil end if args.addstandardqualifs ~= '-' then str = wd.addStandardQualifs(str, statement) end else str = wd.formatSnak( statement.mainsnak, args ) if args.addstandardqualifs ~= '-' then str = wd.addStandardQualifs(str, statement) end end -- ajouts divers if args.showlang == true then local indicateur = showLang(statement) if indicateur then str = indicateur .. ' ' .. str end end if args.showqualifiers then str = wd.showQualifiers(str, statement, args) end if args.showdate then -- when "showdate and chronosort are both set, date retrieval is performed twice local period = wd.getFormattedDate(statement, args, "-") -- 3 arguments indicate the we should not use additional qualifiers, alrady added by wd.formatStatement if period then str = str .. " <small>(" .. period ..")</small>" end end if args.showsource then local sources = wd.getReferences(statement) if sources then local source = wd.sourceStr(sources) str = str .. (source or "") end end return str end function wd.addLinkBack(str, id, property) if not id then id = wd.getEntity() end if not id then return str end if type(property) == 'table' then property = property[1] end if type(id) == 'table' then id = id.id end local class = '' if property then class = 'wd_' .. string.lower(property) end local icon = '[[File:Blue pencil.svg|%s|10px|baseline|class=noviewer|link=%s]]' local title = wd.translate('see-wikidata-value') local url = mw.uri.fullUrl('d:' .. id, 'uselang=pt') url.fragment = property -- adicionar um parâmetro #âncora if "property" definido url = tostring(url) local v = mw.html.create('span') :addClass(class) :wikitext(str) :tag('span') :addClass('noprint wikidata-linkback') :css('padding-left', '0.5em') :wikitext(icon:format(title, url)) :allDone() return tostring(v) end function wd.addRefAnchor(str, id) --[[ Insere uma âncora para uma referência gerada a partir de um elemento wd. O id Wikidata serve como um identificador para a âncora, para usar em modelos como "harvsp" --]] return tostring( mw.html.create('span') :attr('id', id) :attr('class', "livro") :wikitext(str) ) end --=== FUNCTIONS USING AN ENTITY AS ARGUMENT === local function formatStatementsGrouped(args, type) -- grupos afirmações com o mesmo valor em handsnak, mas diferentes qualificadores -- (somente para propriedades de tipo de elemento) local claims = wd.getClaims(args) if not claims then return nil end local groupedClaims = {} -- afirmações de grupo pelo valor mainsnak local function addClaim(claim) local id = wd.getMainId(claim) for i, j in pairs(groupedClaims) do if (j.id == id) then table.insert(groupedClaims[i].claims, claim) return end end table.insert(groupedClaims, {id = id, claims = {claim}}) end for i, claim in pairs(claims) do addClaim(claim) end local stringTable = {} -- instruções ad hoc para configurações para formatar um retorno individual local funs = { {param = "showqualifiers", fun = function(str, claims) local qualifs = {} for i, claim in pairs(claims) do local news = wd.getFormattedQualifiers(claim, args.showqualifiers, args) if news then table.insert(qualifs, news) end end local qualifstr = modules.linguistic.conj(qualifs, "qualif-separator") if not qualifstr then return str end return str .. " " .. modules.linguistic.inparentheses(qualifstr) end }, {param = "showdate", fun = function(str, claims) -- todas as datas são agrupadas dentro dos mesmos parênteses ex "medalha de ouro (1922, 1924)" local dates = {} for i, statement in pairs(claims) do local s = wd.getFormattedDate(statement, args, true) if statement then table.insert(dates, s) end end local datestr = modules.linguistic.conj(dates) if not datestr then return str end return str .. "<small>" .. modules.linguistic.inparentheses(datestr) .. "</small>" end }, {param = "showsource", fun = function(str, claims) -- as fontes são todas exibidas no mesmo lugar, no final -- se duas instruções tiverem a mesma fonte, ela será exibida apenas uma vez local sources = {} local function dupeRef(old, new) for i, j in pairs(old) do if j == new then return true end end end for i, claim in pairs(claims) do local refs = wd.getReferences(claim) if refs then for i, j in pairs(refs) do if not dupeRef(sources, j) then table.insert(sources, j) end end end end return str .. (wd.sourceStr(sources) or "") end } } for i, group in pairs(groupedClaims) do -- bricolage para usar os argumentos de formatStatements local str = wd.formatEntity(group.id, args) for i, fun in pairs(funs) do if args[fun.param] then str = fun.fun(str, group.claims, args) end end table.insert(stringTable, str) end args.valuetable = stringTable return wd.formatStatements(args) end function wd.formatStatements( args )--Format statement and concat them cleanly if args.value == '-' then return nil end local valueexpl = wd.translate("activate-query") --If a value is already set, use it if args.value and (args.value ~= '') and (args.value ~= valueexpl) then return args.value end -- if args.expl: return something only one if explicitly required in Wikitext if args.expl and (args.value ~= valueexpl) then return nil end args.entity = wd.getEntity(args.entity) if args.grouped and args.grouped ~= '' then args.grouped = false return formatStatementsGrouped(args) end local valuetable = args.valuetable -- dans le cas où les valeurs sont déjà formtées local props -- as propriedades realmente usadas (em alguns casos, nem todas de args.property if not valuetable then -- cas le plus courant valuetable, props = wd.stringTable(args) end local str = modules.linguistic.conj(valuetable, args.conjtype) if not str then return nil end if not props then props = wd.splitStr(args.property)[1] end if args.ucfirst == '-' then str = modules.linguistic.lcfirst(str) end if args.addcat and (args.addcat ~= '-') then str = str .. wd.addTrackingCat(props) end --if args.linkback and (args.linkback ~= '-') then -- str = wd.addLinkBack(str, args.entity, props) --end if args.returnnumberofvalues then return str, #valuetable end return str end function wd.formatAndCat(args) if not args then return nil end --args.linkback = args.linkback or true args.addcat = true if args.value then -- do not ignore linkback and addcat, as formatStatements do if args.value == '-' then return nil end local val = args.value .. wd.addTrackingCat(args.property) --val = wd.addLinkBack(val, args.entity, args.property) return val end return wd.formatStatements( args ) end function wd.getTheDate(args) local claims = wd.getClaims(args) if not claims then return nil end local formattedvalues = {} for i, j in pairs(claims) do local v = wd.getFormattedDate(j, args) if v then table.insert(formattedvalues, v ) end end local val = modules.linguistic.conj(formattedvalues) if not val then return nil end if args.addcat == true then val = val .. wd.addTrackingCat(args.property) end val = wd.addLinkBack(val, args.entity, args.property) return val end function wd.keyDate (event, item, params) params = params or {} params.entity = item if type(event) == 'table' then for i, j in pairs(event) do params.targetvalue = nil -- reinicialização bárbara dos parâmetros modificados local s = wd.keyDate(j, item, params) if s then return s end end elseif type(event) ~= 'string' then return formatError('invalid-datatype', type(event), 'string') elseif string.sub(event, 1, 1) == 'Q' then -- pedimos um elemento usado em P: P793 (evento chave) params.property = 'P793' params.targetvalue = event params.addcat = params.addcat or true return wd.getTheDate(params) elseif string.sub(event, 1, 1) == 'P' then -- pedimos uma propriedade params.property = event return wd.formatAndCat(params) else return formatError('invalid-entity-id', event) end end function wd.mainDate(entity) -- essaye P580/P582 local args = {entity = entity, addcat = true} args.property = 'P580' local startpoint = wd.formatStatements(args) args.property = 'P582' local endpoint = wd.formatStatements(args) local str if (startpoint or endpoint) then str = modules.formatDate.daterange(startpoint, endpoint, params) str = wd.addLinkBack(str, entity, 'P582') return str end -- défaut : P585 args.property = {'P585', 'P571'} args.linkback = true return wd.formatStatements(args) end -- === FUNCTIONS FOR TRANSITIVE PROPERTIES === function wd.getIds(item, query) query.excludespecial = true query.displayformat = 'raw' query.entity = item query.addstandardqualifs = '-' return wd.stringTable(query) end -- recursively adds a list of qid to an existing list, based on the results of a query function wd.addVals(list, query, maxdepth, maxnodes, stopval) maxdepth = tonumber(maxdepth) or 10 maxnodes = tonumber(maxnodes) or 100 if (maxdepth < 0) then return list end if stopval and wd.isHere(list, stopval) then return list end local origsize = #list for i = 1, origsize do -- tried a "checkpos" param instead of starting to 1 each time, but no impact on performance local candidates = wd.getIds(list[i], query) list = wd.addNewValues(list, candidates, maxnodes, stopval) if list[#list] == stopval then return list end if #list >= maxnodes then return list end end if (#list == origsize) then return list end return wd.addVals(list, query, maxdepth - 1, maxnodes, stopval, origsize + 1) end -- returns a list of items transitively matching a query (orig item is not included in the list) function wd.transitiveVals(item, query, maxdepth, maxnodes, stopval) maxdepth = tonumber(maxdepth) or 5 if type(query) == "string" then query = {property = query} end -- récupération des valeurs local vals = wd.getIds(item, query) if not vals then return nil end local v = wd.addVals(vals, query, maxdepth - 1, maxnodes, stopval) if not v then return nil end -- réarrangement des valeurs if query.valorder == "inverted" then local a = {} for i, j in pairs(v) do table.insert(a, 1, j) end v = a end return v end -- returns true if an item is the value of a query, transitively function wd.inTransitiveVals(searchedval, sourceval, query, maxdepth, maxnodes ) local vals = wd.transitiveVals(sourceval, query, maxdepth, maxnodes, searchedval ) if (not vals) then return false end for _, val in ipairs(vals) do if (val == searchedval) then return true end end return false end -- returns true if an item is a superclass of another, based on P279 function wd.isSubclass(class, item, maxdepth) local query = {property = 'P279'} if class == item then -- item is a subclass of itself iff it is a class if wd.getIds(item, query) then return true end return false end return wd.inTransitiveVals(class, item, query, maxdepth ) end -- returns true if one of the best ranked P31 values of an item is the target or a subclass of the target -- rank = 'valid' would seem to make sense, but it would need to check for date qualifiers as some P31 values have begin or end date function wd.isInstance(targetclass, item, maxdepth) maxdepth = maxdepth or 10 local directclasses = wd.transitiveVals(item, {property = 'P31'}, 1) if not directclasses then return false end for i, class in pairs(directclasses) do if wd.isSubclass(targetclass, class, maxdepth - 1) then return true end end return false end -- return the first value in a transitive query that belongs to a particular class. For instance find a value of P131 that is a province of Canada function wd.findVal(sourceitem, targetclass, query, recursion, instancedepth) if type(query) == "string" then query = {property = query} end local candidates = wd.getIds(sourceitem, query) if candidates then for i, j in pairs(candidates) do if wd.isInstance(targetclass, j, instancedepth) then return j end end if not recursion then recursion = 3 else recursion = recursion - 1 end if recursion < 0 then return nil end for i, candidate in pairs(candidates) do return wd.findVal(candidate, targetclass, query, recursion, instancedepth) end end end -- === VARIA === function wd.getDescription(entity, lang) lang = lang or defaultlang local description if lang == defaultlang then return mw.wikibase.descriptionl(qid) end if not entity.descriptions then return wd.translate('no description') end local descriptions = entity.descriptions if not descriptions then return nil end if descriptions[lang] then return descriptions[delang].value end return entity.id end function wd.Dump(entity) entity = wd.getEntity(entity) if not entity then return formatError("entity-param-not-provided") end return "<pre>"..mw.dumpObject(entity).."</pre>" end function wd.frameFun(frame) local args = frame.args local funname = args[1] table.remove(args, 1) return wd[funname](args) end return wd