Module:Autotaxobox

From Eat Every Plant
Revision as of 01:45, 31 May 2017 by Keenan (talk | contribs) (1 revision: 'Asparagales')
Jump to navigation Jump to search

--[[ This module provides support to the automated taxobox system – the templates Automatic taxobox, Speciesbox, Subspeciesbox, Infraspeciesbox, etc.

In particular it provides a way of traversing the taxonomic hierarchy encoded in taxonomy templates (templates with names of the form "Template:Taxonomy/TAXON_NAME") without causing template expansion depth errors. ]]

local p = {}

--[[========================================================================= Limit the maximum depth of a taxonomic hierarchy that can be traversed; avoids excessive processing time and protects against incorrectly set up hierarchies, e.g. loops. =============================================================================]] local MaxSearchLevels = 100

function p.getMaxSearchLevels() return MaxSearchLevels end

--[[========================== taxoboxColour ================================ Determines the correct colour for a taxobox, by searching up the taxonomic hierarchy from the supplied taxon for the first taxon (other than 'incertae sedis') that sets a taxobox colour. It is assumed that a valid taxobox colour is defined using CSS rgb() syntax. If no taxon that sets a taxobox colour is found, then 'transparent' is returned unless the taxonomic hierarchy is too deep, when the error colour is returned. Usage: transparent =============================================================================]] function p.taxoboxColour(frame) local currTaxon = frame.args[1] or local i = 1 -- count levels processed local searching = currTaxon ~= -- still searching for a colour? local foundICTaxon = false -- record whether 'incertae sedis' found local colour = -- default is no colour while searching and i <= MaxSearchLevels do local plainCurrTaxon = p.stripExtra(currTaxon) -- remove trailing text after / if string.lower(plainCurrTaxon) == 'incertae sedis' then foundICTaxon = true else local possibleColour = frame:expandTemplate{ title = 'Template:Taxobox colour', args = { plainCurrTaxon } } if string.sub(possibleColour,1,3) == 'rgb' then colour = possibleColour searching = false end end if searching then local ok, parent = p.getTaxonInfoItem(frame, currTaxon, 'parent') if ok and parent ~= then currTaxon = parent i = i + 1 else searching = false -- run off the top of the hierarchy or tried to use non-existent taxonomy template end end end if colour ~= then return colour elseif foundICTaxon then return frame:expandTemplate{ title = 'Template:Taxobox colour', args = { 'incertae sedis' } } elseif searching then -- hierarchy exceeds MaxSearchLevels levels return frame:expandTemplate{ title = 'Template:Taxobox/Error colour', args = { } } else return 'transparent' end end

--[[=========================== taxoboxList ================================= Returns the rows of taxa in an automated taxobox, based on the taxonomic hierarchy for the supplied taxon. Usage:

| colspan=2 | |- |Unrecognized taxon (fix): |[[TAXON ]] |-

|- =============================================================================]] function p.taxoboxList(frame) local currTaxon = frame.args[1] or local displayN = (tonumber(frame.args['display_taxa']) or 1) + 1 local auth = frame.args['authority'] or local parentAuth = frame.args['parent_authority'] or local gParentAuth = frame.args['gparent_authority'] or local ggParentAuth = frame.args['ggparent_authority'] or local gggParentAuth = frame.args['gggparent_authority'] or local boldFirst = frame.args['bold_first'] or 'link' -- values 'link' or 'bold' local taxonTable = p.makeTable(frame, currTaxon) local res = -- display all taxa above possible greatgreatgrandparent for i = taxonTable.n, 6, -1 do res = res .. frame:expandTemplate{ title = 'Template:Taxobox/showtaxon', args = { taxonTable[i], fc = tostring(displayN >= i) } } end -- display greatgreatgrandparent, if it exists if taxonTable.n >= 5 then res = res .. frame:expandTemplate{ title = 'Template:Taxobox/showtaxon', args = { taxonTable[5], authority = gggParentAuth, fc = tostring(displayN >= 5) } } end -- display greatgrandparent, if it exists; force the display if an infrataxon is below if taxonTable.n >= 4 then local force = tostring(displayN >= 4) or frame.expandTemplate{ title = 'Template:Infrataxon()', args = { taxonTable[3] } } == 'true' or frame.expandTemplate{ title = 'Template:Infrataxon()', args = { taxonTable[2] } } == 'true' res = res .. frame:expandTemplate{ title = 'Template:Taxobox/showtaxon', args = { taxonTable[4], authority = ggParentAuth, fc = tostring(force) } } end -- display grandparent, if it exists; force the display if an infrataxon is below if taxonTable.n >= 3 then local force = tostring(displayN >= 3) or

                     frame.expandTemplate{ title = 'Template:Infrataxon()', args = { taxonTable[2] } } == 'true'
       res = res .. frame:expandTemplate{ title = 'Template:Taxobox/showtaxon', args = { taxonTable[3], authority = gParentAuth, fc = tostring(force) } }

end -- display parent, if it exists if taxonTable.n >= 2 then res = res .. frame:expandTemplate{ title = 'Template:Taxobox/showtaxon', args = { taxonTable[2], authority = parentAuth, fc = tostring(displayN >= 2) } } end -- display target taxon res = res .. frame:expandTemplate{ title = 'Template:Taxobox/showtaxon', args = { taxonTable[1], authority = auth, fc = 'true', format = boldFirst } } return res end

--[[========================== taxonomyList ================================= Returns the cells of the taxonomy table displayed on "Template:Taxonomy...." pages. Usage: | colspan=2 | |- |Unrecognized taxon (fix): |TAXON |-

|- =============================================================================]] function p.taxonomyList(frame) local currTaxon = frame.args[1] or if currTaxon == then return '| ||ERROR: no taxon supplied\n|-' end local taxonTable = p.makeTable(frame, currTaxon) local rankTable = p.getRankTable() local lastRankVal = 1000000 local orderOk local res = for i = taxonTable.n, 1, -1 do -- check ranks are in right order in the hierarchy local ok, rank = p.getTaxonInfoItem(frame, taxonTable[i], 'rank') local currRankVal = rankTable[rank] if currRankVal then orderOk = currRankVal < lastRankVal if orderOk then lastRankVal = currRankVal end else orderOk = true end -- now return a row of the taxonomy table with anomalous ranks marked if orderOk then res = res .. frame:expandTemplate{ title = 'Template:Taxonomy links', args = { taxonTable[i] } } else if i ~= 1 then res = res .. frame:expandTemplate{ title = 'Template:Taxonomy links', args = { taxonTable[i], error = 'true' } } else res = res .. frame:expandTemplate{ title = 'Template:Taxonomy links', args = { taxonTable[i], error = 'true', last_error = 'true' } } end end end return res end

--[[========================= callTaxonomyKey =============================== Prepares for, and then calls, Template:Taxonomy key to display a taxonomy template page. It does this by building up the information the template requires, following one 'same as' link, if required. Usage:

Ancestral taxa

Template:Error If the table below looks correct, then the necessary taxonomic information for the target taxon exists.

Template:Taxonomy key/missing template Not sure why you're here? Tell Keenan about it.

=============================================================================]] local SAME_AS = 7 local PARENT = 1 local RANK = 2 local LINK_TARGET = 3 local LINK_TEXT = 4 local ALWAYS_DISPLAY = 5 local EXTINCT = 6 local REFS = 8 function p.callTaxonomyKey(frame) local parent = frame.args['parent'] or local rank = frame.args['rank'] or local extinct = string.lower(frame.args['extinct']) or local alwaysDisplay = string.lower(frame.args['always_display']) or local linkTarget = frame.args['link_target'] or local linkText = frame.args['link_text'] or -- this is the "raw" link text, and can be local refs = frame.args['refs'] or local sameAsTaxon = frame.args['same_as'] or if sameAsTaxon ~= then -- try using the 'same as' taxon; it's an error if it doesn't exist local ok, sameAsInfoStr = pcall(frame.expandTemplate, frame, { title = 'Template:Taxonomy/' .. sameAsTaxon, args = {['machine code'] = 'all' } }) if ok then local sameAsInfo = mw.text.split(sameAsInfoStr, '$', true) --'same as' taxon's taxonomy template must not have a 'same as' link if sameAsInfo[SAME_AS] == then if parent == then parent = sameAsInfo[PARENT] end if rank == then rank = sameAsInfo[RANK] end if extinct == then extinct = string.lower(sameAsInfo[EXTINCT]) end if alwaysDisplay == then alwaysDisplay = string.lower(sameAsInfo[ALWAYS_DISPLAY]) end if linkTarget == then linkTarget = sameAsInfo[LINK_TARGET] end if linkText == then linkText = sameAsInfo[LINK_TEXT] end if refs == and parent == sameAsInfo[PARENT] then refs = sameAsInfo[REFS] end else return 'Error: attempt to follow two "same as" links: same_as = ' .. sameAsTaxon .. ', but Template:Taxonomy/' .. sameAsTaxon .. ' also has asame_as parameter.' end else return frame:expandTemplate{ title = 'Template:Taxonomy key/missing template', args = {taxon=sameAsTaxon, msg='given as the value of same as'} } end end local link = linkTarget if linkText ~= and linkText ~= linkTarget then link = link .. "|" .. linkText end return frame:expandTemplate{ title = 'Template:Taxonomy key', args = {parent=parent, rank=rank, extinct=extinct, always_display=alwaysDisplay, link_target=linkTarget, link=link, refs=refs, same_as=sameAsTaxon} } end --[[============================ taxonInfo ================================== Extracts and returns information from Template:Taxonomy/TAXON, following one 'same as' link if required. Usage: Template:Taxonomy/TAXON ITEM is one of: 'parent', 'rank', 'link target', 'link text', 'link', 'extinct', 'always display', 'refs', 'same as' or 'all'. If ITEM is not specified, the default is 'all' – all values in a single string separated by '$'. =============================================================================]] function p.taxonInfo(frame) local taxon = frame.args[1] or local item = frame.args[2] or if item == then item = 'all' end local ok, info = p.getTaxonInfoItem(frame, taxon, item) return info end --[[============================ taxonLink ================================== Returns a wikilink to a taxon, if required including '†' before it and ' (?)' after it, and optionally italicized or bolded without a wikilink. Usage: [[: target for the wikilink link_text= : text of the wikilink (may be same as link_target), without †, italics, etc.|: having ']] =============================================================================]] function p.taxonLink(frame) local taxon = frame.args['taxon'] or local extinct = string.lower(frame.args['extinct']) or local bold = frame.args['bold'] or local italic = frame.args['italic'] or local linkTarget = frame.args['link_target'] or local linkText = frame.args['link_text'] or frame.args['plain_link_text'] or --temporarily allow alternative args -- if link text is missing, try to find a replacement if linkText == then if string.find(taxon, 'Incertae sedis', 1, true) then linkText = "incertae sedis" linkTarget = 'Incertae sedis' else linkText = p.stripExtra(taxon) end end local link = if bold == 'yes' then link = '' .. linkText .. '' else if linkTarget == linkText or linkTarget == then link = linkText else link = linkTarget .. '|' .. linkText end link = '' .. link .. '' end if italic == 'yes' and not string.find(link, "", 1, true) then link = '' .. link .. '' end if (extinct == 'yes' or extinct == 'true') and not string.find(link, '†', 1, true) then link = '' .. link end if string.sub(taxon, -2) == '/?' and not string.find(link, '?', 1, true) then link = link .. ' (?)' end return link end --[[========================== showRankTable ================================ Returns a wikitable showing the ranks and their values as set up by getRankTable(). Usage:
Parent: [Taxonomy; edit]
Rank: (displays as )
Link: value of 'link' parameter in taxonomy template|value of parameter 2 in taxonomy template(links to value of 'link' parameter in taxonomy template)
Extinct: no
Always displayed: no
Taxonomic references:
Parent's taxonomic references: Template:Taxonomy/
Ranks checked in taxonomy templates
Rank Shown as Value
infratribus Infratribe 697
infraphylum Infraphylum 1497
infraordo Infraorder 997
cohort Cohort 1100
micrordo Microrder 995
genus Genus 600
grandordo Grandorder 1005
microphylum Microphylum 1495
sublegio Sublegion 1198
superphylum Superphylum 1503
supercohort Supercohort 1103
nanordo Nanorder 999
parafamilia Parafamily 800
zoosubsectio Subsection 898
magnordo Magnorder 1006
zoosubdivisio Subdivision 1298
superlegio Superlegion 1203
subregnum Subkingdom 1598
superfamilia Superfamily 803
varietas Variety 200
tribus Tribe 700
infraregnum Infrakingdom 1597
grandordo-mb Grandorder 1002
supertribus Supertribe 703
zoodivisio Division 1300
mirordo Mirorder 1004
zoosectio Section 900
regnum Kingdom 1600
superdivisio Superdivision 1503
superclassis Superclass 1403
divisio Division 1500
subtribus Subtribe 698
classis Class 1400
subspecies Subspecies 298
infraclassis Infraclass 1397
subcohort Subcohort 1098
subfamilia Subfamily 798
subsectio Subsection 498
superregnum Superkingdom 1603
species Species 300
subphylum Subphylum 1498
subordo Suborder 998
subgenus Subgenus 598
ordo Order 1000
subdivisio Subdivision 1498
subclassis Subclass 1398
legio Legion 1200
sectio Section 500
domain Domain 1700
superdomain Superdomain 1703
superordo Superorder 1003
parvordo Parvorder 996
familia Family 800
nanophylum Nanophylum 1499
infralegio Infralegion 1197
forma Form 100
mirordo-mb Mirorder 1001
phylum Phylum 1500

=============================================================================]]

function p.showRankTable(frame) local rankTable = p.getRankTable() local res = '{| class="wikitable sortable"\n|+ Ranks checked in taxonomy templates\n! Rank !! Shown as !! Value\n' for k, v in pairs(rankTable) do local rankShown = frame:expandTemplate{ title = 'Template:Anglicise rank', args = { k } } res = res .. '|-\n|' .. k .. '||' .. rankShown .. '||' .. v .. '\n' end return res .. '|}\n' end

--[[=============================== nth ===================================== External utility function primarily intended for use in checking and debugging. Returns the nth level above a taxon in a taxonomic hierarchy, where the taxon itself is counted as the first level. Usage: Lua error at line 314: attempt to compare number with nil. =============================================================================]] function p.nth(frame) local currTaxon = frame.args[1] or if currTaxon == then return 'ERROR: no taxon supplied' end local n = tonumber(frame.args['n'] or 1) if n > MaxSearchLevels then return 'Exceeded maximum number of levels allowed (' .. MaxSearchLevels .. ')' end local i = 1 local inHierarchy = true -- still in the taxonomic hierarchy or off the top? while i < n and inHierarchy do local ok, parent = p.getTaxonInfoItem(frame, currTaxon, 'parent') if ok and parent ~= then currTaxon = parent i = i + 1 else inHierarchy = false end end if inHierarchy then return currTaxon else return 'Level ' .. n .. ' is past the top of the taxonomic hierarchy' end end

--[[============================= nLevels =================================== External utility function primarily intended for use in checking and debugging. Returns number of levels in a taxonomic hierarchy, starting from the supplied taxon as level 1. Usage: 1 =============================================================================]] function p.nLevels(frame) local currTaxon = frame.args[1] or if currTaxon == then return 'ERROR: no taxon supplied' end local i = 1 local inHierarchy = true -- still in the taxonomic hierarchy or off the top? while inHierarchy and i < MaxSearchLevels do local ok, parent = p.getTaxonInfoItem(frame, currTaxon, 'parent') if ok and parent ~= then currTaxon = parent i = i + 1 else inHierarchy = false end end if inHierarchy then return MaxSearchLevels .. '+' else return i end end

--[[============================= listAll =================================== External utility function primarily intended for use in checking and debugging. Returns a comma separated list of a taxonomic hierarchy, starting from the supplied taxon. Usage: TAXON =============================================================================]] function p.listAll(frame) local currTaxon = frame.args[1] or if currTaxon == then return 'ERROR: no taxon supplied' end return p.listTaxa(p.makeTable(frame, currTaxon)) end

--[[========================================================================= Internal functions =============================================================================]]

--[[= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Internal utility function to strip off any extra parts of a taxon name, i.e. anything after a '/'. Thus "Felidae/?" would be reduced to "Felidae". = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =]] function p.stripExtra(taxonName) local i = string.find(taxonName,'/') if i then return string.sub(taxonName,1,i-1) else return taxonName end end

--[[= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Internal utility function to convert a taxon table to a comma-separated list. = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =]] function p.listTaxa(taxonTable) local lst = taxonTable[1] for i = 2, taxonTable.n, 1 do lst = lst .. ', ' .. taxonTable[i] end return lst end

--[[= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Internal utility function to extract an item of information from a taxonomy template, following one 'same as' link if required. = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =]] function p.getTaxonInfoItem(frame, taxon, item) -- item == 'link' is a special case if item == 'link' then return p.getTaxonInfoLink(frame, taxon) end -- item ~= 'link' local ok, info = pcall(frame.expandTemplate, frame, { title = 'Template:Taxonomy/' .. taxon, args = {['machine code'] = item } }) if ok then if info == then -- try 'same as' local sameAsTaxon = frame:expandTemplate{ title = 'Template:Taxonomy/' .. taxon, args = {['machine code'] = 'same as' } } if sameAsTaxon ~= then ok, info = pcall(frame.expandTemplate, frame, { title = 'Template:Taxonomy/' .. sameAsTaxon, args = {['machine code'] = item } }) end end end if ok then -- if item is 'link_text' check whether '(?)' needs to be added if item == 'link_text' and string.sub(taxon, -2) == '/?' and not string.find(info, '?', 1, true) then info = info .. ' (?)' end else info = 'Template:Taxonomy/' .. taxon .. '' --error indicator in code before conversion to Lua end return ok, info end

function p.getTaxonInfoLink(frame, taxon) local ok, linkText, linkTarget local link = ok, linkText = p.getTaxonInfoItem(frame, taxon, 'link_text') if ok then ok, linkTarget = p.getTaxonInfoItem(frame, taxon, 'link_target') if ok then if linkText == linkTarget then link = linkText else link = linkTarget .. '|' .. linkText end end end return ok, link end

--[[= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Internal utility function to return a table (array) constructed from a taxonomic hierarchy stored in "Template:Taxonomy/..." templates. TABLE.n holds the total number of taxa; TABLE[1]..TABLE[TABLE.n] the taxon names. = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =]] function p.makeTable(frame, currTaxon) local i = 1 local inHierarchy = true -- still in the taxonomic hierarchy or off the top? local taxonTable = {} taxonTable[1] = currTaxon; while i < MaxSearchLevels and inHierarchy do local ok, parent = p.getTaxonInfoItem(frame, currTaxon, 'parent') if ok and parent ~= then currTaxon = parent i = i + 1 taxonTable[i] = currTaxon else inHierarchy = false -- run off the top of the hierarchy or tried to use non-existent taxonomy template end end taxonTable.n = i return taxonTable end

--[[= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Internal utility function to set up a table of numerical values corresponding to 'Linnaean' ranks, with upper ranks having higher values. In a valid taxonomic hierarchy, a lower rank should never have a higher value than a higher rank. The actual numerical values are arbitrary so long as they are ordered. The ranks should correspond to those in Template:Anglicise ranks. = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =]] function p.getRankTable() return { classis = 1400, cohort = 1100, divisio = 1500, domain = 1700, familia = 800, forma = 100, genus = 600, grandordo = 1005, ['grandordo-mb'] = 1002, infraclassis = 1397, infralegio = 1197, infraordo = 997, infraphylum = 1497, infraregnum = 1597, infratribus = 697, legio = 1200, magnordo = 1006, microphylum = 1495, micrordo = 995, mirordo = 1004, ['mirordo-mb'] = 1001, nanophylum = 1499, nanordo = 999, ordo = 1000, parafamilia = 800, parvordo = 996, phylum = 1500, regnum = 1600, sectio = 500, --series = 400, used too inconsistently to check species = 300, subclassis = 1398, subcohort = 1098, subdivisio = 1498, subfamilia = 798, subgenus = 598, sublegio = 1198, subordo = 998, subphylum = 1498, subregnum = 1598, subsectio = 498, subspecies = 298, subtribus = 698, superclassis = 1403, supercohort = 1103, superdivisio = 1503, superdomain = 1703, superfamilia = 803, superlegio = 1203, superordo = 1003, superphylum = 1503, superregnum = 1603, supertribus = 703, tribus = 700, varietas = 200, zoodivisio = 1300, zoosectio = 900, zoosubdivisio = 1298, zoosubsectio = 898, } end

return p