Modiwl:Taxobox
Wikimedia module / From Wikipedia, the free encyclopedia
-- vim: set noexpandtab ft=lua ts=4 sw=4:
local config = {} -- TODO find out what this is supposed to be and fix it! local P_AUDIO = '?' -- TODO find out what this is supposed to be and fix it!
local ENABLE_DEBUG = true
local Cite = require('Modiwl:Cite') local fb = require('Modiwl:Fallback')
local p = {} -- module exports local L = {} -- alias to local functions -- (so it can be iterated by p in debug mode) local _linkconfig -- use links from content language Wikipedia -- or from Wikidata, default to Wikidata local _contentlang local usereferences -- array of references to be prefered in the given order local usetaxa -- array of taxa to be preferred in the given order local hideranks -- array of ranks to be show or hide from display local code = false local visited = {} -- background colors for each code local colors = { [false] = '#d3d3d3', [13011] = '#d3d3a4', -- ICZN [693148] = '#9bcd9b', -- ICNafp [764] = '#a4d3d3', -- ICNCP [743780] = '#d3a4d3', -- BC/ICNP [14920640] = '#2f6fab', -- ICVCN }
local i18nmessages = require("Modiwl:I18n/taxobox")
-- readable taxon properties local P_IMAGE = "P18" local P_INSTANCE_OF = "P31" local P_TAXON_RANK = "P105" local P_IUCN_STATUS = "P141" local P_TAXON_PARENT = "P171" local P_SPREAD_MAP = "P181" local P_TAXON_NAME = "P225" local P_STATED_IN = "P248" local P_AUTHOR = "P405" local P_AUTHOR_ABBR_IPNI = "P428" local P_ERA_START = "P523" local P_ERA_END = "P524" local P_BASINYM= "P566" local P_TAXON_YEAR = "P574" local P_END_TIME = "P582" local P_EX_AUTHOR = "P697" local P_AUTHOR_ABBR_ZOOLOGY = "P835" local P_NOMENCLATURE_CODE = "P944" local P_COMMON_NAME = "P1843"
-- readable item local CLADE = 713623 local GENUS = 34740 local SUBGENUS = 3238261 local RED_DATA_LIST = 32059 local MONOTYPIC_TAXON = 310890 local GEOLOGICAL_ERA = 630830 local SYSTEMATICS = 3516404 local RECOMBINATION = 14594740 local EXTINCT = 237350
local function capitalize(text)
return mw.ustring.gsub(text, "^%l", mw.ustring.upper)
end
local function mergeTable(a, b)
for _, value in ipairs(b) do
a[#a + 1] = value
end
return a
end
L.mergeTable = mergeTable
-- credit to http://lua-users.org/wiki/StringInterpolation
local function namedStringFormat(str, vars)
-- Allow replace_vars{str, vars} syntax as well as
-- replace_vars(str, {vars})
if not vars then
vars = str
str = vars[1]
end
return (string.gsub(str, "({([^}]+)})",
function(whole,i)
return vars[i] or whole
end))
end
L.namedStringFormat = namedStringFormat
local function setLang(contentlang)
_contentlang = contentlang or "en"
end
L.setLang = setLang
local function getLang()
return _contentlang
end
L.getLang = getLang
local function i18n(str)
local message = i18nmessages[str]
if type(message) == 'string' then
return message
end
return fb._langSwitch(message, getLang())
end
L.i18n = i18n
-- parse item-ids like argument (like config[references]) which is a space
-- separated list of item numbers like "Q1 Q2 Q3"
local function parseItemIds(itemids)
local items = {}
local priority = 0
if itemids then
for word in string.gmatch(itemids, "%w+") do
priority = priority + 1
local item = "Q" .. tonumber(string.sub(word, 2))
items[item] = priority
end
end
items.size = priority
return items
end
L.parseItemIds = parseItemIds
-- parse config arguments passed by #invode:taxobox. below are all we
-- support currently:
-- - config[lang]: set content language (default: en)
-- - config[count]: maximum count of taxon to be recursively iterated
-- - config[references]: references to be preffered in the given order
-- - config[dryrun]: generate
block instead of expanding template -- - config[link]: local or wikidata local function parseConfig(args) setLang(args["config[lang]"]) -- TODO what? local count = tonumber(args["config[count]"]) or 10 if count > 25 then -- roughly about 100 expensive parser function calls error(i18n("taxon-count-too-high")) end usereferences = parseItemIds(args["config[references]"]) usetaxa = parseItemIds(args["config[usetaxa]"]) hideranks = {} local displaypattern = "^display%[([^]]+)%]$" local qidpattern = "^Q?(%d+)$" for k, v in pairs(args) do v = mw.ustring.lower(v) if string.match(k, displaypattern) then k = string.gsub(k, displaypattern, "%1") if string.match(k, qidpattern) then k = string.gsub(k, qidpattern, "%1") k = tonumber(k) end -- TODO: i18n? if ({n=true, no=true, ["false"]=true, hide=true})[v] then hideranks[k] = true end end end _linkconfig = "wikidata" if args["config[link]"] and mw.ustring.lower(args["config[link]"]) == "sitelink" then _linkconfig = "sitelink" end return { ["count"] = count, -- ["lang"] = lang, -- this fails with "Tried to read nil global lang" from Module:No_globals required by Module:Wikidata ["lang"] = config['lang'], -- TODO this is a quick guess at what might have been intended ["dryrun"] = args["config[dryrun]"], ["link"] = _linkconfig } end -- check if International Code of Nomenclature for algae, fungi, and plants (ICNafp) applies local function icnafpApplies() return code == 693148 end L.icnafpApplies = icnafpApplies -- the label of the item if present in the specified language or 'no label' -- TODO: merge with getLabel local function getItemLabel(item, lang) local label = i18n('no-label') if item then label = item:getLabel(lang or getLang()) or label end return label end L.getItemLabel = getItemLabel local function getLabel(id) if type(id) == "number" then id = "Q" .. id end return getItemLabel(mw.wikibase.getEntity(id)) end L.getLabel = getLabel local function getLink(id, label, format, named) if type(id) == "number" then id = "Q" .. id end local link = id if _linkconfig == "sitelink" then link = mw.wikibase.sitelink(id) end label = label or getItemLabel(mw.wikibase.getEntity(id)) format = format or "%s" if named then return namedStringFormat{format, link=link, label=label} else return string.format(format, link, label) end end L.getLink = getLink -- 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 local function 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 for prop, refclaim in pairs(ref.snaks[P_STATED_IN] or {}) do refids[tostring('Q' .. refclaim.datavalue.value['numeric-id'])] = true end end end claims[claim.rank][valueid] = refids else claims[claim.rank]['novalue'] = true -- snaktype not value end end end return claims end L.targetIds = targetIds -- Gives the first highest ranked claim and its references. -- use only if the data type of the property is item local function targetId(item, property) local claims = 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 L.targetId = targetId -- 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 local function targetStrs(item, property) local choosenclaim = {preferred = {}, normal = {}, deprecated = {}} local choosenqualifiers = {preferred = {}, normal = {}, deprecated = {}} local 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 local 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 for prop, refclaim in pairs(ref.snaks[P_STATED_IN] or {}) do refids['Q' .. refclaim.datavalue.value['numeric-id']] = true mw.log('string ref', refclaim.datavalue.value['numeric-id']) 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 L.targetStrs = targetStrs -- Gives the first highest ranked claim and its qualifiers and references. -- Use only if the data type of the property is string local function targetStr(item, property) local choosenclaim, choosenqualifiers, choosenreferences = targetStrs(item, property) for _, priority in pairs({"preferred", "normal", "deprecated"}) do local index = next(choosenclaim[priority]) if index then return choosenclaim[priority][index], choosenqualifiers[priority][index], choosenreferences[priority][index] end end return end L.targetStr = targetStr -- helper function to merge all claims, regardless of rank local function mergeClaims(claims, qualifiers, references) local c = {} local q = {} local r = {} for _, priority in pairs({"preferred", "normal", "deprecated"}) do mergeTable(c, claims[priority] or {}) mergeTable(q, qualifiers[priority] or {}) mergeTable(r, references[priority] or {}) end return c, q, r end L.mergeClaims = mergeClaims -- Use only if the data type of the property is time local function 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 L.targetTime = targetTime -- same as targetId but for qualifiers -- TODO merge local function 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 L.qualifierTargetId = qualifierTargetId -- same as targetTime but for qualifiers -- TODO merge local function 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 L.qualifierTargetTime = qualifierTargetTime -- takes a list of item ids (the values of the given table) and creates wikilinks based on their labels local function createLinks(list, authorAbbreviation) local authors = {} for _,authorid in pairs(list) do if authorid then local author = mw.wikibase.getEntity('Q' .. authorid) if author then local label if authorAbbreviation then if icnafpApplies() then -- get author abbrieviation per IPNI set if targetStr(author, P_AUTHOR_ABBR_IPNI) then label = targetStr(author, P_AUTHOR_ABBR_IPNI) end elseif targetStr(author, P_AUTHOR_ABBR_ZOOLOGY) then -- get zoologist author citation set label = targetStr(author, P_AUTHOR_ABBR_ZOOLOGY) end if not label then -- use the "last" name if no abbreviation found -- also don't use the translated name label = getItemLabel(author, "en") if label ~= i18n('no-label') then local _ _, _, label = mw.ustring.find(label, "(%w+)$") end end end table.insert(authors, getLink(authorid, label)) end end end return authors end L.createLinks = createLinks local function vernacularName(item) local vernacularname -- select vernacular name for current language if item.claims and item.claims[P_COMMON_NAME] then for _, claim in pairs(item.claims[P_COMMON_NAME]) do if claim.mainsnak and claim.mainsnak.datavalue and claim.mainsnak.datavalue.type == "monolingualtext" and claim.mainsnak.datavalue.value.language == getLang() then vernacularname = claim.mainsnak.datavalue.value.text break end end if vernacularname == then vernacularname = nil end end if not vernacularname then -- test if item label is not one of the scientific names vernacularname = getItemLabel(item) local scnames = mergeClaims(targetStrs(item, P_TAXON_NAME)) for _, n in pairs(scnames) do if vernacularname == n then return end end end if vernacularname == i18n("no-label") then return end return capitalize(vernacularname) end L.vernacularName = vernacularName local function authorString(item, namequalifiers, pid) pid = pid or P_AUTHOR -- set default property local concatstr = ', ' local authorids = qualifierTargetId(namequalifiers, pid) -- get qualifiers if not next(authorids) then -- no qualifiers found, check properties local authorset = 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 = createLinks(authorids, true) if next(authors) then local last = table.remove(authors) local rest = if #authors > 0 then rest = table.concat(authors, concatstr) .. ' & ' end return rest .. last end end L.authorString = authorString -- create the taxon authors string, including year, ex authors and authors of the basionym local function createAllAuthorsStr(item, namequalifiers, year) local authors = authorString(item, namequalifiers) local authorsstr = if authors or not year == '????' then if icnafpApplies() then -- check for basionym local basionymids = targetId(item, P_BASINYM) local basionymstr = if next(basionymids) then local basionym = mw.wikibase.getEntity('Q' .. next(basionymids)) local _,basionymnamequalifiers = targetStr(basionym, P_TAXON_NAME) basionymstr = createAllAuthorsStr(basionym, basionymnamequalifiers) if basionymstr ~= then basionymstr = '(' .. basionymstr .. ') ' else -- indicate missing basionym author basionymstr = '(????) ' end end -- check ex-authors local exauthors = authorString(nil, namequalifiers, P_EX_AUTHOR) local 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(qualifierTargetId(namequalifiers, P_INSTANCE_OF)) do if tid == RECOMBINATION then recombination = true end end if recombination then authorsstr = '(' .. authorsstr .. ')' end end end return authorsstr end L.createAllAuthorsStr = createAllAuthorsStr -- show the stratigraphic range in which an extinct fossil existed local function fossilParams(item, params) local era1, era1references = next(targetId(item, P_ERA_START)) local era2, era2references = next(targetId(item, P_ERA_END)) if era1 and era2 and not (era1 == 'novalue' or era2 == 'novalue') then local era1item = mw.wikibase.getEntity("Q" .. era1) if era1item then params["era[1][id]"] = era1 params["era[1][label]"] = getItemLabel(era1item) if not (era1 == era2) then local era2item = mw.wikibase.getEntity("Q" .. era2) if era2item then params["era[2][id]"] = era2 params["era[2][label]"] = getItemLabel(era2item) end end -- merge references from era2 to era1, only show once for a, b in pairs(era2references) do era1references[a] = b end -- TODO: return data structure instead of pure str here params["era[references]"] = era1references end end end L.fossilParams = fossilParams -- returns html for the given refids set -- parameters: -- refids: list of integer ID to create a list of <ref>-references local function references(refids) local frame = mw.getCurrentFrame() local refstr = if refids then for id,_ in pairs(refids) do local ref = Cite.citeitem(id, getLang()) or 'Error during creation of citation. Please report ' .. id .. ' at Sgwrs modiwl:Cite' mw.log('refstr for ', id, ref) refstr = refstr .. frame:extensionTag('ref', ref, {name=id}) end end return refstr end L.references = references local function i18nByLatin(ranklatin, str, default) local suc, format = pcall(i18n, str .. "-" .. ranklatin) if not suc then format = default end return format end L.i18nByLatin = i18nByLatin local function formatScientificName(ranklatin, scientific, short) local pf = "scientific-name" if short then pf = "short-" .. pf end local scipattern = i18nByLatin( ranklatin, pf .. "-pattern", i18n(pf .. "-pattern")) local scirepl = i18nByLatin( ranklatin, pf .. "-repl", i18n(pf .. "-repl")) scientific = string.gsub(scientific, scipattern, scirepl) for scipattern, scirepl in pairs( i18nByLatin(ranklatin, pf .. "-replaces", i18n(pf .. "-replaces"))) do scientific = string.gsub(scientific, scipattern, scirepl) end return scientific end L.formatScientificName = formatScientificName local function renderTableHead(text, color) local bgcolor = "" if color then bgcolor = bgcolor .. " background-color: " .. color .. ";" end return mw.text.tag('tr', {}, mw.text.tag('th', { colspan='2', style="text-align: center;" .. bgcolor }, text)) end L.renderTableHead = renderTableHead local function renderTableRow(text, extra_css) local css = "text-align: center;" if extra_css then css = css .. " " .. extra_css end return mw.text.tag('tr', {}, mw.text.tag('td', {colspan='2', style=css}, text)) end L.renderTableRow = renderTableRow local function renderFossilEra(params) local eralink = {} local refstr = references(params["era[references]"]) for i = 1, 2 do local eraid = params[string.format("era[%d][id]", i)] if eraid then eralink[#eralink + 1] = getLink(eraid) end end return renderTableHead(getLink(GEOLOGICAL_ERA) .. refstr, params.color) .. renderTableRow(table.concat(eralink, 'ā')) end L.renderFossilEra = renderFossilEra local function renderIUCNStatus(params) local r = {} local refstr = references(params["iucn_status[references]"]) r[#r + 1] = renderTableHead( getLink(RED_DATA_LIST, getLabel(P_IUCN_STATUS)) .. refstr, params.color) -- TODO: support SVG systemLanguage here r[#r + 1] = renderTableRow( "[[File:" .. params["iucn_status[image]"] .. "|220px|" .. params["iucn_status[label]"] .. "]]") return table.concat(r) end L.renderIUCNStatus = renderIUCNStatus local function formatTaxon( latin, qid, scientific, vernacular, is_subject, is_extinct) local nameformat scientific = scientific or i18n("no-scientific-name") local scientificshort = scientific if latin then scientificshort = formatScientificName(latin, scientific, true) scientific = formatScientificName(latin, scientific) end local nf = "item-format-parent" if is_subject then nf = "item-format-current" end if vernacular then nameformat = i18n(nf .. "-with-vernacular-name") else nameformat = i18n(nf .. "-without-vernacular-name") end local link = qid if _linkconfig == "sitelink" then link = mw.wikibase.sitelink(qid) end if is_extinct then nameformat = i18n("extinct-mark") .. nameformat end return namedStringFormat{ nameformat, link=link, vernacular=vernacular, scientific=scientific, scientificshort=scientificshort}, scientific end L.formatTaxon = formatTaxon local function renderRank(i, params) local row local nameformat local detailrows = {} local pf = string.format("rank[%d]", i) local ranklink = i18n("unknown-rank") local rankid = params[pf .. "[id]"] local ranklatin = params[pf .. "[latin]"] local is_subject = params[pf .. "[is_subject]"] local scientific = params[pf .. "[scientific]"] local formatted = params[pf .. "[taxon]"] if rankid then local linkformat = i18nByLatin( ranklatin, "rank-format", i18n("rank-format")) ranklink = getLink(rankid, nil, linkformat, true) end row = mw.text.tag( 'tr', {}, mw.text.tag('td', {}, ranklink) .. mw.text.tag('td', {}, formatted)) if is_subject then local refstr = references(params[pf .. "[references]"]) local authority = params[pf .. "[authority]"] detailrows = { renderTableHead( string.format(i18n("scientific-name-of-taxon"), getLabel(rankid)) .. refstr, params.color ), renderTableRow(scientific), renderTableRow(authority, "font-variant: small-caps;") } end return row, detailrows end L.renderRank = renderRank -- in case of more than one parent taxa: choose target according to the -- references selected by usereferences local function chooseParent(item) local cand local nextparent = {} for id,refs in pairs(targetId(item, P_TAXON_PARENT)) do -- some taxon, like Q2382443, the parent taxon is null local novalue = id == "novalue" -- try to find match from usetaxa if not novalue and usetaxa["Q" .. id] then table.insert(nextparent, {usetaxa["Q" .. id], id, refs}) end -- or according to usereferences if refs and type(refs) ~= "boolean" then for r, i in pairs(usereferences) do if refs[r] then table.insert(nextparent, {i + usetaxa.size, id, refs}) end end end if not novalue and not cand then -- if no item had references yet cand = {nil, id, refs} -- use this end end -- nextparent is not sorted, so sort it table.sort(nextparent, function(a, b) return a[1] < b[1] end) if next(nextparent) then local _ _, cand = next(nextparent) end if cand and cand[1] == nil and cand[3] then for targetid, _ in pairs(cand[3]) do usereferences.size = usereferences.size + 1 usereferences[targetid] = usereferences.size end end if cand then return cand[2], cand[3] or {} else return nil, {} end end L.chooseParent = chooseParent -- Find out if this taxon is extinct already local function isExtinct(item) -- check IUCN status local statusid, _ = next(targetId(item, P_IUCN_STATUS)) if statusid == EXTINCT then return true end -- check temporal range end local eraend, _ = next(targetId(item, P_ERA_END)) if eraend then return true end -- check end time local endtime = targetTime(item, P_END_TIME) if endtime then return true end return false end L.isExtinct = isExtinct -- Find out if the item is a monotypic taxon local function isMonotypic(item) return next(targetId(item, P_INSTANCE_OF)) == MONOTYPIC_TAXON end L.isMonotypic = isMonotypic local function taxonParams(qid, item, params, fetch_detail, is_parent_extinct) local rankid local ranklatin local is_subject local level = params["rank[size]"] + 1 local pf = string.format("rank[%d]", level) local name, namequalifiers, namereferences = targetStr(item, P_TAXON_NAME) local vernacular = vernacularName(item) local is_extinct = is_parent_extinct or isExtinct(item) if rankid == SUBGENUS and string.match(name, "^%w+$") and params[string.format("rank[%d][id]", level - 1)] == GENUS then -- follow ICZN to prepend genus name in front of subgenus name name = string.format("%s (%s)", params[string.format("rank[%d][raw_scientific]", level - 1)], mw.ustring.lower(name)) end rankid = next(targetId(item, P_TAXON_RANK)) if rankid == "novalue" then rankid = CLADE end if rankid then local rankitem = mw.wikibase.getEntity('Q' .. rankid) ranklatin = rankitem:getLabel('la') end if ranklatin then ranklatin = mw.ustring.lower(ranklatin) end if (hideranks[rankid] or hideranks[ranklatin]) and not usetaxa[qid] then -- interrupt since this rank has been hided from display return params, is_extinct end name = capitalize(name) params["rank[size]"] = level params[pf .. "[id]"] = rankid params[pf .. "[link]"] = qid params[pf .. "[is_monotypic]"] = isMonotypic(item) params[pf .. "[vernacular]"] = vernacular params[pf .. "[raw_scientific]"] = name params[pf .. "[latin]"] = ranklatin params[pf .. "[is_extinct]"] = is_extinct if fetch_detail then -- get detail is_subject = true params[pf .. "[references]"] = namereferences local year = qualifierTargetTime(namequalifiers, P_TAXON_YEAR) or targetTime(item, P_TAXON_YEAR) if year then -- access year in time representation "+1758-00-00T00:00:00Z" year = string.sub(year, 2, 5) else year = '????' end local authorsstr = createAllAuthorsStr(item, namequalifiers, year) params[pf .. "[authority]"] = authorsstr else is_subject = false end local formatted, sciname = formatTaxon( ranklatin, qid, name, vernacular, is_subject, is_extinct) params[pf .. "[scientific]"] = sciname params[pf .. "[is_subject]"] = is_subject params[pf .. "[taxon]"] = formatted return params, is_extinct end L.taxonParams = taxonParams -- performs the loop up the hierarchy using P_TAXON_PARENT local function iterateRanks( qid, count, fetch_detail, child_detailed, child_extinct, params) local inneritem local params = params or {["rank[size]"] = 0} local item = mw.wikibase.getEntity(qid) local name = targetStr(item, P_TAXON_NAME) if name == 'nil' then return params, {}, item end local nextid, references = chooseParent(item) mw.log('nextid', nextid) if not code then local codeid = next(targetId(item, P_NOMENCLATURE_CODE)) if codeid and colors[codeid] then code = codeid end end if visited[nextid] then -- loop detection return params, {}, item elseif nextid then visited[nextid] = true end -- Monotypic taxon can contain extinct taxa, -- should not fetch detail in such circumstances -- for example: Q7105303 local fetch_detail = fetch_detail or (child_detailed and not child_extinct and isMonotypic(item)) local is_extinct, is_parent_extinct if nextid and (not code or count > 0) then local refs, _ params, refs, _, is_parent_extinct = iterateRanks( 'Q' .. nextid, count - 1, false, fetch_detail, isExtinct(item), params) for ref, _ in pairs(refs) do references[ref] = true end end if count > 0 then params, is_extinct = taxonParams( qid, item, params, fetch_detail, is_parent_extinct) end params["code"] = code params["color"] = colors[code] return params, references, item, is_extinct end L.iterateRanks = iterateRanks -- use arguments from second table to override the first table -- support classical Mae'r ID a roddwyd yn anhysbys i'r system. Defnyddiwch ID dilys i'r endid data. parameters like "species", "unranked_ordo" local function overrideParams(params, overrides) overrides = overrides or {} for key, val in pairs(overrides) do params[key] = overrides[key] or params[key] end -- classical taxonomic rank params local unranked = {} for i = 1, params["rank[size]"] do local pf = string.format("rank[%d]", i) local latin = params[pf .. "[latin]"] if latin == "clade" then unranked[#unranked + 1] = i else local txarg = pf .. "[taxon]" local atarg = pf .. "[authority]" params[txarg] = overrides[latin] or params[txarg] params[atarg] = overrides[latin .. "_authority"] or params[atarg] for j = #unranked, 1, -1 do local txarg = string.format("rank[%d][taxon]", unranked[j]) local atarg = string.format("rank[%d][authority]", unranked[j]) local argname = string.format("unranked_%s", latin) if j == #unranked then -- TODO Changed "override" to "overrides" twice, but that just a guess. params[txarg] = overrides[argname] or overrides[argname .. "1"] or params[txarg] params[atarg] = overrides[argname .. "_authority"] or overrides[argname .. "1_authority"] or params[atarg] else params[txarg] = overrides[string.format('%s%d', argname, #unranked - j + 1)] or params[txarg] params[atarg] = overrides[string.format('%s%d_authority', argname, #unranked - j + 1)] or params[atarg] end end unranked = {} end if latin == "species" then local scarg = pf .. "[scientific]" params[scarg] = overrides["binomial"] or params[scarg] elseif ({subspecies=true, varietas=true, forma=true})[latin] then local scarg = pf .. "[scientific]" params[scarg] = overrides["trinomial"] or params[scarg] end end return params end L.overrideParams = overrideParams -- fetch params should passed to 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.localFunction("getTaxoboxParams")('Q729412', 5) local function getTaxoboxParams(qid, count) visited = {} local params, references, item = iterateRanks(qid, count, true) if params["rank[size]"] == 0 then return {} end local scarg = string.format("rank[%d][scientific]", params["rank[size]"]) local vnarg = string.format("rank[%d][vernacular]", params["rank[size]"]) params["name"] = params[vnarg] or params[scarg] capitalize(getItemLabel(item)) local image = targetStr(item, P_IMAGE) params["image"] = image fossilParams(item, params) params["rank[references]"] = references local map = targetStr(item, P_SPREAD_MAP) params["range_map"] = map local statusid, statusreferences = next(targetId(item, P_IUCN_STATUS)) params["iucn_status[id]"] = statusid if statusid then local status = mw.wikibase.getEntity("Q" .. statusid) local img, imgqualifiers, imgreferences = targetStr(status, P_IMAGE) if img then params["iucn_status[label]"] = getItemLabel(status) params["iucn_status[references]"] = statusreferences params["iucn_status[image]"] = img end end local audio, audioreferences = targetStr(item, P_AUDIO) params["audio"] = audio return params end L.getTaxoboxParams = getTaxoboxParams local function callbackTaxobox(template, params, overrides, dryrun) local content = {} local frame = mw.getCurrentFrame() params = overrideParams(params, overrides) for key, val in pairs(params) do if type(val) == "boolean" then val = val and "yes" or "no" elseif type(val) == "table" and string.match(key, "%[references%]$") then local refs = {} for r, _ in pairs(val) do table.insert(refs, r) end val = table.concat(refs, " ") end if dryrun then content[#content + 1] = string.format( "|%s = %s", key, val) else params[key] = val end end if dryrun then table.sort(content, function(a, b) a = string.gsub(a, "%[(%d+)%]", function(i) return "[" .. string.rep("0", 3 - #i) .. i .. "]" end) b = string.gsub(b, "%[(%d+)%]", function(i) return "[" .. string.rep("0", 3 - #i) .. i .. "]" end) return a < b end) content = "{{" .. template .. "\n" .. table.concat(content, "\n") .. "\n}}\n" content = frame:callParserFunction("#tag", "nowiki", content) return mw.text.tag("pre", {}, content) else return frame:expandTemplate{title=template, args=params} end end L.callbackTaxobox = callbackTaxobox -- creates the taxobox from giving params local function renderTaxobox(params, overrides) local content = {} params = overrideParams(params, overrides) local color = params.color -- title content[#content + 1] = renderTableHead(params.name, color) -- image if params.image then content[#content + 1] = renderTableRow( "Delwedd:" .. params.image .. "") end -- fossil era if params["era[1][id]"] then content[#content + 1] = renderFossilEra(params) end -- systematics local refstr = references(params["rank[references]"]) content[#content + 1] = renderTableHead( getLink(SYSTEMATICS) .. refstr, color) -- ranks if params["rank[size]"] > 0 then local taxondetails = {} for i = 1, params["rank[size]"] do local row, detailrows = renderRank(i, params) content[#content + 1] = row taxondetails[#taxondetails + 1] = table.concat(detailrows) end content[#content + 1] = table.concat(taxondetails) end -- range map if params.range_map then content[#content + 1] = renderTableHead(i18n('range-map'), color) content[#content + 1] = renderTableRow( "Delwedd:" .. params.range map .. "") end -- iucn status if params["iucn_status[id]"] then content[#content + 1] = renderIUCNStatus(params) end -- audio if params.audio then content[#content + 1] = renderTableHead(getLink(P_AUDIO), color) content[#content + 1] = renderTableRow("Delwedd:" .. params.audio .. "") end return mw.text.tag('table', { style = [[ width: 200px; border-width: 1px; float: right; border-style: solid; background-color: #f9f9f9; ]] }, table.concat(content)) end L.renderTaxobox = renderTaxobox if debug then function p.debugParams(params) mw.log("Start of logging params") mw.log(string.rep("=", 20)) for k, v in pairs(params) do mw.log(k, v) end mw.log("End of logging params") end function p.localFunction(name) return L[name] end end function p.taxobox(frame) local config = parseConfig(frame.args) local qid = frame.args.qid local params = getTaxoboxParams(qid, config.count) return renderTaxobox(params, frame.args) end function p.callback(frame) local config = parseConfig(frame.args) local qid = frame.args.qid local template = frame.args.template or "Taxobox" local params = getTaxoboxParams(qid, config.count) return callbackTaxobox(template, params, frame.args, config.dryrun) end return p