Documentation for this module may be created at Module:Data/doc
local p = {}
local cargo = require('Module:CargoUtil')
local dataModel = require("Module:Data/Model")
local util_text = require('Module:TextUtil')
local split = util_text.split
local gsplit = util_text.gsplit
local trim = util_text.trim
function getTitlePath()
local curTitle = mw.title.getCurrentTitle()
--if curTitle.prefixedText == "Module:Data" then curTitle = mw.title.new("Game/MasterParam/Unit/UN_V2_LOGI", "Data") end
local titleNodes = {curTitle.subpageText}
while curTitle.baseText ~= curTitle.text do
curTitle = curTitle.basePageTitle
table.insert(titleNodes, 1, curTitle.subpageText)
end
return titleNodes
end
-- Will only actually perform the declares on the CargoTable template pages.
function p.init()
local declareStatements = {}
local recFunc
function recFunc(node)
if node.nodes ~= nil then for k, child in pairs(node.nodes) do recFunc(child) end end
if node.model ~= nil then for tbl, model in pairs(node.model) do
local statement = {"", _table = tbl, server = "String"}
for col, colModel in pairs(model) do
if type(colModel) == "string" then colModel = {colModel} end
local modelType = colModel[1]
if colModel.list ~= nil then modelType = "List ("..colModel.list..") of "..modelType end
statement[col] = modelType
end
declareStatements[tbl] = statement
end end
end
recFunc(dataModel)
local curTitle = mw.title.getCurrentTitle()
if curTitle.basePageTitle.prefixedText == "Template:CargoTable" then
local statement = declareStatements[curTitle.subpageText]
if statement == nil then return "Invalid table "..curTitle.subpageText end
return mw.getCurrentFrame():callParserFunction('#cargo_declare', statement)
end
if mw.getCurrentFrame():getParent() == nil then
return nil
end
return "asdf"
end
-- Called from Data pages
function p.insert(frame)
local args = require('Module:Arguments').getArgs(frame, {parentFirst = true})
return p._insert(args)
end
function p._insert(args)
local curNode = {nodes = {Game = dataModel}}
local titlePath = getTitlePath()
for i, pathItem in ipairs(titlePath) do
if curNode == nil then return nil end
if i == #titlePath then break end
if curNode.nodes == nil then return nil end
curNode = curNode.nodes[pathItem]
end
if curNode.model == nil then return nil end
-- Now that we know this is a valid page
local result = {}
-- Generates table for data page
local insertQueryResult
function insertQueryResult(title, tbl)
local fields = {}
local found = {}
local count = 0
local hideCols = true
for i, row in pairs(tbl) do
count = count + 1
for col in ipairs(row) do
if found[col] == nil then
table.insert(fields, col)
found[col] = true
end
end
for col in pairs(row) do
if found[col] == nil then
table.insert(fields, col)
found[col] = true
hideCols = false
end
end
end
table.sort(fields, function(a, b) return a == "server" or a == "lang" or (b ~= "server" and b ~= "lang" and (a or '') < (b or '')) end)
if count == 0 then return nil end
if title ~= nil then table.insert(result, "=="..title.."==\n") end
table.insert(result, '{| class="wikitable"')
for i, col in ipairs(fields) do
table.insert(result, "\n|-")
if not hideCols then
table.insert(result, "\n! ")
table.insert(result, col)
end
-- Merge matching rows
local allSame = true
local testVal = tbl[next(tbl)][col]
for j, row in pairs(tbl) do
if type(testVal) == 'table' and type(row[col]) == 'table' then
for k, v in pairs(testVal) do if v ~= row[col][k] then allSame = false end end
for k, v in pairs(row[col]) do if v ~= testVal[k] then allSame = false end end
elseif row[col] ~= testVal then allSame = false end
end
local function writeVal(val)
if type(val) == "table" then
table.insert(result, "\n")
insertQueryResult(nil, {val})
else table.insert(result, tostring(val or '')) end
end
if allSame then
table.insert(result, '\n| colspan="'..count..'" | ')
writeVal(testVal)
else
for j, row in pairs(tbl) do
table.insert(result, "\n| ")
writeVal(row[col])
end
end
end
table.insert(result, "\n|}\n")
end
if next(curNode.model) ~= nil then table.insert(result, "=Tables=\nThe following tables have these rows defined by this data entry.\n") end
-- Decode JSON
local jsonDecode = mw.text.jsonDecode
local jsonSets = {}
for server, json in pairs(args) do jsonSets[server] = jsonDecode(json) end
-- Convert Functions
local convertFrom = {
table = function(val, model)
if model.list ~= nil then
return table.concat(val, model.list)
end
error("JSON data for "..model[1].." is a table but is not modeled to handle it.")
end,
}
-- Insert Rows
local where = '_pageId='..mw.title.getCurrentTitle().id
local errors = {}
-- Do loc first
local locSets = jsonSets.loc or {}
jsonSets.loc = nil
local expected = 0
table.insert(result, mw.getCurrentFrame():expandTemplate{ title = "CargoTable/Loc" })
for param, json in pairs(locSets) do
for lang, value in pairs(json) do
table.insert(result, cargo.store{
_table = 'Loc',
lang = lang,
param = param,
value = value,
})
expected = expected + 1
end
end
-- Query loc table
local rows = cargo.query{
tables = 'Loc',
fields = 'lang, param, value',
where = where,
orderBy = 'lang'
}
-- Check if expected row count matches actual row count.
local actual = #rows
if actual ~= expected then
table.insert(result, string.format('Entry count mismatch in table %q (%s expected, got %s)\n', 'Loc', expected, actual))
errors.cargoStore = true
end
-- Generate loc tables from queried data
local queryResult = {}
for i, row in ipairs(rows) do
queryResult[i] = row
end
insertQueryResult(tbl, queryResult)
local iname
for server, json in pairs(jsonSets) do
if not iname and json.iname then
iname = json.iname
end
end
local actualTitle = table.concat(titlePath, '/', 1, 3)
mw.logObject(actualTitle, 'actualTitle')
if actualTitle == 'Game/MasterParam/Buff' then
for server, json in pairs(jsonSets) do
for i=1,11 do
local calc = tonumber(json['calc'..i])
local _type = tonumber(json['type'..i])
local tktag = (json['tktag'..i] or '') ~= '' and json['tktag'..i] or nil
local vini = tonumber(json['vini'..i])
local vmax = tonumber(json['vmax'..i])
local vone = tonumber(json['vone'..i])
local values = {
_table = 'BuffDetail',
idx = i,
buff_iname = iname,
server = server,
calc = calc,
tktag = tktag,
['type'] = _type,
vini = vini,
vmax = vmax,
vone = vone,
}
if type <= 0 then
mw.logObject(values, "Skipped insert into BuffDetail values")
else
table.insert(result, cargo.store(values))
-- json['calc'..i] = nil
-- json['tktag'..i] = nil
-- json['type'..i] = nil
-- json['vini'..i] = nil
-- json['vmax'..i] = nil
-- json['vone'..i] = nil
end
end
end
end
-- Now get the data
for tbl, model in pairs(curNode.model) do
table.insert(result, mw.getCurrentFrame():expandTemplate{ title = "CargoTable/"..tbl })
expected = 0
for server, json in pairs(jsonSets) do
local statement = {_table = tbl, server = server}
for col, colModel in pairs(model) do
if type(colModel) == "string" then colModel = {colModel} end
-- Extract value
local jsonVal = json[colModel.from or col]
json[colModel.from or col] = nil
-- Convert datatypes here
local converter = convertFrom[type(jsonVal)]
if converter ~= nil then jsonVal = converter(jsonVal, colModel) end
-- Append to statement
statement[col] = jsonVal
end
table.insert(result, cargo.store(statement))
expected = expected + 1
end
-- Query the data
local query = {
tables = tbl,
fields = {"server"},
where = where,
orderBy = 'server',
}
for col in pairs(model) do
table.insert(query.fields, col)
end
local rows = cargo.query(query)
local actual = #rows
if actual ~= expected then
table.insert(result, string.format('Entry count mismatch in table %q (%s expected, got %s)\n', tbl, expected, actual))
errors.cargoStore = true
end
-- Generate tables from queried data
queryResult = {}
for i, row in ipairs(rows) do
for col, val in pairs(row) do
if type(model[col]) == "table" then
if model[col].list ~= nil then
val = #val == 0 and {} or split(val, model[col].list, true)
end
if model[col].dataRef ~= nil then
if type(val) == "table" then
for k, v in pairs(val) do
val[k] = "[[Data:Game/"..model[col].dataRef.."/"..v.."|"..v.."]]"
end
elseif #val > 0 then
val = "[[Data:Game/"..model[col].dataRef.."/"..val.."|"..val.."]]"
end
end
end
row[col] = val
end
queryResult[i] = row
end
insertQueryResult(tbl, queryResult)
end
local hasEffects = false
local jsonEffects = {}
for server, json in pairs(jsonSets) do
if json.effects then
hasEffects = true
jsonEffects[server] = {effects = {}}
for i,effect in ipairs(json.effects) do
local values = {}
values._table = 'ConceptCardEffect'
values.server = server
values.cc_iname = iname
jsonEffects[server].effects[i] = {}
for k,v in pairs(effect) do
values[k] = v
jsonEffects[server].effects[i][k] = v
effect[k] = nil
end
table.insert(result, cargo.store(values))
jsonEffects[server].server = server
end
-- A table of failed effect insertions.
local errorEffects = {}
for i, effect in ipairs(json.effects) do
local empty = true
for k, v in pairs(effect) do empty = false end
if not empty then
errorEffects[#errorEffects+1] = effect
end
end
json.effects = #errorEffects > 0 and errorEffects or nil
end
end
if next(jsonEffects) then
table.insert(result, '==ConceptCardEffect==\n')
local toInsert = {}
for k in pairs(jsonEffects) do table.insert(toInsert, k) end
table.sort(toInsert)
for i, k in ipairs(toInsert) do toInsert[i] = jsonEffects[k] end
insertQueryResult(nil, toInsert)
end
local buffDetailRows = cargo.query{
tables = 'BuffDetail',
fields = 'buff_iname, server, idx, calc, type, tktag, vini, vmax, vone',
where = where,
orderBy = 'idx'
}
insertQueryResult('BuffDetail', buffDetailRows)
-- Display unused keys here
local unused = false
for server, json in pairs(jsonSets) do if next(json) ~= nil then
unused = true
json.server = server
end end
if unused then
table.insert(result, "==Unused Keys==\nThe following keys were not used by inserted into any table and are thus unused.\n")
local toInsert = {}
for k in pairs(jsonSets) do table.insert(toInsert, k) end
table.sort(toInsert)
for i, k in ipairs(toInsert) do toInsert[i] = jsonSets[k] end
insertQueryResult(nil, toInsert)
end
table.insert(result, '[[Category:Data pages]]') -- Tracking category.
-- Handle errors encountered
if errors.cargoStore then
table.insert(result, '[[Category:Data pages that failed a table insert]]' )
end
if hasEffects then
table.insert(result, '[[Category:Data pages with effects]]')
end
return table.concat(result)
end
-- Add functions to model
p.model = (function()
dataModel.tables = {}
local recFunc
function recFunc(node)
if node.nodes ~= nil then for k, child in pairs(node.nodes) do recFunc(child) end end
if node.model ~= nil then for tbl, model in pairs(node.model) do dataModel.tables[tbl] = model end end
end
recFunc(dataModel)
-----------
-- Query --
-----------
dataModel.query = function(tables, fields, args)
local query = args
query.tables = tables
query.fields = fields
local result = cargo.query(query)
local toCast = {}
if type(query.tables) == 'string' then
query.tables = split(query.tables, ',', true)
end
if type(query.fields) == 'string' then
query.fields = split(query.fields, ',', true)
end
for _, field in ipairs(query.fields) do
-- Extract alias
local t = split(field, '=', true)
local alias = trim(t[2] or t[1])
local fnMatch = string.match(t[1], "([^()]*)[(](.*)[)](.*)")
if fnMatch ~= nil then
mw.log(fnMatch)
toCast[alias] = {"String"}
else
-- Extract table
local t2 = split(t[1], '.', true)
local tbl = #t2 > 1 and trim(t2[1]) or nil
local key = trim(t2[#t2])
if tbl == nil then
for _, tblName in ipairs(query.tables) do
tblName = trim(tblName)
if (dataModel.tables[tblName] or {})[key] ~= nil then
tbl = tblName
break
end
end
end
local model = (dataModel.tables[tbl] or {})[key]
toCast[alias] = type(model) == "string" and {model} or model
end
end
local castFuncs = {
Integer = tonumber
}
for i, row in ipairs(result) do
for k, castTo in pairs(toCast) do
local castFunc = castFuncs[castTo[1]] or function(v) return v end
if castTo.list ~= nil then
local list = {}
if row[k] and row[k] ~= '' then
for s in gsplit(row[k], castTo.list, true) do
list[#list+1] = castFunc(s)
end
end
row[k] = list
else
row[k] = castFunc(row[k])
end
end
end
return result
end
------------
-- getLoc --
------------
dataModel.getLoc = function(_pageName, param, lang)
if _pageName == nil then return '???' end
local where = "_pageName='".._pageName.."' and param='"..param.."' and value<>'' and lang='"
local thisLang = cargo.query{tables='Loc',fields='value',where=where..(lang or "english'")}
if #thisLang == 0 then thisLang = cargo.query{tables='Loc',fields='value',where=where.."english'"} end
if #thisLang == 0 then thisLang = cargo.query{tables='Loc',fields='value',where=where.."japanese'"} end
if #thisLang == 0 then thisLang = {{value = ''}} end
return thisLang[1].value
end
-------------
-- Sources --
-------------
dataModel.sources = require("Module:Data/Sources")(dataModel)
return dataModel
end)()
return p