Module:Species
Appearance
Documentation for this module may be created at Module:Species/doc
-- ================================================================================
-- Module dependencies
-- ================================================================================
local cargo = mw.ext.cargo
local html = mw.html.create()
local getArgs = require("Module:Arguments").getArgs -- for processing arguments
-- Utilities
local str = require("Module:String utilities")
local out = require("Module:Wiki output utilities")
local cfmt = require("Module:Cargo format utilities")
local sc = str.sc -- Short cut for a safe Lua concatenation function
local ibf = require("Module:Infobox_functions")
-- JSON
local json = require("Module:JSON_pipeline")
-- Taxonomy
local ts = require("Module:Taxonomic schema")
local rank_lookup = ts.rank_lookup
local all_ranks = ts.all_ranks
local rank_dictionary = ts.taxonomic_rank_dictionary
-- ================================================================================
-- Main table
-- ================================================================================
local p = {}
-- ================================================================================
-- Subspecies list for species page
-- ================================================================================
function p.get_subspecies_for_species(frame)
local otu_id, suborder, page_title, err = json.resolve_focal_taxon(frame)
if err then
return out.warning_box("⚠️ Nomenclature lookup error", "Could not resolve focal taxon.")
end
local args = getArgs(frame)
local json_page = args.json_page or "JSON:Species_to_subspecies_map"
local data, data_err = json.load(json_page)
if not data then
return "⚠️ Could not load data: " .. tostring(data_err)
end
local subspecies_list = data[tostring(otu_id)]
if not subspecies_list or #subspecies_list == 0 then
return "No subspecies recorded for this taxon."
end
local items = {}
for _, rec in ipairs(subspecies_list) do
local name = rec.Scientific_name or ""
local author = rec.Author_year or ""
local sub_otu = rec.OTU_ID or ""
local osf_url = "https://orthoptera.speciesfile.org/otus/" .. sub_otu .. "/overview"
table.insert(items, string.format(
"* [%s <i>%s</i>] %s",
osf_url, name, author
))
end
return "==Subspecies==\n\n" .. table.concat(items, "\n")
end
-- ================================================================================
-- Species pest display box
-- ================================================================================
-- Current production version
function p.get_pest_species_table(frame)
-- ========================================
-- Resolve page title and setup
-- ========================================
local args = getArgs(frame)
local page_title = args.page_title
and mw.text.trim(args.page_title)
or mw.text.trim(mw.title.getCurrentTitle().text)
local no_results_message = "No results found. If you have expertise or materials on this topic that you are willing to contribute, [[Hopperwiki:About#Who manages HopperWiki?|please get in touch with an administrator]]."
-- ========================================
-- Query the Pest table
-- ========================================
local results = cargo.query(
"Pest",
"Name, Image, File_name, Original_source_link",
{ where = string.format("All_geography HOLDS '%s'", page_title) }
)
if not results or #results == 0 then
return no_results_message
end
-- ========================================
-- Build linked fact sheet file field
-- ========================================
for _, row in ipairs(results) do
-- Default image fallback
if not row.Image or row.Image == "" then
row.Image = "File:Species icon.png"
end
-- Linked fact sheet file
if row.File_name and row.Original_source_link then
local clean = row.File_name:gsub("^File:", "")
row.Linked_file = string.format("[[File:%s|150px|link=%s]]", clean, row.Original_source_link)
elseif row.File_name then
local clean = row.File_name:gsub("^File:", "")
row.Linked_file = string.format("[[File:%s|150px]]", clean)
else
row.Linked_file = ""
end
end
-- ========================================
-- Format and generate table
-- ========================================
local field_formats = { Name = "page", Image = "file", Linked_file = "raw" }
local field_name_mappings = { Name = "Species", Linked_file = "Fact sheet" }
local display_fields = { "Species", "Image", "Fact sheet" }
local row_cutoff = (tonumber(args.row_cutoff) or 3) + 1
local character_cutoff = tonumber(args.character_cutoff) or 600
local collapse = #results > row_cutoff
local formatted = cfmt.format_cargo_results(results, field_formats, field_name_mappings)
if #formatted > 0 then
return out.generate_wiki_table_enhanced(
formatted,
display_fields,
collapse,
character_cutoff,
nil,
field_name_mappings,
true,
nil,
row_cutoff
)
else
return no_results_message
end
end
-- These two functions are underdevelopment. This was an attempt to make a slicker version of the pest table that has little feature cards.
local function build_pest_card(row)
-- Species photo
local clean_image = (row.Image or ""):gsub("^File:", "")
local photo_html = string.format(
"[[File:%s|300px|link=%s]]",
clean_image,
row.Name or ""
)
-- Fact sheet thumbnail
local clean_file = (row.File_name or ""):gsub("^File:", "")
local sheet_link = row.Original_source_link or ""
local sheet_img_html = ""
if clean_file ~= "" and sheet_link ~= "" then
sheet_img_html = string.format("[[File:%s|300px|link=%s]]", clean_file, sheet_link)
elseif clean_file ~= "" then
sheet_img_html = string.format("[[File:%s|300px]]", clean_file)
end
local label_html
if sheet_link ~= "" then
label_html = string.format(
'<span class="pest-card__sheet-label">[%s Fact sheet ↗]</span>',
sheet_link
)
else
label_html = '<span class="pest-card__sheet-label">Fact sheet</span>'
end
-- Name: split "Common name (Genus species)" if pattern matches
local name = row.Name or ""
local common, sci = name:match("^(.-)%s*%(([^)]+)%)%s*$")
local name_html
if common and sci then
name_html = string.format(
'<p class="pest-card__name">[[%s|%s]]</p><p class="pest-card__sci"><i>%s</i></p>',
name, mw.text.trim(common), mw.text.trim(sci)
)
else
name_html = string.format('<p class="pest-card__name">[[%s]]</p>', name)
end
return table.concat({
'<div class="pest-card">',
'<div class="pest-card__images">',
'<div class="pest-card__photo-wrap">', photo_html, '</div>',
'<div class="pest-card__sheet-wrap">', sheet_img_html, label_html, '</div>',
'</div>',
'<div class="pest-card__body">', name_html, '</div>',
'</div>',
})
end
function p.get_pest_species_table_new(frame)
local args = getArgs(frame)
local page_title = args.page_title
and mw.text.trim(args.page_title)
or mw.text.trim(mw.title.getCurrentTitle().text)
local no_results_message = "No results found. If you have expertise or materials on this topic that you are willing to contribute, [[Hopperwiki:About#Who manages HopperWiki?|please get in touch with an administrator]]."
local results = cargo.query(
"Pest",
"Name, Image, File_name, Original_source_link",
{ where = string.format("All_geography HOLDS '%s'", page_title) }
)
if not results or #results == 0 then
return no_results_message
end
for _, row in ipairs(results) do
if not row.Image or row.Image == "" then
row.Image = "File:Species icon.png"
end
end
local cards = {}
for _, row in ipairs(results) do
table.insert(cards, build_pest_card(row))
end
return '<div class="pest-grid">' .. table.concat(cards) .. '</div>'
end
-- ================================================================================
-- Species title italicizer
-- ================================================================================
-- A function to handle italicizing species names based on the page title format
function p.species_title_italicizer(frame)
local args = getArgs(frame)
local title = mw.title.getCurrentTitle().text
if string.find(title, "%(") then
-- Expand the Italic dab template
return frame:expandTemplate{ title = 'Italic dab' }
else
-- Expand the Italic title template
return frame:expandTemplate{ title = 'Italic title' }
end
end
-- ================================================================================
-- Orthoptera Species File connecting information
-- ================================================================================
-- get_osf: fix the duplicate variable declarations and use correct module
function p.get_osf(frame)
local otu_id, suborder, page_title, err = json.resolve_focal_taxon(frame)
if err then
local msg =
"Could not resolve focal taxon.\n" ..
"Check that the OTU ID is correct for this species in the database."
return out.warning_box("⚠️ Nomenclature lookup error", msg)
end
local osf_url = "https://orthoptera.speciesfile.org/otus/" .. tostring(otu_id) .. "/overview"
local osf_link = "[" .. osf_url .. " Orthoptera Species File]"
return "For full nomenclature and taxonomic details of this taxon, see " .. osf_link
end
return p