Module:Cargo orders: Difference between revisions

From Space Station 14 Wiki
Aliser (talk | contribs)
support for contents
Aliser (talk | contribs)
[wip] support for orGroup
Line 4: Line 4:


local orders_table =  mw.loadJsonData("Module:Cargo orders/data/auto/orders.json")
local orders_table =  mw.loadJsonData("Module:Cargo orders/data/auto/orders.json")
local current_frame = mw.getCurrentFrame()


-- =======================
-- =======================


local function assert_not_nil(value, error_message)
    if value == nil then
        if error_message == nil then
            error("value is nil")
        else
            error(error_message)
        end
    end
end
local function starts_with(str, substr)
    return string.sub(str, 1, string.len(substr)) == substr
end
local function ends_with(str, substr)
    local substr_length = string.len(substr)
    return string.sub(str, string.len(str) - substr_length + 1, string.len(str)) == substr
end
local function starts_with_insensitive(str, substr)
    return starts_with(string.lower(str), string.lower(substr))
end
local function ends_with_insensitive(str, substr)
    return ends_with(string.lower(str), string.lower(substr))
end
local function table_filter(tbl, filterFn)
    local out = {}
    for k, v in pairs(tbl) do
        if filterFn(v, k, tbl) then out[k] = v end
    end
    return out
end
local function num_table_reduce(tbl, reduceFn, initial_value)
    local out = initial_value
    for i, v in ipairs(tbl) do
        out = reduceFn(out, v, i, tbl)
    end
    return out
end
local function table_find(tbl, findFn)
    for k, v in pairs(tbl) do
        if findFn(v, k, tbl) then return v end
    end
end
local function table_contains(tbl, findFn)
    return table_find(tbl, findFn) ~= nil
end
local function table_contains_value(tbl, value)
    return table_find(tbl, function(iter_value)
        return iter_value == value
    end) ~= nil
end
local function table_find_key(tbl, findFn)
    for k, v in pairs(tbl) do
        if findFn(v, k, tbl) then return k end
    end
end
local function table_find_index(tbl, findFn)
    for i, v in ipairs(tbl) do
        if findFn(v, i, tbl) then return i end
    end
end
local function table_map(tbl, mapFn)
    local res = {}
    for k, v in pairs(tbl) do
        table.insert(res, mapFn(v, k, tbl))
    end
    return res
end


local function numeric_table_length(t)
local function numeric_table_length(t)
Line 20: Line 104:
end
end


local function table_has_value(tab, val)
-- Sorts a table into a new table.
     for _, value in ipairs(tab) do
-- An alternative to cases such as when table.sort is not working,
         if value == val then
-- like when trying to sort JSON tables (thanks fuckass lua).
            return true
function table_to_sorted(tbl, sortfn)
         end
    local keys = {}
 
     for key, _ in pairs(tbl) do
         table.insert(keys, key)
    end
 
    table.sort(keys, function(keyA, keyB) return sortfn(tbl[keyA], tbl[keyB]) end)
 
    local t2 = {}
 
    for _, key in ipairs(keys) do
         table.insert(t2, tbl[key])
     end
     end


     return false
     return t2
end
end


local function assert_value_not_nil(value, error_message)
-- Given a table and a property key, attempts to retrieve said property.
     if value == nil then
-- If property does not exists, creates it with by assigning a value from `create_value_fn`.
        if error_message == nil then
-- The created value is then returned.
            error("value is nil")
function get_table_prop_or_create(tbl, prop_key, create_value_fn)
        else
     local value = tbl[prop_key]
            error(error_message)
    if value == nil then
         end
        value = create_value_fn()
         tbl[prop_key] = value
     end
     end
    return value;
end
end


-- Makes the first letter uppercase.
-- ===========================
-- Source: https://stackoverflow.com/a/2421746
 
local function capitalize(str)
-- Formats entity ID.
     return (str:gsub("^%l", string.upper))
local function format_id(id, amount)
     return current_frame:expandTemplate{ title = "item", args = { id, amount} }
end
end


-- Makes the first letter in every word uppercase.
-- Formats a table of "contents" entries that all have an "orGroup" property.
local function capitalize_all(str)
-- "orGroup" behaves like a weight table so we need to treat it like one.
     local res = { }
local function format_or_group(group_name, entries)
    for _, word in ipairs(mw.text.split(str, ' ', true)) do
     if #entries == 0 then
        table.insert(res, capitalize(word))
        error("failed to format 'orGroup': no entries provided")
     end
     end
    return table.concat(res, " ")
end


local function passthrough_assert_true(value, valueToReturnIfTrue, errorMessageOnFalse)
    -- calculate cumulative weight
    if value then
    local cumulative_weight = 0
         return valueToReturnIfTrue
    for _, entry in ipairs(entries) do
    else
         local weight = entry.prob or 1
         error(errorMessageOnFalse)
         cumulative_weight = cumulative_weight + weight
     end
     end
end


    -- generate entries
    local section_el = mw.html.create("div")
        :addClass("pool-section")
   
    local label_el = section_el:tag("span")
        :addClass("pool-section-label")
    label_el:tag("span")
        :addClass("pool-section-label-group")
        :wikitext(group_name)
    label_el:wikitext(" ")
    label_el:tag("span")
        :addClass("pool-section-label-after")
        :wikitext("group")
       
    local list_el = section_el:tag("ul")
        :addClass("pool-section-list")
    for _, entry in ipairs(entries) do
        list_el:tag("li")
            :addClass("pool-section-entry")
            :node(format_id(entry.id, entry.amount or 1))
    end


-- Lookup a field in a json table by key.
    return section_el
-- Json tables are assumed to be tables with lowercase keys. The `key` is automatically lowercased for the lookup.
-- If a value is a string, it's treated as an alias (another key), triggering a new lookup.
--
-- Returns the value by the specified key, or by aliased key if the value was an alias.
-- If value doesn't exist, returns `nil`.
local function lookup_json_table(t, key)
local result = t[string.lower(key)]
if type(result) == "string" then
-- alias. lookup actual definition.
return t[result]
else
return result
end
end
end
-- ===========================


-- Formats "contents" field to wikitext.
-- Formats "contents" field to wikitext.
local function format_contents(frame, contents)
local function format_contents(contents)
     if contents == nil then
     if contents == nil then
         return nil
         return nil
     end
     end


     local res_arr = {}
     local contents_el = mw.html.create("div")
     for _, entry in ipairs(contents) do
        :addClass("pool")
         local res = frame:expandTemplate{ title = "item", args = { entry.id, entry.amount or 1 } }
 
        table.insert(res_arr, res)
    local entries_without_orGroup = {}
    local entries_grouped_by_orGroup = num_table_reduce(
        contents,
        function (out, v, i, tbl)
            local orGroup = v.orGroup
            if not orGroup then
                table.insert(entries_without_orGroup, v)
                return out
            end
 
            local outGroup = get_table_prop_or_create(out, orGroup, function () return {} end)
            table.insert(outGroup, v)
 
            return out
        end,
        {}
    )
 
    -- ungrouped go first
    local ungrouped_list_el = contents_el:tag("ul")
 
     for _, entry in ipairs(entries_without_orGroup) do
         ungrouped_list_el:tag("li")
            :node(format_id(entry.id, entry.amount or 1))
     end
     end


     if #res_arr == 0 then
     -- grouped go second
        return nil
     for group_name, group in pairs(entries_grouped_by_orGroup) do
     else
        contents_el:node(format_or_group(group_name, group))
        local ul_el = mw.html.create("ul")
    end
        for _, entry in ipairs(res_arr) do
            ul_el:tag("li")
                :wikitext(entry)
        end


        return tostring(ul_el)
    return contents_el
    end
end  
end  


Line 152: Line 276:
             row:tag('td'):wikitext(frame:expandTemplate{ title = "spesos", args = { entry.cost }})
             row:tag('td'):wikitext(frame:expandTemplate{ title = "spesos", args = { entry.cost }})
             row:tag('td'):wikitext(entry.description)
             row:tag('td'):wikitext(entry.description)
             row:tag('td'):wikitext(format_contents(frame, entry.contents))
             row:tag('td'):node(format_contents(entry.contents))
         end
         end



Revision as of 05:13, 21 May 2025

Module documentation
View or edit this documentation (about module documentation)

Implements {{Cargo orders}}.


local p = {} --p stands for package
local getArgs = require('Module:Arguments').getArgs
local yesNo = require('Module:Yesno')

local orders_table =  mw.loadJsonData("Module:Cargo orders/data/auto/orders.json")

local current_frame = mw.getCurrentFrame()

-- =======================

local function assert_not_nil(value, error_message)
    if value == nil then
        if error_message == nil then
            error("value is nil")
        else
            error(error_message)
        end
    end
end

local function starts_with(str, substr)
    return string.sub(str, 1, string.len(substr)) == substr
end

local function ends_with(str, substr)
    local substr_length = string.len(substr)
    return string.sub(str, string.len(str) - substr_length + 1, string.len(str)) == substr
end

local function starts_with_insensitive(str, substr)
    return starts_with(string.lower(str), string.lower(substr))
end

local function ends_with_insensitive(str, substr)
    return ends_with(string.lower(str), string.lower(substr))
end

local function table_filter(tbl, filterFn)
    local out = {}

    for k, v in pairs(tbl) do
        if filterFn(v, k, tbl) then out[k] = v end
    end

    return out
end

local function num_table_reduce(tbl, reduceFn, initial_value)
    local out = initial_value

    for i, v in ipairs(tbl) do
        out = reduceFn(out, v, i, tbl)
    end

    return out
end

local function table_find(tbl, findFn)
    for k, v in pairs(tbl) do
        if findFn(v, k, tbl) then return v end
    end
end

local function table_contains(tbl, findFn)
    return table_find(tbl, findFn) ~= nil
end

local function table_contains_value(tbl, value)
    return table_find(tbl, function(iter_value)
        return iter_value == value
    end) ~= nil
end

local function table_find_key(tbl, findFn)
    for k, v in pairs(tbl) do
        if findFn(v, k, tbl) then return k end
    end
end

local function table_find_index(tbl, findFn)
    for i, v in ipairs(tbl) do
        if findFn(v, i, tbl) then return i end
    end
end

local function table_map(tbl, mapFn)
    local res = {}
    for k, v in pairs(tbl) do
        table.insert(res, mapFn(v, k, tbl))
    end
    return res
end

local function numeric_table_length(t)
    local count = 0
    for _ in ipairs(t) do count = count + 1 end
    return count
end

local function table_length(t)
    local count = 0
    for _ in pairs(t) do count = count + 1 end
    return count
end

-- Sorts a table into a new table.
-- An alternative to cases such as when table.sort is not working,
-- like when trying to sort JSON tables (thanks fuckass lua).
function table_to_sorted(tbl, sortfn)
    local keys = {}

    for key, _ in pairs(tbl) do
        table.insert(keys, key)
    end

    table.sort(keys, function(keyA, keyB) return sortfn(tbl[keyA], tbl[keyB]) end)

    local t2 = {}

    for _, key in ipairs(keys) do
        table.insert(t2, tbl[key])
    end

    return t2
end

-- Given a table and a property key, attempts to retrieve said property.
-- If property does not exists, creates it with by assigning a value from `create_value_fn`. 
-- The created value is then returned.
function get_table_prop_or_create(tbl, prop_key, create_value_fn)
    local value = tbl[prop_key]
    if  value == nil then
        value = create_value_fn()
        tbl[prop_key] = value
    end

    return value;
end

-- ===========================

-- Formats entity ID.
local function format_id(id, amount)
    return current_frame:expandTemplate{ title = "item", args = { id, amount} }
end

-- Formats a table of "contents" entries that all have an "orGroup" property.
-- "orGroup" behaves like a weight table so we need to treat it like one.
local function format_or_group(group_name, entries)
    if #entries == 0 then
        error("failed to format 'orGroup': no entries provided")
    end

    -- calculate cumulative weight
    local cumulative_weight = 0
    for _, entry in ipairs(entries) do
        local weight = entry.prob or 1
        cumulative_weight = cumulative_weight + weight
    end

    -- generate entries
    local section_el = mw.html.create("div")
        :addClass("pool-section")
    
    local label_el = section_el:tag("span")
        :addClass("pool-section-label")

    label_el:tag("span")
        :addClass("pool-section-label-group")
        :wikitext(group_name)

    label_el:wikitext(" ")

    label_el:tag("span")
        :addClass("pool-section-label-after")
        :wikitext("group")
        
    local list_el = section_el:tag("ul")
        :addClass("pool-section-list")

    for _, entry in ipairs(entries) do
        list_el:tag("li")
            :addClass("pool-section-entry")
            :node(format_id(entry.id, entry.amount or 1))
    end

    return section_el
end

-- Formats "contents" field to wikitext.
local function format_contents(contents)
    if contents == nil then
        return nil
    end

    local contents_el = mw.html.create("div")
        :addClass("pool")

    local entries_without_orGroup = {}
    local entries_grouped_by_orGroup = num_table_reduce(
        contents,
        function (out, v, i, tbl)
            local orGroup = v.orGroup
            if not orGroup then
                table.insert(entries_without_orGroup, v)
                return out
            end

            local outGroup = get_table_prop_or_create(out, orGroup, function () return {} end)
            table.insert(outGroup, v)

            return out
        end,
        {}
    )

    -- ungrouped go first
    local ungrouped_list_el = contents_el:tag("ul")

    for _, entry in ipairs(entries_without_orGroup) do
        ungrouped_list_el:tag("li")
            :node(format_id(entry.id, entry.amount or 1))
    end

    -- grouped go second
    for group_name, group in pairs(entries_grouped_by_orGroup) do
         contents_el:node(format_or_group(group_name, group))
    end

    return contents_el
end 

-- ===========================

function p.main(frame)
    local args = getArgs(frame)

    -- =======================

    local container = mw.html.create('div')

    local orders_section = mw.html.create('h2')
        :wikitext("Orders")
    container:node(orders_section)

    local category_entries_by_category = {}
    for _, entry in ipairs(orders_table) do
        local category = entry.category;
        if category ~= nil then
            local category_entry = category_entries_by_category[category]
            if category_entry == nil then
                local category_section = mw.html.create('h3')
                    :wikitext(category)
                container:node(category_section)

                local category_table = mw.html.create('table')
                    :addClass("wikitable")
                container:node(category_table)

                local category_table_h = category_table:tag('tr')
                category_table_h:tag('th'):wikitext("Product")
                category_table_h:tag('th'):wikitext("Unit cost")
                category_table_h:tag('th'):wikitext("Description")
                category_table_h:tag('th'):wikitext("Contents")

                category_entry = {
                    category_section = category_section,
                    category_table = category_table
                }

                category_entries_by_category[category] = category_entry
            end

            local row = category_entry.category_table:tag('tr')
            row:tag('td'):wikitext(frame:expandTemplate{ title = "item", args = { entry.product }})
            row:tag('td'):wikitext(frame:expandTemplate{ title = "spesos", args = { entry.cost }})
            row:tag('td'):wikitext(entry.description)
            row:tag('td'):node(format_contents(entry.contents))
        end

    end
    
    return container
end

return p