Module:Cargo orders: Difference between revisions
From Space Station 14 Wiki
support for maxAmount (there are actually NO maxAmount uses why am I doing this) |
sort sort sort (sorted categories) |
||
Line 3: | Line 3: | ||
local yesNo = require('Module:Yesno') | local yesNo = require('Module:Yesno') | ||
local orders_table = | local orders_table = mw.loadJsonData("Module:Cargo orders/data/auto/orders.json") | ||
local current_frame = mw.getCurrentFrame() | local current_frame = mw.getCurrentFrame() | ||
Line 102: | Line 102: | ||
for _ in pairs(t) do count = count + 1 end | for _ in pairs(t) do count = count + 1 end | ||
return count | return count | ||
end | |||
-- Returns keys of a table. | |||
local function table_keys(tbl) | |||
local arr = {} | |||
for key, _ in pairs(tbl) do | |||
table.insert(arr, key) | |||
end | |||
return arr | |||
end | end | ||
Line 126: | Line 135: | ||
-- Given a table and a property key, attempts to retrieve said property. | -- 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`. | -- If property does not exists, creates it with by assigning a value from `create_value_fn`. | ||
-- The created value is then returned. | -- The created value is then returned. | ||
function get_table_prop_or_create(tbl, prop_key, create_value_fn) | function get_table_prop_or_create(tbl, prop_key, create_value_fn) | ||
local value = tbl[prop_key] | local value = tbl[prop_key] | ||
if | if value == nil then | ||
value = create_value_fn() | value = create_value_fn() | ||
tbl[prop_key] = value | tbl[prop_key] = value | ||
Line 153: | Line 162: | ||
local was_percentage_rounded = tostring(percentage) ~= percentage_rounded_str | local was_percentage_rounded = tostring(percentage) ~= percentage_rounded_str | ||
percentage_display = (was_percentage_rounded and "≈" or "") .. percentage_rounded_str .. "%" | percentage_display = (was_percentage_rounded and "≈" or "") .. percentage_rounded_str .. "%" | ||
end | end | ||
Line 164: | Line 173: | ||
local amount_res = maxAmount and (amount .. "-" .. maxAmount) or amount | local amount_res = maxAmount and (amount .. "-" .. maxAmount) or amount | ||
return current_frame:expandTemplate{ title = "item", args = { id, amount_res } } | return current_frame:expandTemplate { title = "item", args = { id, amount_res } } | ||
end | end | ||
Line 184: | Line 193: | ||
local section_el = mw.html.create("div") | local section_el = mw.html.create("div") | ||
:addClass("pool-section") | :addClass("pool-section") | ||
local section_label_el = section_el:tag("span") | local section_label_el = section_el:tag("span") | ||
:addClass("pool-section-label") | :addClass("pool-section-label") | ||
:wikitext(group_name) | :wikitext(group_name) | ||
local list_el = section_el:tag("ul") | local list_el = section_el:tag("ul") | ||
:addClass("pool-section-list") | :addClass("pool-section-list") | ||
-- sort by weight | -- sort by weight | ||
local entries_sorted = table_to_sorted(entries, function (a, b) | local entries_sorted = table_to_sorted(entries, function(a, b) | ||
return (a.prob or 1) > (b.prob or 1); | return (a.prob or 1) > (b.prob or 1); | ||
end) | end) | ||
Line 221: | Line 230: | ||
local entries_grouped_by_orGroup = num_table_reduce( | local entries_grouped_by_orGroup = num_table_reduce( | ||
contents, | contents, | ||
function (out, v, i, tbl) | function(out, v, i, tbl) | ||
local orGroup = v.orGroup | local orGroup = v.orGroup | ||
if not orGroup then | if not orGroup then | ||
Line 228: | Line 237: | ||
end | end | ||
local outGroup = get_table_prop_or_create(out, orGroup, function () return {} end) | local outGroup = get_table_prop_or_create(out, orGroup, function() return {} end) | ||
table.insert(outGroup, v) | table.insert(outGroup, v) | ||
Line 247: | Line 256: | ||
:wikitext(" " .. format_probability(entry.prob or 1)) | :wikitext(" " .. format_probability(entry.prob or 1)) | ||
end | end | ||
end | end | ||
-- grouped go second | -- grouped go second | ||
for group_name, group in pairs(entries_grouped_by_orGroup) do | for group_name, group in pairs(entries_grouped_by_orGroup) do | ||
contents_el:node(format_or_group(group_name, group)) | |||
end | end | ||
return contents_el | return contents_el | ||
end | end | ||
-- =========================== | -- =========================== | ||
Line 272: | Line 280: | ||
container:node(orders_section) | container:node(orders_section) | ||
local | local orders_grouped_by_category = num_table_reduce( | ||
orders_table, | |||
function(accum, order) | |||
local category_orders = get_table_prop_or_create(accum, order.category, function() return {} end) | |||
local | table.insert(category_orders, order); | ||
return accum | |||
end, | |||
{} | |||
) | |||
local categories_sorted = table_keys(orders_grouped_by_category) | |||
table.sort(categories_sorted) | |||
for _, category in ipairs(categories_sorted) do | |||
local category_section = mw.html.create('h3') | |||
:wikitext(category) | |||
container:node(category_section) | |||
local category_table = mw.html.create('table') | |||
:addClass("wikitable cargo-orders-table") | |||
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") | |||
local row = | for _, order in ipairs(orders_grouped_by_category[category]) do | ||
row:tag('td'):wikitext(frame:expandTemplate{ title = "item", args = { | local row = category_table:tag('tr') | ||
row:tag('td'):wikitext(frame:expandTemplate{ title = "spesos", args = { | row:tag('td'):wikitext(frame:expandTemplate { title = "item", args = { order.product } }) | ||
row:tag('td'):wikitext( | row:tag('td'):wikitext(frame:expandTemplate { title = "spesos", args = { order.cost } }) | ||
row:tag('td'):node(format_contents( | row:tag('td'):wikitext(order.description) | ||
row:tag('td'):node(format_contents(order.contents)) | |||
end | end | ||
end | |||
return container | return container | ||
end | end | ||
return p | return p |
Revision as of 11:00, 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
-- Returns keys of a table.
local function table_keys(tbl)
local arr = {}
for key, _ in pairs(tbl) do
table.insert(arr, key)
end
return arr
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 probability to a percentage label with specified number of digits after comma.
-- If rounding occurs, also adds "≈".
local function format_probability(prob, digits)
local percentage = prob * 100
local percentage_display
if digits == nil then
percentage_display = percentage .. "%"
else
local percentage_rounded_str = string.format("%." .. digits .. "f", percentage)
:gsub("%.?0+$", "") -- strip trailing zeros (including the dot if theres all zeros after)
local was_percentage_rounded = tostring(percentage) ~= percentage_rounded_str
percentage_display = (was_percentage_rounded and "≈" or "") .. percentage_rounded_str .. "%"
end
return "[" .. percentage_display .. "]"
end
-- Formats entity ID.
local function format_id(id, amount, maxAmount)
local amount_res = maxAmount and (amount .. "-" .. maxAmount) or amount
return current_frame:expandTemplate { title = "item", args = { id, amount_res } }
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 section_label_el = section_el:tag("span")
:addClass("pool-section-label")
:wikitext(group_name)
local list_el = section_el:tag("ul")
:addClass("pool-section-list")
-- sort by weight
local entries_sorted = table_to_sorted(entries, function(a, b)
return (a.prob or 1) > (b.prob or 1);
end)
for _, entry in ipairs(entries_sorted) do
local prob = (entry.prob or 1) / cumulative_weight;
list_el:tag("li")
:addClass("pool-section-entry")
:node(format_id(entry.id, entry.amount or 1, entry.maxAmount))
:wikitext(" " .. format_probability(prob, 3))
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
local entry_li_el = ungrouped_list_el:tag("li")
:node(format_id(entry.id, entry.amount or 1, entry.maxAmount))
if entry.prob then
entry_li_el
:wikitext(" " .. format_probability(entry.prob or 1))
end
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')
:addClass("cargo-orders-container")
local orders_section = mw.html.create('h2')
:wikitext("Orders")
container:node(orders_section)
local orders_grouped_by_category = num_table_reduce(
orders_table,
function(accum, order)
local category_orders = get_table_prop_or_create(accum, order.category, function() return {} end)
table.insert(category_orders, order);
return accum
end,
{}
)
local categories_sorted = table_keys(orders_grouped_by_category)
table.sort(categories_sorted)
for _, category in ipairs(categories_sorted) do
local category_section = mw.html.create('h3')
:wikitext(category)
container:node(category_section)
local category_table = mw.html.create('table')
:addClass("wikitable cargo-orders-table")
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")
for _, order in ipairs(orders_grouped_by_category[category]) do
local row = category_table:tag('tr')
row:tag('td'):wikitext(frame:expandTemplate { title = "item", args = { order.product } })
row:tag('td'):wikitext(frame:expandTemplate { title = "spesos", args = { order.cost } })
row:tag('td'):wikitext(order.description)
row:tag('td'):node(format_contents(order.contents))
end
end
return container
end
return p