|
|
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 |