Module:MedicoInfobox
From determinar.ia.br - Determine suas informações
Documentation for this module may be created at Module:MedicoInfobox/doc
-- Module:MedicoInfobox v3.1
-- FAQ (P56) e Desambiguação (P74) em divs (tags permitidas pelo Sanitizer)
-- Mapa tenta CID primeiro (comprovado funcionando na sua wiki), depois endereço
local p = {}
local CFG = {
foto = 'P1',
nome = 'P2',
registro = 'P4',
especialidade = 'P10',
subesp = 'P11',
servicos = { 'P13', 'P14', 'P18', 'P19', 'P23' },
logradouro = 'P28',
bairro = 'P29',
email = 'P30',
website = 'P31',
agendamento = 'P32',
lat = 'P33',
lon = 'P34',
convenio = 'P35',
estado = 'P36',
operadora = 'P37',
opinioes = 'P38',
estrelas = 'P39',
duvidas = 'P40',
faq = 'P56',
doctoralia = 'P57',
scholar_user = 'P62',
municipio = 'P71',
cep = 'P72',
bio = 'P73',
escopo_neg = 'P74',
maps_cid = 'P75',
horario = 'P76',
publicacoes = 'P77',
fonte = 'P79',
coleta = 'P80',
}
local EXCLUIR_CHIPS = { 'unimed', 'vivest', 'seguros' }
local function contemLixo( s )
local ls = string.lower( s or '' )
for _, pad in ipairs( EXCLUIR_CHIPS ) do
if ls:find( pad, 1, true ) then return true end
end
return ls:find( 'rua ' ) or ls:find( 'avenida' ) or ls:find( 'av. ' ) or false
end
local function esc( s )
if not s then return '' end
return mw.text.encode( tostring( s ), '<>&"' )
end
local function linkExterno( url, texto )
if not url or url == '' then return esc( texto or '' ) end
return '[' .. url .. ' ' .. ( texto or url ) .. ']'
end
local function rotulo( ent, lang )
if not ent then return nil end
for _, l in ipairs( { 'pt-br', 'pt', 'mul', lang, 'en' } ) do
local lb = ent:getLabel( l )
if lb then return lb end
end
return nil
end
local function descricao( ent, lang )
if not ent then return '' end
for _, l in ipairs( { 'pt-br', 'pt', 'mul', lang, 'en' } ) do
local d = ent:getDescription( l )
if d then return d end
end
return ''
end
local function snakParaTexto( snak, lang )
if not snak or snak.snaktype ~= 'value' then return nil end
local dv = snak.datavalue
if not dv then return nil end
if dv.type == 'string' or dv.type == 'url' or dv.type == 'external-id' then
return dv.value
elseif dv.type == 'monolingualtext' then
return dv.value.text
elseif dv.type == 'quantity' then
return tostring( dv.value.amount ):gsub( '^%+', '' )
elseif dv.type == 'time' then
return ( dv.value.time or '' ):match( '(%d+%-%d+%-%d+)' )
elseif dv.type == 'wikibase-entityid' then
local alvo = mw.wikibase.getEntity( dv.value.id )
return rotulo( alvo, lang ) or dv.value.id
end
return nil
end
local function valor( ent, pid, lang )
if not ent then return nil end
local sts = ent:getBestStatements( pid )
if not sts then return nil end
for _, st in ipairs( sts ) do
local v = snakParaTexto( st.mainsnak, lang )
if v and v ~= '' then return v end
end
return nil
end
local function valores( ent, pid, lang )
local out = {}
if not ent then return out end
local sts = ent:getBestStatements( pid )
if not sts then return out end
for _, st in ipairs( sts ) do
local v = snakParaTexto( st.mainsnak, lang )
if v and v ~= '' then out[#out + 1] = v end
end
return out
end
local function qualificador( ent, pid, qpid, lang )
if not ent then return nil end
local sts = ent:getBestStatements( pid )
if not sts or not sts[1] then return nil end
local qs = sts[1].qualifiers
if not qs then return nil end
local lista = qs[qpid]
if not lista then return nil end
for _, q in ipairs( lista ) do
local v = snakParaTexto( q, lang )
if v then return v end
end
return nil
end
-- Divide "Q: pergunta?A: resposta" em pares { q, a }
local function parseFAQ( texto )
local itens = {}
if not texto then return itens end
if not texto:find( 'Q:', 1, true ) then
local t = texto:gsub( '^%s+', '' ):gsub( '%s+$', '' )
if t ~= '' then itens[#itens + 1] = { q = '', a = t } end
return itens
end
local pos = 1
while true do
local qs = texto:find( 'Q:', pos, true )
if not qs then break end
local qe = texto:find( 'A:', qs + 2, true )
if not qe then break end
local pergunta = texto:sub( qs + 2, qe - 1 ):gsub( '^%s+', '' ):gsub( '%s+$', '' )
local ae = texto:find( 'Q:', qe + 2, true ) or ( #texto + 1 )
local resposta = texto:sub( qe + 2, ae - 1 ):gsub( '^%s+', '' ):gsub( '%s+$', '' )
if pergunta ~= '' then itens[#itens + 1] = { q = pergunta, a = resposta } end
pos = ae
end
return itens
end
local function chips( lista )
local s = ''
for _, v in ipairs( lista ) do
if not contemLixo( v ) then
s = s .. '<span class="chip-especialidade">' .. esc( v ) .. '</span> '
end
end
return s
end
local function estrelas( n )
n = math.floor( tonumber( n ) or 0 )
n = math.max( 0, math.min( 5, n ) )
return string.rep( '★', n ) .. string.rep( '☆', 5 - n )
end
-- Tenta o embed; devolve nil se a extensão rejeitar (cai no link estilizado)
local function embedMapa( frame, url, largura, altura )
local ok, html = pcall( frame.extensionTag, frame, 'perfilexterno', '', {
url = url, largura = largura, altura = altura
} )
if ok and html and html ~= ''
and not html:find( 'não permitida', 1, true )
and not html:find( 'permitida', 1, true ) then
return html
end
return nil
end
function p.main( frame )
local args = frame.args
local id = ( args.ID or args[1] or '' ):match( '^%s*(.-)%s*$' )
local lang = mw.getContentLanguage():getCode()
local ent = mw.wikibase.getEntity( id ~= '' and id or nil )
if not ent then
return '<div class="infobox-medico"><div class="infobox-medico-titulo">Entidade não encontrada — informe ID=Q…</div></div>'
end
local nome = valor( ent, CFG.nome, lang ) or rotulo( ent, lang ) or '—'
local desc = descricao( ent, lang )
local foto = valor( ent, CFG.foto, lang )
local regs = valores( ent, CFG.registro, lang )
local esp = valor( ent, CFG.especialidade, lang )
local sub = valor( ent, CFG.subesp, lang )
local logradouro = valor( ent, CFG.logradouro, lang )
local bairro = valor( ent, CFG.bairro, lang )
local municipio = valor( ent, CFG.municipio, lang )
local estado = valor( ent, CFG.estado, lang )
local cep = valor( ent, CFG.cep, lang )
local horario = valor( ent, CFG.horario, lang )
local convenio = valor( ent, CFG.convenio, lang )
local operadora = valor( ent, CFG.operadora, lang )
local email = valor( ent, CFG.email, lang )
local website = valor( ent, CFG.website, lang )
local agenda = valor( ent, CFG.agendamento, lang )
local doc = valor( ent, CFG.doctoralia, lang )
local estrelasN = valor( ent, CFG.estrelas, lang )
local opinioes = valor( ent, CFG.opinioes, lang )
local bio = valor( ent, CFG.bio, lang )
local cid = valor( ent, CFG.maps_cid, lang )
local pubs = valores( ent, CFG.publicacoes, lang )
local scholar = valor( ent, CFG.scholar_user, lang )
local escopoNeg = valores( ent, CFG.escopo_neg, lang )
local fonteTxt = qualificador( ent, CFG.registro, CFG.fonte, lang ) or qualificador( ent, CFG.bio, CFG.fonte, lang )
local coletaTxt = qualificador( ent, CFG.registro, CFG.coleta, lang ) or qualificador( ent, CFG.bio, CFG.coleta, lang )
local servicos = {}
for _, spid in ipairs( CFG.servicos ) do
for _, v in ipairs( valores( ent, spid, lang ) ) do
servicos[#servicos + 1] = v
end
end
-- FAQ: uma declaração P56 = um par Q&A (varios Q:...?A:... numa string tambem funciona)
local faqs = {}
for _, t in ipairs( valores( ent, CFG.faq, lang ) ) do
for _, par in ipairs( parseFAQ( t ) ) do
faqs[#faqs + 1] = par
end
end
local out = {}
out[#out + 1] = '<div class="infobox-medico">'
if foto then
out[#out + 1] = '<div class="infobox-medico-imagem">'
.. frame:preprocess( '[[File:' .. foto .. '|300px|center]]' ) .. '</div>'
end
out[#out + 1] = '<div class="infobox-medico-titulo">' .. esc( nome ) .. '</div>'
if desc and desc ~= '' then
out[#out + 1] = '<div class="infobox-medico-descricao">' .. esc( desc ) .. '</div>'
end
local espChips = {}
if esp then espChips[#espChips + 1] = esp end
if sub then espChips[#espChips + 1] = sub end
if #espChips > 0 then
out[#out + 1] = '<div class="infobox-medico-chips">' .. chips( espChips ) .. '</div>'
end
if estrelasN then
local nota = '<div class="medico-avaliacao"><span class="medico-estrelas">' .. estrelas( estrelasN )
.. '</span> <span class="medico-nota">' .. esc( estrelasN ) .. '/5</span>'
if opinioes then
if doc then
nota = nota .. ' · ' .. linkExterno( doc, opinioes .. ' opiniões' )
else
nota = nota .. ' · ' .. esc( opinioes ) .. ' opiniões'
end
end
out[#out + 1] = nota .. '</div>'
end
out[#out + 1] = '<table class="infobox-medico-tabela">'
local function linha( r, v )
if v and v ~= '' then
return '<tr><th>' .. esc( r ) .. '</th><td>' .. v .. '</td></tr>'
end
return ''
end
if #regs > 0 then
local regHtml = {}
for _, r in ipairs( regs ) do regHtml[#regHtml + 1] = esc( r ) end
out[#out + 1] = linha( 'Registro', table.concat( regHtml, '<br>' ) )
end
local partesEnd = {}
for _, v in ipairs( { logradouro, bairro, municipio, estado, cep } ) do
if v and v ~= '' then partesEnd[#partesEnd + 1] = v end
end
if #partesEnd > 0 then
out[#out + 1] = linha( 'Endereço', esc( table.concat( partesEnd, ', ' ) ) )
end
if horario then out[#out + 1] = linha( 'Horário', esc( horario ) ) end
if convenio then out[#out + 1] = linha( 'Convênio', esc( convenio ) ) end
if operadora then out[#out + 1] = linha( 'Operadora', esc( operadora ) ) end
if email then out[#out + 1] = linha( 'E-mail', linkExterno( 'mailto:' .. email, email ) ) end
if website then
local siteHref = website:match( '^https?://' ) and website or ( 'https://' .. website )
out[#out + 1] = linha( 'Site', linkExterno( siteHref, website ) )
end
if doc then
out[#out + 1] = linha( 'Doctoralia', linkExterno( doc, 'Ver perfil' ) )
end
out[#out + 1] = '</table>'
if bio and bio ~= '' then
out[#out + 1] = '<div class="infobox-medico-bio">' .. esc( bio ) .. '</div>'
end
if #servicos > 0 then
out[#out + 1] = '<div class="infobox-medico-secao">Procedimentos e serviços</div>'
out[#out + 1] = '<div class="infobox-medico-chips">' .. chips( servicos ) .. '</div>'
end
if agenda then
out[#out + 1] = '<div class="cta-agendar">' .. linkExterno( agenda, 'Agendar consulta' ) .. '</div>'
end
-- Mapa: CID PRIMEIRO (comprovado funcionando na sua wiki), depois endereço
if cid and cid ~= '' or logradouro then
local endMapa = table.concat( { logradouro, bairro, municipio, estado }, ', ' )
local candidatas = {}
if cid and cid ~= '' then
candidatas[#candidatas + 1] = 'https://maps.google.com/maps?cid=' .. cid .. '&output=embed&hl=pt-BR'
candidatas[#candidatas + 1] = 'https://www.google.com/maps?cid=' .. cid .. '&output=embed&hl=pt-BR'
end
if endMapa ~= '' then
candidatas[#candidatas + 1] = 'https://maps.google.com/maps?q=' .. mw.uri.encode( endMapa, 'QUERY' )
.. '&z=16&output=embed&hl=pt-BR'
candidatas[#candidatas + 1] = 'https://www.google.com/maps?q=' .. mw.uri.encode( endMapa, 'QUERY' )
.. '&z=16&output=embed&hl=pt-BR'
end
local htmlMapa = nil
for _, u in ipairs( candidatas ) do
htmlMapa = embedMapa( frame, u, '100%', '300' )
if htmlMapa then break end
end
if htmlMapa then
out[#out + 1] = '<div class="medico-mapa">' .. htmlMapa .. '</div>'
else
local alvo
if cid and cid ~= '' then
alvo = 'https://maps.google.com/maps?cid=' .. cid
else
alvo = 'https://maps.google.com/maps?q=' .. mw.uri.encode( endMapa, 'QUERY' )
end
out[#out + 1] = '<div class="medico-mapa-link">'
.. linkExterno( alvo, 'Ver localização no Google Maps' ) .. '</div>'
end
end
if #pubs > 0 then
out[#out + 1] = '<div class="infobox-medico-secao">Publicações científicas</div>'
out[#out + 1] = '<ul class="infobox-medico-pubs">'
local n = math.min( 5, #pubs )
for i = 1, n do
local u = pubs[i]
out[#out + 1] = '<li>' .. linkExterno( u, u:gsub( '^https?://', '' ):gsub( '^www%.', '' ) ) .. '</li>'
end
out[#out + 1] = '</ul>'
if #pubs > n then
local resto = #pubs - n
local alvo = scholar and ( 'https://scholar.google.com/citations?user=' .. scholar .. '&hl=pt-BR' ) or ''
if alvo ~= '' then
out[#out + 1] = '<div class="medico-mais-pubs">'
.. linkExterno( alvo, '+ ' .. resto .. ' publicações no Google Scholar' ) .. '</div>'
else
out[#out + 1] = '<div class="medico-mais-pubs">+ ' .. resto .. ' publicações científicas</div>'
end
end
end
-- Desambiguação (div, tag permitida) — fica ANTES do FAQ
if #escopoNeg > 0 then
out[#out + 1] = '<div class="medico-desamb">'
out[#out + 1] = '<div class="medico-desamb-titulo">Desambiguação — o que este perfil <b>não</b> é</div>'
out[#out + 1] = '<ul class="medico-desamb-lista">'
for _, v in ipairs( escopoNeg ) do
out[#out + 1] = '<li>' .. esc( v ) .. '</li>'
end
out[#out + 1] = '</ul></div>'
end
-- FAQ logo abaixo da desambiguação
if #faqs > 0 then
out[#out + 1] = '<div class="infobox-medico-secao">Perguntas Frequentes</div>'
for _, par in ipairs( faqs ) do
out[#out + 1] = '<div class="medico-faq">'
if par.q ~= '' then
out[#out + 1] = '<div class="medico-faq-q">' .. esc( par.q ) .. '</div>'
end
out[#out + 1] = '<div class="medico-faq-a">' .. esc( par.a ) .. '</div>'
out[#out + 1] = '</div>'
end
end
if fonteTxt or coletaTxt then
out[#out + 1] = '<div class="infobox-medico-rodape">Dados curados · fonte: ' .. esc( fonteTxt or '—' )
if coletaTxt then out[#out + 1] = ' · verificado em ' .. esc( coletaTxt ) end
out[#out + 1] = '</div>'
end
out[#out + 1] = '</div>'
return table.concat( out )
end
return p
