Module:Data

-- local p = {} local h = {} local cargo = require('Module:CargoUtil') local yesno = require('Module:Yesno') 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

local langs = {'english', 'japanese'} local locPrefixes = { ['Ability'] = true, ['Artifact'] = true, ['ConceptCard'] = true, ['Item'] = true, ['Job'] = true, ['Skill'] = true, ['Unit'] = true, } function h.getEntityType(args) local name = args.name or mw.title.getCurrentTitle.prefixedText return name:match('Data:Game/MasterParam/([^/]+)/.+') end

function p.displayLocs(data) local locs = data.loc if not locs then return end local entityType = h.getEntityType(data) if not entityType or not locPrefixes[entityType] then return end

local definitions = mw.loadData('Module:CargoDeclare/'..entityType..'Loc')

-- HOTFIX: "otherdesc" local temp = {} for param, obj in pairs(locs) do		if param == 'otherdesc' then param = 'desc_ot' end for lang, value in pairs(obj) do			temp[param] = temp[param] or {} temp[param][lang] = value end end locs = temp

-- Build localization wikitable local wikitable = mw.html.create('table'):addClass('wikitable') local tr = wikitable:tag('tr') tr:tag('th'):wikitext('param') for _, lang in ipairs(langs) do		tr:tag('th'):wikitext(lang) end for _, definition in ipairs(definitions) do		local field = definition.field if field and locs[field] then tr = wikitable:tag('tr') tr:tag('th'):wikitext(field) for _, lang in ipairs(langs) do				tr:tag('td'):wikitext(locs[field][lang]) end end end return tostring(wikitable) end

function p.storeLocs(nocargo, data) local locs = data.loc if not locs then return end local entityType = h.getEntityType(data) if not entityType or not locPrefixes[entityType] then return end local iname = (data.gl or data.jp or {}).iname local definitions = mw.loadData('Module:CargoDeclare/'..entityType..'Loc')

-- HOTFIX: "otherdesc" local temp = {} for param, obj in pairs(locs) do		if param == 'otherdesc' then param = 'desc_ot' end for lang, value in pairs(obj) do			temp[param] = temp[param] or {} temp[param][lang] = value end end locs = temp

local rows = {} for _, definition in ipairs(definitions) do		local field = definition.field if field and locs[field] then for lang, value in pairs(locs[field]) do				rows[lang] = rows[lang] or { _table = entityType .. 'Loc', iname = iname, lang = lang, }				rows[lang][field] = value end end end

local result = {} -- Store localization if not yesno(nocargo) then for _, row in pairs(rows) do			mw.logObject(row) result[#result+1] = cargo.store(row) end end return table.concat(result) end

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 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 = {''} 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 nocargo = args.nocargo 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 jsonSets = {} for k, v in pairs(args) do   	if type(v) == 'string' then v = mw.text.decode(mw.text.unstripNoWiki(v)) local success, result = pcall(mw.text.jsonDecode, v)	   	if success then v = result end args[k] = v	   end end local content_sha256s = {} local suffix = '_sha256' for server, json in pairs(args) do       if server == 'gl' or server == 'jp' or server == 'loc' then jsonSets[server] = 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 result[#result+1] = p.storeLocs(nocargo, args) result[#result+1] = p.displayLocs(args)

local locSets = jsonSets.loc or {} jsonSets.loc = nil local expected = 0 table.insert(result, '\n'..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.lang == 'english' and row.param == 'name' then local pagename = string.gsub(row.value, "[%[%]]", {               ["["] = "【",                ["]"] = "】",            }) if pagename ~= row.value then row.value =  .. 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 local definitions = mw.loadData('Module:CargoDeclare/BuffDetail') for server, json in pairs(jsonSets) do           for i=1,11 do            	local values = { _table = 'BuffDetail', }               for _, definition in ipairs(definitions) do                	local field = definition.field if definition.type == 'Integer' then values[field] = tonumber(json[field..i]) else values[field] = json[field..i]	               end end values.server = server values.buff_iname = iname values.idx = i               if (values.type or 0) <= 0 then mw.logObject(values, "Skipped insert into BuffDetail values") else mw.logObject(values, "#cargo_store") table.insert(result, cargo.store(values)) end end end end

if actualTitle == 'Game/MasterParam/Job' then local definitions = mw.loadData('Module:CargoDeclare/JobRank') local jobRanks = {} for server, json in pairs(jsonSets) do       	for i, rank in ipairs(json.ranks) do        		local values = { _table = 'JobRank', }               for _, definition in ipairs(definitions) do                	local field = definition.field if rank[field] ~= nil then if definition.type == 'Integer' then values[field] = tonumber(rank[field]) else values[field] = rank[field] end rank[field] = nil -- remove end end values.server = server values.job_iname = iname values.idx = i           	mw.logObject(values, "#cargo_store") table.insert(result, cargo.store(values)) values._table = nil jobRanks[i] = jobRanks[i] or {} jobRanks[i][#jobRanks[i]+1] = values end json.ranks = nil -- done end table.insert(result, '=== JobRank ===\n') for i, rank in ipairs(jobRanks) do		   h.insertQueryResult(result, nil, rank) 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                               if not v:find('__HIDE__$') then val[k] = ""..v.."" else val[k] = v                               end end elseif #val > 0 and not val:find('__HIDE__$') then 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, '') -- Tracking category. -- Handle errors encountered if errors.cargoStore then table.insert(result, '' ) end if hasEffects then table.insert(result, '') 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 = end return thisLang[1].value end

-   -- Sources -- -   dataModel.sources = require("Module:Data/Sources")(dataModel) return dataModel end)

return p