require('strict');
--
local taxomap_t =
local is_not_italic_virus_taxon_t =
local is_italic_taxon_t =
local anglicize_rank_t =
local is_always_displayed_t =
local base_data_table_name = 'Module:Sandbox/trappist the monk/taxonomy '; -- includes space between base name and suffixlocal modules_loaded = ; -- DEBUG: a list of the modules loaded while crawling the tree; viewable in the lua log
--
local function module_select (taxon) local letter = taxon:match ('^%a'); -- get the first character of the taxon name letter = (letter and letter:upper) or 'symbols'; -- if a letter force uppercase; 'symbols' else
if not taxomap_t[letter] then -- if no table in
for _, map_t in ipairs (taxomap_t[letter]) do -- loop through the
--
local function template_data_get (taxon) local template_name = 'Template:Taxonomy/' .. taxon .. '/sandbox'; -- DEBUG: make a template name from
local template_string;
-- using #ifexist bumps the expensive parser function counter but keeps nonexistent templates out of -- executing a nonexistent template inside a pcall transcludes the nonexistent template -- creating a title object with mw.title.new or mw.title.makeTitle transcludes the nonexistent title -- creating a title object and testing with .exists transcludes the nonexistent title and bumps the expensive parser function counter if 'true'
local raw_taxon_t = mw.text.split (template_string, '$', true); -- split the string into a sequence table local taxon_t = ; local param_names = ; -- list of taxonomy template parameter name for i, v in ipairs (raw_taxon_t) do -- for each template parameter value if ('always_display'
param_names[i]) then -- these take 'boolean' 'yes' and 'true' v = [v:lower]; -- convert case-insensitive 'yes' and 'true' (as strings) to boolean 'true'; nil else end if v and ( ~= v) then -- skip nil and empty string values if '1'
----------------------------< M O D U L E _ D A T A _ G E T >------------------------------------------------
read taxon data from a data module
data-module data are loaded into package.loaded with a pcall wrapped call to require. taxon and same_asdata are extracted from that table. Successive calls to a data module do not unload and then reload that module.Because Felis, Felinae, Felidae, and Feliformia are sequential in the taxonimic hierarchy, the 'F' data moduleis loaded only once for those four taxa. The 'new' data table is consulted before all other data tables, so itis loaded once and not unloaded until _crawl_tree terminates.
local last_loaded_module; -- module scope variable holds name of that last successfully loaded data module
local function module_data_get (taxon, module_name) if 'Life'
taxon or 'Ichnos'
if (base_data_table_name .. 'new') ~= module_name then -- 'new' loads once instead of every time so ignore it if last_loaded_module and (last_loaded_module ~= module_name) then -- if different from currently loaded module package.loaded[last_loaded_module] = nil; -- unload current module to conserve memory last_loaded_module = nil; -- unset end end if package.loaded[module_name] then -- has already been loaded return package.loaded[module_name][taxon]; -- return taxon data if present; nil else else if not pcall (require, module_name) then -- attempt to load; data will be fetched from package.loaded[module_name] return nil; -- failed to load else local suffix = module_name:match ('taxonomy (.+)$'); -- DEBUG: was loaded; save module name for log modules_loaded['taxonomy ' .. suffix] = (modules_loaded['taxonomy ' .. suffix] and (modules_loaded['taxonomy ' .. suffix] + 1)) or 1; -- DEBUG if 'new' ~= suffix then -- not debug last_loaded_module = module_name; -- remember this module name; 'taxonomy new' does not get 'remembered' end end
return package.loaded[module_name][taxon]; -- return taxon data if present; nil else endend
----------------------------< S O U R C E _ D A T A _ G E T >------------------------------------------------
chooses which of module_data_get or template_data_get to call based on the state of
local function source_data_get (taxon, taxon_module_name) if taxon_module_name then return module_data_get (taxon, taxon_module_name); -- get raw taxon data from a data module else return template_data_get (taxon); -- get raw taxon data from a taxonomy template endend
--
local function _taxon_get (taxon, no_follow, taxon_module_name, taxon_t) local same_as; local raw_taxon_t = ; -- holds the data read from the template or from the data module for
raw_taxon_t = source_data_get (taxon, taxon_module_name); -- get raw taxon data from a taxonomy template or a data module; for templates taxon_module_name is nil
if raw_taxon_t then for k, v in pairs (raw_taxon_t) do -- copy content from
if same_as then -- if there is a same_as value in the taxonomy table if taxon_module_name then -- nil when looking for template data local suffix = module_select (same_as); -- make a
if same_as and raw_taxon_t then -- if there is a
--
local function taxon_get (taxon, no_follow) local taxon_t = ; -- the return table
if not taxon then return ; end
_taxon_get (taxon, no_follow, nil, taxon_t); -- attempts to fill
if next (taxon_t) then -- if
local taxon_module_name = base_data_table_name .. 'new'; -- first look in the '~/Taxonomy new' data module _taxon_get (taxon, no_follow, taxon_module_name, taxon_t); -- attempts to fill
if next (taxon_t) then -- if
local suffix = module_select (taxon); -- get the suffix to append to the base data module name taxon_module_name = base_data_table_name .. suffix; -- not in '~/Taxonomy new' data module _taxon_get (taxon, no_follow, taxon_module_name, taxon_t); -- attempts to fill
return taxon_t, suffix;end
----------------------------< I S _ S E T >------------------------------------------------------------------
Returns true if argument is set; false otherwise. Argument is 'set' when it exists (not nil) or when it is not an empty string.
local function is_set(var) return not (var
);end
--[=[-------------------------< M A K E _ W I K I L I N K >---------------------------------------------------- Makes a wikilink; when both link and display text is provided, returns a wikilink in the form [[L|D]]; if onlylink is provided, returns a wikilink in the form L; if neither are provided or link is omitted, returns anempty string.
]=]
local function make_wikilink (link, display) if is_set (link) then if is_set (display) then return table.concat ; else return table.concat ; end else return ; endend
----------------------------< L I N K _ M A K E >------------------------------------------------------------
makes a wikilink from the value assigned to 'link' in
local function link_make (taxon_t, taxon) local link;
if taxon_t.link then local link_label; local link_target; if taxon_t.link:find ('Incertae sedis', 1, true) then link_label = '\'\'incertae sedis\'\; link_target = 'Incertae sedis'; elseif taxon_t.link:match ('([^|]+)|(.*)') then -- is link a piped link? link_target, link_label = taxon_t.link:match ('([^|]+)|(.*)'); else link_label = taxon:match ('([^/]+)/(.*)') or taxon; -- variant of Module:Autotaxobox l.stripExtra(taxon) end
link_target = (link_target and link_target) or link_label; -- when no
if taxon_t.extinct and (not link:find ('†', 1, true)) then link = '
†' .. link; end if taxon:match ('/%?$') and not link:find ('?', 1, true) then link = link .. ' (?)' end endreturn link;end
--
local function _crawl_tree (taxon, tree_t) local starting_taxon = taxon; -- save a copy for error messaging local taxon_t = taxon_get (taxon); -- initialize
while taxon_t and taxon do if taxon_t.rank then -- nil for Taxonomy/Life local styled_rank = anglicize_rank_t[taxon_t.rank]; styled_rank = ((taxon_t.always_display or is_always_displayed_t[taxon_t.rank:lower]) and '\'\'\ .. styled_rank .. '\'\'\) or styled_rank; local linked_taxon = link_make (taxon_t, taxon) local suffix = taxon:match ('%/[%w]+$') or "" -- get suffix (e.g. /skip, /plantae) if suffix ~= "" then linked_taxon = linked_taxon .. " " .. suffix .. ""; end table.insert (tree_t, 1, styled_rank .. ': ' .. linked_taxon); if taxon:find ('/skip', 1, true) then table.insert (tree_t, 1, string.rep ('·', 5) .. ': ' .. string.rep ('·', 5)); end if taxon_t.parent then taxon = taxon_t.parent; -- get the next taxon taxon_t = taxon_get (taxon); -- and get its taxon table
if nil
taxon) or ('Ichnos'
taxon = nil; -- no next taxon end end
if last_loaded_module then package.loaded[last_loaded_module] = nil; -- unload to conserve memory end package.loaded[base_data_table_name .. 'new'] = nil; return tree_tend
----------------------------< W I K I D A T A _ G E T >------------------------------------------------------
local TAXON_NAME_P = 'P225'; -- mainsnak.datavalue["type"] = "string", mainsnak.datavalue.value = Felis, mainsnak["property"] = "P225", mainsnak["snaktype"] = "value",local TAXON_RANK_P = 'P105'; -- mainsnak.datavalue.value["entity-type"] = "item", mainsnak.datavalue.value.id = qid, mainsnak["property"] = "P105", mainsnak["snaktype"] = "value",local TAXON_PARENT_P = 'P171'; -- mainsnak.datavalue.value["entity-type"] = "item", mainsnak.datavalue.value.id = qid, mainsnak["property"] = "P171", mainsnak["snaktype"] = "value",
local function wikidata_get (qid, prop) local s_qid; local wd_table_t = mw.wikibase.getBestStatements (qid, prop)[1]; -- attempt to get the taxon name if not wd_table_t then error ('no data for ' .. qid .. ' ' .. prop); -- some sort of better error handling needed end
if wd_table_t.mainsnak.datavalue then if 'string'
if 'table'
--
local function _crawl_wikidata_tree (taxon_qid) local out_t = ;
local taxon; local rank; local _;
while taxon_qid do taxon = wikidata_get (taxon_qid, TAXON_NAME_P); rank = wikidata_get (taxon_qid, TAXON_RANK_P); _, taxon_qid = wikidata_get (taxon_qid, TAXON_PARENT_P); -- parent taxon name discarded, reset taxon_qid to the parent taxon's qid
taxon = (is_italic_taxon_t[rank] and '\'\ .. taxon .. '\'\) or taxon; -- italicize when appropriate
rank = anglicize_rank_t[rank] or rank:gsub ('(%a)', string.upper, 1); -- anglicize accepted ranks; uppercase first letter for all others rank = (is_always_displayed_t[rank:lower] and '\'\'\ .. rank .. '\'\'\) or rank; -- and italicize those that should be
table.insert (out_t, 1, rank .. ': ' .. taxon); -- save at the top of the list end
return table.concat (out_t, '
'); -- make a big string and doneend
--crawl_wikidata_tree|Q...}}
local function crawl_wikidata_tree (frame) return _crawl_wikidata_tree (frame.args[1]);end
----------------------------< C R A W L _ T R E E >----------------------------------------------------------
local function crawl_tree (frame) local tree_t = ; local loaded = ; local taxon = frame.args[1] or 'Felis';
local wikidata_id = mw.wikibase.getEntityIdForTitle (taxon); -- only works when no disambiguation
tree_t = _crawl_tree (taxon, tree_t) -- crawl the tree to get the debug taxon list of taxa and their ranks
local count = 0; -- DEBUG: tally of total modules loaded for module, v in pairs (modules_loaded) do -- DEBUG: make a sortable list of module names for log count = count + v; table.insert (loaded, module); end
local function comp (a, b) -- DEBUG: sort module names for log local letter_a, enum_a = a:match ('(%a)(%d*)$'); -- get letter and enumerator from 'taxonomy
enum_a = tonumber (enum_a); -- convert enumerators to number type if present; nil else enum_b = tonumber (enum_b);
if (letter_a
table.sort (loaded, comp); -- DEBUG: sort module names for log for _, module in ipairs (loaded) do -- DEBOG: log module names mw.log (module .. ': ' .. modules_loaded[module]); end mw.log ('total modules loaded: ' .. count); -- DEBUG: add tally of loaded modules to log
local out_t = ; -- render crude tree from data modules, from wikidata, and taxonomy list from Module:Autotaxobox for comparison table.insert (out_t, '
lua data module experiment | wikidata experiment | autotaobox reference\n | -\n | '); table.insert (out_t, table.concat (tree_t, ' ')); table.insert (out_t, '\n | '); table.insert (out_t, _crawl_wikidata_tree (wikidata_id)); table.insert (out_t, '\n | '); table.insert (out_t, frame:callParserFunction ('#invoke',)); table.insert (out_t, '\n | -\n |
---|
return table.concat (out_t);end
--
local function _make_tables (taxon, taxon_tree_t, rank_tree_t) local taxon_t = taxon_get (taxon); -- initialize
while taxon_t and taxon do if taxon_t.rank then -- nil for Taxonomy/Life table.insert (taxon_tree_t, taxon); -- add taxon name table.insert (rank_tree_t, taxon_t.rank); -- add taxon rank if taxon_t.parent then taxon = taxon_t.parent; -- get the next taxon taxon_t = taxon_get (taxon); -- and get its taxon table end else if ('Life'
taxon) or ('Ichnos'
taxon = nil; -- no next taxon end end
taxon_tree_t.n = #taxon_tree_t; -- add the number of taxa in this table return taxon_tree_t, rank_tree_tend
--
local function make_tables (taxon) local taxon_t = ; local rank_t = ; local _;
taxon_t, rank_t = _make_tables (taxon, taxon_t, rank_t); -- crawl the tree to get the taxon list and the rank list; empty table not used here
return taxon_t, rank_t;end
--
local function data_table_wikilink_make (suffix, taxon, caption) local wikilink_t = ;
table.insert (wikilink_t, '
'); if suffix then return ; -- do not display wikilink/edit link at right of caption for data modules else -- here when caption and template so show template edit link only table.insert (wikilink_t, '['); -- open plainlinks span table.insert (wikilink_t, 'begin edit link table.insert (wikilink_t, taxon); -- add the taxon name table.insert (wikilink_t, '&preload=Template:Taxonomy/preload edit'); -- close the edit link table.insert (wikilink_t, ']'); -- close the plainlinks span and the styling span return table.concat (wikilink_t); -- make a big string and done end else table.insert (wikilink_t, '.04em">') endtable.insert (wikilink_t, '[taxonomy '); -- first part of the wikilink label table.insert (wikilink_t, suffix); -- add
if not suffix then -- don't add 'edit' link for data modules table.insert (wikilink_t, ';
'); -- open plainlinks span table.insert (wikilink_t, 'begin edit link table.insert (wikilink_t, taxon); -- add the taxon name table.insert (wikilink_t, '&preload=Template:Taxonomy/preload edit'); -- close the edit link table.insert (wikilink_t, ''); -- close the plainlinks span end table.insert (wikilink_t, ']'); -- close enclosing brackets-- table.insert (wikilink_t, ']]]'); -- close wikilink; close enclosing brackets if caption then -- if this wikilink is for the table caption -- table.insert (wikilink_t, ''); -- close styling span tag end table.insert (wikilink_t, ''); return table.concat (wikilink_t); -- make a big string and doneend--
local function _template_skeleton (frame, taxon) if [taxon:lower] then -- these are the taxonomy end taxons return '
error: taxon: ' .. taxon .. ''; -- error return for these; TODO: is this necessary? endlocal taxon_t = ; local out_t =
local taxon_module_name = base_data_table_name .. 'new'; -- first look in the '~/Taxonomy new' data module _taxon_get (taxon, true, taxon_module_name, taxon_t); -- attempts to fill
if not next (taxon_t) then -- if
-- return frame:callParserFunction ;end
--
local function template_skeleton (frame) return _template_skeleton (frame, frame.args[1]);end
--
local function show_taxon_data (frame) local this_page = mw.title.getCurrentTitle.prefixedText;
local code_open_tag = ''; local taxon = frame.args[1]; local out_t = ; local taxon_t, suffix = taxon_get (taxon, true); -- get taxon data; do not follow same_as
table.insert (out_t, '
-\n | Parent:\n | '); if 'Life' taxon or 'Veterovata'taxon or 'Ichnos' taxon then table.insert (out_t, 'none'); else table.insert (out_t, code_open_tag); -- open code tag table.insert (out_t, taxon_t.parent); -- add parent taxon name table.insert (out_t, ' '); -- close code tag; include space before lua data table wikilink table.insert (out_t, data_table_wikilink_make (psuffix, taxon_t.parent)); -- add bracketed wikilink to lua data table for this
| -\n | Rank:\n | '); local rank; if taxon_t.rank then rank = anglicize_rank_t[taxon_t.rank]; local rank_t = ; table.insert (rank_t, code_open_tag); -- open code tag table.insert (rank_t, taxon_t.rank); -- insert raw rank from taxon data table.insert (rank_t, ' [displays as: '); -- start the message
local always_display = taxon_t.always_display or is_always_displayed_t[taxon_t.rank]; if is_italic_taxon_t[taxon_t.rank] then -- for italicized taxon ranks table.insert (rank_t, (always_display and '\'\'\'\'\) or '\'\); -- open italic markup; bold if always displayed table.insert (rank_t, rank); -- add anglicized rank table.insert (rank_t, (always_display and '\'\'\'\'\) or '\'\);-- close italic/bold markup else table.insert (rank_t, (always_display and '\'\'\) or ); -- bold if always displayed table.insert (rank_t, rank); -- add anglicized rank table.insert (rank_t, (always_display and '\'\'\) or ); -- close italic/bold markup end table.insert (rank_t, ']'); -- finish the message rank = table.concat (rank_t); -- and make a big string elseif 'Life' taxon or 'Veterovata'taxon or 'Ichnos' taxon then rank = 'none'; else rank = '– a rank must be supplied'; end table.insert (out_t, rank);local link = link_make (taxon_t, taxon); if link then link = table.concat ; end table.insert (out_t, '\n | -\n | Link:\n | '); table.insert (out_t, (link and link) or '–'); local extinct = (taxon_t.extinct and (code_open_tag .. 'true')) or 'no'; if not taxon.extinct and parent_t.extinct then extinct = ' parent is marked as extinct'; endtable.insert (out_t, '\n | -\n | Extinct:\n | '); table.insert (out_t, extinct); table.insert (out_t, '\n | -\n | Always displayed:\n | '); table.insert (out_t, (taxon_t.always_display and (code_open_tag .. 'true')) or ((is_always_displayed_t[taxon_t.rank] and 'yes (major rank)') or 'no')) table.insert (out_t, '\n | -\n | Taxonomic references:\n | '); table.insert (out_t, taxon_t.refs or '–'); table.insert (out_t, '\n | -\n | Parent\'s taxonomic references:\n | '); table.insert (out_t, parent_t.refs or '–'); if same_as then table.insert (out_t, '\n | -\n | Same as taxon:\n | '); table.insert (out_t, code_open_tag); -- open code tag table.insert (out_t, same_as); -- add same_as taxon name table.insert (out_t, ' '); -- close code tag; include space before lua data table wikilink table.insert (out_t, data_table_wikilink_make (ssuffix, same_as)); -- add bracketed wikilink to lua data table for this if taxon:find ('/skip$') then local skipped_taxon = taxon:match ('([^/]+)/skip$'); table.insert (out_t, '\n | -\n | For the suffix "/skip", see Skip taxonomy templates. '); table.insert (out_t, 'For the skipped taxa, see '); -- start the message and open wikilink markup table.insert (out_t, skipped_taxon); -- add skipped taxon name without '/skip' suffix table.insert (out_t, ''); -- close wikimarkup-- table.insert (out_t, 'For the skipped taxa, see '); -- start the message and open code tag
-- table.insert (out_t, skipped_taxon); -- add skipped taxon name without '/skip' suffix
-- table.insert (out_t, ' '); -- close code tag; include space before lua data table wikilink-- table.insert (out_t, data_table_wikilink_make (suffix, skipped_taxon)); -- add bracketed wikilink to lua data table for this if taxon:find ('/%?$') then table.insert (out_t, '\n | -\n | For the suffix "/?", see Questionable assignments.'); end if taxon:find ('Incertae sedis') then table.insert (out_t, '\n | -\n | '); table.insert (out_t, 'For taxon names with "Incertae sedis", see \'\'Incertae sedis\'\' taxonomy templates.'); end if suffix then table.insert (out_t, '\n | - style="vertical-align: top;"\n | Template skeleton: data loaded from '); table.insert (out_t, '[taxonomy '); -- first part of the wikilink label table.insert (out_t, suffix); table.insert (out_t, ']'); -- close enclosing brackets; close wikilink table.insert (out_t, ' to change these data:\n#copy the template skeleton\n#click ['); table.insert (out_t, 'begin edit link table.insert (out_t, taxon); -- add the taxon name table.insert (out_t, '&preload=Template:Taxonomy/preload create]\n#paste, and edit'); -- close the edit link table.insert (out_t, '\n | '); table.insert (out_t, _template_skeleton (frame, taxon)); end table.insert (out_t, '\n |
--
local function delete_taxon (frame) local taxon = frame.args[1]; local taxonomy_t = ; local suffix = module_select (taxon);
local content = mw.title.new (base_data_table_name .. suffix):getContent local found = false;
local out_t = ;
for entry in content:gmatch ('\t*%[\'[^\r\n]+},[\r\n]+') do local entry_taxon = entry:match ('^\t*%[\'([^=]+)\'%]%s*='); if entry_taxon
if not found then return '
Taxon: ' .. taxon .. ' not found in '; end table.sort (out_t)table.insert (out_t, 1, 'return ')
return 'deleted: ' .. taxon .. '\n\n' .. frame:callParserFunction ;
end
--qids_get|
local function qids_get (frame) local select = frame.args[1]; local module_name = 'Module:Sandbox/trappist_the_monk/taxonomy_' .. frame.args[2]; if not pcall (require, module_name) then -- attempt to load; data will be fetched from package.loaded[module_name] return '
failed to load: Module:Sandbox/trappist_the_monk/taxonomy_' .. frame.args[2] .. ''; -- failed to load endlocal qids = -- sequence table of links and their qids local no_qids = ; -- sequence table of links that do not have a qid local no_links = ; -- sequence table of taxons that do not have links for taxon, taxon_t in pairs (package.loaded[module_name]) do local link = taxon_t.link; -- because taxon_t.link (from ms.loadData) is read only if link then link = link:gsub ('(.+)|.*', '%1'); -- remove any link label local qid = mw.wikibase.getEntityIdForTitle (link, 'enwiki'); -- qid referred to by link in module data if qid then table.insert (qids, '*' .. link .. '' .. ': ' .. qid .. ' from ' .. taxon); else table.insert (no_qids, '*' .. link .. '' .. ' from ' .. taxon); end else table.insert (no_links, taxon); end end
package.loaded[module_name]=nil; if 'no qids'
select then table.sort (no_links); return table.concat (no_links, '\n'); else table.sort (qids); return table.concat (qids, '\n'); endend
----------------------------< E X P O R T E D F U N C T I O N S >------------------------------------------
return