DataTableParserV2: Difference between revisions
From Dune Awakening DB
mNo edit summary Tag: Reverted |
mNo edit summary |
||
| (22 intermediate revisions by the same user not shown) | |||
| Line 2: | Line 2: | ||
-- Handles display and formatting of building data for Templates using External Data. | -- Handles display and formatting of building data for Templates using External Data. | ||
-- Data is fetched from your SQL tables (data_buildings and data_refining_recipes) | -- Data is fetched from your SQL tables (data_buildings and data_refining_recipes) | ||
-- via the ExternalData extension | -- via the ExternalData extension. | ||
local p = {} | local p = {} | ||
| Line 64: | Line 64: | ||
-------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ||
-- Function: loadBuildingData | -- Function: loadBuildingData | ||
-- Uses | -- Uses ExternalData (via the parser function) to fetch building data from data_buildings. | ||
-------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ||
local function | local function splitBySeparator(input, sep) | ||
local | local result = {} | ||
local pos = 1 | |||
while true do | |||
local i, j = input:find(sep, pos, true) | |||
if not i then | |||
table.insert(result, mw.text.trim(input:sub(pos))) | |||
break | |||
end | |||
table.insert(result, mw.text.trim(input:sub(pos, i - 1))) | |||
pos = j + 1 | |||
end | end | ||
return result | |||
end | |||
local | local function loadBuildingData(frame) | ||
local | local buildingName = frame.args[1] or frame.args.name or mw.title.getCurrentTitle().text | ||
local edQuery = string.format([[ | |||
{{#get_external_data: source=externaldb | |||
|from=data_buildings | |||
|data=ID=id,BuildingType=building_type,Name=name,Description=description,PowerCost=power_cost,GeneratesPower=generates_power,StorageSlots=storage_slots,StorageCapacity=storage_capacity,SchematicRequirement=schematic_requirement,JourneyRequirement=journey_requirement,Health=health,PlacedWith=placed_with,AdditionalNotes=additional_notes,RecipeToBuild=recipe_to_build,ImageFile=image_file,IconFile=icon_file,Category1=category_1,Category2=category_2,Category3=category_3,Gallery1=gallery_1,Gallery2=gallery_2,Gallery3=gallery_3,Gallery4=gallery_4,YoutubeVideoLink=youtube_video_link | |||
|cache=yes | |||
|where=name='%s' | |||
if | |limit=1 | ||
return nil, " | |format=plain | ||
|separator=|| | |||
}} | |||
]], buildingName) | |||
local rawOutput = mw.text.trim(frame:preprocess(edQuery)) | |||
if rawOutput == "" then | |||
return nil, "No data found for building: " .. buildingName | |||
end | end | ||
local | local fields = { | ||
"ID", "BuildingType", "Name", "Description", "PowerCost", "GeneratesPower", | |||
"StorageSlots", "StorageCapacity", "SchematicRequirement", "JourneyRequirement", | |||
"Health", "PlacedWith", "AdditionalNotes", "RecipeToBuild", "ImageFile", | |||
"IconFile", "Category1", "Category2", "Category3", "Gallery1", "Gallery2", | |||
"Gallery3", "Gallery4", "YoutubeVideoLink" | |||
} | |||
local values = splitBySeparator(rawOutput, "||") | |||
if #values < #fields then | |||
return nil, "Incomplete data returned for building: " .. buildingName | |||
end | end | ||
for i, | local colMap = {} | ||
colMap[ | for i, field in ipairs(fields) do | ||
colMap[field] = i | |||
end | end | ||
local buildingData = { colMap = colMap, rows = { values } } | |||
return buildingData, nil | |||
end | end | ||
-------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ||
-- Function: loadRefiningData | -- Function: loadRefiningData | ||
-- Uses | -- Uses ExternalData (via parser function) to fetch refining recipes from data_refining_recipes. | ||
-------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ||
local function loadRefiningData(frame) | local function loadRefiningData(frame) | ||
local edQuery = [[ | local buildingName = frame.args[1] or frame.args.name or mw.title.getCurrentTitle().text | ||
{{#get_external_data: source=externaldb | local edQuery = string.format([[ | ||
{{#get_external_data: source=externaldb | |||
|from=data_refining_recipes | |||
|data=Refiner=Refiner,Output=Output,Ingredients=Ingredients,Time=Time,Recipe=Recipe | |||
|cache=yes | |||
}} | |where=Refiner='%s' | ||
}} | |||
]], buildingName) | |||
local result = frame:preprocess(edQuery) | |||
local refiningData = {} | |||
{ | local colMap = {} | ||
{ | local rows = {} | ||
{ | local fields = {"Refiner", "Output", "Ingredients", "Time", "Recipe"} | ||
local firstRow = {} | |||
for i, field in ipairs(fields) do | |||
{ | colMap[field] = i | ||
local value = frame:preprocess("{{{" .. field .. "}}}") | |||
if value and value ~= "" and value ~= "{{{" .. field .. "}}}" then | |||
] | firstRow[i] = value | ||
else | |||
firstRow[i] = "" | |||
end | |||
end | end | ||
local hasFirstRow = false | |||
local | for _, v in pairs(firstRow) do | ||
for _, | if v ~= "" then | ||
hasFirstRow = true | |||
if | break | ||
end | end | ||
end | end | ||
if | |||
if hasFirstRow then | |||
table.insert(rows, firstRow) | |||
end | end | ||
-- Check for additional rows | |||
for i = 1, 50 do | |||
local rowData = {} | |||
local hasData = false | |||
for j, field in ipairs(fields) do | |||
local value = frame:preprocess("{{{" .. field .. "_" .. i .. "}}}") | |||
if value and value ~= "" and value ~= "{{{" .. field .. "_" .. i .. "}}}" then | |||
rowData[j] = value | |||
hasData = true | |||
else | |||
rowData[j] = "" | |||
if | |||
end | end | ||
end | |||
if hasData then | |||
table.insert(rows, rowData) | |||
else | |||
break | |||
end | end | ||
end | end | ||
if #rows == 0 then | |||
return nil, "No refining recipes found for building: " .. buildingName | |||
end | end | ||
refiningData.colMap = colMap | |||
refiningData.rows = rows | |||
return refiningData, nil | |||
return | |||
end | end | ||
-------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ||
-- Function: getBuildingData | -- Function: getBuildingData | ||
-- Returns the row (and colMap) for a given building name | -- Returns the row (and colMap) for a given building name. | ||
-------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ||
function p.getBuildingData(buildingName, frame) | function p.getBuildingData(buildingName, frame) | ||
local buildingData, err = loadBuildingData(frame) | local buildingData, err = loadBuildingData(frame) | ||
if not buildingData | if not buildingData or #buildingData.rows == 0 then | ||
return nil, err or "Building not found" | |||
return nil, " | |||
end | end | ||
return buildingData.rows[1], buildingData.colMap | |||
end | end | ||
| Line 293: | Line 217: | ||
local buildingName = frame.args[1] or mw.title.getCurrentTitle().text | local buildingName = frame.args[1] or mw.title.getCurrentTitle().text | ||
local refiningData, err = loadRefiningData(frame) | local refiningData, err = loadRefiningData(frame) | ||
if not refiningData then | if not refiningData then | ||
return " | return '<tr><td colspan="3" style="text-align:center;">No refining recipes found for this building.</td></tr>' | ||
end | end | ||
local rows = refiningData.rows | local rows = refiningData.rows | ||
local | local colMap = refiningData.colMap | ||
if #rows == 0 then | |||
if # | |||
return '<tr><td colspan="3" style="text-align:center;">No refining recipes found for this building.</td></tr>' | return '<tr><td colspan="3" style="text-align:center;">No refining recipes found for this building.</td></tr>' | ||
end | end | ||
local output = "<tr>\n" .. | local output = "<tr>\n" .. | ||
"<th style=\"text-align:left;\">Output</th>\n" .. | "<th style=\"text-align:left;\">Output</th>\n" .. | ||
| Line 328: | Line 234: | ||
"<th style=\"text-align:left;\">Craft Time</th>\n" .. | "<th style=\"text-align:left;\">Craft Time</th>\n" .. | ||
"</tr>\n" | "</tr>\n" | ||
for _, recipe in ipairs( | |||
local outputItem = recipe[ | for _, recipe in ipairs(rows) do | ||
local ingredients = recipe[ | local outputItem = recipe[colMap["Output"]] or "" | ||
local time = recipe[ | local ingredients = recipe[colMap["Ingredients"]] or "" | ||
local recipeQty = recipe[ | local time = recipe[colMap["Time"]] or "" | ||
local recipeQty = recipe[colMap["Recipe"]] or "" | |||
local qty = "1" | local qty = "1" | ||
if recipeQty ~= "" then | if recipeQty ~= "" then | ||
| Line 340: | Line 248: | ||
end | end | ||
end | end | ||
local formattedOutput = p.iconize({args = {[1] = outputItem}}) | local formattedOutput = p.iconize({args = {[1] = outputItem}}) | ||
if qty ~= "1" then | if qty ~= "1" then | ||
formattedOutput = formattedOutput .. " × " .. qty | formattedOutput = formattedOutput .. " × " .. qty | ||
end | end | ||
local formattedIngredients = p.formatRecipeList(ingredients) | |||
output = output .. "<tr>\n" .. | output = output .. "<tr>\n" .. | ||
"<td style=\"text-align:left;\">" .. formattedOutput .. "</td>\n" .. | "<td style=\"text-align:left;\">" .. formattedOutput .. "</td>\n" .. | ||
| Line 364: | Line 263: | ||
"</tr>\n" | "</tr>\n" | ||
end | end | ||
return output | return output | ||
end | end | ||
| Line 378: | Line 277: | ||
end | end | ||
local | if frame.args.debug then | ||
return p.debugBuildingData(frame) | |||
end | |||
local buildingData, err = loadBuildingData(frame) | |||
if not buildingData then | |||
return "Error: " .. (err or "Building '" .. buildingName .. "' not found") | |||
end | |||
local colMap = buildingData.colMap | |||
local building = buildingData.rows[1] | |||
if not building then | if not building then | ||
return "Error: | return "Error: No data row found for building '" .. buildingName .. "'" | ||
end | end | ||
local params = { | local params = { | ||
Name = building[colMap["Name"]] or | Name = building[colMap["Name"]] or buildingName, | ||
Tier = building[colMap[" | Tier = building[colMap["BuildingType"]] or "Unknown", | ||
Description | Description = building[colMap["Description"]] or "", | ||
JourneyRequirement | JourneyRequirement = building[colMap["JourneyRequirement"]] or "", | ||
Health = building[colMap["Health"]] or "", | Health = building[colMap["Health"]] or "0", | ||
EnergyConsumption = building[colMap["PowerCost"]] or "", | EnergyConsumption = building[colMap["PowerCost"]] or "0", | ||
GeneratesPower = building[colMap["GeneratesPower"]] or "", | GeneratesPower = building[colMap["GeneratesPower"]] or "0", | ||
StorageSlots = building[colMap["StorageSlots"]] or "", | StorageSlots = building[colMap["StorageSlots"]] or "0", | ||
StorageVolume = building[colMap[" | StorageVolume = building[colMap["StorageCapacity"]] or "0", | ||
Components = | Components = building[colMap["RecipeToBuild"]] or "", | ||
PlacedWith = | PlacedWith = building[colMap["PlacedWith"]] or "", | ||
AdditionalNotes = building[colMap["AdditionalNotes"]] or "", | |||
ImageFile = building[colMap["ImageFile"]] or "", | ImageFile = building[colMap["ImageFile"]] or "", | ||
PrimarySource = "Crafting" | |||
PrimarySource = "Crafting" | |||
} | } | ||
if params.Description ~= "" then | |||
params.Description = p.iconize({args = {[1] = params.Description}}) | |||
end | |||
if params.JourneyRequirement ~= "" then | |||
params.JourneyRequirement = p.iconize({args = {[1] = params.JourneyRequirement}}) | |||
end | |||
if params.Components ~= "" then | |||
params.Components = p.formatComponent(params.Components) | |||
end | |||
if params.PlacedWith ~= "" then | |||
params.PlacedWith = p.iconize({args = {[1] = params.PlacedWith}}) | |||
end | |||
if params.AdditionalNotes ~= "" then | |||
params.AdditionalNotes = p.iconize({args = {[1] = params.AdditionalNotes}}) | |||
end | |||
if colMap["YoutubeVideoLink"] and building[colMap["YoutubeVideoLink"]] and building[colMap["YoutubeVideoLink"]] ~= "" then | |||
params.YoutubeEmbed = p.getYoutubeEmbed(frame) | |||
else | |||
params.YoutubeEmbed = "Coming Soon" | |||
end | |||
params.RefiningRecipes = p.getRefiningRecipes(frame) | params.RefiningRecipes = p.getRefiningRecipes(frame) | ||
params. | |||
params.Category3 = | if colMap["Category3"] and building[colMap["Category3"]] and building[colMap["Category3"]] ~= "" then | ||
params.Category3 = building[colMap["Category3"]] | |||
else | |||
params.Category3 = "Buildings" | |||
end | |||
params.RelatedBuildings = p.relatedBuildings(frame) | params.RelatedBuildings = p.relatedBuildings(frame) | ||
| Line 415: | Line 355: | ||
return frame:preprocess(templateCall) | return frame:preprocess(templateCall) | ||
end | end | ||
| Line 441: | Line 367: | ||
local dimensions = width .. "x" .. height | local dimensions = width .. "x" .. height | ||
local | local building, colMap = p.getBuildingData(buildingName, frame) | ||
if not | if not building then | ||
return "Error loading data" | return "Error loading data" | ||
end | end | ||
local | local youtubeUrl = building[colMap["YoutubeVideoLink"]] or "" | ||
if youtubeUrl == "" or youtubeUrl == "-" then | |||
if | |||
return "Coming Soon" | return "Coming Soon" | ||
end | end | ||
local videoId = youtubeUrl:match("v=([%w-_]+)") | local videoId = youtubeUrl:match("v=([%w-_]+)") | ||
if not videoId then | if not videoId then | ||
| Line 467: | Line 384: | ||
local ytMarkup = string.format('<youtube dimensions="%s" alignment="center">%s</youtube>', dimensions, videoId) | local ytMarkup = string.format('<youtube dimensions="%s" alignment="center">%s</youtube>', dimensions, videoId) | ||
return frame:preprocess(ytMarkup) | return frame:preprocess(ytMarkup) | ||
end | end | ||
| Line 543: | Line 392: | ||
function p.relatedBuildings(frame) | function p.relatedBuildings(frame) | ||
local buildingName = frame.args[1] or mw.title.getCurrentTitle().text | local buildingName = frame.args[1] or mw.title.getCurrentTitle().text | ||
local | local building, colMap = p.getBuildingData(buildingName, frame) | ||
if not | if not building then | ||
return "Error loading building data" | return "Error loading building data" | ||
end | end | ||
local targetCategory = building[colMap["Category3"]] or "" | |||
local targetCategory = | if targetCategory == "-" or targetCategory == "" then | ||
return "No related buildings found" | |||
end | end | ||
if | if targetCategory:find("%[%[") then | ||
targetCategory = targetCategory:match("%[%[(.-)%]%]") | |||
end | end | ||
targetCategory = mw.text.trim(targetCategory) | |||
local allData, err = loadBuildingData(frame) | |||
if not allData then | |||
return "Error loading building data" | |||
end | |||
local rows = allData.rows | |||
local relatedBuildings = {} | local relatedBuildings = {} | ||
for _, row in ipairs(rows) do | for _, row in ipairs(rows) do | ||
if row[ | if row[colMap["Name"]] ~= buildingName then | ||
local category = row[ | local category = row[colMap["Category3"]] or "" | ||
if category ~= "-" and category ~= "" then | if category ~= "-" and category ~= "" then | ||
if category:find("%[%[") then | if category:find("%[%[") then | ||
| Line 592: | Line 437: | ||
output = output .. "<th style=\"text-align:left;\">Description</th>\n" | output = output .. "<th style=\"text-align:left;\">Description</th>\n" | ||
output = output .. "</tr>\n" | output = output .. "</tr>\n" | ||
for _, | for _, b in ipairs(relatedBuildings) do | ||
local name = | local name = b[colMap["Name"]] or "" | ||
local tier = | local tier = b[colMap["BuildingType"]] or "" | ||
local description = | local description = b[colMap["Description"]] or "" | ||
if tier == "-" then tier = "" end | if tier == "-" then tier = "" end | ||
if description == "-" then description = "" end | if description == "-" then description = "" end | ||
local nameWithIcon = "[[" .. name .. "]]" | local nameWithIcon = "[[" .. name .. "]]" | ||
local iconFile = | local iconFile = b[colMap["ImageFile"]] or "" | ||
if iconFile ~= "-" and iconFile ~= "" then | if iconFile ~= "-" and iconFile ~= "" then | ||
nameWithIcon = "[[File:" .. iconFile .. "|20px]] [[" .. name .. "]]" | nameWithIcon = "[[File:" .. iconFile .. "|20px]] [[" .. name .. "]]" | ||
| Line 612: | Line 457: | ||
return output | return output | ||
end | end | ||
-------------------------------------------------------------------------------- | |||
-- Function: debugBuildingData | |||
-- Outputs debugging information. | |||
-------------------------------------------------------------------------------- | |||
function p.debugBuildingData(frame) | |||
local buildingName = frame.args[1] or mw.title.getCurrentTitle().text | |||
local buildingData, err = loadBuildingData(frame) | |||
if not buildingData then | |||
return "Error: " .. (err or "Unknown error") | |||
end | |||
local output = "Debug information for '" .. buildingName .. "':\n\n" | |||
output = output .. "Raw data from database:\n" | |||
output = output .. "<pre>" | |||
output = output .. "Column mapping:\n" | |||
for name, idx in pairs(buildingData.colMap) do | |||
output = output .. name .. " => " .. idx .. "\n" | |||
end | |||
output = output .. "\nRows (" .. #buildingData.rows .. " found):\n" | |||
for i, row in ipairs(buildingData.rows) do | |||
output = output .. "Row " .. i .. ":\n" | |||
for j, cell in ipairs(row) do | |||
local colName = "Unknown" | |||
for name, idx in pairs(buildingData.colMap) do | |||
if idx == j then | |||
colName = name | |||
break | |||
end | |||
end | |||
output = output .. " " .. colName .. " (" .. j .. "): '" .. cell .. "'\n" | |||
end | |||
end | |||
output = output .. "</pre>" | |||
return output | |||
end | |||
-------------------------------------------------------------------------------- | |||
-- Function: formatRecipeList | |||
-- Outputs format for multiple recipe lines | |||
-------------------------------------------------------------------------------- | |||
function p.formatRecipeList(frameOrText) | |||
local text = "" | |||
-- Handle both direct string or frame.args | |||
if type(frameOrText) == "string" then | |||
text = frameOrText | |||
elseif type(frameOrText) == "table" then | |||
text = frameOrText.args and frameOrText.args[1] or "" | |||
end | |||
if not text or text == "" then | |||
return "" | |||
end | |||
local components = mw.text.split(text, ";") | |||
local formatted = {} | |||
for _, raw in ipairs(components) do | |||
local line = mw.text.trim(raw) | |||
if line ~= "" then | |||
local withIcon = p.iconize({ args = { line } }) | |||
table.insert(formatted, withIcon) | |||
end | |||
end | |||
return table.concat(formatted, "<br>") | |||
end | |||
return p | return p | ||
Latest revision as of 01:49, 1 April 2025
Documentation for this module may be created at Module:DataTableParserV2/doc
-- Module:DataTableParserV2
-- Handles display and formatting of building data for Templates using External Data.
-- Data is fetched from your SQL tables (data_buildings and data_refining_recipes)
-- via the ExternalData extension.
local p = {}
--------------------------------------------------
-- Helper: Get icon file reference for a resource
--------------------------------------------------
local function getResourceIcon(resourceName)
if not resourceName or resourceName == "" then
return ""
end
local fileName = resourceName:gsub("%s+", "_") .. "_-_Icon.png"
local fileTitle = mw.title.new("File:" .. fileName)
if fileTitle and fileTitle.exists then
return "[[File:" .. fileName .. "|20px]]"
else
return ""
end
end
---------------------------------------------
-- Function: iconize
-- Adds icons to resource links in text
---------------------------------------------
function p.iconize(frame)
local text = frame.args[1] or ""
text = text:gsub("%[%[([^%]]+)%]%]", function(resourceName)
local icon = getResourceIcon(resourceName)
if icon ~= "" then
return icon .. " [[" .. resourceName .. "]]"
else
return "[[" .. resourceName .. "]]"
end
end)
return text
end
------------------------------------------------------
-- Function: formatComponent
-- Formats a recipe component by adding icons to resource links.
------------------------------------------------------
function p.formatComponent(text)
if not text or text == "" then
return ""
end
local components = mw.text.split(text, ";")
local formatted = {}
for i, component in ipairs(components) do
component = mw.text.trim(component)
local itemName, quantity = component:match("%[%[([^%]]+)%]%] x (%d+)")
if itemName and quantity then
local icon = getResourceIcon(itemName)
table.insert(formatted, icon .. " [[" .. itemName .. "]] x " .. quantity)
else
table.insert(formatted, component)
end
end
return table.concat(formatted, "<br>")
end
--------------------------------------------------------------------------------
-- Function: loadBuildingData
-- Uses ExternalData (via the parser function) to fetch building data from data_buildings.
--------------------------------------------------------------------------------
local function splitBySeparator(input, sep)
local result = {}
local pos = 1
while true do
local i, j = input:find(sep, pos, true)
if not i then
table.insert(result, mw.text.trim(input:sub(pos)))
break
end
table.insert(result, mw.text.trim(input:sub(pos, i - 1)))
pos = j + 1
end
return result
end
local function loadBuildingData(frame)
local buildingName = frame.args[1] or frame.args.name or mw.title.getCurrentTitle().text
local edQuery = string.format([[
{{#get_external_data: source=externaldb
|from=data_buildings
|data=ID=id,BuildingType=building_type,Name=name,Description=description,PowerCost=power_cost,GeneratesPower=generates_power,StorageSlots=storage_slots,StorageCapacity=storage_capacity,SchematicRequirement=schematic_requirement,JourneyRequirement=journey_requirement,Health=health,PlacedWith=placed_with,AdditionalNotes=additional_notes,RecipeToBuild=recipe_to_build,ImageFile=image_file,IconFile=icon_file,Category1=category_1,Category2=category_2,Category3=category_3,Gallery1=gallery_1,Gallery2=gallery_2,Gallery3=gallery_3,Gallery4=gallery_4,YoutubeVideoLink=youtube_video_link
|cache=yes
|where=name='%s'
|limit=1
|format=plain
|separator=||
}}
]], buildingName)
local rawOutput = mw.text.trim(frame:preprocess(edQuery))
if rawOutput == "" then
return nil, "No data found for building: " .. buildingName
end
local fields = {
"ID", "BuildingType", "Name", "Description", "PowerCost", "GeneratesPower",
"StorageSlots", "StorageCapacity", "SchematicRequirement", "JourneyRequirement",
"Health", "PlacedWith", "AdditionalNotes", "RecipeToBuild", "ImageFile",
"IconFile", "Category1", "Category2", "Category3", "Gallery1", "Gallery2",
"Gallery3", "Gallery4", "YoutubeVideoLink"
}
local values = splitBySeparator(rawOutput, "||")
if #values < #fields then
return nil, "Incomplete data returned for building: " .. buildingName
end
local colMap = {}
for i, field in ipairs(fields) do
colMap[field] = i
end
local buildingData = { colMap = colMap, rows = { values } }
return buildingData, nil
end
--------------------------------------------------------------------------------
-- Function: loadRefiningData
-- Uses ExternalData (via parser function) to fetch refining recipes from data_refining_recipes.
--------------------------------------------------------------------------------
local function loadRefiningData(frame)
local buildingName = frame.args[1] or frame.args.name or mw.title.getCurrentTitle().text
local edQuery = string.format([[
{{#get_external_data: source=externaldb
|from=data_refining_recipes
|data=Refiner=Refiner,Output=Output,Ingredients=Ingredients,Time=Time,Recipe=Recipe
|cache=yes
|where=Refiner='%s'
}}
]], buildingName)
local result = frame:preprocess(edQuery)
local refiningData = {}
local colMap = {}
local rows = {}
local fields = {"Refiner", "Output", "Ingredients", "Time", "Recipe"}
local firstRow = {}
for i, field in ipairs(fields) do
colMap[field] = i
local value = frame:preprocess("{{{" .. field .. "}}}")
if value and value ~= "" and value ~= "{{{" .. field .. "}}}" then
firstRow[i] = value
else
firstRow[i] = ""
end
end
local hasFirstRow = false
for _, v in pairs(firstRow) do
if v ~= "" then
hasFirstRow = true
break
end
end
if hasFirstRow then
table.insert(rows, firstRow)
end
-- Check for additional rows
for i = 1, 50 do
local rowData = {}
local hasData = false
for j, field in ipairs(fields) do
local value = frame:preprocess("{{{" .. field .. "_" .. i .. "}}}")
if value and value ~= "" and value ~= "{{{" .. field .. "_" .. i .. "}}}" then
rowData[j] = value
hasData = true
else
rowData[j] = ""
end
end
if hasData then
table.insert(rows, rowData)
else
break
end
end
if #rows == 0 then
return nil, "No refining recipes found for building: " .. buildingName
end
refiningData.colMap = colMap
refiningData.rows = rows
return refiningData, nil
end
--------------------------------------------------------------------------------
-- Function: getBuildingData
-- Returns the row (and colMap) for a given building name.
--------------------------------------------------------------------------------
function p.getBuildingData(buildingName, frame)
local buildingData, err = loadBuildingData(frame)
if not buildingData or #buildingData.rows == 0 then
return nil, err or "Building not found"
end
return buildingData.rows[1], buildingData.colMap
end
--------------------------------------------------------------------------------
-- Function: getRefiningRecipes
-- Returns a table of refining recipes matching the building name.
--------------------------------------------------------------------------------
function p.getRefiningRecipes(frame)
local buildingName = frame.args[1] or mw.title.getCurrentTitle().text
local refiningData, err = loadRefiningData(frame)
if not refiningData then
return '<tr><td colspan="3" style="text-align:center;">No refining recipes found for this building.</td></tr>'
end
local rows = refiningData.rows
local colMap = refiningData.colMap
if #rows == 0 then
return '<tr><td colspan="3" style="text-align:center;">No refining recipes found for this building.</td></tr>'
end
local output = "<tr>\n" ..
"<th style=\"text-align:left;\">Output</th>\n" ..
"<th style=\"text-align:left;\">Ingredients</th>\n" ..
"<th style=\"text-align:left;\">Craft Time</th>\n" ..
"</tr>\n"
for _, recipe in ipairs(rows) do
local outputItem = recipe[colMap["Output"]] or ""
local ingredients = recipe[colMap["Ingredients"]] or ""
local time = recipe[colMap["Time"]] or ""
local recipeQty = recipe[colMap["Recipe"]] or ""
local qty = "1"
if recipeQty ~= "" then
local extractQty = recipeQty:match("x%s*(%d+)")
if extractQty then
qty = extractQty
end
end
local formattedOutput = p.iconize({args = {[1] = outputItem}})
if qty ~= "1" then
formattedOutput = formattedOutput .. " × " .. qty
end
local formattedIngredients = p.formatRecipeList(ingredients)
output = output .. "<tr>\n" ..
"<td style=\"text-align:left;\">" .. formattedOutput .. "</td>\n" ..
"<td style=\"text-align:left;\">" .. formattedIngredients .. "</td>\n" ..
"<td style=\"text-align:left;\">" .. time .. "</td>\n" ..
"</tr>\n"
end
return output
end
--------------------------------------------------------------------------------
-- Function: formatBuilding
-- Formats building data as a template call to BuildingRefinerDisplayV2.
--------------------------------------------------------------------------------
function p.formatBuilding(frame)
local buildingName = frame.args[1] or frame.args.name or mw.title.getCurrentTitle().text
if not buildingName then
return "Error: No building name provided"
end
if frame.args.debug then
return p.debugBuildingData(frame)
end
local buildingData, err = loadBuildingData(frame)
if not buildingData then
return "Error: " .. (err or "Building '" .. buildingName .. "' not found")
end
local colMap = buildingData.colMap
local building = buildingData.rows[1]
if not building then
return "Error: No data row found for building '" .. buildingName .. "'"
end
local params = {
Name = building[colMap["Name"]] or buildingName,
Tier = building[colMap["BuildingType"]] or "Unknown",
Description = building[colMap["Description"]] or "",
JourneyRequirement = building[colMap["JourneyRequirement"]] or "",
Health = building[colMap["Health"]] or "0",
EnergyConsumption = building[colMap["PowerCost"]] or "0",
GeneratesPower = building[colMap["GeneratesPower"]] or "0",
StorageSlots = building[colMap["StorageSlots"]] or "0",
StorageVolume = building[colMap["StorageCapacity"]] or "0",
Components = building[colMap["RecipeToBuild"]] or "",
PlacedWith = building[colMap["PlacedWith"]] or "",
AdditionalNotes = building[colMap["AdditionalNotes"]] or "",
ImageFile = building[colMap["ImageFile"]] or "",
PrimarySource = "Crafting"
}
if params.Description ~= "" then
params.Description = p.iconize({args = {[1] = params.Description}})
end
if params.JourneyRequirement ~= "" then
params.JourneyRequirement = p.iconize({args = {[1] = params.JourneyRequirement}})
end
if params.Components ~= "" then
params.Components = p.formatComponent(params.Components)
end
if params.PlacedWith ~= "" then
params.PlacedWith = p.iconize({args = {[1] = params.PlacedWith}})
end
if params.AdditionalNotes ~= "" then
params.AdditionalNotes = p.iconize({args = {[1] = params.AdditionalNotes}})
end
if colMap["YoutubeVideoLink"] and building[colMap["YoutubeVideoLink"]] and building[colMap["YoutubeVideoLink"]] ~= "" then
params.YoutubeEmbed = p.getYoutubeEmbed(frame)
else
params.YoutubeEmbed = "Coming Soon"
end
params.RefiningRecipes = p.getRefiningRecipes(frame)
if colMap["Category3"] and building[colMap["Category3"]] and building[colMap["Category3"]] ~= "" then
params.Category3 = building[colMap["Category3"]]
else
params.Category3 = "Buildings"
end
params.RelatedBuildings = p.relatedBuildings(frame)
local templateCall = "{{BuildingRefinerDisplayV2"
for key, value in pairs(params) do
if value and value ~= "" then
templateCall = templateCall .. "\n|" .. key .. "=" .. value
end
end
templateCall = templateCall .. "\n}}"
return frame:preprocess(templateCall)
end
--------------------------------------------------------------------------------
-- Function: getYoutubeEmbed
-- Returns a processed YouTube embed tag.
--------------------------------------------------------------------------------
function p.getYoutubeEmbed(frame)
local buildingName = frame.args[1] or mw.title.getCurrentTitle().text
local width = frame.args.width or 400
local height = frame.args.height or 300
local dimensions = width .. "x" .. height
local building, colMap = p.getBuildingData(buildingName, frame)
if not building then
return "Error loading data"
end
local youtubeUrl = building[colMap["YoutubeVideoLink"]] or ""
if youtubeUrl == "" or youtubeUrl == "-" then
return "Coming Soon"
end
local videoId = youtubeUrl:match("v=([%w-_]+)")
if not videoId then
return "Coming Soon"
end
local ytMarkup = string.format('<youtube dimensions="%s" alignment="center">%s</youtube>', dimensions, videoId)
return frame:preprocess(ytMarkup)
end
--------------------------------------------------------------------------------
-- Function: relatedBuildings
-- Finds and formats related buildings based on Category3.
--------------------------------------------------------------------------------
function p.relatedBuildings(frame)
local buildingName = frame.args[1] or mw.title.getCurrentTitle().text
local building, colMap = p.getBuildingData(buildingName, frame)
if not building then
return "Error loading building data"
end
local targetCategory = building[colMap["Category3"]] or ""
if targetCategory == "-" or targetCategory == "" then
return "No related buildings found"
end
if targetCategory:find("%[%[") then
targetCategory = targetCategory:match("%[%[(.-)%]%]")
end
targetCategory = mw.text.trim(targetCategory)
local allData, err = loadBuildingData(frame)
if not allData then
return "Error loading building data"
end
local rows = allData.rows
local relatedBuildings = {}
for _, row in ipairs(rows) do
if row[colMap["Name"]] ~= buildingName then
local category = row[colMap["Category3"]] or ""
if category ~= "-" and category ~= "" then
if category:find("%[%[") then
category = category:match("%[%[(.-)%]%]")
end
category = mw.text.trim(category)
if category == targetCategory then
table.insert(relatedBuildings, row)
end
end
end
end
if #relatedBuildings == 0 then
return "No other " .. targetCategory .. " found"
end
local output = '<table class="infobox-dune" style="width:100%">\n'
output = output .. "<tr>\n"
output = output .. "<th style=\"text-align:left;\">Name</th>\n"
output = output .. "<th style=\"text-align:left;\">Tier</th>\n"
output = output .. "<th style=\"text-align:left;\">Description</th>\n"
output = output .. "</tr>\n"
for _, b in ipairs(relatedBuildings) do
local name = b[colMap["Name"]] or ""
local tier = b[colMap["BuildingType"]] or ""
local description = b[colMap["Description"]] or ""
if tier == "-" then tier = "" end
if description == "-" then description = "" end
local nameWithIcon = "[[" .. name .. "]]"
local iconFile = b[colMap["ImageFile"]] or ""
if iconFile ~= "-" and iconFile ~= "" then
nameWithIcon = "[[File:" .. iconFile .. "|20px]] [[" .. name .. "]]"
end
output = output .. "<tr>\n"
output = output .. "<td>" .. nameWithIcon .. "</td>\n"
output = output .. "<td>" .. tier .. "</td>\n"
output = output .. "<td>" .. description .. "</td>\n"
output = output .. "</tr>\n"
end
output = output .. "</table>"
return output
end
--------------------------------------------------------------------------------
-- Function: debugBuildingData
-- Outputs debugging information.
--------------------------------------------------------------------------------
function p.debugBuildingData(frame)
local buildingName = frame.args[1] or mw.title.getCurrentTitle().text
local buildingData, err = loadBuildingData(frame)
if not buildingData then
return "Error: " .. (err or "Unknown error")
end
local output = "Debug information for '" .. buildingName .. "':\n\n"
output = output .. "Raw data from database:\n"
output = output .. "<pre>"
output = output .. "Column mapping:\n"
for name, idx in pairs(buildingData.colMap) do
output = output .. name .. " => " .. idx .. "\n"
end
output = output .. "\nRows (" .. #buildingData.rows .. " found):\n"
for i, row in ipairs(buildingData.rows) do
output = output .. "Row " .. i .. ":\n"
for j, cell in ipairs(row) do
local colName = "Unknown"
for name, idx in pairs(buildingData.colMap) do
if idx == j then
colName = name
break
end
end
output = output .. " " .. colName .. " (" .. j .. "): '" .. cell .. "'\n"
end
end
output = output .. "</pre>"
return output
end
--------------------------------------------------------------------------------
-- Function: formatRecipeList
-- Outputs format for multiple recipe lines
--------------------------------------------------------------------------------
function p.formatRecipeList(frameOrText)
local text = ""
-- Handle both direct string or frame.args
if type(frameOrText) == "string" then
text = frameOrText
elseif type(frameOrText) == "table" then
text = frameOrText.args and frameOrText.args[1] or ""
end
if not text or text == "" then
return ""
end
local components = mw.text.split(text, ";")
local formatted = {}
for _, raw in ipairs(components) do
local line = mw.text.trim(raw)
if line ~= "" then
local withIcon = p.iconize({ args = { line } })
table.insert(formatted, withIcon)
end
end
return table.concat(formatted, "<br>")
end
return p
