Module:T-Campaign

Revision as of 22:59, 29 June 2025 by MarkWD (talk | contribs) (// via Wikitext Extension for VSCode)

Documentation for this module may be created at Module:T-Campaign/doc

--[[
 * T-Campaign.lua
 * Generic campaign template that dynamically loads campaign data from JSON files. This template provides a flexible, data-driven approach to campaign templates without cluttering ConfigRepository. It dynamically generates field definitions based on the JSON structure and renders any campaign content.
 *
 * Usage: {{#invoke:T-Campaign|render|campaign_name=ASP2025}}
]]

local p = {}

-- Required modules
local Blueprint = require('Module:LuaTemplateBlueprint')
local ErrorHandling = require('Module:ErrorHandling')
local DatasetLoader = require('Module:DatasetLoader')

-- Register the Campaign template with Blueprint
local template = Blueprint.registerTemplate('Campaign', {
    features = { 
        title = true, 
        fields = true, 
        categories = true,
        errorReporting = true
    }
})

-- Dynamic configuration - will be populated based on JSON structure
template.config = {
    fields = {}, -- Will be dynamically generated
    categories = {
        base = {} -- Will be populated dynamically based on campaign
    },
    constants = {
        title = "Campaign Information" -- Default title, can be overridden by JSON
    }
}

-- Generic field processor that handles different data types
local function processFieldValue(value)
    if type(value) == "table" then
        if #value > 0 then
            -- Array of values - render as bullet list
            return "* " .. table.concat(value, "\n* ")
        else
            -- Object with key-value pairs - render as sections
            local output = {}
            for category, content in pairs(value) do
                local categoryTitle = "'''" .. category:gsub("^%l", string.upper):gsub("_", " ") .. "'''"
                table.insert(output, categoryTitle)
                if type(content) == "table" and #content > 0 then
                    table.insert(output, "* " .. table.concat(content, "\n* "))
                else
                    table.insert(output, tostring(content))
                end
            end
            return table.concat(output, "\n")
        end
    else
        return tostring(value)
    end
end

-- Custom preprocessor to load campaign data and generate fields dynamically
Blueprint.addPreprocessor(template, function(template, args)
    -- Get campaign_name from args (which should already be merged by Blueprint)
    -- But also check the current frame for direct module invocations
    local campaignName = args.campaign_name
    
    -- If not found in args, try to get it from the current frame
    if (not campaignName or campaignName == "") and template.current_frame then
        local frameArgs = template.current_frame.args or {}
        campaignName = frameArgs.campaign_name
    end
    
    if not campaignName or campaignName == "" then
        ErrorHandling.addError(
            template._errorContext or ErrorHandling.createContext('T-Campaign'),
            'preprocessor',
            'No campaign_name provided',
            nil,
            false
        )
        return args
    end
    
    -- Load campaign data from JSON
    local campaignData = DatasetLoader.load('Data:Campaigns/' .. campaignName .. '.json')
    if not campaignData then
        ErrorHandling.addError(
            template._errorContext or ErrorHandling.createContext('T-Campaign'),
            'preprocessor',
            'Failed to load campaign data for: ' .. campaignName,
            nil,
            false
        )
        return args
    end
    
    -- Dynamically generate field definitions based on JSON structure
    local fields = {}
    for key, value in pairs(campaignData) do
        -- Skip campaign_name as it's a control parameter
        if key ~= "campaign_name" then
            -- Create human-readable labels from JSON keys
            local label = key:gsub("_", " "):gsub("^%l", string.upper)
            table.insert(fields, {key = key, label = label})
        end
    end
    
    -- Sort fields to ensure consistent ordering
    table.sort(fields, function(a, b) return a.key < b.key end)
    
    template.config.fields = fields
    
    -- Override title if provided in JSON
    if campaignData.title then
        template.config.constants.title = campaignData.title
    end
    
    -- Merge campaign data into args for rendering
    for k, v in pairs(campaignData) do
        args[k] = v
    end
    
    -- Add campaign-specific category
    template.config.categories.base = {campaignName}
    
    return args
end)

-- Initialize field processors for the template
-- Set up a universal processor that can handle any field type
if not template._processors then
    template._processors = {}
end

-- Set up a universal field processor that will be used for all fields
-- This processor will be called by the Blueprint system for each field
template._processors.default = processFieldValue

-- Also set up specific processors for common field patterns
template._processors.title = function(value) return tostring(value) end
template._processors.regions_covered = processFieldValue
template._processors.services_provided = processFieldValue
template._processors.areas_of_expertise = processFieldValue
template._processors.icann_experience = function(value) 
    if value and value ~= "" then
        return tostring(value)
    end
    return nil -- Don't display empty fields
end

-- Export the render function
function p.render(frame)
    return template.render(frame)
end

return p