Module:BuildVersionTree: Difference between revisions

From Seven Sages of Rome
No edit summary
No edit summary
Line 2: Line 2:
local p = {}
local p = {}


-- Helper function to safely access table values
-- Helper function to dump a table for debugging
local function safeGet(tbl, key)
local function dumpTable(tbl, indent)
     if tbl and type(tbl) == "table" then
     indent = indent or ""
         return tbl[key]
    local result = {}
   
    for k, v in pairs(tbl) do
        if type(v) == "table" then
            table.insert(result, indent .. tostring(k) .. ":")
            table.insert(result, dumpTable(v, indent .. "  "))
        else
            table.insert(result, indent .. tostring(k) .. ": " .. tostring(v))
         end
     end
     end
     return nil
   
     return table.concat(result, "\n")
end
end


Line 16: Line 25:
      
      
     -- First pass: Create all nodes
     -- First pass: Create all nodes
     for _, version in ipairs(versions) do
     for _, version in pairs(versions) do
         if version and version.fulltext then
         if version.fulltext then
             nodes[version.fulltext] = {
             nodes[version.fulltext] = {
                 title = version.fulltext,
                 title = version.fulltext,
Line 27: Line 36:
      
      
     -- Second pass: Build relationships
     -- Second pass: Build relationships
     for _, version in ipairs(versions) do
     for _, version in pairs(versions) do
         if version and version.fulltext then
         if version.fulltext then
             local parent = nil
             local parentValue = version[parentField]
            if version[parentField] and type(version[parentField]) == "table" then
            local hasParent = parentValue and parentValue[1] and parentValue[1] ~= ""
                parent = version[parentField][1]
            end
              
              
             if parent and nodes[parent] then
             if hasParent and nodes[parentValue[1]] then
                 table.insert(nodes[parent].children, nodes[version.fulltext])
                -- If it has a valid parent, add it as a child
                 table.insert(nodes[parentValue[1]].children, nodes[version.fulltext])
             else
             else
                 -- If no parent or parent doesn't exist, it's a root node
                 -- If no parent or invalid parent, it's a root node
                 table.insert(tree, nodes[version.fulltext])
                 table.insert(tree, nodes[version.fulltext])
             end
             end
Line 188: Line 196:
-- Main function to process the versions and generate tree view
-- Main function to process the versions and generate tree view
function p.getTree(frame)
function p.getTree(frame)
    -- Debug output
    local debug = {}
   
     local versions = mw.smw.ask({
     local versions = mw.smw.ask({
         "[[Category:Version||Secondary Version]]",
         "[[Category:Version||Secondary Version]]",
         "|?Has Parent Version"
         "?Has Parent Version"
     })
     })
      
      
     if not versions or type(versions) ~= "table" or #versions == 0 then
     -- Debug output
         return "No versions found in the Category:Version or Secondary Version."
    local debug = {'SMW Query Results:'}
    table.insert(debug, dumpTable(versions))
   
    if not versions then
         return "SMW query returned no results.\n\nDebug info:\n<pre>" .. table.concat(debug, "\n") .. "</pre>"
     end
     end
      
      
     -- Transform SMW results into a format suitable for tree building
     -- Process versions
     local processedVersions = {}
     local processedVersions = {}
     for _, version in ipairs(versions) do
     for k, v in pairs(versions) do
         if version and version.fulltext then
         if v.fulltext then
             table.insert(processedVersions, {
             processedVersions[k] = {
                 fulltext = version.fulltext,
                 fulltext = v.fulltext,
                 ["Has Parent Version"] = version["Has Parent Version"]
                 ["Has Parent Version"] = v["Has Parent Version"]
             })
             }
            -- Add debug information
            table.insert(debug, "Processing: " .. version.fulltext)
            if version["Has Parent Version"] then
                table.insert(debug, "  Parent: " .. tostring(version["Has Parent Version"][1]))
            end
         end
         end
     end
     end
      
      
     if #processedVersions == 0 then
    -- Add processed versions to debug output
         return "No valid versions found to process."
    table.insert(debug, "\nProcessed Versions:")
    table.insert(debug, dumpTable(processedVersions))
   
     if next(processedVersions) == nil then
         return "No valid versions found to process.\n\nDebug info:\n<pre>" .. table.concat(debug, "\n") .. "</pre>"
     end
     end
      
      
     -- Build tree
     -- Build tree
     local tree = buildTree(processedVersions, "Has Parent Version")
     local tree = buildTree(processedVersions, "Has Parent Version")
   
    -- Add tree structure to debug output
    table.insert(debug, "\nTree Structure:")
    table.insert(debug, dumpTable(tree))
      
      
     -- Sort root nodes
     -- Sort root nodes
Line 241: Line 253:
     table.insert(result, '</div>')
     table.insert(result, '</div>')
      
      
     -- Add debug information if needed
     -- Add debug information
     -- table.insert(result, '<pre>' .. table.concat(debug, '\n') .. '</pre>')
     table.insert(result, '<pre class="debug-info">' .. table.concat(debug, '\n') .. '</pre>')
      
      
     return table.concat(result, '\n')
     return table.concat(result, '\n')

Revision as of 18:55, 23 October 2024

Documentation for this module may be created at Module:BuildVersionTree/doc

-- Module:VersionTree
local p = {}

-- Helper function to dump a table for debugging
local function dumpTable(tbl, indent)
    indent = indent or ""
    local result = {}
    
    for k, v in pairs(tbl) do
        if type(v) == "table" then
            table.insert(result, indent .. tostring(k) .. ":")
            table.insert(result, dumpTable(v, indent .. "  "))
        else
            table.insert(result, indent .. tostring(k) .. ": " .. tostring(v))
        end
    end
    
    return table.concat(result, "\n")
end

-- Helper function to build a tree structure from flat data
local function buildTree(versions, parentField)
    local tree = {}
    local nodes = {}
    
    -- First pass: Create all nodes
    for _, version in pairs(versions) do
        if version.fulltext then
            nodes[version.fulltext] = {
                title = version.fulltext,
                children = {},
                processed = false
            }
        end
    end
    
    -- Second pass: Build relationships
    for _, version in pairs(versions) do
        if version.fulltext then
            local parentValue = version[parentField]
            local hasParent = parentValue and parentValue[1] and parentValue[1] ~= ""
            
            if hasParent and nodes[parentValue[1]] then
                -- If it has a valid parent, add it as a child
                table.insert(nodes[parentValue[1]].children, nodes[version.fulltext])
            else
                -- If no parent or invalid parent, it's a root node
                table.insert(tree, nodes[version.fulltext])
            end
        end
    end
    
    return tree
end

-- Helper function to generate unique IDs for nodes
local function generateId(text)
    return 'node-' .. mw.uri.encode(text):gsub('%%', '-')
end

-- Helper function to render a tree node and its children
local function renderNode(node, level)
    local result = {}
    
    -- Generate unique ID for this node
    local nodeId = generateId(node.title)
    
    -- Create node container
    table.insert(result, string.format('<div class="tree-node level-%d" id="%s">', level, nodeId))
    
    -- Add node content
    table.insert(result, string.format('<div class="node-content"><div class="node-box">[[%s]]</div></div>', node.title))
    
    if #node.children > 0 then
        -- Add container for children
        table.insert(result, '<div class="node-children">')
        
        -- Sort children by title
        table.sort(node.children, function(a, b)
            return a.title < b.title
        end)
        
        -- Render children
        for _, child in ipairs(node.children) do
            table.insert(result, renderNode(child, level + 1))
        end
        
        table.insert(result, '</div>')
    end
    
    table.insert(result, '</div>')
    
    return table.concat(result, '\n')
end

-- Function to generate CSS styles
local function getStyles()
    return [[
<style>
.version-tree {
    text-align: center;
    padding: 20px;
    font-family: sans-serif;
}

.tree-node {
    display: inline-block;
    vertical-align: top;
    text-align: center;
    padding: 0 10px;
    position: relative;
}

.node-content {
    position: relative;
    padding: 10px 0;
}

.node-box {
    display: inline-block;
    border: 1px solid #ccc;
    border-radius: 5px;
    padding: 5px 15px;
    background: #f8f9fa;
    position: relative;
    z-index: 1;
}

.node-children {
    position: relative;
    margin-top: 20px;
}

/* Lines connecting nodes */
.node-children:before {
    content: '';
    position: absolute;
    top: -20px;
    left: 50%;
    border-left: 1px solid #ccc;
    height: 20px;
}

.tree-node .tree-node:before {
    content: '';
    position: absolute;
    top: 0;
    left: 50%;
    border-left: 1px solid #ccc;
    height: 10px;
}

.node-children .tree-node:first-child:after,
.node-children .tree-node:last-child:after {
    content: '';
    position: absolute;
    top: 0;
    width: 50%;
    height: 10px;
    border-top: 1px solid #ccc;
}

.node-children .tree-node:first-child:after {
    right: 50%;
}

.node-children .tree-node:last-child:after {
    left: 50%;
}

.node-children .tree-node:only-child:after {
    display: none;
}

/* Horizontal line connecting siblings */
.node-children {
    position: relative;
}

.node-children:after {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    border-top: 1px solid #ccc;
}

.node-children .tree-node:only-child:before {
    height: 10px;
}
</style>
]]
end

-- Main function to process the versions and generate tree view
function p.getTree(frame)
    local versions = mw.smw.ask({
        "[[Category:Version||Secondary Version]]",
        "?Has Parent Version"
    })
    
    -- Debug output
    local debug = {'SMW Query Results:'}
    table.insert(debug, dumpTable(versions))
    
    if not versions then
        return "SMW query returned no results.\n\nDebug info:\n<pre>" .. table.concat(debug, "\n") .. "</pre>"
    end
    
    -- Process versions
    local processedVersions = {}
    for k, v in pairs(versions) do
        if v.fulltext then
            processedVersions[k] = {
                fulltext = v.fulltext,
                ["Has Parent Version"] = v["Has Parent Version"]
            }
        end
    end
    
    -- Add processed versions to debug output
    table.insert(debug, "\nProcessed Versions:")
    table.insert(debug, dumpTable(processedVersions))
    
    if next(processedVersions) == nil then
        return "No valid versions found to process.\n\nDebug info:\n<pre>" .. table.concat(debug, "\n") .. "</pre>"
    end
    
    -- Build tree
    local tree = buildTree(processedVersions, "Has Parent Version")
    
    -- Add tree structure to debug output
    table.insert(debug, "\nTree Structure:")
    table.insert(debug, dumpTable(tree))
    
    -- Sort root nodes
    table.sort(tree, function(a, b)
        return a.title < b.title
    end)
    
    -- Generate output
    local result = {
        getStyles(),
        '<div class="version-tree">'
    }
    
    -- Render each root node
    for _, node in ipairs(tree) do
        table.insert(result, renderNode(node, 0))
    end
    
    table.insert(result, '</div>')
    
    -- Add debug information
    table.insert(result, '<pre class="debug-info">' .. table.concat(debug, '\n') .. '</pre>')
    
    return table.concat(result, '\n')
end

return p