Module:LuaTemplateBlueprint: Difference between revisions

// via Wikitext Extension for VSCode
// via Wikitext Extension for VSCode
 
(31 intermediate revisions by the same user not shown)
Line 65: Line 65:
-- Wiki link pattern for regex matching
-- Wiki link pattern for regex matching
local WIKI_LINK_PATTERN = '%[%[.-%]%]'
local WIKI_LINK_PATTERN = '%[%[.-%]%]'
-- Semantic property HTML templates
local PROPERTY_DIV_PREFIX = '<div style="display:none;">\n  {{#set: '
local PROPERTY_DIV_SUFFIX = ' }}\n</div>'
-- Empty table for returning when needed
-- Empty table for returning when needed
local EMPTY_OBJECT = {}
local EMPTY_OBJECT = {}
Line 75: Line 72:
local ErrorHandling = require('Module:ErrorHandling')
local ErrorHandling = require('Module:ErrorHandling')
local ConfigRepository = require('Module:ConfigRepository')
local ConfigRepository = require('Module:ConfigRepository')
local ConfigHelpers = require('Module:ConfigHelpers')
local TemplateHelpers = require('Module:TemplateHelpers')
local TemplateHelpers = require('Module:TemplateHelpers')
local TemplateStructure = require('Module:TemplateStructure')
local TemplateStructure = require('Module:TemplateStructure')
Line 343: Line 341:


-- ========== Configuration Integration ==========
-- ========== Configuration Integration ==========
-- Standard configuration sections used by templates
p.configSections = {
    'meta',
    'categories',
    'patterns',
    'fields',
    'mappings',
    'constants',
    'semantics'
}
-- Initialize the standard configuration for a template
-- Initialize the standard configuration for a template
-- Combines base config from ConfigRepository with template overrides
-- Combines base config from ConfigRepository with template overrides
Line 361: Line 348:
     local templateType = template.type
     local templateType = template.type
     local configOverrides = template.config or {}
     local configOverrides = template.config or {}
   
 
     -- Get base configuration from repository
     -- Get base configuration from repository
     local baseConfig = ConfigRepository.getStandardConfig(templateType)
     local baseConfig = ConfigRepository.getStandardConfig(templateType)
   
 
     -- Apply overrides to each section
     -- Use ConfigHelpers to deep merge configurations
     local config = {}
     local config = ConfigHelpers.deepMerge(baseConfig, configOverrides)
    for _, section in ipairs(p.configSections) do
 
        config[section] = config[section] or {}
       
        -- Copy base config for this section if available
        if baseConfig[section] then
            for k, v in pairs(baseConfig[section]) do
                config[section][k] = v
            end
        end
       
        -- Apply overrides for this section if available
        if configOverrides[section] then
            for k, v in pairs(configOverrides[section]) do
                config[section][k] = v
            end
        end
    end
   
     -- Store complete config in template
     -- Store complete config in template
     template.config = config
     template.config = config
Line 398: Line 368:
         end)
         end)
     end
     end
   
 
     return config
     return config
end
end
Line 439: Line 409:
                     args,
                     args,
                     TEMPLATE_TITLE_CLASS_PREFIX .. string.lower(templateId),
                     TEMPLATE_TITLE_CLASS_PREFIX .. string.lower(templateId),
                     titleText
                     titleText,
                    template.titleId
                 )
                 )
                 end,
                 end,
Line 703: Line 674:
     template._processors = TemplateFieldProcessor.initializeProcessors(template)
     template._processors = TemplateFieldProcessor.initializeProcessors(template)
     return template._processors
     return template._processors
end
-- Get field value from args (delegated to TemplateHelpers)
-- @param field table The field definition
-- @param args table The template arguments
-- @return string|nil The field value or nil if not found
function p.getFieldValue(field, args, template)
    local _, value = TemplateHelpers.getFieldValue(args, field)
    return value
end
end


Line 887: Line 849:
       (not template._propertyProviders or #template._propertyProviders == 0) then
       (not template._propertyProviders or #template._propertyProviders == 0) then
         return EMPTY_STRING
         return EMPTY_STRING
    end
   
    -- Set time budget for semantic property processing (450ms)
    local startTime = os.clock()
    local timeLimit = 0.45  -- seconds
    local checkInterval = 10  -- check every N properties
    local propertyCounter = 0
   
    local function checkTimeLimit()
        propertyCounter = propertyCounter + 1
        if propertyCounter % checkInterval == 0 then
            if os.clock() - startTime > timeLimit then
                return true  -- time exceeded
            end
        end
        return false
     end
     end
      
      
Line 963: Line 941:
     end
     end
      
      
    -- Process country and region properties with early deduplication
    -- if a country field exists and country is not in skipProperties
    if args.country and args.country ~= '' and not skipProperties["Has country"] then
        -- Load CountryData lazily to avoid circular dependencies
        local CountryData = require('Module:CountryData')
        local countryProperties = p.protectedExecute(
            template,
            'CountryData_Properties',
            function() return CountryData.getSemanticCountryRegionProperties(args.country) end,
            {},
            args.country
        )
       
        -- Process country properties through deduplication
        if countryProperties and next(countryProperties) then
            for property, values in pairs(countryProperties) do
                -- Skip if explicitly marked to skip
                if not skipProperties[property] then
                    if type(values) == "table" then
                        -- Process each value through deduplication
                        for _, value in ipairs(values) do
                            deduplicateProperty(collector, property, value)
                        end
                    else
                        -- Single value
                        deduplicateProperty(collector, property, values)
                    end
                end
            end
        end
    end
      
      
     -- Process property providers with early deduplication
     -- Process property providers with early deduplication
Line 1,015: Line 962:
                             -- Provider returned an array of values
                             -- Provider returned an array of values
                             for _, v in ipairs(value) do
                             for _, v in ipairs(value) do
                                 deduplicateProperty(collector, property, v)
                                 local validated = validatePropertyValue(v)
                                if validated and validated ~= '' then
                                    if not collector.properties[property] then
                                        collector.properties[property] = {}
                                    end
                                    table.insert(collector.properties[property], validated)
                                    collector.count = collector.count + 1
                                end
                             end
                             end
                         else
                         else
                             -- Provider returned a single value
                             -- Provider returned a single value
                             deduplicateProperty(collector, property, value)
                             local validated = validatePropertyValue(value)
                            if validated and validated ~= '' then
                                if not collector.properties[property] then
                                    collector.properties[property] = {}
                                end
                                table.insert(collector.properties[property], validated)
                                collector.count = collector.count + 1
                            end
                         end
                         end
                     end
                     end
Line 1,070: Line 1,031:
     -- Add deduplicated additional properties (these have actual values)
     -- Add deduplicated additional properties (these have actual values)
     for property, value in pairs(collector.properties) do
     for property, value in pairs(collector.properties) do
         finalProperties[property] = value
         finalProperties[property] = value -- This might be an array of values or a single value
    end
 
    -- Process fixed properties
    if semanticConfig.fixedProperties and type(semanticConfig.fixedProperties) == 'table' then
        for propName, propValue in pairs(semanticConfig.fixedProperties) do
            if not skipProperties[propName] then -- Check skipProperties as well
                local validatedValue = validatePropertyValue(propValue)
                if validatedValue and validatedValue ~= '' then
                    -- Pass as a single-item array to be processed by the
                    -- 'Direct array of values' path in SemanticAnnotations.lua
                    finalProperties[propName] = {validatedValue}
                end
            end
        end
     end
     end
      
      
Line 1,192: Line 1,167:
-- @return string The rendered template HTML
-- @return string The rendered template HTML
function p.renderTemplate(template, frame)
function p.renderTemplate(template, frame)
    template.current_frame = frame -- Store frame on template instance
   
    -- Generate a unique ID for the title element for ARIA
    local pageId = TemplateHelpers.getCurrentPageId() or '0'
    template.titleId = 'template-title-' .. template.type .. '-' .. pageId
    -- Check recursion depth to prevent infinite loops
    local depth = 0
    if frame.args and frame.args._recursion_depth then
        depth = tonumber(frame.args._recursion_depth) or 0
    elseif frame:getParent() and frame:getParent().args and frame:getParent().args._recursion_depth then
        depth = tonumber(frame:getParent().args._recursion_depth) or 0
    end
   
    if depth > 3 then
        return '<span class="error">Template recursion depth exceeded (limit: 3)</span>'
    end
   
     if not template._errorContext then
     if not template._errorContext then
         template._errorContext = p.createErrorContext(template)
         template._errorContext = p.createErrorContext(template)
     end
     end
   
    ErrorHandling.addStatus(template._errorContext, "LuaTemplateBlueprint", "Now rendering " .. template.type)
      
      
     if not template.config.meta then
     if not template.config.meta then
Line 1,202: Line 1,197:
     local args = frame:getParent().args or {}
     local args = frame:getParent().args or {}
     args = TemplateHelpers.normalizeArgumentCase(args)
     args = TemplateHelpers.normalizeArgumentCase(args)
   
    -- Increment recursion depth for any child template calls
    args._recursion_depth = tostring(depth + 1)
      
      
     args = p.runPreprocessors(template, args)
     args = p.runPreprocessors(template, args)
Line 1,213: Line 1,211:
         tableClass = tableClass,
         tableClass = tableClass,
         blocks = {},
         blocks = {},
         containerTag = template.features.fullPage and "div" or "table"
         containerTag = template.features.fullPage and "div" or "table",
        ariaLabelledBy = template.titleId
     }
     }
      
      
Line 1,228: Line 1,227:
     end
     end
      
      
     return TemplateStructure.render(args, structureConfig, template._errorContext)
     local result = TemplateStructure.render(args, structureConfig, template._errorContext)
   
    -- Append status and error divs to the final output
    result = result .. ErrorHandling.formatCombinedOutput(template._errorContext)
   
    template.current_frame = nil -- Clear frame from template instance
   
    return result
end
end