Modiwl:Taxobox
Lua error in Modiwl:Cite at line 166: attempt to concatenate local 'journaltitle' (a nil value). Lua error in Modiwl:Cite at line 166: attempt to concatenate local 'journaltitle' (a nil value).
This module is still unstable. Use with your own caution and report bugs and feature requests at Module talk:Taxobox or Wikidata talk:WikiProject_Taxonomy.
Taxobox.lua is a lua module which can automatically generate taxonomy infobox and is overwritable by classic taxobox parameters like species
, unranked_ordo
etc.
This module infobox is designed to be a replacement of Wikipedia's Taxobox. It provides configuration options which can control hypernym paths, show or hide certain ranks, specify content language and its configs, and make a callback and pass parameters to "classic" taxobox.
The following code
{{Taxobox | qid=Q464424 }}
creates the taxobox on the right hand side. The item to show is given with qid.
If you want to have this taxobox show up on each Wikidata taxon item: [1]
Internal
[golygu cod]Method taxobox
[golygu cod]The taxobox
method provides function runs the above example. The method itself can be invoked with
{{#invoke: Taxobox | taxobox | qid=Q464424 | config[count]=10 }}
The number of parent taxons to show is given by config[count]
.
Method callback
[golygu cod]The callback
method provides function to retrieve all internal parameters and pass them to an external template. The method can be invoked with like this
{{#invoke: Taxobox | callback | qid=Q464424 | template=OtherTaxobox | config[count]=10 }}
callback
accepts all arguments that taxobox
accepted. It also accepts an extra argument template
to specify name of the template to be expanded.
I18n
[golygu cod]Change the i18n messages in Module:I18n/taxobox. I18n also specifys some format strings which can be use to customize the infobox output of certain language.
message | description | example(s) |
---|---|---|
rank-format
|
Format of instances of rheng tacson (Q427626), or cytras (Q713623). Will be passed into 2 named arguments when rendering ranks:
|
|
rank-format-<latinrank>
|
This argument is similar to rank-format but can be use to specify the format of certain rank. The "<latinrank>" is a latin name, can be a instance of rheng tacson (Q427626), or cytras (Q713623). For example "rank-format-cladus " for cytras (Q713623). It accepts same 2 named arguments like rank-format
|
|
item-format-current-with-vernacular-name
|
Format of instances of tacson (Q16521) or tacson un eitem (Q310890). current means either the item is the main taxon (specified by module argument qid ), or the item and all taxa between the item and main taxon (if any) are all tacson un eitem (Q310890). with-vernacular-name means the item's or label exists and is different from .
4 named arguments will be passed on redering:
Note: Don't use italic style here on scientific names. This style can be set by |
|
item-format-current-with-vernacular-name
|
Format of instances of tacson (Q16521) or tacson un eitem (Q310890). without-vernacular-name means either both and label (in specified language) are empty, or the vernacular name is the same as .
4 named arguments will be passed on rendering, and they are same to |
|
scientific-name-pattern , scientific-name-repl
|
Pattern replacement to generate full scientific name. Can be override per rank by scientific-name-pattern-<latinrank> and scientific-name-repl-<latinrank> .
Not the format of |
|
short-scientific-name-pattern , short-scientific-name-repl
|
Pattern replacement to generate short scientific name. Can be override per rank by short-scientific-name-pattern-<latinrank> and short-scientific-name-repl-<latinrank> .
|
|
scientific-name-pattern-<latinrank> , scientific-name-repl-<latinrank>
|
Pattern replcacement to generate full scientific name per rank. The "<latinrank>" is a latin name, can be a instance of rheng tacson (Q427626), or cytras (Q713623). For example "scientific-name-pattern-species " for rhywogaeth (Q7432).
|
|
short-scientific-name-pattern-<latinrank> , short-scientific-name-repl-<latinrank>
|
Pattern replcacement to generate short scientific name per rank. The "<latinrank>" is a latin name, can be a instance of rheng tacson (Q427626), or cytras (Q713623). For example "short-scientific-name-pattern-species " for rhywogaeth (Q7432).
|
|
scientific-name-replaces or short-scientific-name-replaces
|
Pattern replacements to apply for all full (or short) scientific names, after the name has been processed by pattern-repl pair described above.
The value for each of two messages is not a string but a table. The table contains multiple pattern-repl pairs which will be applied to scientific names. Note: Lua's table object doesn't sort, so the replacement sequence CAN NOT be guaranteed. DON'T DEPEND ON THE SEQUENCE YOU SAW! |
Input Parameters
[golygu cod]The "<latinrank>" below is a latin name, can be an instance of rheng tacson (Q427626), or cytras (Q713623). For example "display[cladus]"
.
Config Options
[golygu cod]config[lang]
: content language (default:en
).config[count]
: maximum count of taxon to be recursively iterated (default: 10).config[references]
: a space-separated list of item ids. The references to favor in case of alternative claims. Optional.config[usetaxa]
: a space-separated list of item ids. The taxa to favor in case of alternative claims. Optional.config[link]
: if the value is "sitelink" it will use local wiki site links instead of wikidata item links.config[dryun]
: used forcallback
method. Displaying a <pre> block contains wikitext instead of expanding and rendering the template. Dryrun can be used to find parameters to be overrided.
Examples
[golygu cod]Lua error in Modiwl:Cite at line 166: attempt to concatenate local 'journaltitle' (a nil value).
{{#invoke: taxobox | taxobox | qid = Q140 | config[usetaxa] = Q27379 |config[count] = 7 }}
Display Options
[golygu cod]display[<latinrank>]
: if the value is "n", "no", "false" or "hide", the specified rank (in latin name or QID) will be hide. Otherwise the rank will display.
For example, to hide all clades: Lua error in Modiwl:Cite at line 166: attempt to concatenate local 'journaltitle' (a nil value).
{{#invoke:taxobox |taxobox |qid=Q140 |display[cladus]=hide }}
Classic Parameters
[golygu cod]<latinrank>
: The classic taxon parameters used by most Nodyn:Blwch tacson (Q52496). All of them can be overrided manually.<latinrank>_authority
: The classic taxon authority parameters used by most Nodyn:Blwch tacson (Q52496). All of them can be overrided manually.unranked_<latinrank>
: The classic unranked parameters used by most Nodyn:Blwch tacson (Q52496). All of them can be overrided manually.
If there are more than one clades between two taxon ranks, you can override them by appending [<number>]
index to the unranked_<latinrank>
parameter.
For example, there are 3 clades between rank genus and rank species. You can override them like this:
{{#invoke:taxobox |taxobox |... |unranked_species[3] = Cladus closest to genus rank |unranked_species[2] = The middle clade |unranked_species[1] = Cladus closest to species rank |... }}
Output Parameters
[golygu cod]The best way to see all output parameters is to use the config[dryrun]
parameter:
{{#invoke:taxobox |callback |qid=Q140 |config[dryrun]=yes }}
The result:
{{Taxobox |code = 13011 |color = #d3d3a4 |config[dryrun] = yes |image = 002 The lion king Snyggve in the Serengeti National Park Photo by Giles Laurent.jpg |iucn_status[id] = 278113 |iucn_status[image] = Status iucn3.1 VU.svg |iucn_status[label] = Vulnerable |iucn_status[references] = Q115962546 |name = Lion |qid = Q140 |range_map = Lion distribution.png |rank[1][id] = 713623 |rank[1][is_extinct] = no |rank[1][is_monotypic] = no |rank[1][is_subject] = no |rank[1][latin] = cladus |rank[1][link] = Q5852697 |rank[1][raw_scientific] = Prozostrodontia |rank[1][scientific] = Prozostrodontia |rank[1][taxon] = [[Q5852697|Prozostrodontia]] |rank[2][id] = 713623 |rank[2][is_extinct] = no |rank[2][is_monotypic] = no |rank[2][is_subject] = no |rank[2][latin] = cladus |rank[2][link] = Q28746016 |rank[2][raw_scientific] = Mammaliamorpha |rank[2][scientific] = Mammaliamorpha |rank[2][taxon] = [[Q28746016|Mammaliamorpha]] |rank[3][id] = 713623 |rank[3][is_extinct] = no |rank[3][is_monotypic] = no |rank[3][is_subject] = no |rank[3][latin] = cladus |rank[3][link] = Q2082668 |rank[3][raw_scientific] = Mammaliaformes |rank[3][scientific] = Mammaliaformes |rank[3][taxon] = [[Q2082668|Mammaliaformes]] |rank[4][id] = 37517 |rank[4][is_extinct] = no |rank[4][is_monotypic] = no |rank[4][is_subject] = no |rank[4][latin] = classis |rank[4][link] = Q7377 |rank[4][raw_scientific] = Mammalia |rank[4][scientific] = Mammalia |rank[4][taxon] = [[Q7377|Mammalia]] |rank[4][vernacular] = Mammal |rank[5][id] = 36602 |rank[5][is_extinct] = no |rank[5][is_monotypic] = no |rank[5][is_subject] = no |rank[5][latin] = ordo |rank[5][link] = Q25306 |rank[5][raw_scientific] = Carnivora |rank[5][scientific] = Carnivora |rank[5][taxon] = [[Q25306|Carnivora]] |rank[6][id] = 5867959 |rank[6][is_extinct] = no |rank[6][is_monotypic] = no |rank[6][is_subject] = no |rank[6][latin] = subordo |rank[6][link] = Q27070 |rank[6][raw_scientific] = Feliformia |rank[6][scientific] = Feliformia |rank[6][taxon] = [[Q27070|Feliformia]] |rank[7][id] = 35409 |rank[7][is_extinct] = no |rank[7][is_monotypic] = no |rank[7][is_subject] = no |rank[7][latin] = familia |rank[7][link] = Q25265 |rank[7][raw_scientific] = Felidae |rank[7][scientific] = Felidae |rank[7][taxon] = [[Q25265|Felidae]] |rank[8][id] = 164280 |rank[8][is_extinct] = no |rank[8][is_monotypic] = no |rank[8][is_subject] = no |rank[8][latin] = subfamilia |rank[8][link] = Q230177 |rank[8][raw_scientific] = Pantherinae |rank[8][scientific] = Pantherinae |rank[8][taxon] = [[Q230177|Pantherinae]] |rank[9][id] = 34740 |rank[9][is_extinct] = no |rank[9][is_monotypic] = no |rank[9][is_subject] = no |rank[9][latin] = genus |rank[9][link] = Q127960 |rank[9][raw_scientific] = Panthera |rank[9][scientific] = <i>Panthera</i> |rank[9][taxon] = [[Q127960|<i>Panthera</i>]] |rank[9][vernacular] = Big cats |rank[10][authority] = [[Q1043|Linnaeus]], 1758 |rank[10][id] = 7432 |rank[10][is_extinct] = no |rank[10][is_monotypic] = no |rank[10][is_subject] = yes |rank[10][latin] = species |rank[10][link] = Q140 |rank[10][raw_scientific] = Panthera leo |rank[10][references] = Q1538807 |rank[10][scientific] = <i>Panthera leo</i> |rank[10][taxon] = <b><i>P. leo</i></b> |rank[10][vernacular] = Lion |rank[references] = Q30135809 Q1538807 Q796451 Q30136284 Q33883775 Q19302303 Q28191107 Q30136117 Q82575 |rank[size] = 10 }}
All output parameters can be overrided by specifying same name input parameters. For example this will replace genws (Q34740) to "Foo" and Isdeulu (Q164280) "Bar":
Lua error in Modiwl:Cite at line 166: attempt to concatenate local 'journaltitle' (a nil value).
{{#invoke:taxobox |taxobox |qid=Q140 |subfamilia=<strong style="color: green">Bar</strong> |rank[9][taxon]=<strong style="color: red">Foo</strong> }}
Supported properties
[golygu cod]The taxobox currently supports:
- for taxon images and red list status
- : for tacson un eitem (Q310890) and (Q14594740)
- also mark the taxon extinct if the value is difodwyd (Q237350)
- for references
- also mark the taxon extinct if present
- mark the taxon extinct if present
- if ICNafp applies, otherwise:
- , otherwise the last name of English language (en) is shown.
- for authority string format and color.
- , common name of a language to override the item label of the language
Wikipedia use
[golygu cod]This module is designed to be a replacement for Wikipedia taxoboxes. However, it is still unstable and need plenty extra template works to allow a classic Nodyn:Blwch tacson (Q52496) to accept the new callback parameters. Suggestion and bug reports are welcome at Module talk:Taxobox or Wikidata talk:WikiProject_Taxonomy.
References
[golygu cod]- ↑ Integrated Taxonomic Information System
- ↑ Martin Schorr, Dennis R. Paulson, Klaas-Douwe B. Dijkstra, Cyrille Deliry and Federico Lozano, World Odonata List, University of Alabama
- ↑ National Center for Biotechnology Information (ed.), Q13711410 (no title property provided!)
- ↑ Fossilworks
- ↑ Klaas-Douwe B. Dijkstra, Günter Bechly, Seth M. Bybee, Rory A. Dow, Henri J. Dumont, Günther Fleck, Rosser W. Garrison, Matti Hämäläinen, Vincent J. Kalkman, Haruki Karube, Michael L. May, Albert G. Orr, Dennis R. Paulson, Andrew C. Rehn, Günther Theischinger, John W. H. Trueman, Jan van Tol, Natalia von Ellenrieder and Jessica Ware, "The classification and diversity of dragonflies and damselflies (Odonata)", [[Q18642812|Zootaxa]], vol. 3703, 1, , doi: 10.11646/ZOOTAXA.3703.1.9
-- 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 <pre> 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|%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 {{taxobox}} 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(
"[[File:" .. params.image .. "|220px|.]]")
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(
"[[File:" .. params.range_map .. "|220px|.]]")
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("[[File:" .. 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