Модуль:Таксобокс
Перейти до навігації
Перейти до пошуку
![{{i}}](http://upload.wikimedia.org/wikipedia/commons/thumb/1/19/Test_Template_Info-Icon.svg/50px-Test_Template_Info-Icon.svg.png)
![]() |
Зараз цей модуль не має відповідної документації. Якщо вам відомі призначення та засади використання цього модуля, будь ласка, напишіть хоча б коротеньку документацію. |
Дописувачі можуть експериментувати на підсторінках пісочниця (створити | дзеркало) та тести (створити) цього шаблону. Підсторінки цієї сторінки. |
local p = {}
local initialitem
local lang
local usereferences = {} -- array of references to be preffered in the given order
local hierarchy = {}
local taxondetails = ''
local code = false
local visited = {}
-- ICZN ICNafp ICNCP BC/ICNP ICVCN
local colors = {[false]='#d3d3d3', [13011]='#d3d3a4', [693148]='#9bcd9b', [764]='#a4d3d3', [743780]='#d3a4d3', [14920640]='#2f6fab'} -- background colors for each code
function p.taxobox(frame)
local config = frame.args
local count = tonumber(config.count) or 10
if count > 10 then
return 'count too high'
end
lang = config.lang or 'uk'
p.parsereferences(config.references)
return p.createbox(config.qid or mw.wikibase.getEntity().id, count)
end
-- parse references argument which is a space separated list of item numbers like "Q1 Q2 Q3" which should be preferred as references, i.e., if possible the parent taxon is choosen according to this priority list
function p.parsereferences(references)
if references then
for word in string.gmatch(references, "%w+") do
ref = tonumber(string.sub(word, 2))
mw.log('selected ref', ref)
table.insert(usereferences, ref)
end
end
end
-- creates the taxobox for the given qid (e.g., qid=Q729412 for Heloderma) and count higher levels of the taxon hierarchy.
-- developers: use this method for tests in the debug console, e.g., p.createbox('Q729412', 5)
function p.createbox(qid, count)
visited = {}
local content, references = p.iterate(qid, count, true, true)
if not content then
return
end
local item = initialitem
local header = mw.text.tag( 'tr', {}, mw.text.tag( 'th', { colspan='2', style='text-align: center; background-color: ' .. colors[code] }, p.getItemLabel(item)))
local image = p.targetStr(item, 'P18')
if image then
header = header .. mw.text.tag( 'tr', {}, mw.text.tag( 'td', {colspan='2'}, '[[File:' .. image .. '|220px|.]]'))
end
header = header .. p.fossilInfo(item)
local refstr = p.references(references)
header = header .. mw.text.tag( 'tr', {}, mw.text.tag( 'th', { colspan='2', style='text-align: center; background-color: ' .. colors[code] }, '[[Q3516404|'.. (p.getItemLabel(mw.wikibase.getEntityObject('Q3516404'))) ..']]' .. refstr))
mw.log('header', header)
mw.log('hierarchy', content)
content = header .. content
content = content .. taxondetails
mw.log('nomenclature', taxondetails)
footer = p.map(item) .. p.iucn(item) .. p.audio(item)
mw.log('footer', footer)
content = content .. footer
return mw.text.tag( 'table', { style = "width: 200px; border-width: 1px; border-style: solid; background-color: #f9f9f9; float: right;" }, content )
end
-- performs the loop up the hierarchy using P171 (parent taxon)
function p.iterate(qid, count, first, showtaxoname)
local item = mw.wikibase.getEntityObject(qid)
name = p.targetStr(item, 'P225')
if name == 'nil' then
return
end
local nextid, references = p.chooseparent(item)
mw.log('nextid', nextid)
if not code then
codeid = next(p.targetId(item, 'P944')) -- code of nomenclature
if codeid and colors[codeid] then
code = codeid
end
end
if visited[nextid] then -- loop detection
return '', {}
elseif nextid then
visited[nextid] = true
end
local showtaxoname = first or (showtaxoname and next(p.targetId(item, 'P31')) == 310890)
local content = ''
if nextid and (not code or count > 0) then
output, refs = p.iterate('Q' .. nextid, count - 1 , false, showtaxoname)
for ref,_ in pairs(refs) do
references[ref] = true
end
content = content .. output
end
if count > 0 then
nextcontent = p.itemOutput(qid, item, first, showtaxoname)
if nextcontent then
content = content .. nextcontent
end
end
return content, references
end
-- in case of more than one parent taxa: choose target according to the references selected by usereferences
function p.chooseparent(item)
local nextid = {} -- list of targets from which the first one will be used
local references = {}
local candnextid, candreferences
mw.log('chooseparent', item)
for i,j in pairs(usereferences) do
mw.log('usereferences', i, j)
end
for id,refs in pairs(p.targetId(item, 'P171')) do
if refs then
for i,r in pairs(usereferences) do
mw.log('testing', r, 'as reference')
if refs[r] then
mw.log('found', r)
nextid[i] = id
references[i] = refs
end
end
if not candreferences then
candreferences = refs
end
else
mw.log('no refs found')
end
if not candnextid then -- if no item had references yet
candnextid = id -- use this
end
end
if next(nextid) then
_,candnextid = next(nextid)
end
if next(references) then
_,candreferences = next(references)
else -- a new reference is used, append it to usereferences for further usage
if candreferences then
for targetid,_ in pairs(candreferences) do
table.insert(usereferences, targetid)
mw.log('appended ref', targetid)
end
end
end
return candnextid, candreferences or {}
end
-- formats each item in the taxon hierarchy by using p105 (taxon rank) and p225 (scientific name)
function p.itemOutput(qid, item, first, showtaxonname)
local rankid
rankid = next(p.targetId(item, 'P105'))
-- format rank
local rankstr = mw.text.tag( 'td', {}, 'unknown rank')
local ranklink = 'taxon'
if rankid then
if rankid == 'novalue' then
rankstr = mw.text.tag( 'td', {}, '')
else
local rankitem = mw.wikibase.getEntityObject('Q' .. rankid)
if rankitem then
ranklink = '[[Q' .. rankid .. '|' .. p.getItemLabel(rankitem) .. ']]'
if rankid == 713623 then
rankstr = mw.text.tag( 'td', {}, '')
else
rankstr = mw.text.tag( 'td', {}, ranklink)
end
end
end
end
-- format vernacular and scientific names
local vernacularname = p.vernacularname(item)
local name, namequalifiers, namereferences = p.targetStr(item, 'P225') -- scientific name
local plainname = name or 'no scientific name'
if rankid then
hierarchy[rankid] = plainname
end
if rankid == 7432 or rankid == 68947 or rankid == 34740 or rankid == 3238261 then -- italic in case of species (7432) or subspecies (34740) or genus (34740) or subgenus(3238261) rank
plainname = mw.text.tag('i', {}, plainname)
end
if vernacularname and showtaxonname then -- print only trivial label if taxon details are printed
plainname = mw.text.tag('td', {}, '[[' .. qid .. '|' .. vernacularname .. ']]')
elseif vernacularname then
plainname = mw.text.tag('td', {}, '[[' .. qid .. '|' .. vernacularname .. ']] (' .. plainname .. ')')
else
plainname = mw.text.tag('td', {}, '[[' .. qid .. '|' .. plainname .. ']]')
end
if first then
initialname = taxonname
initialitem = item
end
if showtaxonname then
p.taxondetails(item, name, rankid, ranklink, namequalifiers, namereferences)
end
return mw.text.tag( 'tr', {}, rankstr .. plainname)
end
function p.vernacularname(item)
local vernacularname = item:formatPropertyValues('P1843') -- vernacular name
if vernacularname then
vernacularname = vernacularname['value']
if vernacularname == '' then
vernacularname = nil
end
end
if not vernacularname then
vernacularname = p.getItemLabel(item) -- test if item label is not one of the scientific names
scnames = p.mergeClaims(p.targetStrs(item, 'P225'))
for _,n in pairs(scnames) do
if vernacularname == n then
return
end
end
end
return vernacularname
end
-- shows detailed information for current and all next monotypic taxa
function p.taxondetails(item, name, rankid, rank, namequalifiers, namereferences)
mw.log('show details for', name, rank, code)
-- remove already displayed references
if namereferences then
for refid,_ in pairs(namereferences) do
mw.log('name reference', refid)
end
end
local refstr = p.references(namereferences)
taxondetails = taxondetails .. mw.text.tag('tr', {}, mw.text.tag( 'th', { colspan="2", style='text-align: center; background-color: ' .. colors[code] }, 'scientific name of ' .. rank .. refstr))
local fullname = name
if rankid == 3238261 and p.icnafpApplies() then -- format subgenus according to ICNafp §5A.1. (genus name is part of P225)
strstart, strend = string.find(fullname, "%s+subg%.%s+")
if strstart and strend then
fullname = mw.text.tag('i', {}, string.sub(fullname, 1, strstart)) .. ' subg. ' .. mw.text.tag('i', {}, string.sub(fullname, strend))
end
elseif rankid == 3238261 and hierarchy[34740] then -- format subgenus according to ICZN (genus name is not part of P225)
fullname = mw.text.tag('i', {}, hierarchy[34740] .. ' (' .. fullname .. ')')
elseif rankid == 7432 or rankid == 68947 or rankid == 34740 or rankid == 3238261 then -- italic in case of species (7432) or subspecies (34740) or genus (34740) or subgenus(3238261) rank
fullname = mw.text.tag('i', {}, fullname)
end
taxondetails = taxondetails .. mw.text.tag('tr', {}, mw.text.tag( 'td', {colspan="2", style="text-align: center"}, fullname))
local year = p.qualifierTargetTime(namequalifiers, 'P574') or p.targetTime(item, 'P574') -- date of taxon name publication
if year then
year = string.sub(year, 2, 5) -- access year in time representation "+1758-00-00T00:00:00Z"
else
year = '????'
end
local authorsstr = p.createAllAuthorsStr(item, namequalifiers, year)
if authorsstr then
taxondetails = taxondetails .. mw.text.tag( 'tr', {}, mw.text.tag( 'td', {colspan="2", style="text-align: center; font-variant:small-caps"}, authorsstr))
end
end
-- create the taxon authors string, including year, ex authors and authors of the basionym
function p.createAllAuthorsStr(item, namequalifiers, year)
local authors = p.authorString(item, namequalifiers)
local authorsstr = ''
if authors or not year == '????' then
if p.icnafpApplies() then
-- check for basionym
local basionymids = p.targetId(item, 'P566')
local basionymstr = ''
if next(basionymids) then
local basionym = mw.wikibase.getEntityObject('Q' .. next(basionymids))
local _,basionymnamequalifiers = p.targetStr(basionym, 'P225')
basionymstr = p.createAllAuthorsStr(basionym, basionymnamequalifiers)
if basionymstr then
basionymstr = '(' .. basionymstr .. ') '
end
end
-- check ex-authors
local exauthors = p.authorString(nil, namequalifiers, 'P697')
exauthorsstr = ''
mw.log(exauthors)
if exauthors then
exauthorsstr = exauthors .. ' ex '
end
authorsstr = basionymstr .. exauthorsstr .. authors
if year then
authorsstr = authorsstr .. ' (' .. year .. ')'
end
else
authorsstr = authors .. ', ' .. year
-- parentheses needed if instance of recombination
local recombination = false
for _,tid in pairs(p.qualifierTargetId(namequalifiers, 'P31')) do
if tid == 14594740 then
recombination = true
end
end
if recombination then
authorsstr = '(' .. authorsstr .. ')'
end
end
return authorsstr
end
end
function p.authorString(item, namequalifiers, pid)
if not pid then
pid = 'P405' -- property
end
local authorids = p.qualifierTargetId(namequalifiers, pid) -- get qualifiers
if not next(authorids) then -- no qualifiers found, check properties
local authorset = p.targetId(item, pid)
local authors = {}
if authorset then -- create list from set
authorids = {}
for author,_ in pairs(authorset) do
table.insert(authorids, author)
end
end
end
local authors = p.createLinks(authorids, true)
if next(authors) then
local concatstr = ', '
local last = table.remove(authors)
local rest = ''
if #authors > 0 then
rest = table.concat(authors, ', ') .. ' & '
end
return rest .. last
end
end
-- check if International Code of Nomenclature for algae, fungi, and plants (ICNafp) applies
function p.icnafpApplies()
return code == 693148
end
-- show the stratigraphic range in which an extinct fossil existed
function p.fossilInfo(item)
local info = ''
local era1, era1references = next(p.targetId(item, 'P523'))
local era2, era2references = next(p.targetId(item, 'P524'))
if era1 and era2 and not (era1 == 'novalue' or era2 == 'novalue') then
local era1item = mw.wikibase.getEntityObject('Q' .. era1)
if era1item then
eralink = '[[Q' .. era1 .. '|' .. p.getItemLabel(era1item) .. ']]'
if not (era1 == era2) then
local era2item = mw.wikibase.getEntityObject('Q' .. era2)
if era2item then
eralink = eralink .. '—[[Q' .. era2 .. '|' .. p.getItemLabel(era2item) .. ']]'
end
end
for a, b in pairs(era2references) do -- show references for both, era1 and era2, only once
era1references[a] = b
end
local refstr = p.references(era1references)
info = info .. mw.text.tag( 'tr', {}, mw.text.tag( 'th', {colspan='2', style='text-align: center; background-color: ' .. colors[code]}, '[[Q630830|era]]' .. refstr))
info = info .. mw.text.tag( 'tr', {}, mw.text.tag( 'td', {colspan='2', style='text-align: center;'}, eralink))
end
end
return info
end
-- the label of the item if present in the specified language or 'no label'
function p.getItemLabel(item)
local label = 'no label'
if item then
label = item:getLabel(lang) or label
end
return label
end
-- Gives the first highest ranked claim and its references.
-- use only if the data type of the property is item
function p.targetId( item, property)
local claims = p.targetIds(item, property)
if next(claims.preferred) then
return claims.preferred
end
if next(claims.normal) then
return claims.normal
end
return claims.deprecated
end
-- Collect all claims of the given property of the item
-- Returns all claims and their references in tables combined by the claims' rank.
-- result.preferred[target id of claim] = [target id of P248 reference]
-- use only if the data type of the property is item
function p.targetIds(item, property)
local claims = {preferred = {}, normal = {}, deprecated = {}}
if item and item.claims and item.claims[property] then
for _,claim in pairs(item.claims[property]) do
if claim.mainsnak.datavalue and claim.mainsnak.datavalue.value then
local valueid = claim.mainsnak.datavalue.value['numeric-id']
local refids = {}
if claim.references then
for _,ref in pairs(claim.references) do
if ref.snaks['P248'] then
for prop, refclaim in pairs(ref.snaks['P248']) do
refids[tostring('Q' .. refclaim.datavalue.value['numeric-id'])] = true
end
end
end
end
claims[claim.rank][valueid] = refids
else
claims[claim.rank]['novalue'] = true -- snaktype not value
end
end
end
return claims
end
-- Gives the first highest ranked claim and its qualifiers and references.
-- Use only if the data type of the property is string
function p.targetStr(item, property)
choosenclaim, choosenqualifiers, choosenreferences = p.targetStrs(item, property)
index = next(choosenclaim['preferred'])
if index then
return choosenclaim['preferred'][index], choosenqualifiers['preferred'][index], choosenreferences['preferred'][index]
end
index = next(choosenclaim['normal'])
if index then
return choosenclaim['normal'][index], choosenqualifiers['normal'][index], choosenreferences['normal'][index]
end
index = next(choosenclaim['deprecated'])
if index then
return choosenclaim['deprecated'][index], choosenqualifiers['deprecated'][index], choosenreferences['deprecated'][index]
end
return
end
-- Collect all claims of the given property of the item
-- Returns a triple of claims, their qualifiers, and their references in tables combined by the claims' rank.
-- Use only if the data type of the property is string
function p.targetStrs(item, property)
choosenclaim = {preferred = {}, normal = {}, deprecated = {}}
choosenqualifiers = {preferred = {}, normal = {}, deprecated = {}}
choosenreferences = {preferred = {}, normal = {}, deprecated = {}}
if item and item.claims and item.claims[property] then
for _,claim in pairs(item.claims[property]) do
if claim.mainsnak and claim.mainsnak.datavalue then
index = #choosenclaim[claim.rank] + 1
mw.log(index, claim.mainsnak.datavalue.value)
local refids = {}
if claim.references then
for _,ref in pairs(claim.references) do
if ref.snaks['P248'] then
for prop, refclaim in pairs(ref.snaks['P248']) do
refids['Q' .. refclaim.datavalue.value['numeric-id']] = true
mw.log('string ref', refclaim.datavalue.value['numeric-id'])
end
end
end
end
if claim.mainsnak.datatype == 'monolingualtext' then
choosenclaim[claim.rank][index] = tostring(claim.mainsnak.datavalue.value)
else
choosenclaim[claim.rank][index] = tostring(claim.mainsnak.datavalue.value)
end
choosenqualifiers[claim.rank][index] = claim.qualifiers
choosenreferences[claim.rank][index] = refids
end
end
end
return choosenclaim, choosenqualifiers, choosenreferences
end
-- helper function to merge all claims, regardless of rank
function p.mergeClaims(claims, qualifiers, references)
c = claims['preferred'] or {}
if qualifiers then
q = qualifiers['preferred'] or {}
end
if references then
r = references['preferred'] or {}
end
if claims and claims['normal'] then
for i, value in ipairs(claims['normal']) do
c[#c + 1] = value
if qualifiers then
q[#q + 1] = qualifiers['normal'][i]
end
if references then
r[#r + 1] = references['normal'][i]
end
end
end
if claims and claims['deprecated'] then
for i, value in ipairs(claims['deprecated']) do
c[#c + 1] = value
if qualifiers then
q[#q + 1] = qualifiers['deprecated'][i]
end
if references then
r[#r + 1] = references['deprecated'][i]
end
end
end
return c, q, r
end
-- Use only if the data type of the property is time
function p.targetTime(item, property)
if item and item.claims and item.claims[property] then
for _,claim in pairs(item.claims[property]) do
if claim.mainsnak and claim.mainsnak.datavalue then
return claim.mainsnak.datavalue.value.time
end
end
end
end
-- same as targetId but for qualifiers
-- TODO merge
function p.qualifierTargetId(qualifiers, property)
local claims = {}
if qualifiers and qualifiers[property] then
for _,claim in pairs(qualifiers[property]) do
local valueid = claim.datavalue.value['numeric-id']
table.insert(claims, valueid)
end
end
return claims
end
-- same as targetTime but for qualifiers
-- TODO merge
function p.qualifierTargetTime(qualifiers, property)
local claims = {}
if qualifiers and qualifiers[property] then
for _,claim in pairs(qualifiers[property]) do
if claim.datavalue then
mw.log('time qualifier', property, claim.datavalue.value.time)
return claim.datavalue.value.time
end
end
end
end
-- takes a list of item ids (the values of the given table) and creates wikilinks based on their labels
function p.createLinks(list, authorAbbreviation)
local authors = {}
for _,authorid in pairs(list) do
if authorid then
local author = mw.wikibase.getEntityObject('Q' .. authorid)
if author then
local label = p.getItemLabel(author)
if authorAbbreviation then
if p.icnafpApplies() then
if p.targetStr(author, 'P428') then -- P428 author citation per IPNI set
label = p.targetStr(author, 'P428')
end
elseif p.targetStr(author, 'P835') then -- P835 author citation set
label = p.targetStr(author, 'P835')
elseif label and label ~= 'no label' then
for word in mw.ustring.gmatch(label, "%w+") do
label = word
end
end
end
table.insert(authors, '[[Q' .. tostring(authorid) .. '|' .. label .. ']]')
end
end
end
return authors
end
function p.map(item)
local map = p.targetStr(item, 'P181')
if map then
return mw.text.tag( 'tr', {}, mw.text.tag( 'th', { colspan='2', style='text-align: center; background-color: ' .. colors[code] }, 'range map')) .. mw.text.tag( 'tr', {}, mw.text.tag( 'td', {colspan='2'}, '[[File:' .. map .. '|220px|.]]'))
end
return ''
end
function p.iucn(item)
local statusid, statusreferences = next(p.targetId(item, 'P141')) -- IUCN conservation status
if statusid then
local status = mw.wikibase.getEntityObject('Q' .. statusid)
local img, imgqualifiers, imgreferences = p.targetStr(status, 'P18') -- image
if img then
local refstr = p.references(statusreferences)
return mw.text.tag( 'tr', {}, mw.text.tag( 'th', { colspan='2', style='text-align: center; background-color: ' .. colors[code] }, '[[Q32059|red list status]]' .. refstr)) .. mw.text.tag( 'tr', {}, mw.text.tag( 'td', {colspan='2'}, '[[File:' .. img .. '|220px|' .. p.getItemLabel(status) .. ']]'))
end
end
return ''
end
-- shows the audio file
function p.audio(item)
local audio, audioreferences = p.targetStr(item, 'P51') -- audio file
if audio then
return mw.text.tag( 'tr', {}, mw.text.tag( 'th', { colspan='2', style='text-align: center; background-color: ' .. colors[code] }, '[[Property:P51|audio]]')) .. mw.text.tag( 'tr', {}, mw.text.tag( 'td', {colspan='2'}, '[[File:' .. audio .. ']]'))
end
return ''
end
-- returns html for the given refids set
-- parameters:
-- refids: list of integer ID to create a list of <ref>-references
function p.references(refids)
local frame = mw.getCurrentFrame()
local refstr = ''
if refids then
for id,_ in pairs(refids) do
--local ref = Cite.citeitem(id, lang or 'en') or 'Error during creation of citation. Please report [[' .. id .. ']] at [[Module_talk:Cite]]'
mw.log('refstr for ', id, ref)
refstr = refstr .. frame:extensionTag('ref', ref, {name=id})
end
end
return refstr
end
return p