Editing Module:Products
Jump to navigation
Jump to search
The edit can be undone. Please check the comparison below to verify that this is what you want to do, and then publish the changes below to finish undoing the edit.
Latest revision | Your text | ||
Line 1: | Line 1: | ||
require('Module:Mw.html extension') |
|||
local Array = require('Module:Array') |
|||
local currency = require('Module:Currency').parse |
local currency = require('Module:Currency').parse |
||
local yesno = require('Module:Yesno') |
|||
local purge = require('Module:Purge')._purge |
|||
local p = {} |
local p = {} |
||
Line 9: | Line 5: | ||
function p.main(frame) |
function p.main(frame) |
||
return p._main(frame:getParent().args) |
return p._main(frame:getParent().args) |
||
end |
|||
local function recipe_sort(recipe_a, recipe_b) |
|||
--if one is nil but not both, put nil levels after known levels |
|||
if (recipe_a.level == nil) ~= (recipe_b.level == nil) then |
|||
return recipe_b.level == nil |
|||
end |
|||
-- if both are nil, sort by name |
|||
if recipe_a.level == nil then |
|||
return recipe_a.output[1].name < recipe_b.output[1].name |
|||
end |
|||
-- if neither is nil, sort first by level |
|||
if recipe_a.level ~= recipe_b.level then |
|||
return recipe_a.level < recipe_b.level |
|||
end |
|||
-- if neither is nil and levels are the same, sort by name |
|||
return recipe_a.output[1].name < recipe_b.output[1].name |
|||
end |
end |
||
function p._main(args) |
function p._main(args) |
||
args = args or {} |
args = args or {} |
||
args.item = args.item or mw.title.getCurrentTitle().text |
|||
local showPrices = |
local showPrices = args.showPrices or false |
||
local showValues = |
local showValues = args.showValues or false |
||
local |
local products = p.getProducts(args) |
||
if |
if products ~= 0 then |
||
local recipeNames = p.extractRecipeNames(products) |
|||
limit = 500 |
|||
local prodprices = p.getShopBuyPrices(products) |
|||
end |
|||
-- Generate and return a table containing the product information |
|||
local output = p.displayProductTable(prodprices, showPrices, showValues) |
|||
-- Query for data |
|||
return output |
|||
local smw_data = mw.smw.ask{ |
|||
else |
|||
'[[Uses item::' .. item .. ']] OR [[Activity input::' .. item .. ']] OR [[Activity container::' .. item .. ']]', |
|||
return "There are no known products for item '''args.item'''" |
|||
'?Recipe JSON', |
|||
'?Activity JSON', |
|||
showValues and '?Value', |
|||
limit = limit |
|||
} |
|||
if not smw_data then |
|||
return ":''No products found. To force an update, click " |
|||
..purge('dml-'..mw.uri.anchorEncode(item), 'here', 'span') |
|||
..".''[[Category:Empty products lists]]" |
|||
end |
end |
||
end |
|||
function p.extractRecipeNames(products) |
|||
-- Create a list of all recipes that use this item |
|||
local |
local recipeNames = {} |
||
for _, |
for _, recipeInfo in ipairs(products) do |
||
local recipeLink = recipeInfo["Recipe"] |
|||
-- this part will need to be addressed if a page has both a recipe and an activity |
|||
if recipeLink then |
|||
local jsons = product['Recipe JSON'] or product['Activity JSON'] |
|||
local displayName = recipeLink:match("%[%[.-|(.+)%]%]") |
|||
if type(jsons) == 'string' then |
|||
if displayName then |
|||
jsons = { jsons } |
|||
table.insert(recipeNames, displayName) |
|||
end |
|||
for _, json in ipairs(jsons) do |
|||
local json = mw.text.jsonDecode(json) |
|||
json.Value = product.Value |
|||
-- Filter out when this item isn't actually used (Will happen on pages with multiple recipes) |
|||
if Array.any(json.materials, function(mat) |
|||
return mat.name == item |
|||
end) then |
|||
table.insert(recipes, json) |
|||
end |
end |
||
end |
end |
||
end |
end |
||
return recipeNames |
|||
end |
|||
function p.getProducts(args) |
|||
-- Sort list of recipes by the level of the recipe (cross-profession) |
|||
local item = mw.smw.ask('[[:+]][[Uses item::' .. args.item .. ']]|?Uses item |?Uses item and quantity |?Profession A |?Profession Level A|?Value |mainlabel=Recipe') or 0 |
|||
table.sort(recipes, recipe_sort) |
|||
return item |
|||
end |
|||
function p.getShopBuyPrices(products) |
|||
-- if recipe is passive, want per hour |
|||
for _, |
for _, product in ipairs(products) do |
||
local usesItems = product["Uses item and quantity"] |
|||
recipe.recipePerHour = recipe.duration and math.floor(3600/recipe.duration) |
|||
if type(usesItems) == "string" then |
|||
-- replace quantities with per-hour quantities |
|||
-- Single value |
|||
-- if recipe.passive then |
|||
usesItems = { usesItems } |
|||
for _,item in ipairs(recipe.output) do |
|||
end |
|||
item.displayQuantity = (item.quantity and recipe.recipePerHour and item.quantity * recipe.recipePerHour) or '' |
|||
for _, item in ipairs(usesItems) do |
|||
end |
|||
-- Extract item name and quantity from the "item,#" format |
|||
for _,item in ipairs(recipe.materials) do |
|||
local itemName, quantity = item:match("^([^,]+),([0-9.]+)$") |
|||
item.displayQuantity = (item.quantity and recipe.recipePerHour and item.quantity * recipe.recipePerHour) or '' |
|||
quantity = tonumber(quantity) or 1 -- Default to 1 if quantity is missing |
|||
end |
|||
-- end |
|||
end |
|||
-- Calculate shop prices |
|||
if showPrices then |
|||
local price_cache = {} |
|||
for _, recipe in ipairs(recipes) do |
|||
for _, item in ipairs(recipe.materials) do |
|||
-- Extract item name and quantity from the "item,#" format |
|||
if itemName then |
|||
-- Query for the shop buy price of the item |
-- Query for the shop buy price of the item |
||
local shopPriceQuery = '[[:+]][[Sold item::' .. itemName .. ']]|?Shop buy price|mainlabel=' .. itemName |
|||
local shopPriceResult = price_cache[item.name] or mw.smw.ask{ |
|||
local shopPriceResult = mw.smw.ask(shopPriceQuery) or {} |
|||
'[[Sold item::' .. item.name .. ']]', |
|||
local shopPrice = 0 |
|||
} or {} |
|||
price_cache[item.name] = shopPriceResult |
|||
local shopPrice |
|||
if shopPriceResult[1] then |
if shopPriceResult[1] and shopPriceResult[1]["Shop buy price"] then |
||
shopPrice = tonumber(shopPriceResult[1][ |
shopPrice = tonumber(shopPriceResult[1]["Shop buy price"]) or 0 |
||
end |
end |
||
-- |
-- Multiply the price by the quantity |
||
local totalPrice = shopPrice * quantity |
|||
item.price = shopPrice * item.quantity |
|||
-- Update the product with the formatted string and total price |
|||
end |
|||
product[itemName .. "_Shop_buy_price"] = totalPrice > 0 and totalPrice or "N/A" |
|||
product[itemName .. "_Formatted"] = tostring(quantity) .. " × " .. itemName |
|||
end |
end |
||
end |
end |
||
end |
end |
||
return products |
|||
end |
|||
function p.displayProductTable(products, showPrices, showValues) |
|||
-- Create table |
|||
local out = |
local out = {} |
||
table.insert(out, '{| class="wikitable"') |
|||
:addClass('wikitable align-right-1 sortable') |
|||
:tag('tr') |
|||
:tag('th') |
|||
:attr{ colspan = '3' } |
|||
:wikitext('Product') |
|||
:done() |
|||
:tag('th'):wikitext('Level'):done() |
|||
:IF(showValues) |
|||
:tag('th'):wikitext('Value'):done() |
|||
:END() |
|||
:tag('th'):wikitext('Inputs'):done() |
|||
:IF(showPrices) |
|||
:tag('th'):wikitext('Price'):done() |
|||
:END() |
|||
:done() |
|||
local headerRow = {} |
|||
for _, recipe in ipairs(recipes) do |
|||
table.insert(headerRow, '!colspan="2" | Recipe') |
|||
local row = out:tag('tr') |
|||
table.insert(headerRow, '!! Level') |
|||
:tag('td') |
|||
:css{ ['border-right'] = '0', ['padding-right'] = '0' } |
|||
:attr{ ['data-sort-value'] = recipe.output[1].name } |
|||
:IF(recipe.passive) |
|||
:wikitext(recipe.output[1].displayQuantity .. ' (/hr) ×') |
|||
:ELSE() |
|||
:wikitext(recipe.output[1].quantity .. ' ×') |
|||
:END() |
|||
:done() |
|||
:tag('td') |
|||
:addClass('plinkt-image no-border') |
|||
:css{ ['border-left'] = '0', ['padding-left'] = '0' } |
|||
:wikitext('[[File:' .. recipe.output[1].name .. '.png|link=' .. recipe.output[1].name .. '|30px]]') |
|||
:done() |
|||
:tag('td') |
|||
:addClass('plinkt-link no-border') |
|||
:wikitext('[[' .. recipe.output[1].name .. ']]') |
|||
:done() |
|||
:tag('td') |
|||
:IF(recipe.profession) |
|||
:wikitext(('[[File:%s small icon.png|15px|link=%s]] %s'):format(recipe.profession, recipe.profession, recipe.level or 'Unknown')) |
|||
:ELSE() |
|||
:wikitext(('[[FileUnknown profession small icon.png|15px|link=Professions]] %s'):format(recipe.level or 'Unknown')) |
|||
:END() |
|||
:done() |
|||
if showValues then |
|||
table.insert(headerRow, '!! Value') |
|||
if recipe.Value then |
|||
end |
|||
row:tag('td') |
|||
:wikitext(currency(recipe.Value)) |
|||
table.insert(headerRow, '!! Ingredients') |
|||
:done() |
|||
else |
|||
if showPrices then |
|||
row:tag('td') |
|||
table.insert(headerRow, '!! Price') |
|||
:attr{ ['data-sort-value'] = '' } |
|||
end |
|||
:wikitext('Unknown') |
|||
:done() |
|||
table.insert(out, table.concat(headerRow, ' ')) |
|||
for _, product in ipairs(products) do |
|||
local recipeRawText = product["Recipe"]:match("%[%[.-|(.+)%]%]") |
|||
local recipeImage = "[[File:" .. recipeRawText .. ".png|30px]]" |
|||
local recipe = product["Recipe"] or "Unknown" |
|||
local professionName = product["Profession A"]:match("%[%[.-|(.+)%]%]") or product["Profession A"]:match("%[%[(.-)%]%]") or product["Profession A"] or "Unknown" |
|||
local professionLevel = "[[File:" .. professionName .. " small icon.png|15px]] " .. (product["Profession Level A"] or "Unknown") |
|||
local usesItems = product["Uses item"] or {} |
|||
local value = currency(product["Value"]) or "Unknown" |
|||
-- Concatenate uses items and their shop buy prices in unordered lists |
|||
local usesItemStr = "<ul style='list-style:none; margin:0; padding-left:0;'>" |
|||
local shopBuyPriceStr = "<ul style='list-style:none; margin:0; padding-left:0; text-align:right;'>" |
|||
if type(usesItems) == "table" then |
|||
for _, item in ipairs(usesItems) do |
|||
local itemName = item:match("%[%[.-|(.+)%]%]") or item:match("%[%[(.-)%]%]") or item |
|||
usesItemStr = usesItemStr .. "<li>" .. product[itemName .. "_Formatted"] .. "</li>" |
|||
local shopBuyPrice = product[itemName .. "_Shop_buy_price"] or 0 |
|||
if shopBuyPrice == "N/A" then shopBuyPrice = 0 end |
|||
shopBuyPriceStr = shopBuyPriceStr .. "<li>" .. currency(shopBuyPrice) .. "</li>" |
|||
end |
end |
||
elseif type(usesItems) == "string" then |
|||
local itemName = usesItems:match("%[%[.-|(.+)%]%]") or usesItems:match("%[%[(.-)%]%]") or usesItems |
|||
usesItemStr = usesItemStr .. "<li>" .. product[itemName .. "_Formatted"] .. "</li>" |
|||
local shopBuyPrice = product[itemName .. "_Shop_buy_price"] or 0 |
|||
if shopBuyPrice == "N/A" then shopBuyPrice = 0 end |
|||
shopBuyPriceStr = shopBuyPriceStr .. "<li>" .. currency(shopBuyPrice) .. "</li>" |
|||
end |
end |
||
usesItemStr = usesItemStr .. "</ul>" |
|||
local ingredients = row:tag('td') |
|||
shopBuyPriceStr = shopBuyPriceStr .. "</ul>" |
|||
:attr{ ['data-sort-value'] = table.concat(Array.map(recipe.materials, function(item) return item.name end), '\0') } |
|||
:tag('ul') |
|||
:css{ ['list-style'] = 'none', ['margin'] = '0', ['padding-left'] = '0' } |
|||
table.insert(out, '|-') |
|||
for _, item in ipairs(recipe.materials) do |
|||
ingredients:tag('li') |
|||
local valuesRow = {} |
|||
:IF(recipe.passive) |
|||
:wikitext(('%s (/hr) × [[File:%s.png|link=%s|18px]] [[%s]]'):format(item.displayQuantity, item.name, item.name, item.name)) |
|||
table.insert(valuesRow, '| ' .. recipeImage) |
|||
:ELSE() |
|||
table.insert(valuesRow, '|| ' .. recipe) |
|||
:wikitext(('%s × [[File:%s.png|link=%s|18px]] [[%s]]'):format(item.quantity, item.name, item.name, item.name)) |
|||
table.insert(valuesRow, '|| ' .. professionLevel) |
|||
:END() |
|||
:done() |
|||
if showValues then |
|||
table.insert(valuesRow, '|| ' .. value) |
|||
end |
end |
||
table.insert(valuesRow, '|| ' .. usesItemStr) |
|||
if showPrices then |
|||
local prices = row:tag('td') |
|||
:tag('ul') |
|||
:css{ ['list-style'] = 'none', ['margin'] = '0', ['padding-left'] = '0' } |
|||
if showPrices then |
|||
for _, item in ipairs(recipe.materials) do |
|||
table.insert(valuesRow, '|| ' .. shopBuyPriceStr) |
|||
if item.price then |
|||
prices:tag('li'):wikitext(currency(item.price)):done() |
|||
else |
|||
prices:tag('li'):wikitext('Unknown'):done() |
|||
end |
|||
end |
|||
end |
end |
||
table.insert(out, table.concat(valuesRow, ' ')) |
|||
--table.insert(out, '| ' .. recipe .. ' || ' .. professionLevel .. ' || ' .. value .. ' || ' .. usesItemStr .. ' || ' .. shopBuyPriceStr) |
|||
end |
end |
||
table.insert(out, '|}') |
|||
return out |
|||
return table.concat(out, '\n') |
|||
end |
end |
||