Module:ServicesData: Difference between revisions

From Against the Storm Official Wiki
m (76561197969631737 moved page Module:ServicesRecipesData to Module:ServicesData without leaving a redirect: Moving so I can replace this outdated CSV data model with some service data utilities)
(Overhauling into a small utility method for Services information, since there isn't a special data model for services anymore.)
Tag: Replaced
Line 1: Line 1:
---
---@module ServicesData
--- Module for compiling services recipes information from wiki data sources.
local ServicesData = {}
--- Restructures the flat data tables that are produced from CsvUtils to make
--- them more conducive to Lua methods that need to display the information.
---
--- The standard way of using this module is a call like the following:
---
--- goodID = ServicesRecipesData.getServiceGoodID(serviceName)
---
--- This will return the ID of the good that is used to satisfy the service
--- need. It is preferable to call getter methods with the ID of the service
--- rather than retrieving the entire record for the recipe, so that your code
--- stays protected from variations in this module.
---
--- * serviceID = ServicesRecipesData.getServiceRecipeID(serviceName)
--- * gradeStars = ServicesRecipesData.getServiceRecipeGrade(serviceName)
--- * goodID = ServicesRecipesData.getServiceGoodID(serviceName)
---
--- The most specific way of retrieving data that does not return a table is
--- the preferred way of retrieving the data, because it is the most protected
--- from changes in this module.
---
--- As a last resort, you may use methods that begin with "getAll..." that
--- return tables of data (that is, other than the ones that get lists of
--- recipeIDs), but as mentioned they are less preferable, because the internal
--- workings of this module may change, and these tables may suddenly change
--- their structure, ruining your code. When in doubt, take the extra time to
--- write code that uses one of the specific getter methods (that begin with
--- "getRecipe...").
---
--- The data table for these services recipes has the following structure:
---
--- recipesTable = {
--- ["recipe1_ID"] = {
--- ["id"] = "recipe1_ID",
--- ["gradeID"] = 1,
--- ["serviceNeed"] = "Education"
--- ["serviceGoods"] = {
--- [1] = {
--- ["goodID"] = "good1_ID",
--- ["stackSize"] = 1
--- },
--- [2] = { ... } or missing if fewer
--- }
--- },
--- ["recipe2_ID"] = {
--- ...
--- },
--- ["recipe3_ID"] = {
--- ...
--- },
--- ...
--- }
---
--- @module ServicesRecipesData
local ServicesRecipesData = {}
 
 
 
local CsvUtils = require("Module:CsvUtils")
 
 
 
--region Private member variables
 
--- Main data tables, like this:  table[recipeID] = table containing recipe
--- data
local recipesTable
 
--- Lookup map. Built once and reused on subsequent calls within this session,
--- like this:  table[serviceName] = { recipeID, recipeID, ... }
local mapNamesToIDs
--- Lookup map. Built once and reused on subsequent calls within this session,
--- like this:  table[goodID] = { serviceNeed1, serviceNeed2, ... }
local mapGoodIDsToRecipeIDs
 
--endregion
 
 
 
--region Private constants
 
local DATA_TEMPLATE_NAME = "Template:Institutions_Recipes_csv"
 
local INDEX_ID = "id"
local INDEX_GRADE = "gradeID"
local INDEX_SERVICE_NAME = "serviceNeed"
local INDEX_GOODS = "serviceGoods"
 
local INDEX_GOODS_GOOD_ID = "goodID"
local INDEX_GOODS_STACK_SIZE = "stackSize"
 
local PATTERN_SPLIT_STACK_AND_ID = "(%d+)%s([%[%]%s%a]+)"
local PATTERN_CAPTURE_END_NUMBER = "Grade(%d)"
 
local SERVICE_ICONS = {
["Brawling"] = "Icon_Need_Brawling.png",
["Education"] = "Icon_Need_Education.png",
["Leisure"] = "Icon_Need_Leisure.png",
["Luxury"] = "Icon_Need_Luxury.png",
["Religion"] = "Icon_Need_Religion.png",
["Treatment"] = "Icon_Need_Treatment.png"
}
 
--endregion
 
 
 
--region Private methods
 
---
--- Transforms the originalRecipesTable returned from CSV processing to be more
--- conducive to member functions looking up data. Converts text strings
--- into subtables. Converts header row into field keys on every record and
--- stores records by id rather than arbitrary integer.
---
--- @param originalRecipesTable table of CSV-based data, with header row, data rows
--- @param recipesHeaderLookup table lookup table of headers to get indexes
--- @return table better structured with IDs as keys
local function restructureRecipesTable(originalRecipesTable, recipesHeaderLookup)
 
-- A few constants we'll need only within this function.
local DATA_ROWS = 2
local INDEX_ORIGINAL_ID = 1
local INDEX_ORIGINAL_GRADE = 2
local INDEX_ORIGINAL_SERVED_NEED = 3
local MAX_SERVICES_GOODS = 6
local LOOKUP_SERVICE_GOODS_BASE_STRING = "Good"
 
mapNamesToIDs = {}
mapGoodIDsToRecipeIDs = {}
 
local newRecipeTable = {}
for _, originalRecipe in ipairs(originalRecipesTable[DATA_ROWS]) do
 
local newRecipe = {}
 
newRecipe[INDEX_ID] = originalRecipe[INDEX_ORIGINAL_ID]
newRecipe[INDEX_GRADE] = tonumber(originalRecipe[INDEX_ORIGINAL_GRADE]:match(PATTERN_CAPTURE_END_NUMBER))
newRecipe[INDEX_SERVICE_NAME] = originalRecipe[INDEX_ORIGINAL_SERVED_NEED]
 
local serviceGoods = {}
for i = 1, MAX_SERVICES_GOODS do
local goodIndex = recipesHeaderLookup[LOOKUP_SERVICE_GOODS_BASE_STRING .. i]
local stackSize, goodID = originalRecipe[goodIndex]:match(PATTERN_SPLIT_STACK_AND_ID)
 
if not goodID then
break
end
 
local newGood = {
[INDEX_GOODS_GOOD_ID] = goodID,
[INDEX_GOODS_STACK_SIZE] = tonumber(stackSize)
}
table.insert(serviceGoods, newGood)
 
-- Populate the lookup map for goods.
if not mapGoodIDsToRecipeIDs[goodID] then
mapGoodIDsToRecipeIDs[goodID] = {}
end
table.insert(mapGoodIDsToRecipeIDs[goodID], newRecipe[INDEX_ID])
 
end
 
newRecipe[INDEX_GOODS] = serviceGoods
 
-- Populate the lookup map for services.
if not mapNamesToIDs[ newRecipe[INDEX_SERVICE_NAME] ] then
mapNamesToIDs[ newRecipe[INDEX_SERVICE_NAME] ] = {}
end
table.insert( mapNamesToIDs[ newRecipe[INDEX_SERVICE_NAME] ], newRecipe[INDEX_ID])
 
newRecipeTable[ newRecipe[INDEX_ID] ] = newRecipe
end
 
return newRecipeTable
end
 
 
 
---
--- Data loader function that uses the utility module and restructures the data
--- to be easier to access for invoking methods and later method calls. This
--- method is automatically called by all public member functions if the main
--- data table has not yet been populated in the current session.
local function loadData()
 
-- Utility module retrieves the data as basic, flat lua tables.
local originalRecipesTable, recipesHeaderLookup = CsvUtils.extractTables(DATA_TEMPLATE_NAME)
 
-- Now restructure to be more conducive.
recipesTable = restructureRecipesTable(originalRecipesTable, recipesHeaderLookup)
end
 
--endregion




Line 199: Line 6:
--region Public methods
--region Public methods


---@public
---Gets the icon filename associated with the specified service.
---
---
--- Returns the whole table of data for the specified recipe. Instead of this,
---@param name string service
--- you should probably be calling the individual getter methods.
---@return string icon filename
---
function ServicesData.getIcon(name)
--- Throws an error if called with a nil or empty string. Returns nil if the
    return "Icon_Need_" .. name .. ".png"
--- specified recipe cannot be found.
---
---@param recipeID string the ID of the recipe
---@return table containing the data for the specified recipe, or nil if not found
function ServicesRecipesData.getAllDataForRecipeByID(recipeID)
 
-- At runtime, this should never be nil or empty.
if not recipeID or recipeID == "" then
error("Parameter is nil or empty for recipe ID.")
end
 
if not recipesTable then
loadData()
end
 
return recipesTable[recipeID]
end
 
 
 
---
--- Retrieve all IDs for recipes for the specified service.
---
--- @param serviceName string the name of the service
--- @return table the recipes that result in the product
function ServicesRecipesData.getAllRecipeIDsForServiceName(serviceName)
 
-- At runtime, this should never be nil or empty.
if not serviceName or serviceName == "" then
error("Parameter is nil or empty for product ID.")
end
 
if not recipesTable then
loadData()
end
 
return mapNamesToIDs[serviceName]
end
 
 
 
---
--- Retrieves all IDs for recipes for which the specified good is necessary.
---
--- @param goodID string the ID of the ingredient (good)
--- @return table the recipes that could use that ingredient
function ServicesRecipesData.getAllRecipeIDsWithServiceGoodID(goodID)
 
-- At runtime, this should never be nil or empty.
if not goodID or goodID == "" then
error("Parameter is nil or empty for ingredient ID.")
end
 
if not recipesTable then
loadData()
end
 
return mapGoodIDsToRecipeIDs[goodID]
end
 
 
 
---
--- Retrieves the grade code for the recipe specified by its ID.
---
--- Returns nil if the recipe ID was not found.
---
---@param recipeID string the ID of the recipe
---@return string the code of the grade.
function ServicesRecipesData.getRecipeGradeByID(recipeID)
 
local recipe = ServicesRecipesData.getAllDataForRecipeByID(recipeID)
 
if not recipe then
return nil
end
 
return recipe[INDEX_GRADE]
end
 
 
 
---
--- Retrieves the service name for the recipe specified by its ID.
---
--- Returns nil if the recipe ID was not found.
---
---@param recipeID string the ID of the recipe
---@return string the ID of the good produced by the recipe
function ServicesRecipesData.getRecipeServiceNameByID(recipeID)
 
local recipe = ServicesRecipesData.getAllDataForRecipeByID(recipeID)
 
if not recipe then
return nil
end
 
return recipe[INDEX_SERVICE_NAME]
end
 
 
 
---
function ServicesRecipesData.getRecipeServiceIconByID(recipeID)
 
local recipe = ServicesRecipesData.getAllDataForRecipeByID(recipeID)
 
if not recipe then
return nil
end
 
return SERVICE_ICONS[ recipe[INDEX_SERVICE_NAME] ]
end
 
 
 
---
--- Retrieves the subtable of data for ingredients for the recipe specified by
--- its ID.
---
--- Returns nil if the recipe ID was not found.
---
---@param recipeID string the ID of the recipe
---@return table of good IDs and stack sizes
function ServicesRecipesData.getAllRecipeServiceGoodsByID(recipeID)
 
local recipe = ServicesRecipesData.getAllDataForRecipeByID(recipeID)
 
if not recipe then
return nil
end
 
return recipe[INDEX_GOODS]
end
 
 
 
---
--- Retrieves the number of ingredients for the recipe specified by its ID.
---
--- Returns nil if the recipe ID was not found.
---
---@param recipeID string the ID of the recipe
---@return number of ingredient groups
function ServicesRecipesData.getRecipeNumberOfServiceGoodsByID(recipeID)
 
local recipe = ServicesRecipesData.getAllDataForRecipeByID(recipeID)
 
if not recipe then
return nil
end
 
return #recipe[INDEX_GOODS]
end
end


 
---@public
 
---Gets the ID for the specified service
---
--- Retrieves the specified service goods for the recipe specified by its ID.
---
--- Returns nil if the recipe ID was not found.
---
---
---@param recipeID string the ID of the recipe
---@param name string service
---@param goodIndex number which ingredient to retrieve
---@return string ID
---@return string the ID of the good that is the specified option, or nil if none
function ServicesData.getID(name)
---@return number the stack size of that good, or nil if none
    --Yep, IDs are just names right now.
function ServicesRecipesData.getRecipeOptionByID(recipeID, goodIndex)
    return name
 
local recipe = ServicesRecipesData.getAllDataForRecipeByID(recipeID)
 
if not recipe then
return nil
end
 
local goods = ServicesRecipesData.getAllRecipeServiceGoodsByID(recipeID)
if not goods then
return nil
end
 
local good = goods[goodIndex]
 
return good[INDEX_GOODS_GOOD_ID], good[INDEX_GOODS_STACK_SIZE]
end
 
 
function ServicesRecipesData.isValidName(serviceName)
 
local list = ServicesRecipesData.getAllRecipeIDsForServiceName(serviceName)
if not list or #list < 1 then
return false
else
return true
end
end
end


--endregion
--endregion


return ServicesRecipesData
return ServicesData

Revision as of 22:55, 10 November 2024

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

---@module ServicesData
local ServicesData = {}



--region Public methods

---@public
---Gets the icon filename associated with the specified service.
---
---@param name string service
---@return string icon filename
function ServicesData.getIcon(name)
    return "Icon_Need_" .. name .. ".png"
end

---@public
---Gets the ID for the specified service
---
---@param name string service
---@return string ID
function ServicesData.getID(name)
    --Yep, IDs are just names right now.
    return name
end

--endregion

return ServicesData