The Alchemist Code Wiki

READ MORE

The Alchemist Code Wiki
Advertisement

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
        	json = mw.text.decode(json)
            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
Advertisement