Jump to content

Module:Species

From HopperWiki

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
Cookies help us deliver our services. By using our services, you agree to our use of cookies.