Difference between revisions of "Module:Data"

From The Alchemist Code Wiki
Jump to: navigation, search
(Attempt linking.)
(Link to page.)
Line 1: Line 1:
  +
-- <nowiki>
 
local p = {}
 
local p = {}
 
local h = {}
 
local cargo = require('Module:CargoUtil')
 
local cargo = require('Module:CargoUtil')
 
local dataModel = require("Module:Data/Model")
 
local dataModel = require("Module:Data/Model")
Line 7: Line 9:
 
local trim = util_text.trim
 
local trim = util_text.trim
   
function getTitlePath()
+
function h.getTitlePath()
 
local curTitle = mw.title.getCurrentTitle()
 
local curTitle = mw.title.getCurrentTitle()
 
--if curTitle.prefixedText == "Module:Data" then curTitle = mw.title.new("Game/MasterParam/Unit/UN_V2_LOGI", "Data") end
 
--if curTitle.prefixedText == "Module:Data" then curTitle = mw.title.new("Game/MasterParam/Unit/UN_V2_LOGI", "Data") end
Line 17: Line 19:
 
return titleNodes
 
return titleNodes
 
end
 
end
 
-- Generates table for data page
 
function h.insertQueryResult(result, 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")
 
h.insertQueryResult(result, 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
 
p.insertQueryResult = h.insertQueryResult
   
 
-- Will only actually perform the declares on the CargoTable template pages.
 
-- Will only actually perform the declares on the CargoTable template pages.
 
function p.init(frame)
 
function p.init(frame)
local args = require('Module:Arguments').getArgs(frame, {parentFirst=true})
+
local args = require('Module:Arguments').getArgs(frame, {parentFirst=true})
return p._init(args)
+
return p._init(args)
 
end
 
end
 
function p._init(args)
 
function p._init(args)
local yesno = require('Module:Yesno')
+
local yesno = require('Module:Yesno')
 
local declareStatements = {}
 
local declareStatements = {}
 
local recFunc
 
local recFunc
Line 43: Line 110:
   
 
local curTitle = mw.title.getCurrentTitle()
 
local curTitle = mw.title.getCurrentTitle()
if args[1] then
+
if args[1] then
curTitle = mw.title.new(args[1])
+
curTitle = mw.title.new(args[1])
end
+
end
 
if curTitle.basePageTitle.prefixedText == "Template:CargoTable" then
 
if curTitle.basePageTitle.prefixedText == "Template:CargoTable" then
 
local statement = declareStatements[curTitle.subpageText]
 
local statement = declareStatements[curTitle.subpageText]
 
if statement == nil then return "Invalid table "..curTitle.subpageText end
 
if statement == nil then return "Invalid table "..curTitle.subpageText end
local tbl = {'{{#cargo_declare:', '}}'}
+
local tbl = {'{{#cargo_declare:', '}}'}
for k,v in pairs(statement) do
+
for k,v in pairs(statement) do
if k ~= 1 then
+
if k ~= 1 then
tbl[#tbl+1] = '| ' .. k..' = '..v
+
tbl[#tbl+1] = '| ' .. k..' = '..v
end
+
end
end
+
end
table.sort(tbl)
+
table.sort(tbl)
local declare = table.concat(tbl, '\n')
+
local declare = table.concat(tbl, '\n')
if yesno(args.debug) then
+
if yesno(args.debug) then
return tostring(mw.html.create('pre'):wikitext(declare))
+
return tostring(mw.html.create('pre'):wikitext(declare))
 
else
 
else
mw.log(declare)
+
mw.log(declare)
return mw.getCurrentFrame():callParserFunction('#cargo_declare', statement)
+
return mw.getCurrentFrame():callParserFunction('#cargo_declare', statement)
end
+
end
 
end
 
end
 
if mw.getCurrentFrame():getParent() == nil then
 
if mw.getCurrentFrame():getParent() == nil then
Line 72: Line 139:
 
-- Called from Data pages
 
-- Called from Data pages
 
function p.insert(frame)
 
function p.insert(frame)
local args = require('Module:Arguments').getArgs(frame, {parentFirst = true})
+
local args = require('Module:Arguments').getArgs(frame, {parentFirst = true})
return p._insert(args)
+
return p._insert(args)
 
end
 
end
 
function p._insert(args)
 
function p._insert(args)
 
local curNode = {nodes = {Game = dataModel}}
 
local curNode = {nodes = {Game = dataModel}}
local titlePath = getTitlePath()
+
local titlePath = h.getTitlePath()
 
for i, pathItem in ipairs(titlePath) do
 
for i, pathItem in ipairs(titlePath) do
 
if curNode == nil then return nil end
 
if curNode == nil then return nil end
Line 89: Line 156:
 
-- Now that we know this is a valid page
 
-- Now that we know this is a valid page
 
local result = {}
 
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
 
local isName = col == 'name'
 
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
 
val = tostring(val or '')
 
if val ~= '' and isName then
 
val = '[[' .. val .. ']]'
 
end
 
table.insert(result, val)
 
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
 
if next(curNode.model) ~= nil then table.insert(result, "==Tables==\nThe following tables have these rows defined by this data entry.\n") end
Line 171: Line 167:
 
for server, json in pairs(args) do
 
for server, json in pairs(args) do
 
json = unstripNoWiki(json)
 
json = unstripNoWiki(json)
if server == 'gl' or server == 'jp' or server == 'loc' then
+
if server == 'gl' or server == 'jp' or server == 'loc' then
jsonSets[server] = jsonDecode(json)
+
jsonSets[server] = jsonDecode(json)
elseif string.sub(server, -string.len(suffix)) == suffix then
+
elseif string.sub(server, -string.len(suffix)) == suffix then
-- TODO: Store this in a cargo table somewhere.
+
-- TODO: Store this in a cargo table somewhere.
content_sha256s[string.sub(server, 1, -string.len(suffix))] = json
+
content_sha256s[string.sub(server, 1, -string.len(suffix))] = json
end
+
end
end
+
end
 
 
 
-- Convert Functions
 
-- Convert Functions
Line 199: Line 195:
 
table.insert(result, mw.getCurrentFrame():expandTemplate{ title = "CargoTable/Loc" })
 
table.insert(result, mw.getCurrentFrame():expandTemplate{ title = "CargoTable/Loc" })
 
for param, json in pairs(locSets) do
 
for param, json in pairs(locSets) do
if type(json) == "table" then
+
if type(json) == "table" then
for lang, value in pairs(json) do
+
for lang, value in pairs(json) do
table.insert(result, cargo.store{
+
table.insert(result, cargo.store{
_table = 'Loc',
+
_table = 'Loc',
lang = lang,
+
lang = lang,
param = param,
+
param = param,
value = value,
+
value = value,
})
+
})
expected = expected + 1
+
expected = expected + 1
end
+
end
 
end
 
end
 
end
 
end
 
-- Query loc table
 
-- Query loc table
 
local rows = cargo.query{
 
local rows = cargo.query{
tables = 'Loc',
+
tables = 'Loc',
fields = 'lang, param, value',
+
fields = 'lang, param, value',
where = where,
+
where = where,
orderBy = 'lang'
+
orderBy = 'lang'
 
}
 
}
-- Check if expected row count matches actual row count.
+
-- Check if expected row count matches actual row count.
 
local actual = #rows
 
local actual = #rows
 
if actual ~= expected then
 
if actual ~= expected then
Line 228: Line 224:
 
local queryResult = {}
 
local queryResult = {}
 
for i, row in ipairs(rows) do
 
for i, row in ipairs(rows) do
  +
if row.param == 'name' then
  +
local pagename = string.gsub(row.value, "[%[%]]", {
  +
["["] = "【",
  +
["]"] = "】",
  +
})
 
if pagename ~= row.value then
  +
row.value = '[[' .. pagename .. '|' .. row.value .. ']]'
  +
else
 
row.value = '[[' .. row.value .. ']]'
 
end
  +
end
 
queryResult[i] = row
 
queryResult[i] = row
 
end
 
end
insertQueryResult(tbl, queryResult)
+
h.insertQueryResult(result, tbl, queryResult)
 
 
 
local iname
 
local iname
 
for server, json in pairs(jsonSets) do
 
for server, json in pairs(jsonSets) do
if not iname and json.iname then
+
if not iname and json.iname then
iname = json.iname
+
iname = json.iname
end
+
end
end
+
end
   
local actualTitle = table.concat(titlePath, '/', 1, 3)
+
local actualTitle = table.concat(titlePath, '/', 1, 3)
mw.logObject(actualTitle, 'actualTitle')
+
mw.logObject(actualTitle, 'actualTitle')
 
if actualTitle == 'Game/MasterParam/Buff' then
 
if actualTitle == 'Game/MasterParam/Buff' then
 
for server, json in pairs(jsonSets) do
 
for server, json in pairs(jsonSets) do
 
for i=1,11 do
 
for i=1,11 do
local calc = tonumber(json['calc'..i])
+
local calc = tonumber(json['calc'..i])
local _type = tonumber(json['type'..i]) or 0
+
local _type = tonumber(json['type'..i]) or 0
local tktag = (json['tktag'..i] or '') ~= '' and json['tktag'..i] or nil
+
local tktag = (json['tktag'..i] or '') ~= '' and json['tktag'..i] or nil
local vini = tonumber(json['vini'..i])
+
local vini = tonumber(json['vini'..i])
local vmax = tonumber(json['vmax'..i])
+
local vmax = tonumber(json['vmax'..i])
local vone = tonumber(json['vone'..i])
+
local vone = tonumber(json['vone'..i])
 
local values = {
 
local values = {
 
_table = 'BuffDetail',
 
_table = 'BuffDetail',
Line 262: Line 269:
 
vone = vone,
 
vone = vone,
 
}
 
}
if _type <= 0 then
+
if _type <= 0 then
mw.logObject(values, "Skipped insert into BuffDetail values")
+
mw.logObject(values, "Skipped insert into BuffDetail values")
 
else
 
else
 
table.insert(result, cargo.store(values))
 
table.insert(result, cargo.store(values))
Line 299: Line 306:
 
end
 
end
   
-- Query the data
+
-- Query the data
 
local query = {
 
local query = {
tables = tbl,
+
tables = tbl,
fields = {"server"},
+
fields = {"server"},
where = where,
+
where = where,
orderBy = 'server',
+
orderBy = 'server',
 
}
 
}
 
for col in pairs(model) do
 
for col in pairs(model) do
table.insert(query.fields, col)
+
table.insert(query.fields, col)
 
end
 
end
 
local rows = cargo.query(query)
 
local rows = cargo.query(query)
Line 339: Line 346:
 
queryResult[i] = row
 
queryResult[i] = row
 
end
 
end
insertQueryResult(tbl, queryResult)
+
h.insertQueryResult(result, tbl, queryResult)
 
end
 
end
   
local hasEffects = false
+
local hasEffects = false
local jsonEffects = {}
+
local jsonEffects = {}
 
for server, json in pairs(jsonSets) do
 
for server, json in pairs(jsonSets) do
if json.effects then
+
if json.effects then
hasEffects = true
+
hasEffects = true
jsonEffects[server] = {effects = {}}
+
jsonEffects[server] = {effects = {}}
for i,effect in ipairs(json.effects) do
+
for i,effect in ipairs(json.effects) do
local values = {}
+
local values = {}
values._table = 'ConceptCardEffect'
+
values._table = 'ConceptCardEffect'
values.server = server
+
values.server = server
values.cc_iname = iname
+
values.cc_iname = iname
jsonEffects[server].effects[i] = {}
+
jsonEffects[server].effects[i] = {}
for k,v in pairs(effect) do
+
for k,v in pairs(effect) do
values[k] = v
+
values[k] = v
jsonEffects[server].effects[i][k] = v
+
jsonEffects[server].effects[i][k] = v
effect[k] = nil
+
effect[k] = nil
 
end
end
 
table.insert(result, cargo.store(values))
+
table.insert(result, cargo.store(values))
jsonEffects[server].server = server
+
jsonEffects[server].server = server
  +
end
end
 
-- A table of failed effect insertions.
+
-- A table of failed effect insertions.
local errorEffects = {}
+
local errorEffects = {}
for i, effect in ipairs(json.effects) do
+
for i, effect in ipairs(json.effects) do
local empty = true
+
local empty = true
for k, v in pairs(effect) do empty = false end
+
for k, v in pairs(effect) do empty = false end
if not empty then
+
if not empty then
errorEffects[#errorEffects+1] = effect
+
errorEffects[#errorEffects+1] = effect
 
end
end
 
  +
end
end
 
json.effects = #errorEffects > 0 and errorEffects or nil
+
json.effects = #errorEffects > 0 and errorEffects or nil
end
+
end
end
+
end
  +
 
if next(jsonEffects) then
+
if next(jsonEffects) then
table.insert(result, '===ConceptCardEffect===\n')
+
table.insert(result, '===ConceptCardEffect===\n')
local toInsert = {}
+
local toInsert = {}
for k in pairs(jsonEffects) do table.insert(toInsert, k) end
+
for k in pairs(jsonEffects) do table.insert(toInsert, k) end
table.sort(toInsert)
+
table.sort(toInsert)
for i, k in ipairs(toInsert) do toInsert[i] = jsonEffects[k] end
+
for i, k in ipairs(toInsert) do toInsert[i] = jsonEffects[k] end
insertQueryResult(nil, toInsert)
+
h.insertQueryResult(result, nil, toInsert)
end
+
end
   
 
local buffDetailRows = cargo.query{
 
local buffDetailRows = cargo.query{
tables = 'BuffDetail',
+
tables = 'BuffDetail',
fields = 'buff_iname, server, idx, calc, type, tktag, vini, vmax, vone',
+
fields = 'buff_iname, server, idx, calc, type, tktag, vini, vmax, vone',
where = where,
+
where = where,
orderBy = 'idx'
+
orderBy = 'idx'
 
}
 
}
insertQueryResult('BuffDetail', buffDetailRows)
+
h.insertQueryResult(result, 'BuffDetail', buffDetailRows)
   
  +
 
 
-- Display unused keys here
 
-- Display unused keys here
 
local unused = false
 
local unused = false
Line 405: Line 412:
 
table.sort(toInsert)
 
table.sort(toInsert)
 
for i, k in ipairs(toInsert) do toInsert[i] = jsonSets[k] end
 
for i, k in ipairs(toInsert) do toInsert[i] = jsonSets[k] end
insertQueryResult(nil, toInsert)
+
h.insertQueryResult(result, nil, toInsert)
 
end
 
end
 
 
Line 411: Line 418:
 
-- Handle errors encountered
 
-- Handle errors encountered
 
if errors.cargoStore then
 
if errors.cargoStore then
table.insert(result, '[[Category:Data pages that failed a table insert]]' )
+
table.insert(result, '[[Category:Data pages that failed a table insert]]' )
end
+
end
if hasEffects then
+
if hasEffects then
table.insert(result, '[[Category:Data pages with effects]]')
+
table.insert(result, '[[Category:Data pages with effects]]')
end
+
end
   
 
return table.concat(result)
 
return table.concat(result)
Line 434: Line 441:
 
-----------
 
-----------
 
dataModel.query = function(tables, fields, args)
 
dataModel.query = function(tables, fields, args)
local query = args
+
local query = args
query.tables = tables
+
query.tables = tables
query.fields = fields
+
query.fields = fields
 
local result = cargo.query(query)
 
local result = cargo.query(query)
 
local toCast = {}
 
local toCast = {}
 
if type(query.tables) == 'string' then
 
if type(query.tables) == 'string' then
query.tables = split(query.tables, ',', true)
+
query.tables = split(query.tables, ',', true)
 
end
 
end
 
if type(query.fields) == 'string' then
 
if type(query.fields) == 'string' then
query.fields = split(query.fields, ',', true)
+
query.fields = split(query.fields, ',', true)
 
end
 
end
 
for _, field in ipairs(query.fields) do
 
for _, field in ipairs(query.fields) do

Revision as of 14:11, 18 November 2020

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

-- <nowiki>
local p = {}
local h = {}
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 h.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
-- Generates table for data page
function h.insertQueryResult(result, 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")
                h.insertQueryResult(result, 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
p.insertQueryResult = h.insertQueryResult

-- Will only actually perform the declares on the CargoTable template pages.
function p.init(frame)
    local args = require('Module:Arguments').getArgs(frame, {parentFirst=true})
    return p._init(args)
end
function p._init(args)
    local yesno = require('Module:Yesno')
    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 args[1] then
        curTitle = mw.title.new(args[1])
    end
    if curTitle.basePageTitle.prefixedText == "Template:CargoTable" then
        local statement = declareStatements[curTitle.subpageText]
        if statement == nil then return "Invalid table "..curTitle.subpageText end
        local tbl = {'{{#cargo_declare:', '}}'}
        for k,v in pairs(statement) do
            if k ~= 1 then
                tbl[#tbl+1] = '| ' .. k..' = '..v
            end
        end
        table.sort(tbl)
        local declare = table.concat(tbl, '\n')
        if yesno(args.debug) then
            return tostring(mw.html.create('pre'):wikitext(declare))
        else
            mw.log(declare)
            return mw.getCurrentFrame():callParserFunction('#cargo_declare', statement)
        end
    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 = h.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 = {}
    
    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 unstripNoWiki = mw.text.unstripNoWiki
    local jsonSets = {}
    local content_sha256s = {}
    local suffix = '_sha256'
    for server, json in pairs(args) do
        json = unstripNoWiki(json)
        if server == 'gl' or server == 'jp' or server == 'loc' then
            jsonSets[server] = jsonDecode(json)
        elseif string.sub(server, -string.len(suffix)) == suffix then
            -- TODO: Store this in a cargo table somewhere.
            content_sha256s[string.sub(server, 1, -string.len(suffix))] = json
        end
    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
        if type(json) == "table" then
            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
    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
        if row.param == 'name' then
            local pagename = string.gsub(row.value, "[%[%]]", {
                ["["] = "【",
                ["]"] = "】",
            })
            if pagename ~= row.value then
                row.value = '[[' .. pagename .. '|' .. row.value .. ']]'
            else
                row.value = '[[' .. row.value .. ']]'
            end
        end
        queryResult[i] = row
    end
    h.insertQueryResult(result, 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]) or 0
                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
            mw.logObject(statement, 'statement')
            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
        h.insertQueryResult(result, 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
        h.insertQueryResult(result, nil, toInsert)
    end

    local buffDetailRows = cargo.query{
        tables = 'BuffDetail',
        fields = 'buff_iname, server, idx, calc, type, tktag, vini, vmax, vone',
        where = where,
        orderBy = 'idx'
    }
    h.insertQueryResult(result, '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
        h.insertQueryResult(result, 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