Módulo:Infobox/Wikidata/Testes

Documentação do módulo[ver] [editar] [histórico] [purgar]



Módulo a ser usado para mostrar e formatar dados do Wikidata. No caso de querer desligar dados do Wikidata informe o parâmetro |wikidata=-

Tanto quanto possível, este módulo deve ser linguísticamente neutro. Para textos portugueses, veja Módulo:Wikidata/i18n/Testes e Módulo:Wikidata/i18n.

Funções exportáveis editar

Funções que operam em um snak editar

Nome Argumentos Descrição
formatSnak snak, params Obtenha o valor de um snak e coloque-o em forma
getDataValue snak, params Recupera o valor de um snak com um valor do tipo "value" e o formata
isSpecial snak retorna true se um snak é um valor especial (novalue ou somevalue)
getId snak retorna o identificador Qdd do elemento usado como um valor de snack

Funções que operam no nível de uma afirmação editar

Nome Argumentos Descrição
formatStatement params Transforma uma declaração individual em uma string wikitexto, usando os mesmos parâmetros que stringTable
getDate statement, qualifs recupera as informações de data armazenadas em uma declaração Wikidata (qualificadores ou valor principal) e as armazena como uma tabela
getFormattedDate statement, params recupera a data associada a uma declaração Wikidata como getDate, mas a retorna como uma string formatada de acordo com os parâmetros params
hasQualifier claim, acceptedqualifs, acceptedvals, excludequalifiervalues retorna true se a instrução tiver qualificadores usando as propriedades acceptedqualifs com os valores acceptedvals e sem os valqualifiervalues. Se acceptedqualifs não for preenchido, todas as propriedades serão aceitas. Se acceptedvals não for preenchido, todos os valores serão aceitos.
getMainId claim retorna o identificador Qdd do elemento usado como o valor do "mainsnak" de uma declaração
getFormattedQualifiers statement, qualifs, params a partir de uma afirmação, retorna uma string contendo os qualificadores solicitados. A tabela params permite personalizar a exibição, veja #Settings.
getReferences statement recupera e exibe a parte de referências de uma instrução Wikidata.
addtrackingcat property, cat categoriza em [[Categoria:!Artigos que utilizam $property]]

Funções operando em um nível de entidade editar

Nome Argumentos Descrição
sortClaims claims Coloque uma série de valores em ordem. A ordem pode ser 'cronológica'ou'invertida' (ordem cronológica inversa). Nesse caso, as datas usadas são aquelas dadas como o valor handsnak ou nos qualificadores. O pedido também pode ser uma função (consulte a ajuda Lua para a classificação da tabela). Essa função é chamada pelo argumento sorttype de wd.formatGetClaims e, portanto, também wd.formatStatements
filterClaims claims, params Obtém uma tabela de instruções e exclui aquelas que não correspondem aos critérios fornecidos na tabela params </ code>. Para a lista desses argumentos, veja a ajuda abaixo
getClaims params entity = e |property = , e pode conter muitos outros parâmetros opcionais, consulte #Configuração.
getIds params Seleciona instruções da mesma forma que os conjuntos getClaims em vez de retornar instruções completas, apenas retorna o identificador de seu mainsnak (só funciona para dados do tipo elemento).
stringTable Retorna as mesmas asserções que getClaims, exceto possivelmente aquelas suprimidas pelo parâmetro removedupes. Cada um é processado como uma string Wikitexto diretamente utilizável. Os parâmetros de formatação também estão contidos na tabela params.
formatStatements params Retorna os mesmos valores que stringTable, mas concatenados como uma única string. O método de concatenação é definido pelo parâmetro conjtype que usa Módulo:Linguística. Se linkback e addcat estiverem habilitados, um trackback e uma categoria de manutenção também podem ser adicionados.
formatAndCat params Mesma função que formatStatements, mas adicione um trackback ao Wikidata e uma categoria de manutenção
getTheDate params exibe uma data que está no valor principal ou como um qualificador da propriedade prop do elemento item
mainDate entity Recupera e formata a "data principal" que pode ser associada ao elemento, com base em suas propriedades P580 (« data inicial »), P582 (« data final ») e P585 (« data de ocorrência »)
keyDate event, item, params Recupera e formata uma data do elemento item. event deve ser um identificador de propriedade, caso em que a função recuperará os valores nessa propriedade, que é um identificador de elemento. Nesse caso, ele procurará esse elemento nos valores de P793 (« evento notável ») e retornará a data indicada como um qualificador. <Código>evento também pode ser uma lista de identificadores no formato de tabela, Nesse caso, o valor retornado será o do primeiro identificador para o qual algo é encontrado. Por exemplo, wd.keyDate ('Q1417098', 'P1690'} irá procurar o valor em P793 (« evento notável ») Q1417098 (« tomada de posse ») e se não encontrar nada, irá procurar em P1690 (« ICD-10-PCS »).
getLabel entity, lang, labelformat Retorna por padrão o rótulo Wikidata da entidade em, português ou no idioma especificado no segundo parâmetro. O parâmetro opcional labelformat é usado para definir uma exibição alternativa. Seu valor deve ser uma função tomando como argumento a entidade e retornando uma string.
siteLink entity, lang, project retorna um link para a página do projeto solicitada ('wikipédia', 'wikivoyage', 'commons') etc. vinculado à entidade Wikidata especificada, no idioma solicitado. Por padrão, o idioma é português e o projeto Wikipédia.
formatEntity entity, params Obtém o identificador (ou a tabela de dados) fornecido no parâmetro da entidade e o transforma, retornando-o na forma de um texto contendo um rótulo e, possivelmente, um link interno.
getDescription entity, lang exibe a descrição do Wikidata para uma determinada entidade, em um determinado idioma, ou "lang" (o padrão é o português)
getNumericId snak retorna o identificador numérico do elemento usado como o valor de um snack
getEntity str retorna a tabela de dados correspondente a um identificador (inverso de getEntityId)
getEntityId str retorna o identificador de uma entidade Wikidata passada na forma de uma tabela de dados (inversa de getEntity)


Funções recuperando dados em diversas entidades editar

Nome Argumentos Descrição
isSubclass class, item, maxdepth
isinstance class, item, maxdepth
inTransitiveVals searchedval, sourceval, query, maxdepth, maxnodes

Diversos editar

Nome Argumentos Descrição
Dump entity exibir o conteúdo de uma entidade Wikidata
sourceStr
frameFun (frame) chama uma das funções anteriores do quadro. O nome da função é dado no argumento 1
addRefAnchor
addLinkback adicionar um trackback (normalmente usado por formatStatements {arglinkback = true}, mas pode ser útil como uma função independente quando os dados necessários estão sujeitos a muitas manipulações antes de sua renderização final
isHere searchset, val retorna true se a string val estiver na tabela searchset
addNewValues old, new adiciona novos valores a uma tabela somente se eles ainda não estiverem lá

Configurações editar

Lista de chaves que podem ser usadas na tabela "params" mencionada abaixo..

Escolha de declarações editar

Parâmetros usados pela função filterclaims e, portanto, indiretamente pelas funções de chamada, como getClaims e formatStatements.

Nome Mode de utilização Descrição
claims Lua Lista de instruções a serem usadas. Quando esse parâmetro está ativado, os parâmetros property e entity não são usados para recuperação de dados.
entity Lua e wikicode O identificador da entidade Wikidata para usar. Por padrão, o link para a página.
Em Lua, você pode passar uma entidade já ocupada em vez de seu identificador.
property Lua e wikicode O nome da propriedade a ser usada, no formato property = PXX. Em Lua, pode-se usar várias propriedades na forma de tabelas
excludespecial Lua e wikicode Remove declarações cujo valor principal (mainsnak) não é do tipo "value", ou seja, as declarações "valor desconhecido" ou "sem valor".

No wikicode: ativado por |excludespecial = true
Em Lua, ativado por um booleano (excludespecial = true)

targetvalue Lua e wikicode Mantém apenas a(s) instrução(ões) onde o valor do snak principal corresponde ao valor (ou valores) indicado
excludevalues Lua e wikicode Exclui instruções em que o valor do snak principal corresponde ao valor indicado (ou valores)
qualifier Lua e wikicode Apenas declarações contendo este qualificador são retornadas. Para ativar a exibição do qualificador, consulte showqualifiers
qualifiervalue Lua e wikicode O qualificador usado pelo argumento qualifier deve ter esse valor (ou um desses valores, se for uma tabela de propriedade ou uma tabela separada por vírgula)
excludequalifier Lua e wikicode Declarações contendo este qualificador são excluídasLes déclarations contenant ce qualificatif sont exclues
excludequalifiervalue Lua e wikicode Se este parâmetro for preenchido, excludequalifier será ativado apenas quando o valor do qualificador estiver naqueles indicados.
withsource Lua e wikicode Retorna apenas os valores que contêm uma origem, usando a origem especificada na propriedade P248 (« afirmado em ») ou outra propriedade definida pelo parâmetro sourceproperty. Para aceitar qualquer fonte, desde que use a propriedade solicitada, defina o valor any. Para desabilitar esse parâmetro, defina o valor -.
sourceproperty Lua e wikicode Propriedade a ser usada para o parâmetro withsource
rank Lua e wikicocde Ranks aceites :
  • preferred
  • normal
  • deprecated
  • valid (= preferred + normal)
  • best (preferred, ou, se nenhum atende aos outros critérios da consulta, normal)

Valor por padrão : best

numval Lua e wikicode Número máximo de valores a serem retornados (os primeiros n valores da lista estabelecidos pelos outros parâmetros)
withlink Lua e wikicode Retorna apenas os valores que contêm um link para o site especificado. Se o valor do parâmetro for simplesmente true, o site usado será a Wikipédia em português.
withdate Lua e wikicode Somente valores de retorno com um qualificador de data : P585 (« data de ocorrência »), P580 (« data inicial »), P582 (« data final »)
atdate Lua e wikicode Exclui valores cujos qualificadores P580 (« data inicial ») ou P582 (« data final ») indicam que ele era inválido para o período especificado. atdate talvez uma data no formato wikibase ou no formato ISO. Valores não ordenados não são excluídos (eles podem ser excluídos usando a função withdate). Para solicitar o valor na data de hoje, use: |atdate = today.
minprecision Lua e Wikicode Grau mínimo de precisão aceite para dados do tipo de data. Deve ser um valor numérico correspondente ao modelo Wikidata (por exemplo, ano = 9)
condition Lua Para definir uma função livre Se a função retornar true , a declaração será mantida
sorttype Lua e wikicode Como classificar declarações. Valores possíveis:* chronological
  • inverted (cronológica invertida
  • funções Lua (ver a função sortClaims)

Activação / desativação de Wikidata editar

Nome Mode de utilização Descrição
value Lua e wikicode Quando o parâmetro value não está vazio, o módulo não pesquisa dados do Wikidata, mas usa aqueles fornecidos localmente por esse parâmetro. A função formatAndCat em vez disso aplica linkback e addcat)
expl Lua e wikicode Quando o parâmetro expl estiver presente, a função formatStatements não fará nada se o parâmetro value não tiver o valor de resultado {{ WD}} (útil para dados atualizados, mas cuja utilidade em cada artigo é difícil) decidir automaticamente).

Formatação de dados editar

Nome Nível de aplicação Modo de utilização Descrição
conjtype lista das declarações Lua e wikicode Como coordenar declarações, usando a função conj do Módulo:Linguística. Valores possíveis:
  • and adicionando "ou" entre o penúltimo e último valor
  • or adicionando "ou" entre o penúltimo e último valor
  • comma vírgulas em todos os lugares
  • new line um retorno de linha

Qualquer outro valor dado ao parâmetro será inserido entre cada valor (|conjtype = e então também irá adicionar "e então também" entre cada valor)

linkback lista das declarações Lua e wikicode Adicione um trackback ao Wikidata como um lápis «  »
addcat lista das declarações Lua e wikicode Coloque a página em uma categoria de rastreamento (veja Categoria:!Artigos que utilizam Wikidata por propriedade). O nome da categoria é o valor do parâmetro. Se este valor for simplesmente true, a categoria é uma função do parâmetro da propriedade.

Atenção, este parâmetro pode ser uma fonte de erros. Por exemplo, se a propriedade for usada em um URL ou link.

removedupes escolha das declarações
(no final, numa função diferente)
Lua e wikicode Quando, após toda a formatação, duas declarações são renderizadas da mesma maneira, apenas uma é preservada.
lang formatar os dados Lua e Wikicode Idioma em que o texto deve ser retornado.
snak (entidade) Lua e wikicode Código da Wikimedia da linguagem usada para exibir os rótulos (por padrão : português)
ucfirst lista das declarações Lua e Wikicode ucfirst = - para desabilitar isso
statementformat declaração Lua deve ser uma função function (statement) retornar XX retornando uma string. Será aplicado a cada afirmação (simplifica alguma infobox Lua)
showdate declaração Lua e wikicode Para exibir a data entre parênteses em pequena para cada instrução usando os mesmos qualificadores como (withdate). Quando nenhuma data é encontrada, o valor é exibido sem qualquer data.
displayformat snak Lua e wikicode Para alterar o formato padrão entre cada valor.
  • weblink transforma dados de string em um link da web
  • raw retorna um formato mais bruto, às vezes útil para codificação
  • latitude para Módulo:Coordenadas
  • longitude para Module:Coordenadas

Em lua, o valor do parâmetro também pode ser uma função.

qualifdisplayformat snak Lua e wikicode Exibir formato dos qualificadores. O mesmo que displayformat. Quando este parâmetro é deixado vazio, os qualificadores usam o parâmetro displayformat como o valor principal.
showsource declaração Lua e wikicode Identificador de uma fonte que alguém gostaria de ver exibido em referência se estiver presente. true mostrará todas as fontes usando a propriedade P248 (« afirmado em »).
linktopic snak (temporada) Lua e wikicode Tipo de link para adicionar às datas (consulte Módulo: Data). Para não colocar nenhum link : linktopic = -
precision snak (temporal) Lua e wikicode Precisão com quais datas devem ser exibidas. Valores possíveis:
  • day
  • month
  • year

Valor por padrão : day

textformat snak (temporal) Lua e wikicode Exibir formato de intervalos de tempo, conforme definido por Módulo:Data complexa
  • minimum exibirá "1995" em vez de "de 1995".
speciallabels snak (entidade) Lua Tabelas de valores especiais para usar em vez do rótulo Wikidata para o valor de alguns elementos, por exemplo Módulo:Wikidata/Dicionário profissões.
labelformat snak (entidade) Lua Função de formatação de etiquetas
showlang declaração Lua e wikicode Quando o valor principal é do tipo "texto monolíngua", o seu código de idioma é exibido (usado pelo Módulo:Website oficial)
showqualifiers declaração Lua e wikicode Qualificador para retornar entre parênteses por trás do valor da propriedade principal solicitada. Em wikicode no formato |showqualifiers = P460 , em Lua como showqualifiers = "P460" ou showqualifiers = {"P460 "," P461 "}
showonlyqualifier declaração Lua e wikicode Qualificador do mesmo, mas para retornar sozinho sem o valor da propriedade principal solicitada
link snak (entité) Lua e wikicode Site ao qual os dados do tipo de item devem ser vinculados. Por padrão: Wikidata em português. Se igual a "-", nenhum link será criado.
defaultlink snak (entidade) Lua e wikicode Quando o site solicitado pelo parâmetro link não retorna um link, exibe um link entre parênteses para o site solicitado. Padrão: Wikipédia em inglês ou, se não, Wikidata. O valor "-" desabilita o parâmetro.
defaultlinkquery snak (entidade) Lua defaultlinkquery = {property = 'P279'} o link criado será aquele fornecido pelo elemento usado na propriedade P279 (« subclasse de »).
targetunit snak (quantidade) Lua e Wikicode Unidade na qual os dados do tipo de quantidade devem ser convertidos. Os valores possíveis são visíveis no Módulo:Conversão/Dados, por exemplo, km ou km2.
showunit snak (quantidade) Lua e Wikicode

Como exibir a unidade solicitada. Valores possíveis:

  • 'long': exibe o nome completo da unidade (por exemplo, "12,7 metros")
  • 'short' exibe a abreviação usada convencionalmente ("12,7 m")
  • '-' : nenhuma unidade exibida

padrão: 'curto'

rounding snak (quantidade) número de dígitos Lua e Wikicode Número de dígitos significativos a serem exibidos em dados de quantidade, após possível conversão (padrão: depende da precisão indicada no Wikidata)
urlpattern snak (string) Lua e wikicode O valor do parâmetro é uma string representando uma URL, a expressão "$1" será substituída pelo valor Wikidata
Com a propriedade P214 (« identificador VIAF ») e o valor do parâmetro http://viaf.org/viaf/$1, o valor do ID VIAF armazenado no Wikidata será usado para criar um link externo.
text snak (string) Lua e wikicode Texto para exibir quando o valor cria um link externo, ou seja, para dados de string com displayformat = "weblink" ou com o parâmetro urlpattern preenchido . Padrão: o valor inicial da declaração. Usado no Módulo:Website oficial Módulo:Bases Archi.
novaluelabel snak (novalue) Lua e wikicode Rótulo para retornar quando o valor é do tipo "novalue". Padrão: "-".

Em Lua, o valor pode ser uma função.

somevaluelabel snak (somevalue) Lua e wikicode Rótulo para retornar quando o valor é do tipo "somevalue". Padrão: "desconhecido".

Em Lua, o valor pode ser uma função.

returnnumberofvalues lista das declarações Lua retorna no segundo valor o número de valores recuperados por getClaims (se a função for chamada do Wikicode, o número será concatenado ao resto da string retornada

Exemplos editar

Definição de links editar

Code Resultado Observações
{{#invoke:Infobox/Wikidata|frameFun|formatStatements|entity = Q79 | property = P36}} Cairo Capital do Egito, formato padrão
{{#invoke:Infobox/Wikidata|frameFun|formatStatements|entity = Q79 | property = P36|link=-}} Cairo Capital do Egito, sem link
{{#invoke:Infobox/Wikidata|frameFun|formatStatements|entity = Q79 | property = P36|link=wikidata}} Cairo Capital do Egito, com link para Wikidata
{{#invoke:Infobox/Wikidata|frameFun|formatStatements|entity = Q937 | property = P569}} O aniversário de Albert Einstein, usando o formato padrão da Wikipédia em português
{{#invoke:Infobox/Wikidata|frameFun|formatStatements|entity = Q937 | property = P569|linktopic = futebol }} Data de nascimento do Einstein, com links especializados em futebol
{{#invoke:Infobox/Wikidata|frameFun|formatStatements|entity = Q937 | property = P569|linktopic = -}} Data de nascimento do Einstein, sem links

Gama editar

Existem três tipos de classificação no Wikidata: "normal", "preferido" e "obsoleto". A classificação descontinuada é reservada para dados que sabemos serem falsos, mas que mantemos para a memória, por exemplo, para documentar uma crença antiga que foi invalidada por estudos mais recentes.

Por padrão, apenas a classificação "preferida" é recuperada, mas o parâmetro "rank" permite que você altere isso.

Code Resultado Observações
{{#invoke:Infobox/Wikidata|frameFun|formatStatements|entity =Q12418 | property = P186}} tinta a óleo e poplar panel (d) Material da Mona Lisa, exibição padrão (portanto, somente aqueles com a classificação "preferida", se houver)
{{#invoke:Infobox/Wikidata|frameFun|formatStatements|entity =Q12418 | property = P186|rank=valid}} tinta a óleo, poplar panel (d) e madeira rank = "valid" aceita valores de classificação "normal" e "preferencial" (por exemplo, o material de um quadro que foi adicionado posteriormente à Mona Lisa)
{{#invoke:Infobox/Wikidata|frameFun|formatStatements|entity =Q12418 | property = P186|rank=normal}} madeira rank = "normal" recupera apenas dados com uma classificação "normal" e ignora aqueles com classificação "preferida"

Qualificadores editar

Qualificadores são dados adicionais incorporados em uma instrução Wikidata que podem ser especificados.

Code Resultado Observações
{{#invoke:Infobox/Wikidata|frameFun|formatStatements|entity =Q12418 | property = P186|showqualifiers = P518}} poplar panel (d) somente retém valores com um qualificador P518 (« aplicável a »)
{{#invoke:Infobox/Wikidata|frameFun|formatStatements|entity =Q12418 | property = P186|showqualifiers = P518|qualifiervalue=Q1737943}} madeira qualiervalue define o valor que o qualificador possui. Aqui, Q1737943 (« moldura ») especifica que o material da estrutura é obrigatório. Note que nenhum valor de classificação "preferido" é encontrado, um valor de classificação "normal" é retornado.
{{#invoke:Infobox/Wikidata|frameFun|formatStatements|entity =Q12418 | property = P186|showqualifiers = P518|rank=valid}} tinta a óleo, poplar panel (d) (suporte da pintura (en)) e madeira (moldura (en)) showqualifiers exibe o valor dos qualificadores solicitados entre parênteses, após o valor principal
{{#invoke:Infobox/Wikidata|frameFun|formatStatements|entity =Q12418 | property = P186|showdate = true|rank=valid}} tinta a óleo, poplar panel (d) e madeira (a partir de ) se os qualificadores que indicam a data forem fornecidos, eles serão exibidos

Mostrando fontes editar

Outras opções

  • getImageLegend: devolve uma legenda da imagem (imagem é a propriedade P18; a legenda da imagem é a propriedade P2096).
    Faz a chamada como {{#invoke:Wikidata |getImageLegend | <PARAMETER> | lang=<ISO-639code> |id=<QID>}}
    Devolve PARÂMETRO, a menos que seja igual a "FETCH_WIKIDATA", de Item QID (chamada cara). Se o QID for omitido ou vazio, o artigo corrente é usado (não é uma chamada cara). Se lang for omitido, ele usa a língua local, de outra modo ele usa o código de língua ISO-639.
    A etiqueta é devolvida da primeira imagem com rank 'preferido'; ou da primeira imagem com rank 'normal' se nenhuma imagem tem rank preferido.


Outra documentação:

--script that retrieves basic data stored in Wikidata, for the datamodel, see https://www.mediawiki.org/wiki/Extension:Wikibase_Client/Lua

local wd = {}

-- creation of a subobject to store comparison funtions, used for sorting claims
-- to be able to build more complex sorts like topological sorts

wd.compare = {}

local databases = { }
local modules = { }
local databasesNames = { -- módulo de dados estatísticos que se podem chamar através de mw.loadData(), não necessitam mais require()
    i18n = 'Module:Wikidata/i18n',
    globes = 'Module:Wikidata/Globes',
    langhierarchy = 'Module:Wikidata/Hierarquia das línguas',
    langcodes = 'Module:Dicionário Wikidata/Códigos língua', -- big, infrequently useda
    invertedlangcodes = 'Module:Dicionário Wikidata/Código de língua/inverso'
}
local modulesNames = {
    reference = 'Module:Wikidata/Referências',
    linguistic = 'Module:Linguística',
    datemodule = 'Module:Data',
    formatDate = 'Module:Data complexa',
    formatNum = 'Module:Conversão',
    langmodule = 'Module:Língua',
    cite = 'Module:Citação/CS1',
    weblink = 'Module:Weblink'
}

local function loadDatabase( t, key )
    if databasesNames[key] then
        local m = mw.loadData( databasesNames[key] )
        t[key] = m
        return m
    end
end
local function loadModule( t, key )
    if modulesNames[key] then
        local m = require( modulesNames[key] )
        t[key] = m
        return m
    end
end
setmetatable( databases, { __index = loadDatabase } )
setmetatable( modules, { __index = loadModule } ) -- ainsi le require() sera opéré seulement si nécessaire par modules.(nom du module)

local datequalifiers = {'P585', 'P571', 'P580', 'P582', 'P1319', 'P1326'}

-- === I18n ===
local defaultlang = mw.getContentLanguage():getCode()

function wd.translate(str, rep1, rep2)
    str = databases.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  '[[Categoria:' .. cat .. '|' .. sortkey .. ']]'
    end
    return '[[Categoria:' .. cat  .. ']]'
end

local function formatError( key , category, debug)
    if debug then
        return error(databases.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
    elseif type(entity) == 'table' then
        return entity.id
    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 self_id = mw.wikibase.getEntityIdForCurrentPage()
    return self_id ~= nil and qid == self_id
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, matchfunction)
    for i, j in pairs(searchset) do
        if matchfunction then
            if matchfunction(val,j) then
                return true
            end
        else
            if val == j then
                return true
            end
        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}
    }

    local entityid = entity.id or entity

    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 = mw.wikibase.getSitelink(entityid, 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 type

    if claim.mainsnak ~= nil then
        type = claim.mainsnak.snaktype
    else
        -- condition a respeito e quando showonlyqualifier é um parâmetro informado
        -- neste caso, claim não é uma declaração inteirra, mas um snak qualificado do main snak
        type = claim.snaktype
    end

    return type == '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 hasTargetClass(claim, targets, maxdepth) -- retourne true si la valeur est une instance d'une classe dans la liste des target, ou si c'est une valeur spéciale filtrée séparément par excludespecial
    local id = wd.getMainId(claim)
    local targets = wd.splitStr(targets)
    local maxdepth = maxdepth or 10
    local matchfunction = function(value, target) return wd.isInstance(target, value, maxdepth) end
    return wd.isHere(targets, id, matchfunction) or wd.isSpecial(claim.mainsnak)
end

local function excludeClasses(claim, classes) -- true si la valeur n'est pas une instance d'une classe dans la liste, ou si c'est une valeur spéciale (filtrée à part par excludespecial)
    return wd.isSpecial(claim.mainsnak) or not ( hasTargetClass(claim, classes, maxdepth) )
end

local function hasTargetSuperclass(claim, targets, maxdepth) -- retourne true si la valeur est une sous-classe d'une classe dans la liste des target, ou si c'est une valeur spéciale filtrée séparément par excludespecial
    local id = wd.getMainId(claim)
    local targets = wd.splitStr(targets)
    local maxdepth = maxdepth or 10
    local matchfunction = function(value, target) return wd.isSubclass(target, value, maxdepth) end
    return wd.isHere(targets, id, matchfunction) or wd.isSpecial(claim.mainsnak)
end

local function excludeSuperclasses(claim, classes) -- true si la valeur n'est pas une sous-classe d'une classe dans la liste, ou si c'est une valeur spéciale (filtrée à part par excludespecial)
    return wd.isSpecial(claim.mainsnak) or not ( hasTargetSuperclass(claim, classes, maxdepth) )
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

function wd.hasQualifierNumber(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) -- vérification pour un qualificatif individuel
        if not claimqualifs[qualif] then
            return false
        end
        if not (acceptedvals) then  -- si aucune valeur spécifique n'est demandée, OK
            return true
        end
        for i, wanted in pairs(acceptedvals) do
            for j, actual in pairs(claimqualifs[qualif]) do
                if mw.wikibase.renderSnak(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

function wd.hasDate(claim)
    if not claim then
        return false --error() ?
    end
    if wd.getDateFromQualif(claim, 'P585') or wd.getDateFromQualif(claim, 'P580') or wd.getDateFromQualif(claim, 'P582') then
        return true
    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 = databases.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, condiçã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 lastVals(claims, numval2) -- retourn les valeurs de la table claims à partir de numval2
    local numval2 = tonumber(numval2) or 0 -- raise a error if numval is not a positive integer ?
    if not claims then
        return nil
    end
    for i=1,numval2 do
        table.remove(claims, 1)
    end
    return claims
end

-- retourne les valeurs de la table claims à partir de removedupesdate,
-- sans les dates en doublons avec conversion entre les calendrier julien et grégorien,
-- ou uniquement en catégorisant si le paramètre removedupesdate est égale à 'cat'
local function removeDupesDate(claims, removedupesdate)
    if not claims or #claims < 2 then
        return claims, ''
    end
    local cat = ''
    local newClaims = {}
    local newIsos = {}

    local function findIndex(searchset, val) -- similaire à wd.isHere mais retourne l'index de la valeur trouvée
        for i, j in pairs(searchset) do
            if val == j then
                return i
            end
        end
        return -1
    end

    for _, claim in ipairs( claims ) do
        local snak = claim.mainsnak or claim
        if (snak.snaktype == 'value') and (snak.datatype == 'time') and snak.datavalue.value.precision >= 11 then -- s'il s'agit d'un time et que la précision est au moins l'année
            local iso = snak.datavalue.value.time
            _, _, iso = string.find(iso, "(+%d+-%d+-%d+T)")
            local deleteIfDuplicate = false
            if snak.datavalue.value.calendarmodel == 'http://www.wikidata.org/entity/Q1985727' then -- si la date est grégorienne
                if modules.formatDate.before('+1582', iso) then -- si avant 1582 on calcule la date julienne
                    _, _, y, m, d = string.find(iso, "+(%d+)-(%d+)-(%d+)T")
                    y, m , d = modules.datemodule.gregorianToJulian(y, m , d)
                    if m < 10 then m = '0' .. m end
                    if d < 10 then d = '0' .. d end
                    iso = '+' .. y .. '-' .. m .. '-' .. d .. 'T'
                    deleteIfDuplicate = true
                end
                local index = findIndex(newIsos, iso)
                if index >= 0 then -- si la date est déjà présente
                    cat = cat .. '[[Catégorie:Article avec des dates identiques venant de wikidata dans le code de l\'infobox]]'
                    if removedupesdate == "cat" then -- ne faire que catégoriser
                        table.insert(newIsos, iso)
                        table.insert(newClaims, claim)
                    elseif not deleteIfDuplicate then -- supprimer l'autre date si la date courante n'a pas été convertie
                        newClaims[index] = claim
                    end -- sinon supprimer la date courante
                else -- pas de doublon
                    table.insert(newIsos, iso)
                    table.insert(newClaims, claim)
                end
            elseif snak.datavalue.value.calendarmodel == 'http://www.wikidata.org/entity/Q1985786' then -- si date julienne
                if not modules.formatDate.before('+1582', iso) then -- si après 1582 on calcule la date grégorienne
                    _, _, y, m, d = string.find(iso, "+(%d+)-(%d+)-(%d+)T")
                    y, m , d = modules.datemodule.julianToGregorian(y, m , d)
                    if m < 10 then m = '0' .. m end
                    if d < 10 then d = '0' .. d end
                    iso = '+' .. y .. '-' .. m .. '-' .. d .. 'T'
                    deleteIfDuplicate = true
                end
                local index = findIndex(newIsos, iso)
                if index >= 0 then -- si date déjà présente
                    cat = cat .. '[[Catégorie:Article avec des dates identiques venant de wikidata dans le code de l\'infobox]]'
                    if removedupesdate == "cat" then -- ne faire que catégoriser
                        table.insert(newIsos, iso)
                        table.insert(newClaims, claim)
                    elseif not deleteIfDuplicate then -- supprimer l'autre date si la date courante n'a pas été convertie
                        newClaims[index] = claim
                    end -- sinon supprimer la date courante
                else -- pas de doublon
                    table.insert(newIsos, iso)
                    table.insert(newClaims, claim)
                end
            else -- autre calendrier
                table.insert(newIsos, iso)
                table.insert(newClaims, claim)
            end
        else -- précision insuffisante
            table.insert(newIsos, iso)
            table.insert(newClaims, claim)
        end
    end
    return newClaims, cat
end

local function timeFromQualifs(claim, qualifs)
    local claimqualifs = claim.qualifiers
    if not claimqualifs then
        return nil
    end
    for i, qualif in ipairs(qualifs or datequalifiers) do
        local vals = claimqualifs[qualif]
        if vals and (vals[1].snaktype == 'value') then
            return vals[1].datavalue.value.time, vals[1].datavalue.value.precision
        end
    end
end

local function atDate(claim, mydate)
    if mydate == "today" then
        mydate = os.date("!%Y-%m-%dT%TZ")
    end
    -- determines required precision depending on the atdate format
    local d = mw.text.split(mydate, "-")

    local myprecision
    if d[3] then
        myprecision =  11 -- day
    elseif d[2] then
        myprecision = 10 -- month
    else
        myprecision = 9 -- year
     end

    -- with point in time
    local d, storedprecision = timeFromQualifs(claim, {'P585'})
    if d then
        return modules.formatDate.equal(mydate, d, math.min(myprecision, storedprecision))
    end
    -- with start or end date -- TODO: precision
    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
    return false
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 snak
    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
                snak = claim.qualifiers[j][1]
                break
            end
        end
    end
    if not snak then
        snak = claim.mainsnak or claim
    end
    if (snak.snaktype == 'value') and (snak.datatype == 'time') and (snak.datavalue.value.precision < minprecision) then
        return false
    end
    return true
end

function wd.sortClaims(claims, sorttype)
    if not claims then
        return nil
    end
    if wd.isHere({'chronological', 'order', 'inverted', 'age', 'ageinverted'}, sorttype) then
        return wd.chronoSort(claims, sorttype)
    elseif sorttype == 'ascending' then
        return wd.quantitySort(claims)
    elseif sorttype == 'descending' then
        return wd.quantitySort(claims, true)
    elseif type(sorttype) == 'function' then
        table.sort(claims, sorttype)
        return claims
    elseif type(sorttype) == 'string' and sorttype:sub(1, 1) == 'P' then
        return wd.numericPropertySort(claims, sorttype)
    end
    return claims
end

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('targetclass', hasTargetClass, {'targetclass'} )
        filter('targetsuperclass', hasTargetSuperclass, {'targetsuperclass'} )
        filter('atdate', atDate, {'atdate'} )
        filter('qualifier', wd.hasQualifier, {'qualifier', 'qualifiervalue'} )
        filter('qualifiernumber', wd.hasQualifierNumber, {'qualifiernumber', 'qualifiernumbervalue'} )
        filter('excludequalifier', excludeQualifier, {'excludequalifier', 'excludequalifiervalue'} )
        filter('withsource', hasSource, {'withsource', 'sourceproperty'} )
        filter('withdate', wd.hasDate, {} )
        filter('excludevalues', excludeValues, {'excludevalues'})
        filter('excludeclasses', excludeClasses, {'excludeclasses'})
        filter('excludesuperclasses', excludeSuperclasses, {'excludesuperclasses'})
        filter('withlink', hasLink, {'withlink', 'linklang'} )
        filter('minprecision', minPrecision, {'minprecision'} )
        claims = withRank(claims, args.rank or 'best')
    end
    if #claims == 0 then
        return nil
    end
    if args.sorttype then
        claims = wd.sortClaims(claims, args.sorttype)
    end
    if args.numval2 then
        claims = lastVals(claims, args.numval2)
    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 == '-') or (entity == wd.translate('somevalue')) or (entity == modules.linguistic.ucfirst(wd.translate('somevalue'))) then
        return nil
    end
    if args.labelformat and args.labelformat == 'gendered' then
        local longgender = {m = 'male', f = 'female'}
        args.labelformat = longgender[wd.getgender(entity)]
    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, lang1, lang2)

    if (not entity) then
        return nil -- ou option de gestion des erreurs ?
    end
    entity = entity.id or ( type(entity) == "string" and entity)
    if not(type(entity) == 'string') then return nil end

    lang1 = lang1 or defaultlang

    local str, lang --str : texte rendu, lang : langue de celui-ci
    if lang1 == defaultlang then -- le plus économique
        str, lang = mw.wikibase.getLabelWithLang(entity) -- le libellé peut être en français ou en anglais
    else
        str = mw.wikibase.getLabelByLang(entity, lang1)
        if str then lang = lang1 end
    end
    if str and (lang == lang1) then --pas de catégorie "à traduire" si on a obtenu un texte dans la langue désirée (normalement fr)
        return str
    end
    if lang2 then -- langue secondaire, avec catégorie "à traduire"
        str2 = mw.wikibase.getLabelByLang(entity, lang2)
        if str2 then
            lang = lang2
            str = str2
        end
    end
    if not str then --si ni lang1, ni lang2 ni l'anglais ne sont présents,  parcours de la hiérarchie des langues
        for _, trylang in ipairs(databases.langhierarchy.codes) do
            str = mw.wikibase.getLabelByLang(entity, trylang)
            if str then
                lang = trylang
                break
            end
        end
    end

    if str then
        local translationCat = databases.i18n['to translate']
        translationCat = translationCat .. (databases.langhierarchy.cattext[lang] or '')
        translationCat = addCat(translationCat)
        return str, translationCat
    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 labelformat2 = params.labelformat2
    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
    if params.labelformat == 'male' then
        labelformat = function(objectid) return wd.genderedlabel(objectid, 'm') end
    end
    if params.labelformat == 'female' then
        labelformat = function(objectid) return wd.genderedlabel(objectid, 'f') end
    end

    local link, label, translationCat

    if type(labelformat) == 'function' then -- sert à des cas particuliers
        label, translationCat = labelformat(entity)
    end

    if not label then label, translationCat = wd.getLabel(entity, lang, params.wikidatalang) end

    translationCat = translationCat or "" -- sera toujours ajoutée au résultat mais sera vide si la catégorie de maintenance n'est pas nécessaire

    if type(labelformat2) == 'function' then -- sert à des cas particuliers
        label = labelformat2(label)
    end

    -- détermination du fait qu'on soit ou non en train de rendre l'élément sur la page de son article
    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 '[[' .. link .. '|' .. id .. ']]' .. translationCat
	-- 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 label .. translationCat
    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
        if mw.ustring.sub(link, 1, 9) == "Category:" or mw.ustring.sub(link, 1, 10) == "Catégorie:" then
            link = ":" .. link            --lier vers une catégorie au lieu de catégoriser
        end
        return '[[' .. link .. '|' .. label .. ']]' .. translationCat
    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 label .. ' <small>(' .. val .. ')</small>' .. translationCat
    end
    return label .. translationCat
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 databases.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
        langlist.maxLang = maxLang
        return modules.langmodule.indicationMultilingue(langlist)
    end
end


-- === DATE HANDLING ===

local function fuzzydate(str, precision) -- ajoute le qualificatif "vers" à une date
    if not str then
        return nil
    end
    if (precision >= 11) or (precision == 7) or (precision == 6)  then --dates avec jour, siècles, millénaires
        return "circa de" .. str
    end
    if (precision == 8) then --décennies ("années ...")
        return "circa " .. str
    end
    return "vers " .. str
end

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") or (v == "Q56644435") or (v == "Q30230067") then
                str = wd.translate('uncertain-information', str)
            elseif (v == "Q5727902") then
                if (statement.mainsnak.datatype == 'time') then
                    local datevalue = statement.mainsnak.datavalue
                    if datevalue then str = fuzzydate(str, datevalue.value.precision) end
                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

function wd.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 = wd.getDateFromQualif(statement, 'P585') -- devolver um dateobject
    if period then
        return period
    end
    local begin, ending = wd.getDateFromQualif(statement, 'P580'),  wd.getDateFromQualif(statement, 'P582')
    if begin or ending then
        return rangeObject(begin, ending) -- retourne un rangeobject fait de deux dateobject
    end
    return nil
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 = wd.getDateFromQualif(statement, 'P1319'), wd.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

wd.compare.by_quantity = function(c1, c2)
    local v1 = wd.getDataValue(c1.mainsnak)
    local v2 = wd.getDataValue(c2.mainsnak)
    if not (v1 and v2) then
            return true
    end
    return v1 < v2
end

--[[ tri chronologique générique :
             retourne une fonction de tri de liste de déclaration
             en fonction d’une fonction qui calcule la clé de tri
             et d’une fonction qui compare les clés de tri

   parâmetro nomeado: (chama type wikidata.compare.chrono_key_sort{sortKey="sem chave"})
        sortKey (optionnel)   : chaine, le nom de la clé utilisée pour un tri
                                (pour éviter de rentrer en collision avec "dateSortKey"
                                utilisé par chronoSort au besoin)
        snak_key_get_function : fonction qui calcule la valeur de la clé à partir d’un snak ou d’une déclaration,
        (obligatoire)           le résultat n’est calculé qu’une fois et est stocké en cache dans claim[sortKey]
        key_compare_function  : fonction de comparaison des clés calculées par snak_key_get_function
        (optionnel)
--]]

function wd.chrono_key_sort(arg)
	
	local snak_key_get_function = arg.snak_key_get_function
	local sortKey = arg.sortKey or "dateSortKey"
	local key_compare_function = arg.key_compare_function or
									function(c1, c2) return c1 < c2 end
	return function(claims)
		for _, claim in ipairs( claims ) do
			if not claim[sortKey] then
				local key = snak_key_get_function(claim)
				if key then
					claim[sortKey] = wd.compare.get_claim_date(key)
				else
					claim[sortKey] = 0
				end
			end
		end
		table.sort(
			claims,
			function(c1, c2)
				return key_compare_function(c1[sortKey], c2[sortKey])
			end
		)
		return claims
	end
end

function wd.quantitySort(claims, inverted)
    local function sort(c1, c2)
        local v1 = wd.getDataValue(c1.mainsnak)
        local v2 = wd.getDataValue(c2.mainsnak)
        if not (v1 and v2) then
                return true
        end
        if inverted then
            return v2 < v1
        end
        return v1 < v2
    end
    table.sort(claims, sort )
    return claims
end

function wd.compare.get_claim_date(claim, datetype) -- rend une date au format numérique pour faire des comparaisons
    local snak = claim.mainsnak or claim

    if datetype and datetype == 'personbirthdate' then -- fonctionne avec un claim dont la valeur est une personne dont on va rendre la date de naissance
        if (snak.snaktype == 'value') and (snak.datatype == 'wikibase-item') then
            local personid = wd.getId(snak)
            local birthclaims = wd.getClaims({ entity = personid, property = 'P569', numval = 1})
            if birthclaims then
                return wd.compare.get_claim_date(birthclaims[1] or birthclaims)
            else return math.huge end
        else return math.huge end -- en cas de donnée manquante, valeur infinie qui entraîne le classement en fin de liste
    end

    local iso, datequalif, isonumber
    if (snak.snaktype == 'value') and (snak.datatype == 'time') then
        iso = snak.datavalue.value.time
    else
        for i, dqualif in ipairs(datequalifiers) do
            iso = timeFromQualifs(claim, {dqualif})
            if iso then
                datequalif = dqualif
                break
            end
        end
        if not iso then return math.huge end
    end
    -- transformation en nombre (indication de la base car gsub retourne deux valeurs)
    isonumber = tonumber( iso:gsub( '(%d)%D', '%1' ), 10 )

    -- ajustement de la date tenant compte du qualificatif dont elle est issue : un fait se terminant à une date est antérieur à un autre commençant à cette date
    if datequalif == 'P582' then --date de fin
        isonumber = isonumber - 2
    elseif datequalif == 'P1326' then -- date au plus tard
        isonumber = isonumber - 1
    elseif datequalif == 'P1319' then -- date au plus tôt
        isonumber = isonumber + 1
    elseif datequalif == 'P571' or datequalif == 'P580' then -- date de début et date de création
        isonumber = isonumber + 2
    end

    return isonumber
end

function wd.compare.chronoCompare(c1, c2)
    return wd.compare.get_claim_date(c1) < wd.compare.get_claim_date(c2)
end

-- fonction pour renverser l’ordre d’une autre fonction
function wd.compare.rev(comp_criteria)
    return function(c1, c2)
        -- attention les tris en lua attendent des fonctions de comparaison strictement inférieur, on doit
        -- vérifier la non égalité quand on inverse l’ordre d’un critère, d’ou "and comp_criteria(c2,c1)"
        return not(comp_criteria(c1,c2)) and comp_criteria(c2,c1)
    end
end

-- Fonction qui trie des Claims de type time selon l'ordre chronologique
-- Une clé de tri nomée « dateSortKey » est ajouté à chaque claim.
-- Si des clés de tri de ce nom existent déjà, elles sont utilisées sans modification.

function wd.chronoSort( claims, sorttype )
    for _, claim in ipairs( claims ) do
        if not claim.dateSortKey then
            if sorttype and (sorttype == 'age' or sorttype == 'ageinverted') then
                claim.dateSortKey = wd.compare.get_claim_date(claim, 'personbirthdate')
            else
                claim.dateSortKey = wd.compare.get_claim_date(claim)
            end
            if sorttype and (sorttype == 'inverted' or sorttype == 'ageinverted') and claim.dateSortKey == math.huge then
                claim.dateSortKey = -math.huge -- quand la donnée est manquante on lui assigne la valeur qui entraîne le classement en fin de liste
            end
        end
    end
    table.sort(
        claims,
        function ( c1, c2 )
            if sorttype and (sorttype == 'inverted' or sorttype == 'ageinverted') then
                return c2.dateSortKey < c1.dateSortKey
            end
            return c1.dateSortKey < c2.dateSortKey
        end
    )
    return claims
end

local function get_numeric_claim_value(claim, propertySort)
    local val
    local claimqualifs = claim.qualifiers
    if claimqualifs then
        local vals = claimqualifs[propertySort]
        if vals and vals[1].snaktype == 'value' then
            val = vals[1].datavalue.value
        end
    end
    return tonumber(val or 0)
end

function wd.compare.numeric(propertySort)
    return function(c1, c2)
        return get_numeric_claim_value(c1, propertySort) < get_numeric_claim_value(c2, propertySort)
    end
end

-- Fonction qui trie des Claims de type value selon l'ordre de la propriété fournit
-- Une clé de tri nomée « dateSortKey » est ajouté à chaque claim.
-- Si des clés de tri de ce nom existent déjà, elles sont utilisées sans modification.

function wd.numericPropertySort( claims, propertySort )
    for _, claim in ipairs( claims ) do
        if not claim.dateSortKey then
            local val = get_numeric_claim_value(claim, propertySort)
            claim.dateSortKey = tonumber(val or 0)
        end
    end
    table.sort(
        claims,
        function ( c1, c2 )
            return c1.dateSortKey < c2.dateSortKey
        end
    )
    return claims
end

--[[
test possible en console pour la fonction précédente :
= p.formatStatements{entity = "Q375946", property = 'P50', sorttype = 'P1545', linkback = "true"}
--]]

-- ===================
function wd.getReferences(statement)
    local refdata = statement.references
    if not refdata then
        return nil
    end

    local refs = {}
    local hashes = {}
    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 -- cas lorsque P248 (affirmé dans) est utilisé
            for j, source in pairs(ref.snaks.P248) do
                if source.snaktype == 'value' then
                    local page, accessdate, quotation
                    if hasValue('P304') then -- page
                        page = wd.formatSnak(ref.snaks.P304[1])
                    end
                    if hasValue('P813') then -- date de consultation
                        accessdate = wd.formatSnak(ref.snaks.P813[1])
                    end
                    if hasValue('P1683') then -- citation
                        quotation = wd.formatSnak(ref.snaks.P1683[1])
                    end
                    local sourceId = wd.getId(source)
                    s = modules.reference.citeitem(sourceId, {['page'] = page, ['accessdate'] = accessdate, ['citation'] = quotation})
                    table.insert(refs, s)
                    table.insert(hashes, ref.hash .. sourceId)
                end
            end

        elseif hasValue('P8091') or hasValue('P854') then -- cas lorsque P8091 (Archival Resource Key) ou P854 (URL de la référence)est utilisé
            local arkKey, url, title, author, publisher, accessdate, publishdate, publishlang, quotation, description

            if hasValue('P8091') then
                arkKey = wd.formatSnak(ref.snaks.P8091[1], {text = "-"})
                url = 'https://n2t.net/' .. arkKey
                if hasValue('P1476') then
                    title = wd.formatSnak(ref.snaks.P1476[1])
                else
                    title = arkKey
                end
            elseif hasValue('P854') then
                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
            end

            --todo : handle multiple values for author, etc.
            if hasValue('P1810') then -- sous le nom
                description = 'sous le nom ' .. wd.formatSnak(ref.snaks.P1810[1])
            end
            if hasValue('P813') then -- date de consultation
                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 -- éditeur
                publisher = wd.formatSnak(ref.snaks.P123[1])
            end
            if hasValue('P1683') then -- citation
                quotation = wd.formatSnak(ref.snaks.P1683[1])
            end
            if hasValue('P577') then -- date de publication
                publishdate = wd.formatSnak(ref.snaks.P577[1])
            end
            if hasValue('P407') then -- langue de l'œuvre
                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, ['acessodata'] = accessdate, ['citacao'] = quotation}
            table.insert(hashes, ref.hash)
            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(hashes, ref.snaks.P854[1].hash)
            table.insert(refs, s)
        end
    end
    if #refs > 0 then
        if #hashes == #refs then
            return refs, hashes
        end
        return refs
    end
end

function wd.sourceStr(sources, hashes)
    if not sources or (#sources == 0) then
        return nil
    end
    local useHashes = hashes and #hashes == #sources
    for i, j in ipairs(sources) do
        local refArgs = {name = 'ref', content = j}
        if useHashes and hashes[i] ~= '-' then
            refArgs.args = {name = 'wikidata-' .. hashes[i]}
        end
        sources[i] = mw.getCurrentFrame():extensionTag(refArgs)
    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
        if params.displayformat == 'raw' then
            return value
        else
            return modules.weblink.makelink(value, params.text)
        end
    end

    if datatype == 'math' then
        return mw.getCurrentFrame():extensionTag( "math", value)
    end

    if datatype == 'tabular-data' then
        return mw.ustring.sub(value, 6, 100)  -- returns the name of the file, without the "Data:" prefix
    end

    if (datatype == 'string') or (datatype == 'external-id') or (datatype == 'commonsMedia') then -- toutes les données de type string sauf "math"
        if params.urlpattern then
            local urlpattern = params.urlpattern
            if type(urlpattern) == 'function' then
                urlpattern = urlpattern(value)
            end
            -- encodage de l'identifiant qui se retrouve dans le path de l'URL, à l'exception des slashes parfois rencontrés, qui sont des séparateurs à ne pas encoder
            local encodedValue = mw.uri.encode(value, 'PATH'):gsub('%%2F', '/')
            -- les parenthèses autour du encodedValue:gsub() sont nécessaires, sinon sa 2e valeur de retour est aussi passée en argument au mw.ustring.gsub() parent
            local url = mw.ustring.gsub(urlpattern, '$1', (encodedValue:gsub('%%', '%%%%')))
            value = '[' .. url .. ' ' .. (params.text 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 = databases.globes[value.globe] -- transforma o globo ID em nome inglês utilizável por geohack
            return coordvalue
        end
    end
    --nota : As coordenadas do Wikidata podem ser usadas em Módulo:Coordenadas. Devemos também permitir chamar Módulo:Coordenadores aqui?

    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

        if not unit then
            unit = 'dimensionless'
        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, nocat=true})
        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
    local cat = ''

    if not claims then
        claims = wd.getClaims(args)
    end
    if not claims or claims == {} then
        return {}, {}, cat
    end
    if args.removedupesdate and (args.removedupesdate ~= '-') then
        claims, cat = removeDupesDate(claims, args.removedupesdate)
    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, cat
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.labelformat2 = args.qualiflabelformat2 or args.labelformat2
    qualifargs.link = args.qualiflink or args.link
    qualifargs.linktopic = args.qualiflinktopic or args.linktopic
    qualifargs.conjtype = args.qualifconjtype
    qualifargs.precision = args.qualifprecision
    qualifargs.targetunit = args.qualiftargetunit
    qualifargs.defaultlink = args.qualifdefaultlink or args.defaultlink
    qualifargs.defaultlinkquery = args.qualifdefaultlinkquery or args.defaultlinkquery

    local formattedqualifs
    if args.qualifformat and type (args.qualifformat) == 'function' then
        formattedqualifs = args.qualifformat(statement, qualifs, qualifargs)
    else
        formattedqualifs = wd.getFormattedQualifiers(statement, qualifs, qualifargs)
    end
    if formattedqualifs and formattedqualifs ~= "" then
        str = str .. " (" .. formattedqualifs .. ")"
    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 ) -- FONCTION A REORGANISER (pas très lisible)
    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 ~= '-') and (args.displayformat ~= 'raw') then
            str = wd.addStandardQualifs(str, statement)
        end
    end

    -- ajustes diversos
    if args.showlang == true then
        local indicateur = showLang(statement, args.maxLang)
        if indicateur then
            str = indicateur .. '&nbsp;' .. 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 and args.showsource ~= '-' then
        local sources, hashes = wd.getReferences(statement)
        if sources then
            local source = wd.sourceStr(sources, hashes)
            if source then
                str = str .. source
            end
        end
    end

    return str
end

function wd.addLinkBack(str, id, property)
    if not id or id == '' then
        id = wd.getEntityIdForCurrentPage()
    end
    if not id then
        return str
    end
    if type(property) == 'table' then
        property = property[1]
    end

    id = wd.entityId(id)

    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, wd.translate("qualif-separator"))
            if qualifstr and qualifstr ~= "" then
                str = str .. " (" .. qualifstr .. ")"
            end
            return str
            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 datestr and datestr ~= "" then
                str = str .. " <small>(" .. datestr .. ")</small>"
            end
            return str
            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 hashes = {}

            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, refHashes = wd.getReferences(claim)
                if refs then
                    for i, j in pairs(refs) do
                        if not dupeRef(sources, j) then
                            table.insert(sources, j)
                            local hash = (refHashes and refHashes[i]) or '-'
                            table.insert(hashes, hash)
                        end
                    end
                end
            end
            return str .. (wd.sourceStr(sources, hashes) or "")
            end
        }
    }

    for i, group in pairs(groupedClaims) do -- bricolage para usar os argumentos de formatStatements
        local str = wd.formatEntity(group.id, args)
        if not str then
            str = '???' -- pour éviter erreur Lua si formatEntity a retourné nil
        end
        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

    -- If a value is already set: use it, except if it's the special value {{WD}} (use wikidata)
    if args.value and args.value ~= '' then
        local valueexpl = wd.translate("activate-query")
        if args.value ~= valueexpl then
            return args.value
        end
    -- There is no value set, and args.expl disables wikidata on empty values
    elseif args.expl then
        return nil
    end

    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à formatées
    local props -- as propriedades realmente usadas (em alguns casos, nem todas de args.property
    local cat = ''
    if not valuetable then -- cas le plus courant
        valuetable, props, cat = wd.stringTable(args)
    end

    if args.ucfirst == '-' and args.conjtype == 'new line' then args.conjtype = 'lowercase new line' end
    local str = modules.linguistic.conj(valuetable, args.conjtype)
    if not str then
        return args.default
    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) .. cat
    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

-- ==== Fonctions sur le genre ====

function wd.getgender(id)
    local vals = {
        ['Q6581072'] = 'f', -- féminin
        ['Q6581097'] = 'm', -- masculin
        ['Q1052281'] = 'f', -- femme transgenre
        ['Q2449503'] = 'm', -- homme transgenre
        ['Q17148251'] = 'f', -- en:Travesti (gender identity)
        ['Q43445'] = 'f', -- femelle
        ['Q44148'] = 'm', -- mâle
        default      = '?'
    }
    local gender = wd.formatStatements{entity = id, property = 'P21', displayformat = 'raw', numval = 1}
    return vals[gender] or vals.default
end

-- catégories de genre/nombre
function wd.getgendernum(claims)
    local personid, gender
    local anym = false
    local anyf = false
    local anyunknown = false

    for i, claim in pairs(claims) do
        local snak = claim.mainsnak or claim
        if(snak.snaktype == 'value') and (snak.datatype == 'wikibase-item') then
            personid = wd.getId(snak)
            gender = wd.getgender(personid)
            anym = anym or (gender == 'm')
            anyf = anyf or (gender == 'f')
            anyunknown = anyunknown or (gender == '?')
        else
            anyunknown = true
        end
    end

    local gendernum
    if #claims > 1 then
        if anyunknown then
            gendernum = 'p'
        else
            if anym and not anyf then gendernum = 'mp' end
            if anyf and not anym then gendernum = 'fp' end
            if anym and anyf then gendernum = 'mixtep' end
        end
    else
        gendernum = 's'
        if anym then gendernum = 'ms' end
        if anyf then gendernum = 'fs' end
    end

    return gendernum
end


-- récupération des libellés genrés de Wikidata
function wd.genderedlabel(id, labelgender)
    local label
    if not labelgender then return nil end
    if labelgender == 'f' then -- femme : chercher le libellé dans P2521 (libellé féminin)
        label = wd.formatStatements{entity = id, property = 'P2521', isinlang = 'fr', numval = 1, ucfirst = '-'}
    elseif labelgender == 'm' then -- homme : chercher le libellé dans P3321 (libellé masculin)
        label = wd.formatStatements{entity = id, property = 'P3321', isinlang = 'fr', numval = 1, ucfirst = '-'}
    end
    if not label then
        label = wd.getLabel(id)
    end
    return label
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 = #v, 1, -1 do
            a[#a+1] = v[i]
        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.description(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