Module:CSVReader: Difference between revisions
From Terra Invicta Official Wiki
No edit summary |
No edit summary |
||
| (11 intermediate revisions by 2 users not shown) | |||
| Line 24: | Line 24: | ||
local MINIMUM_LENGTH_THAT_SUGGESTS_VALID_CONTENT = 4 | local MINIMUM_LENGTH_THAT_SUGGESTS_VALID_CONTENT = 4 | ||
local tinsert = table.insert | |||
local slen = string.len | |||
local sfind = string.find | |||
local ssub = string.sub | |||
--endregion | --endregion | ||
| Line 36: | Line 41: | ||
--region Private methods | --region Private methods | ||
-- Splits the CSV file into lines. | |||
local fileSplit = function(text) | |||
local ret = {} | |||
local s, l = 1, slen( text ) | |||
while s do | |||
local e, n = sfind( text, '\n', s, true ) | |||
if not e then | |||
ret[#ret+1] = ssub ( text, s ) | |||
s = nil | |||
elseif n < e then | |||
-- Empty separator! | |||
ret[#ret+1] = ssub ( text, s, e ) | |||
if e < l then | |||
s = e + 1 | |||
else | |||
s = nil | |||
end | |||
else | |||
ret[#ret+1] = e > s and ssub( text, s, e - 1 ) or '' | |||
s = n + 1 | |||
end | |||
end | |||
return ret | |||
end | |||
-- Splits the lines of the CSV file on the commas. | -- Splits the lines of the CSV file on the commas. | ||
-- Special handling made for one set of double quotations. | -- Special handling made for one set of double quotations. | ||
-- Someone better at coding than I am please generalize this to handle multiple quotations and nested quotations. | -- Someone better at coding than I am please generalize this to handle multiple quotations and nested quotations. | ||
function | local commaSplit = function(text) | ||
local ret = {} | local ret = {} | ||
local s, l = 1, | local s, l = 1, slen( text ) | ||
while s do | while s do | ||
local e, n = | local e, n = sfind( text, ',', s, true) | ||
--Deal with one possible double quote. If there are more or even nested quotes... then I don't know. | --Deal with one possible double quote. If there are more or even nested quotes... then I don't know. | ||
local qe, qn = | local qe, qn = sfind( text, '"', s, true) | ||
if qe and e and qe < e then | if qe and e and qe < e then | ||
local qqe, qqn = | local qqe, qqn = sfind( text, '"', qe+1, true) | ||
e,n = | e,n = sfind( text, ',', qqe+1, true) | ||
end | end | ||
if not e then | if not e then | ||
ret[#ret+1] = | ret[#ret+1] = ssub ( text, s ) | ||
s = nil | s = nil | ||
else | else | ||
ret[#ret+1] = e > s and | ret[#ret+1] = e > s and ssub( text, s, e - 1 ) or '' | ||
s = e + 1 | s = e + 1 | ||
end | end | ||
| Line 73: | Line 103: | ||
function CSVReader.convertCSVToLuaTable(wikiPageName) | function CSVReader.convertCSVToLuaTable(wikiPageName) | ||
-- If Table, convert to String | -- If Args Table from #invoke, then convert to String | ||
if type(wikiPageName) == 'table' then | if type(wikiPageName) == 'table' then | ||
wikiPageName = wikiPageName[1] | wikiPageName = wikiPageName.args[1] | ||
end | end | ||
| Line 102: | Line 132: | ||
local decodedTable = {} -- Stores the table to return | local decodedTable = {} -- Stores the table to return | ||
local CSVlines = | local CSVlines = fileSplit(unparsedContent) -- split the CSV into separate lines. | ||
local headerNames = | local headerNames = commaSplit(CSVlines[1]) -- get the names of each column from the first line. | ||
-- Most TI template files have a dataName column. Find this dataName column, if it exists. | -- Most TI template files have a dataName column. Find this dataName column, if it exists. | ||
| Line 119: | Line 149: | ||
if j > 1 then -- Skip the header line. | if j > 1 then -- Skip the header line. | ||
-- Create a row with name set to the value in the dataName column. | -- Create a row with name set to the value in the dataName column. | ||
local | local commaSplitLine = commaSplit(CSVline) | ||
local rowDataName = commaSplitLine[DataNameColumnIndex] | |||
decodedTable[rowDataName] = {} | decodedTable[rowDataName] = {} | ||
for i, cellData in ipairs( | for i, cellData in ipairs(commaSplitLine) do | ||
-- Fill in the cell data for the row corresponding to the dataName and the column corresponding to headerNames[i]. | -- Fill in the cell data for the row corresponding to the dataName and the column corresponding to headerNames[i]. | ||
local columnName = headerNames[i] | local columnName = headerNames[i] | ||
| Line 133: | Line 164: | ||
-- Create a row with index j - 1. | -- Create a row with index j - 1. | ||
decodedTable[j-1] = {} | decodedTable[j-1] = {} | ||
for i, cellData in ipairs( | for i, cellData in ipairs(commaSplit(CSVline)) do | ||
-- Fill in the cell data for the j-1-th row and the column corresponding to headerNames[i]. | -- Fill in the cell data for the j-1-th row and the column corresponding to headerNames[i]. | ||
decodedTable[j-1][headerNames[i]] = cellData | decodedTable[j-1][headerNames[i]] = cellData | ||
| Line 167: | Line 198: | ||
returnString = returnString .. '|}' | returnString = returnString .. '|}' | ||
return returnString | return returnString | ||
end | |||
---Loads CSV data from TIBilateralTemplate and turns it into a Lua table of claims. | |||
---@return table | |||
function CSVReader.convertBilateralToClaimTable() | |||
-- Verify that the name is valid and that the page exists. | |||
local titleObject = mw.title.new('TIBilateralTemplate_CSV') | |||
if not titleObject then | |||
error(ERROR_PREFIX_TITLE_INVALID_NAME .. 'TIBilateralTemplate_CSV') | |||
end | |||
if not titleObject.exists then | |||
error(ERROR_PREFIX_TITLE_PAGE_DOES_NOT_EXIST .. 'TIBilateralTemplate_CSV') | |||
end | |||
-- Verify that the page content can be loaded and that there is something there to use. | |||
local unparsedContent = titleObject:getContent() | |||
if not unparsedContent then | |||
error(ERROR_PREFIX_CONTENT_NOT_FOUND .. 'TIBilateralTemplate_CSV') | |||
end | |||
if #unparsedContent < MINIMUM_LENGTH_THAT_SUGGESTS_VALID_CONTENT then | |||
error(ERROR_PREFIX_CONTENT_NOT_LONG_ENOUGH .. 'TIBilateralTemplate_CSV') | |||
end | |||
local nationToRegionTable = {} | |||
local regionToNationTable = {} | |||
local claimTypeTable = {} | |||
local capitalToNationTable = {} | |||
local peacefulNationToRegionTable = {} | |||
local CSVlines = fileSplit(unparsedContent) -- split the CSV into separate lines. | |||
local headerNames = commaSplit(CSVlines[1]) -- get the names of each column from the first line. | |||
local headerNamesIndices = {} | |||
for i, headerName in ipairs(headerNames) do -- Find the indices of the target headers. | |||
headerNamesIndices[headerName] = i | |||
end | |||
for j, CSVline in ipairs(CSVlines) do -- Consider each CSV line one by one: | |||
local rowData = commaSplit(CSVline) | |||
if rowData[headerNamesIndices['relationType']] == 'Claim' then -- Skip any non-claim lines. | |||
local nationName = rowData[headerNamesIndices['nation1']] | |||
local regionName = rowData[headerNamesIndices['region1']] | |||
if not nationToRegionTable[nationName] then | |||
nationToRegionTable[nationName] = {} | |||
end | |||
tinsert(nationToRegionTable[nationName],regionName) | |||
if not regionToNationTable[regionName] then | |||
regionToNationTable[regionName] = {} | |||
end | |||
tinsert(regionToNationTable[regionName],nationName) | |||
if not claimTypeTable[nationName] then | |||
claimTypeTable[nationName] = {} | |||
end | |||
if not claimTypeTable[nationName][regionName] then | |||
claimTypeTable[nationName][regionName] = {} | |||
end | |||
claimTypeTable[nationName][regionName]['initialOwner'] = rowData[headerNamesIndices['initialOwner']] | |||
claimTypeTable[nationName][regionName]['capitalClaim'] = rowData[headerNamesIndices['capitalClaim']] | |||
claimTypeTable[nationName][regionName]['projectUnlockName'] = rowData[headerNamesIndices['projectUnlockName']] | |||
claimTypeTable[nationName][regionName]['initialColony'] = rowData[headerNamesIndices['initialColony']] | |||
claimTypeTable[nationName][regionName]['hostileClaim'] = rowData[headerNamesIndices['hostileClaim']] | |||
if not peacefulNationToRegionTable[nationName] then | |||
peacefulNationToRegionTable[nationName] = {} | |||
end | |||
if not rowData[headerNamesIndices['hostileClaim']] or rowData[headerNamesIndices['hostileClaim']] ~= 'true' then | |||
tinsert(peacefulNationToRegionTable[nationName],regionName) | |||
end | |||
if not capitalToNationTable[regionName] then | |||
capitalToNationTable[regionName] = {} | |||
end | |||
if rowData[headerNamesIndices['capitalClaim']] and rowData[headerNamesIndices['capitalClaim']] == 'true' then | |||
tinsert(capitalToNationTable[regionName],nationName) | |||
end | |||
end | |||
end | |||
return {nationToRegionTable,regionToNationTable,claimTypeTable,peacefulNationToRegionTable,capitalToNationTable} | |||
end | end | ||
Latest revision as of 07:47, 28 November 2025
Documentation for this module may be created at Module:CSVReader/doc
---
---Convert the specified CSV file on the wiki into a Lua table.
---
---The page must contain properly formatted CSV data and only the CSV data, without any explanation, wiki markup, or other content.
---
---@module CSVReader
local CSVReader = {}
--region Dependencies
-- none
--endregion
--region Private constants
local ERROR_MESSAGE_PARAMETER_MISSING = "CSVReader requires as its parameter that you provide the name of a page on the wiki that contains valid CSV data. Call the main function with a parameter that is the name of the desired page, like this:\n `CSVReader.main('Template:Data_CSV`)`"
local ERROR_PREFIX_TITLE_INVALID_NAME = "CSVReader failed to create a Mediawiki title object with the page name you provided. Double-check that the page name you provided is a properly formatted string and valid page title: "
local ERROR_PREFIX_TITLE_PAGE_DOES_NOT_EXIST = "CSVReader checked the page name you provided, and the page does not exist. Double-check that the page name you provided is correct: "
local ERROR_PREFIX_CONTENT_NOT_FOUND = "CSVReader did not find content on the page whose name you provided. Double check that the page exists and that it contains content: "
local ERROR_PREFIX_CONTENT_NOT_LONG_ENOUGH = "CSVReader found content on the page whose name you provided, but it is too short to be useful. Double-check that you provided the correct name, and that there is content on the page: "
local MINIMUM_LENGTH_THAT_SUGGESTS_VALID_CONTENT = 4
local tinsert = table.insert
local slen = string.len
local sfind = string.find
local ssub = string.sub
--endregion
--region Private member variables
--none
--endregion
--region Private methods
-- Splits the CSV file into lines.
local fileSplit = function(text)
local ret = {}
local s, l = 1, slen( text )
while s do
local e, n = sfind( text, '\n', s, true )
if not e then
ret[#ret+1] = ssub ( text, s )
s = nil
elseif n < e then
-- Empty separator!
ret[#ret+1] = ssub ( text, s, e )
if e < l then
s = e + 1
else
s = nil
end
else
ret[#ret+1] = e > s and ssub( text, s, e - 1 ) or ''
s = n + 1
end
end
return ret
end
-- Splits the lines of the CSV file on the commas.
-- Special handling made for one set of double quotations.
-- Someone better at coding than I am please generalize this to handle multiple quotations and nested quotations.
local commaSplit = function(text)
local ret = {}
local s, l = 1, slen( text )
while s do
local e, n = sfind( text, ',', s, true)
--Deal with one possible double quote. If there are more or even nested quotes... then I don't know.
local qe, qn = sfind( text, '"', s, true)
if qe and e and qe < e then
local qqe, qqn = sfind( text, '"', qe+1, true)
e,n = sfind( text, ',', qqe+1, true)
end
if not e then
ret[#ret+1] = ssub ( text, s )
s = nil
else
ret[#ret+1] = e > s and ssub( text, s, e - 1 ) or ''
s = e + 1
end
end
return ret
end
--endregion
--region Public methods
---Loads CSV data from the specified page on the wiki into a Lua table.
---@param wikiPageName string the title of the wiki page, including
---@return table
function CSVReader.convertCSVToLuaTable(wikiPageName)
-- If Args Table from #invoke, then convert to String
if type(wikiPageName) == 'table' then
wikiPageName = wikiPageName.args[1]
end
-- Verify the parameter.
if not wikiPageName or "" == wikiPageName then
error(ERROR_MESSAGE_PARAMETER_MISSING)
end
-- Verify that the name is valid and that the page exists.
local titleObject = mw.title.new(wikiPageName)
if not titleObject then
error(ERROR_PREFIX_TITLE_INVALID_NAME .. wikiPageName)
end
if not titleObject.exists then
error(ERROR_PREFIX_TITLE_PAGE_DOES_NOT_EXIST .. wikiPageName)
end
-- Verify that the page content can be loaded and that there is something there to use.
local unparsedContent = titleObject:getContent()
if not unparsedContent then
error(ERROR_PREFIX_CONTENT_NOT_FOUND .. wikiPageName)
end
if #unparsedContent < MINIMUM_LENGTH_THAT_SUGGESTS_VALID_CONTENT then
error(ERROR_PREFIX_CONTENT_NOT_LONG_ENOUGH .. wikiPageName)
end
local decodedTable = {} -- Stores the table to return
local CSVlines = fileSplit(unparsedContent) -- split the CSV into separate lines.
local headerNames = commaSplit(CSVlines[1]) -- get the names of each column from the first line.
-- Most TI template files have a dataName column. Find this dataName column, if it exists.
local hasDataNameColumn = false
local DataNameColumnIndex = 0
for i, columnName in ipairs(headerNames) do
if columnName == 'dataName' then
hasDataNameColumn = true
DataNameColumnIndex = i
end
end
if hasDataNameColumn then
for j, CSVline in ipairs(CSVlines) do -- Consider each CSV line one by one:
if j > 1 then -- Skip the header line.
-- Create a row with name set to the value in the dataName column.
local commaSplitLine = commaSplit(CSVline)
local rowDataName = commaSplitLine[DataNameColumnIndex]
decodedTable[rowDataName] = {}
for i, cellData in ipairs(commaSplitLine) do
-- Fill in the cell data for the row corresponding to the dataName and the column corresponding to headerNames[i].
local columnName = headerNames[i]
decodedTable[rowDataName][columnName] = cellData
end
end
end
else
for j, CSVline in ipairs(CSVlines) do -- Consider each CSV line one by one:
if j > 1 then -- Skip the header line.
-- Create a row with index j - 1.
decodedTable[j-1] = {}
for i, cellData in ipairs(commaSplit(CSVline)) do
-- Fill in the cell data for the j-1-th row and the column corresponding to headerNames[i].
decodedTable[j-1][headerNames[i]] = cellData
end
end
end
end
return decodedTable
end
---Loads CSV data from the specified page on the wiki into a wiki table.
---@param wikiPageName string the title of the wiki page, including
---@return table
function CSVReader.convertCSVToWikiTable(wikiPageName)
local decodedTable = CSVReader.convertCSVToLuaTable(wikiPageName)
local returnString = '{| class="wikitable sortable mw-collapsible mw-collapsed" style="text-align: center;\n|-\n'
local headersNotDone = true
for rowID, row in pairs(decodedTable) do
if headersNotDone then
for column, cellData in pairs(row) do
returnString = returnString .. '! ' .. column .. '\n'
end
returnString = returnString .. '|-\n'
headersNotDone = false
end
for column, cellData in pairs(row) do
returnString = returnString .. '| ' .. cellData .. '\n'
end
returnString = returnString .. '|-\n'
end
returnString = returnString .. '|}'
return returnString
end
---Loads CSV data from TIBilateralTemplate and turns it into a Lua table of claims.
---@return table
function CSVReader.convertBilateralToClaimTable()
-- Verify that the name is valid and that the page exists.
local titleObject = mw.title.new('TIBilateralTemplate_CSV')
if not titleObject then
error(ERROR_PREFIX_TITLE_INVALID_NAME .. 'TIBilateralTemplate_CSV')
end
if not titleObject.exists then
error(ERROR_PREFIX_TITLE_PAGE_DOES_NOT_EXIST .. 'TIBilateralTemplate_CSV')
end
-- Verify that the page content can be loaded and that there is something there to use.
local unparsedContent = titleObject:getContent()
if not unparsedContent then
error(ERROR_PREFIX_CONTENT_NOT_FOUND .. 'TIBilateralTemplate_CSV')
end
if #unparsedContent < MINIMUM_LENGTH_THAT_SUGGESTS_VALID_CONTENT then
error(ERROR_PREFIX_CONTENT_NOT_LONG_ENOUGH .. 'TIBilateralTemplate_CSV')
end
local nationToRegionTable = {}
local regionToNationTable = {}
local claimTypeTable = {}
local capitalToNationTable = {}
local peacefulNationToRegionTable = {}
local CSVlines = fileSplit(unparsedContent) -- split the CSV into separate lines.
local headerNames = commaSplit(CSVlines[1]) -- get the names of each column from the first line.
local headerNamesIndices = {}
for i, headerName in ipairs(headerNames) do -- Find the indices of the target headers.
headerNamesIndices[headerName] = i
end
for j, CSVline in ipairs(CSVlines) do -- Consider each CSV line one by one:
local rowData = commaSplit(CSVline)
if rowData[headerNamesIndices['relationType']] == 'Claim' then -- Skip any non-claim lines.
local nationName = rowData[headerNamesIndices['nation1']]
local regionName = rowData[headerNamesIndices['region1']]
if not nationToRegionTable[nationName] then
nationToRegionTable[nationName] = {}
end
tinsert(nationToRegionTable[nationName],regionName)
if not regionToNationTable[regionName] then
regionToNationTable[regionName] = {}
end
tinsert(regionToNationTable[regionName],nationName)
if not claimTypeTable[nationName] then
claimTypeTable[nationName] = {}
end
if not claimTypeTable[nationName][regionName] then
claimTypeTable[nationName][regionName] = {}
end
claimTypeTable[nationName][regionName]['initialOwner'] = rowData[headerNamesIndices['initialOwner']]
claimTypeTable[nationName][regionName]['capitalClaim'] = rowData[headerNamesIndices['capitalClaim']]
claimTypeTable[nationName][regionName]['projectUnlockName'] = rowData[headerNamesIndices['projectUnlockName']]
claimTypeTable[nationName][regionName]['initialColony'] = rowData[headerNamesIndices['initialColony']]
claimTypeTable[nationName][regionName]['hostileClaim'] = rowData[headerNamesIndices['hostileClaim']]
if not peacefulNationToRegionTable[nationName] then
peacefulNationToRegionTable[nationName] = {}
end
if not rowData[headerNamesIndices['hostileClaim']] or rowData[headerNamesIndices['hostileClaim']] ~= 'true' then
tinsert(peacefulNationToRegionTable[nationName],regionName)
end
if not capitalToNationTable[regionName] then
capitalToNationTable[regionName] = {}
end
if rowData[headerNamesIndices['capitalClaim']] and rowData[headerNamesIndices['capitalClaim']] == 'true' then
tinsert(capitalToNationTable[regionName],nationName)
end
end
end
return {nationToRegionTable,regionToNationTable,claimTypeTable,peacefulNationToRegionTable,capitalToNationTable}
end
--endregion
return CSVReader


