Módulo:Propriedade Wikidata
Este módulo pode ter sua documentação deficiente. Por favor, documente-o ou melhore suas explicações caso o saiba usar ou tenha conhecimentos para tal. |
wd = {}
-- Carrega as propriedades da página no Wikidata, se existir e não tiverem já sido carregadas
if not wdEntity then
wdEntity = mw.wikibase.getEntity()
end
wd.props = wdEntity and wdEntity['claims']
-- Tabela para dados temporários, mantidos durante o processamento de uma propriedade
wd.temp = {}
-- Tabela para adicionar a data de cada propriedade, quando existir
wd.data = {}
meses = {['01']='janeiro', ['02']='fevereiro', ['03']='março', ['04']='abril', ['05']='maio', ['06']='junho',
['07']='julho', ['08']='agosto', ['09']='setembro', ['10']='outubro', ['11']='novembro', ['12']='dezembro' }
-- Processa a propriedade ou qulificador pedido
wd.dados = function (prop)
local p = {}
local lingua = params and params['língua']
for i in string.gmatch(prop, '[^:]+') do
table.insert(p, i)
end
if not p[1] or not string.match(p[1], '^P%d+$') then
wd.temp.debug = 'não é uma propriedade'
return nil
end
if ext and ext.prop and ext.prop[p[1]] then
wd.temp.debug = 'processada pela extensão'
return ext.prop[p[1]](prop)
end
prop = wd.props and wd.props[p[1]]
if not prop then
wd.temp.debug = 'item não tem essa propriedade'
return nil
end
local snak
local arg = 2
if p[2] then
if string.match(p[2], '^P%d+$') then
local qprop = p[2] -- qualificador da propriedade (Pq)
for _, propn in ipairs(prop) do
if propn['qualifiers'] and propn['qualifiers'][qprop] then
if p[3] and string.match(p[3], '^Q%d+$') then
-- P:Pq:Qq
local qvalue = p[3] -- valor do qualificador (Qq)
for i, v in ipairs(propn['qualifiers'][qprop]) do
if v['datatype'] == 'wikibase-item' and v['datavalue']['value']['id'] == qvalue then
snak = propn['mainsnak']
arg = 4
break
end
end
else
-- P:Pq
snak = propn['qualifiers'][qprop][1]
arg = 3
break
end
end
end
elseif string.match(p[2], '^Q%d+$') then
local pvalue = p[2] -- valor da propriedade (Qv)
if p[3] and string.match(p[3], '^P%d+$') then
-- P:Qv:Pq
local qprop = p[3] -- qualificador (Pq)
for _, propn in ipairs(prop) do
if propn['mainsnak']['datatype'] == 'wikibase-item' and
propn['mainsnak']['datavalue']['value']['id'] == pvalue then
if propn['qualifiers'] and propn['qualifiers'][qprop] then
snak = propn['qualifiers'][qprop][1]
arg = 4
else
wd.temp.debug = 'esse valor não possui o qualificador ' .. qprop
return nil
end
end
end
else
wd.temp.debug = 'faltou o qualificador (último P do formato P:Qv:Pq)'
return nil
end
end
end
if arg == 2 and p[2] == 'lista' then
-- Listar todos valores de uma propriedade
local lista = {}
for _, propn in ipairs(prop) do
local datatype = propn['mainsnak']['datatype']
wd.temp.tipo = datatype
if datatype == 'string' or datatype == 'external-id' then
table.insert(lista, propn['mainsnak']['datavalue']['value'])
elseif datatype == 'wikibase-item' then
if not propn['mainsnak']['datavalue'] then
wd.temp.debug = 'propriedade não possui valor'
return nil
end
local qid = propn['mainsnak']['datavalue']['value']['id']
local labels = mw.wikibase.getEntity(qid)['labels']
if lingua and labels[lingua] then
table.insert(lista, labels[lingua])
else
local pt, en, outra
for lang, v in pairs(labels) do
if lang == 'pt' then
pt = v['value']
break
elseif lang == 'pt-br' then
pt = v['value']
elseif lang == 'en' then
en = v['value']
elseif lang == 'es' or lang == 'fr' or lang == 'it' then -- outras linguas latinas
outra = v['value']
end
end
if pt or en or outra then
table.insert(lista, pt or en or outra)
end
end
else
wd.temp.debug = 'esse tipo de dados em pt, pt-br, en, es, fr, ou it'
return nil
end
end
if #lista > 1 then
wd.temp.debug = 'lista com ' .. #lista .. ' valores'
return table.concat(lista, ', ', 1, #lista - 1) .. ' e ' .. lista[#lista]
elseif #lista == 1 then
wd.temp.debug = 'lista só possui um valor'
return lista[1]
else
wd.temp.debug = 'não possui nenhum valor que pode ser retornado'
return nil
end
end
-- Escolher só um valor para retornar
if not snak then
if prop[1]['mainsnak']['datatype'] == 'monolingualtext' then
wd.temp.tipo = 'monolingualtext' -- A FAZER: escolher língua
local i = 1
for n, v in pairs(prop) do
if v['mainsnak']['datavalue']['value']['language'] == 'pt' then
i = n
break
elseif v['mainsnak']['datavalue']['value']['language'] == 'pt-br' then
i = n
end
end
wd.temp.debug = 'valor obtido'
return prop[i]['mainsnak']['datavalue']['value']['text']
else
local mostrecent = ""
for _, propn in ipairs(prop) do
local time
if arg == 2 and propn['qualifiers'] and propn['qualifiers']['P585'] then
time = propn['qualifiers']['P585'][1]['datavalue']['value']['time']
if time > mostrecent then
mostrecent = time
wd.data[p[1]] = time
snak = propn['mainsnak']
end
end
if propn['rank'] == 'preferred' then
snak = propn['mainsnak']
wd.data[p[1]] = time
break
elseif not snak and propn['rank'] == 'normal' then
snak = propn['mainsnak']
end
end
end
end
local args = {}
for n, a in ipairs(p) do
if n >= arg then
table.insert(args, p[n])
end
end
if snak then
return wd.valorsnak(snak, args)
end
end
-- Retorna o valor de um snak de acordo com o tipo de dado
wd.valorsnak = function(snak, args)
local datatype = snak['datatype']
wd.temp.tipo = datatype
if datatype == 'string' or datatype == 'url' or datatype == 'external-id' or datatype == 'math' or
datatype == 'commonsMedia' then
wd.temp.debug = #args == 0 and 'valor obtido' or 'esse tipo de dado não recebe modificações'
return snak['datavalue']['value']
elseif datatype == 'monolingualtext' then
wd.temp.debug = 'obtido valor na língua ' .. snak['datavalue']['value']['language']
return snak['datavalue']['value']['text']
elseif datatype == 'wikibase-item' then
if not snak['datavalue'] then
wd.temp.debug = 'propriedade não possui valor'
return nil
end
local qid = snak['datavalue']['value']['id']
local item = mw.wikibase.getEntity(qid)
local labels = item['labels']
local pt, en, outra
for lang, v in pairs(labels) do
if lang == 'pt' then
pt = v['value']
break
elseif lang == 'pt-br' then
pt = v['value']
elseif lang == 'en' then
en = v['value']
elseif lang == 'es' or lang == 'fr' or lang == 'it' then -- outras linguas latinas
outra = v['value']
end
end
local valor = pt or en or outra
if not valor then
wd.temp.debug = 'item não tem rótulo em pt, pt-br, en, es, fr ou it'
return qid
end
if args[1] == 'link' and item['sitelinks'] and item['sitelinks']['ptwiki'] then
wd.temp.debug = 'valor obtido e adicionado link'
return '[[' .. item['sitelinks']['ptwiki']['title'] .. '|' .. valor .. ']]'
end
wd.temp.debug = args[1] == 'link' and 'valor obtido sem link pois não possui artigo' or 'valor obtido'
return valor
elseif datatype == 'time' then
local ac, ano, mes, dia, hora, minuto, segundo = string.match(
snak['datavalue']['value']['time'], '([+-])(%d+)%-(%d%d)%-0?(%d%d?)T(%d%d):(%d%d):(%d%d)Z')
if hora == '00' and minuto == '00' and segundo == '00' then
if mes == '00' then
wd.temp.debug = 'data só possui o ano'
return ano .. (ac == '-' and ' AC' or '')
end
wd.temp.debug = 'data obtida, não possui hora'
return dia .. ' de ' .. meses[mes] .. ' de ' .. ano .. (ac == '-' and ' AC' or '')
else
wd.temp.debug = 'obtida data e hora'
return string.format('%d/%d/%d %d:%d:%d', dia, mes, ano, hora, minuto, segundo)
end
elseif datatype == 'quantity' then
local amount = tonumber(snak['datavalue']['value']['amount'])
local unit = string.match(snak['datavalue']['value']['unit'], '//www.wikidata.org/entity/(Q%d+)')
return wd.quantidade(amount, unit, args)
elseif datatype == 'globe-coordinate' then
local lat = tonumber(snak['datavalue']['value']['latitude'])
local long = tonumber(snak['datavalue']['value']['longitude'])
return wd.coordenadas(lat, long, args)
else
wd.temp.debug = 'esse tipo de dado não é suportado'
return nil
end
end
-- Retorna número com determinados algarimos significativos
local algSig = function(n, alg)
local a = alg - math.ceil(math.log10(math.abs(n)))
return math.floor(n * 10 ^ a + 0.5) / 10 ^ a
end
-- Formata um número usando espaço como separador de milhar e vírgula como separador decimal
local formatnum = function(n)
n = tostring(n)
local m = 1
while m > 0 do
n, m = string.gsub(n, '^(%-?%d+)(%d%d%d)', '%1 %2')
end
return string.gsub(n, '(%d)%.(%d)', '%1,%2')
end
-- Retorna valor de uma quantidade de acordo com argumentos
wd.quantidade = function(n, qid, args)
if args[1] == 'dividido' or args[1] == 'vezes' or args[1] == 'mais' or args[1] == 'menos' then
wd.temp.debug = 'valor com operação matemática'
resp = wd.matematica(n, args)
return resp and formatnum(resp)
elseif qid then
return wd.unidade(n, qid, args)
else
wd.temp.debug = 'obtido valor sem unidade'
return n and formatnum(n)
end
end
-- Retorna unidade relacionada a um item do Wikidata baseado nos dados do Módulo:Unidades
wd.unidade = function(n, qid, args, loop)
if not unidades then
unidades = mw.loadData('Módulo:Unidades')
end
local u = type(unidades[qid]) == 'string' and unidades[unidades[qid]] or unidades[qid]
if not u then
wd.temp.debug = 'a unidade ' .. qid .. 'não é conhecida, se ela existir adicione no [[Módilo:Unidades]]'
return n .. ' (unidade ' .. qid .. ')'
end
local nome = u['nome']
local arg = table.remove(args, 1)
-- Mudar a unidade se for pedido --
if arg == 'unidade' and args[1] then
nome = table.remove(args, 1)
arg = table.remove(args, 1)
local u2 = type(unidades[nome]) == 'string' and unidades[unidades[nome]] or unidades[nome]
if u2 and u['grandeza'] ~= u2['grandeza'] then
wd.temp.debug = 'tentando converter grandezas diferentes: ' .. u['nome'] .. ' (' .. u['grandeza'] ..
') para ' .. u2['nome'] .. ' (' .. u2['grandeza'] .. ')'
return nil
end
if u2 and u ~= u2 then
n = n * u['si'] / u2['si'] -- convertendo
u = u2
end
end
local unidade
if u['unidade'] then
unidade = u['unidade']
elseif n ~= 1 and u['plural'] then
unidade = u['plural']
elseif n == 1 and u['nome'] then
unidade = u['nome']
else
wd.temp.debug = 'unidade ' .. nome .. ' não possui nome da unidade' .. (n ~= 1 and ' nem plural' or '') ..
' para se colocado após o número, corrija no [[Módulo:Unidades]]'
return ' (unidade ' .. qid .. ')'
end
local alg = 4
if arg == 'alg' and tonumber(args[1]) then
alg = math.max(1, math.min(12, math.floor(table.remove(args, 1))))
arg = table.remove(args, 1)
end
wd.temp.debug = alg .. ' algarísmos significativos'
local resp = formatnum(algSig(n, alg)) .. ' '
if arg == 'link' and u['artigo'] then
arg = table.remove(args, 1)
resp = resp .. '[[' .. u['artigo'] .. '|' .. unidade .. ']]'
wd.temp.debug = wd.temp.debug .. ', adicionado link'
else
resp = resp .. unidade
end
-- Exibir a conversão quando for pedido --
if arg == 'converter' and args[1] and unidades[args[1]] and not loop then
table.insert(args, 1, 'unidade')
resp = resp .. ' (' .. wd.unidade(n, nome or qid, args, true) .. ')'
wd.temp.debug = wd.temp.debug .. ', adicionado conversão'
end
return resp
end
-- Realiza operações matemáticas com quantidades
wd.matematica = function(n, args)
local op = table.remove(args, 1)
local prop = table.remove(args, 1)
local n2
if string.match(prop, '^%-?%d+$') then
n2 = tonumber(prop)
wd.temp.debug = 'valor ' .. n .. ' ' .. op .. (op == 'dividido' and ' por' or '') .. ' ' .. n2
elseif string.match(prop, '^P%d+$') then
local p2 = wd.props and wd.props[prop] and wd.props[prop][1]
if not (p2 and p2['mainsnak']['datatype'] == 'quantity') then
wd.temp.debug = p2 and prop .. ' não é do tipo quantity' or prop .. ' não existe no item'
return
end
n2 = p2['mainsnak']['datavalue']['value']['amount']
wd.temp.debug = 'operação com ' .. prop .. ' = ' .. n2
else
wd.temp.debug = 'operação matemática sem informar propriedade ou número'
return nil
end
if op == 'dividido' then
local r = n / n2
if r ~= math.floor(r) then
return tostring(algSig(r, 4))
else
return tostring(r)
end
elseif op == 'vezes' then
return tostring(n * n2)
elseif op == 'mais' then
return tostring(n + n2)
elseif op == 'menos' then
return tostring(n - n2)
end
end
-- Retorna as coordenadas no formato G° M' S" [NS] G° M' S" [LO] e coloca link quando pedido
wd.coordenadas = function(lat, long, args)
local abs = math.abs(lat)
local grau = math.floor(abs)
local min = math.floor((abs - grau) * 60)
local seg = math.floor(((abs - grau) * 60 - min) * 60)
local hemi = lat >= 0 and 'N' or 'S'
lat = string.format('%02d° %02d\' %02d" %s', grau, min, seg, hemi)
abs = math.abs(long)
grau = math.floor(abs)
min = math.floor((abs - grau) * 60)
seg = math.floor(((abs - grau) * 60 - min) * 60)
hemi = long >= 0 and 'L' or 'O'
long = string.format('%02d° %02d\' %02d" %s', grau, min, seg, hemi)
local coor = string.gsub(lat, ' ', ' ') .. ' ' .. string.gsub(long, ' ', ' ')
if args[1] == 'link' then
local link = '<span class="plainlinks" style="white-space:nowrap" title="Mapas, fotos aéreas e outros dados para este local">[//tools.wmflabs.org/geohack/geohack.php?language=pt&pagename='
local pagename = mw.uri.encode(mw.title.getCurrentTitle().fullText)
local coorlink = string.gsub(string.gsub(string.gsub(coor, '[^0-9NSLO]+', '_'), 'O', 'W'), 'L', 'E')
local i = 2
while #args > i do
if args[i] == '' or args[i + 1] == '' then
break
end
coorlink = coorlink .. '_' .. args[i] .. ':' .. string.gsub(args[i + 1], ' ', '_')
i = i + 2
end
wd.temp.debug = 'obtida coordenada e adicionado link'
return link .. pagename .. '¶ms=' .. coorlink .. ' ' .. coor .. ']</span>'
end
wd.temp.debug = 'obtida coordenada'
return coor
end
wd.parser = function(s)
local i = 1
local grupos = {}
local props = {}
while i < #s do
local abre = string.find(s, '{', i, true)
local fecha = string.find(s, '}', i, true)
if abre and not (fecha and fecha < abre) then
table.insert(grupos, {abre})
i = abre + 1
else
if fecha and #grupos > 0 then
local encontrou
for n = 0, #grupos - 1 do
if not grupos[#grupos - n][2] then
grupos[#grupos - n][2] = fecha
encontrou = true
end
end
if encontrou then
i = fecha + 1
end
elseif fecha then
i = fecha + 1
else
break
end
end
end
i = 1
while i < #s do
local pos, fim = mw.ustring.find(s, 'P%d[%w:%-/]*', i)
if pos then
props[string.sub(s, pos, fim)] = pos
i = fim + 1
else
break
end
end
return grupos, props
end
-- Processa expressões que pedem dados do Wikidata
wd.expandir = function(str)
local grupos, props = wd.parser(str)
local expandido = {}
local debug = {}
for prop, pos in pairs(props) do
local valor = wd.dados(prop)
if valor then
table.insert(expandido, pos)
props[prop] = valor
else
props[prop] = false
end
table.insert(debug, prop .. ' [' .. (wd.temp.tipo or '?') .. ']: ' .. (wd.temp.debug or ''))
wd.temp = {}
end
if #expandido == 0 then
wd.temp.debug = #debug > 0 and table.concat(debug, '\n') or 'nenhuma propriedade em "' .. str .. '"'
return nil
end
wd.temp.debug = #debug > 0 and table.concat(debug, '\n') or 'houve algum problema ao espandir "' .. str .. '"'
for _, g in ipairs(grupos) do
if #g == 2 then
local apagar = true
for _, pos in ipairs(expandido) do
if g[1] < pos and g[2] > pos then
apagar = false
break
end
end
if apagar then
-- os grupos sem propriedades expandidas é preenchido com caracteres { para ser apagado
str = str:sub(1, g[1]) .. string.rep('{', g[2] - g[1] - 1) .. str:sub(g[2])
end
end
end
str = str:gsub('[{}]+', '') -- apaga caracteres { e }
local oprops = {}
-- Ordenando as propriedades pelo tamanho da string para evitar problemas quando uma é substring da outra
for prop, valor in pairs(props) do
local i = #oprops + 1
for n, s in ipairs(oprops) do
if #s < #prop then
i = n
break
end
end
table.insert(oprops, i, prop)
end
-- Substituindo as propriedades pelo seu valor expandido
for n, prop in ipairs(oprops) do
local escprop = prop:gsub('[%^%$%(%)%%%.%[%]%*%+%-%?]','%%%1') -- escapando caracteres especiais
str = str:gsub(escprop, type(props[prop]) == 'string' and props[prop] or '')
end
return str
end
wd.formatardata = function(t)
local ano, mes, dia = string.match(t, '^%+(%d+)%-(%d%d)%-0?(%d%d?)T')
if not ano then
return nil
elseif mes == '00' then
return ano
else
return meses[mes] .. ' de ' .. ano
end
end
-- Tabela para funções invocadas diretamente
invoke = {}
invoke.dados = function(frame)
local arg = frame.args['1']
if arg and arg ~= '' then
local resp = (wd.dados(arg) or '')
if frame.args['2'] == 'debug' then
resp = resp .. (wd.temp.debug and ' (' .. wd.temp.debug .. ')' or '')
end
return resp
else
arg = frame:getParent().args['1']
if arg and arg ~= '' then
local resp = (wd.dados(arg) or '')
return resp
end
end
return '(erro: nenhuma propriedade fornecida)'
end
invoke.expandir = function(frame)
local arg = frame.args['1']
if arg and arg ~= '' then
local resp = (wd.expandir(arg) or '')
if frame.args['2'] == 'debug' then
resp = resp .. (wd.temp.debug and ' (' .. wd.temp.debug .. ')' or '')
end
return resp
else
args = frame:getParent().args
if args['1'] and args['1'] ~= '' then
local resp = (wd.expandir(args['1']) or '')
if resp == '' and args['local'] then
return args['local']
elseif args['data'] and wd.data[args['data']] then
local data = wd.formatardata(wd.data[args['data']])
if data then
return resp ..' (' .. data .. ')'
end
end
return resp
end
end
return '(erro: nenhuma propriedade fornecida)'
end
return invoke