Module:Version: Difference between revisions

From Path of Exile Wiki
Jump to navigation Jump to search
No edit summary
(Translation strings moved to Module:Version/config. No globals. Use invoker factory from Module:Util.)
Line 1: Line 1:
local getArgs = require('Module:Arguments').getArgs
require('Module:No globals')
local m_util = require('Module:Util')
local m_util = require('Module:Util')
local m_cargo = require('Module:Cargo')
local m_cargo = require('Module:Cargo')
local f_item_link = require('Module:Item link').item_link
local f_item_link = require('Module:Item link').item_link
local cargo = mw.ext.cargo
local cargo = mw.ext.cargo


local string_format = string.format
-- Should we use the sandbox version of our submodules?
local table_concat = table.concat
local use_sandbox = m_util.misc.maybe_sandbox('Version')


local mw_html = mw.html
-- The cfg table contains all localisable strings and configuration, to make it
-- easier to port this module to another wiki.
local cfg = use_sandbox and mw.loadData('Module:Version/config/sandbox') or mw.loadData('Module:Version/config')


local mw_language = mw.getLanguage('en')
local i18n = cfg.i18n
 
local p = {}
 
local i18n = {
    categories = {
        versions = 'Versions',
        timelines = 'Timelines',
    },
   
    show_date = {
        before = '← [[Version %s|%s]]<br>%s',
        after = '[[Version %s|%s]] →<br>%s',
    },
    version = {
        required_args = 'Arguments "patch" and "patchdate" are required',
        multiple_versions = 'There are multiple versions with the same name',
        header = '[[Version history|Version History]]',
    },
    timeline = {
        version = 'Version',
    },
}


-- ---------------------------------------------------------------------
-- ---------------------------------------------------------------------
Line 40: Line 21:
local h = {}
local h = {}


function h.cargo_query(tpl_args)
function h.cargo_query(args)
     --[[
     --[[
     Returns a Cargo query of all the results.
     Returns a Cargo query of all the results.
      
      
     tpl_args should include these keys:
     args should include these keys:
     tpl_args.tables
     args.tables
     tpl_args.fields
     args.fields
     tpl_args.q_*
     args.q_*
      
      
     ]]
     ]]
      
      
     local tables = m_util.string.split(tpl_args.tables, ', ')
     local tables = m_util.string.split(args.tables, ', ')
     local fields = m_util.string.split(tpl_args.fields, ', ')
     local fields = m_util.string.split(args.fields, ', ')
      
      
     -- Parse query arguments
     -- Parse query arguments
     local query = {
     local query = {
     }
     }
     for key, value in pairs(tpl_args) do  
     for key, value in pairs(args) do  
         if string.sub(key, 0, 2) == 'q_' then
         if string.sub(key, 0, 2) == 'q_' then
             query[string.sub(key, 3)] = value
             query[string.sub(key, 3)] = value
Line 92: Line 73:
         },
         },
     }
     }
      
 
     local lang = mw.getContentLanguage()
     local date_format = arg_list['format']['default']
     local date_format = arg_list['format']['default']
     local timestamp = mw_language:formatDate(date_format, value)
     local timestamp = lang:formatDate(date_format, value)
      
      
     -- If the time is 00:00:00 then assume that the time isn't defined:
     -- If the time is 00:00:00 then assume that the time isn't defined:
     if mw_language:formatDate('H:i:s', timestamp) == '00:00:00' then  
     if lang:formatDate('H:i:s', timestamp) == '00:00:00' then  
         date_format = arg_list['format']['no_time']
         date_format = arg_list['format']['no_time']
     end
     end
Line 111: Line 93:
     local out
     local out
     if value ~= nil then
     if value ~= nil then
         out = mw_language:formatDate(date_format, timestamp)
         out = lang:formatDate(date_format, timestamp)
     end
     end
      
      
Line 117: Line 99:
end
end


 
function h.validate_version(value)
-- ---------------------------------------------------------------------
-- Template: Version
-- ---------------------------------------------------------------------
 
 
local function validate_version(value)
     if value == nil then
     if value == nil then
         return value
         return value
Line 131: Line 107:
end
end


local function show_date(args)
function h.show_date(args)
     return function(tpl_args, frame)
     return function(targs)
         local version = tpl_args[args.key]
         local version = targs[args.key]
         local date = tpl_args[string.format('%s_date', args.key)]
         local date = targs[string.format('%s_date', args.key)]
         if version and date then
         if version and date then
             date = h.date(date) or ''
             date = h.date(date) or ''
             if args.key == 'before' then
             if args.key == 'before' then
                 return string_format(i18n.show_date.before, version, version, date)
                 return string.format(i18n.show_date.before, version, version, date)
             elseif args.key == 'after' then
             elseif args.key == 'after' then
                 return string_format(i18n.show_date.after, version, version, date)
                 return string.format(i18n.show_date.after, version, version, date)
             end
             end
         else
         else
Line 148: Line 124:
end
end


-- ----------------------------------------------------------------------------
-- Cargo tables
-- ----------------------------------------------------------------------------


local version_map = {
local tables = {}
 
tables.versions ={
     table = 'versions',
     table = 'versions',
     fields = {
     fields = {
Line 155: Line 136:
             field = 'version',
             field = 'version',
             type = 'String',
             type = 'String',
             validate = validate_version,
             validate = h.validate_version,
         },
         },
         patchdate = {
         patchdate = {
Line 181: Line 162:
             field = 'previous',
             field = 'previous',
             type = 'String',
             type = 'String',
             validate = validate_version,
             validate = h.validate_version,
             show = show_date{key='before'},
             show = h.show_date{key='before'},
         },
         },
         after = {
         after = {
             field = 'after',
             field = 'after',
             type = 'String',
             type = 'String',
             validate = validate_version,
             validate = h.validate_version,
             show = show_date{key='after'},
             show = h.show_date{key='after'},
         },
         },
     },
     },
}
}


p.table_versions = m_cargo.declare_factory{data=version_map}
-- ----------------------------------------------------------------------------
-- Main functions
-- ----------------------------------------------------------------------------


p.version = function(frame)
local function _version(args)
     --[[
     --[[
     This function creates a infobox and stores the data in a cargo table.
     This function creates a infobox and stores the data in a cargo table.
Line 208: Line 191:
     --]]
     --]]


    local tpl_args = getArgs(frame, {parentFirst = true})
     for k, data in pairs(tables.versions.fields) do
    local frame = m_util.misc.get_frame(frame)
 
     for k, data in pairs(version_map.fields) do
         if data.validate ~= nil then
         if data.validate ~= nil then
             tpl_args[k] = data.validate(tpl_args[k])
             args[k] = data.validate(args[k])
         end
         end
     end
     end
Line 219: Line 199:
     -- Workaround for patchdate returning the string 'nil' when it's nil,  
     -- Workaround for patchdate returning the string 'nil' when it's nil,  
     -- not sure if that's intentional:
     -- not sure if that's intentional:
     if not tpl_args.patch or not tpl_args.patchdate or tpl_args.patchdate == 'nil' then
     if not args.patch or not args.patchdate or args.patchdate == 'nil' then
         error(i18n.version.required_args)
         error(i18n.version.required_args)
     end
     end
      
      
     local version_parts = m_util.cast.version(tpl_args.patch, {return_type='table'})
     local version_parts = m_util.cast.version(args.patch, {return_type='table'})
     tpl_args.major_part = tonumber(version_parts[1])
     args.major_part = tonumber(version_parts[1])
     tpl_args.minor_part = tonumber(version_parts[2])
     args.minor_part = tonumber(version_parts[2])
     tpl_args.patch_part = tonumber(version_parts[3])
     args.patch_part = tonumber(version_parts[3])
     if version_parts[4] then
     if version_parts[4] then
         tpl_args.revision_part = version_parts[4]
         args.revision_part = version_parts[4]
     end
     end


     -- Check and set 'before' and 'after' tpl_args
     -- Check and set 'before' and 'after' args
     local edge_names = {'before', 'after'}
     local edge_names = {'before', 'after'}
     for _, key in ipairs(edge_names) do
     for _, key in ipairs(edge_names) do
         local v = tpl_args[key]
         local v = args[key]
         if v then
         if v then
             local results = cargo.query(
             local results = cargo.query(
Line 246: Line 226:
             )
             )
             if #results == 1 then
             if #results == 1 then
                 tpl_args[string.format('%s_date', key)] = results[1]['versions.release_date']
                 args[string.format('%s_date', key)] = results[1]['versions.release_date']
             elseif #results > 1 then
             elseif #results > 1 then
                 error(i18n.version.multiple_versions)
                 error(i18n.version.multiple_versions)
Line 256: Line 236:
     -- Set Cargo data
     -- Set Cargo data
     local _properties = {
     local _properties = {
         _table = version_map.table,
         _table = tables.versions.table,
     }
     }
     for key, data in pairs(version_map.fields) do
     for key, data in pairs(tables.versions.fields) do
         if tpl_args[key] ~= nil then
         if args[key] ~= nil then
             _properties[data.field] = tpl_args[key]
             _properties[data.field] = args[key]
       end
       end
     end
     end
Line 266: Line 246:
     m_cargo.store(_properties)
     m_cargo.store(_properties)


     frame:expandTemplate{
     mw.getCurrentFrame():expandTemplate{
         title = 'Template:Version/cargo/versions/attach',
         title = 'Template:Version/cargo/versions/attach',
         args = {}
         args = {}
Line 272: Line 252:


     -- Generate output
     -- Generate output
     local release_date = h.date(tpl_args.patchdate)
     local release_date = h.date(args.patchdate)
     local tbl = mw_html.create('table')
     local tbl = mw.html.create('table')
     tbl
     tbl
         :addClass('wikitable successionbox')
         :addClass('wikitable successionbox')
Line 285: Line 265:
             :tag('td')
             :tag('td')
                 :cssText('width: 30%')
                 :cssText('width: 30%')
                 :wikitext(version_map.fields.before.show(tpl_args, frame))
                 :wikitext(tables.versions.fields.before.show(args))
                 :done()
                 :done()
             :tag('td')
             :tag('td')
                 :cssText('width: 40%')
                 :cssText('width: 40%')
                 :wikitext(string_format('<b>%s</b><br>%s', tpl_args.patch, release_date))
                 :wikitext(string.format('<b>%s</b><br>%s', args.patch, release_date))
                 :done()
                 :done()
             :tag('td')
             :tag('td')
                 :cssText('width: 30%')
                 :cssText('width: 30%')
                 :wikitext(version_map.fields.after.show(tpl_args, frame))
                 :wikitext(tables.versions.fields.after.show(args))


     local cats = {
     local cats = {
Line 302: Line 282:
end
end


-- ---------------------------------------------------------------------
local function _timeline(args)
-- Timeline
-- ---------------------------------------------------------------------
 
 
function p.timeline(frame)  
     --[[  
     --[[  
     Add a timeline when versions or items were added to the game.
     Add a timeline when versions or items were added to the game.
Line 328: Line 303:
         q_limit = 5000,
         q_limit = 5000,
     }
     }
      
     --]]
    ]]
   
   
    -- Get args:
    local tpl_args = getArgs(frame, {parentFirst = true})
    local frame = m_util.misc.get_frame(frame)


     -- Query results:
     -- Query results:
     local results = h.cargo_query(tpl_args)
     local results = h.cargo_query(args)
      
      
     -- Preallocate:
     -- Preallocate:
Line 417: Line 386:
end
end


-----
-- ----------------------------------------------------------------------------
-- Exported functions
-- ----------------------------------------------------------------------------
 
local p = {}
 
p.table_versions = m_cargo.declare_factory{data=tables.versions}
 
--
-- Template:Version
--
p.version = m_util.misc.invoker_factory(_version, {
    parentFirst = true,
})
 
--
-- Template:Timeline
--
p.timeline = m_util.misc.invoker_factory(_timeline, {
    parentFirst = true,
})


return p
return p

Revision as of 16:39, 6 April 2024

Module documentation[view] [edit] [history] [purge]


Templates

require('Module:No globals')
local m_util = require('Module:Util')
local m_cargo = require('Module:Cargo')

local f_item_link = require('Module:Item link').item_link
local cargo = mw.ext.cargo

-- Should we use the sandbox version of our submodules?
local use_sandbox = m_util.misc.maybe_sandbox('Version')

-- The cfg table contains all localisable strings and configuration, to make it
-- easier to port this module to another wiki.
local cfg = use_sandbox and mw.loadData('Module:Version/config/sandbox') or mw.loadData('Module:Version/config')

local i18n = cfg.i18n

-- ---------------------------------------------------------------------
-- Helper functions
-- ---------------------------------------------------------------------

local h = {}

function h.cargo_query(args)
    --[[
    Returns a Cargo query of all the results.
    
    args should include these keys:
    args.tables
    args.fields
    args.q_*
    
    ]]
    
    local tables = m_util.string.split(args.tables, ', ')
    local fields = m_util.string.split(args.fields, ', ')
    
    -- Parse query arguments
    local query = {
    }
    for key, value in pairs(args) do 
        if string.sub(key, 0, 2) == 'q_' then
            query[string.sub(key, 3)] = value
        end
    end
    
    -- Query cargo rows:
    local results = m_cargo.query(tables, fields, query)
    
    return results
end

function h.date(value, args)
    --[[
    Format dates in correct and useable form.
    
    Parameters
    ----------
    value : String, required
        Date
    args : Table
        Table with extra formatting args.
    
    ]]
    
    local args = args or {}
    
    -- List of allowed extra arguments:
    local arg_list = {
        format = {
            default = 'F j, Y H:i:s',
            cargo   = 'Y-m-d H:i:s',
            no_time = 'F j, Y',
        },
    }

    local lang = mw.getContentLanguage()
    local date_format = arg_list['format']['default']
    local timestamp = lang:formatDate(date_format, value)
    
    -- If the time is 00:00:00 then assume that the time isn't defined:
    if lang:formatDate('H:i:s', timestamp) == '00:00:00' then 
        date_format = arg_list['format']['no_time']
    end
    
    -- Add the extra arguments:
    for i,v in pairs(args) do
        if i == 'format' then
            date_format = arg_list[i][v]            
        end
    end
    
    -- Return the final timestamp format:
    local out
    if value ~= nil then
        out = lang:formatDate(date_format, timestamp)
    end
    
    return out
end

function h.validate_version(value)
    if value == nil then
        return value
    else
        return m_util.cast.version(value, {return_type='string'})
    end
end

function h.show_date(args)
    return function(targs)
        local version = targs[args.key]
        local date = targs[string.format('%s_date', args.key)]
        if version and date then
            date = h.date(date) or ''
            if args.key == 'before' then
                return string.format(i18n.show_date.before, version, version, date)
            elseif args.key == 'after' then
                return string.format(i18n.show_date.after, version, version, date)
            end
        else
            return ''
        end
    end
end

-- ----------------------------------------------------------------------------
-- Cargo tables
-- ----------------------------------------------------------------------------

local tables = {}

tables.versions ={
    table = 'versions',
    fields = {
        patch = {
            field = 'version',
            type = 'String',
            validate = h.validate_version,
        },
        patchdate = {
            field = 'release_date',
            type = 'Datetime',
            validate = tostring,
        },
        major_part = {
            field = 'major_part',
            type = 'Integer',
        },
        minor_part = {
            field = 'minor_part',
            type = 'Integer',
        },
        patch_part = {
            field = 'patch_part',
            type = 'Integer',
        },
        revision_part = {
            field = 'revision_part',
            type = 'String',
        },
        before = {
            field = 'previous',
            type = 'String',
            validate = h.validate_version,
            show = h.show_date{key='before'},
        },
        after = {
            field = 'after',
            type = 'String',
            validate = h.validate_version,
            show = h.show_date{key='after'},
        },
    },
}

-- ----------------------------------------------------------------------------
-- Main functions
-- ----------------------------------------------------------------------------

local function _version(args)
    --[[
    This function creates a infobox and stores the data in a cargo table.
    
    Examples:
    = p.version{
        before = '2.4.1',
        patch = '2.4.1b',
        patchdate = 'October 18, 2016',
        after = '2.4.2',
    }
    --]]

    for k, data in pairs(tables.versions.fields) do
        if data.validate ~= nil then
            args[k] = data.validate(args[k])
        end
    end
    
    -- Workaround for patchdate returning the string 'nil' when it's nil, 
    -- not sure if that's intentional:
    if not args.patch or not args.patchdate or args.patchdate == 'nil' then
        error(i18n.version.required_args)
    end
    
    local version_parts = m_util.cast.version(args.patch, {return_type='table'})
    args.major_part = tonumber(version_parts[1])
    args.minor_part = tonumber(version_parts[2])
    args.patch_part = tonumber(version_parts[3])
    if version_parts[4] then
        args.revision_part = version_parts[4]
    end

    -- Check and set 'before' and 'after' args
    local edge_names = {'before', 'after'}
    for _, key in ipairs(edge_names) do
        local v = args[key]
        if v then
            local results = cargo.query(
                'versions', 
                'versions.release_date', 
                {
                    where=string.format('version="%s"', v),
                    -- Cargo bug work around
                    groupBy='versions._pageID',
                }
            )
            if #results == 1 then
                args[string.format('%s_date', key)] = results[1]['versions.release_date']
            elseif #results > 1 then
                error(i18n.version.multiple_versions)
            end
        end
    end

    
    -- Set Cargo data
    local _properties = {
        _table = tables.versions.table,
    }
    for key, data in pairs(tables.versions.fields) do
        if args[key] ~= nil then
            _properties[data.field] = args[key]
       end
    end
    
    m_cargo.store(_properties)

    mw.getCurrentFrame():expandTemplate{
        title = 'Template:Version/cargo/versions/attach',
        args = {}
    }

    -- Generate output
    local release_date = h.date(args.patchdate)
    local tbl = mw.html.create('table')
    tbl
        :addClass('wikitable successionbox')
        :tag('tr')
            :tag('th')
                :attr('colspan', 3)
                :wikitext(i18n.version.header)
                :done()
            :done()
        :tag('tr')
            :tag('td')
                :cssText('width: 30%')
                :wikitext(tables.versions.fields.before.show(args))
                :done()
            :tag('td')
                :cssText('width: 40%')
                :wikitext(string.format('<b>%s</b><br>%s', args.patch, release_date))
                :done()
            :tag('td')
                :cssText('width: 30%')
                :wikitext(tables.versions.fields.after.show(args))

    local cats = {
        i18n.categories.versions,
    }

    return tostring(tbl) .. m_util.misc.add_category(cats)
end

local function _timeline(args)
    --[[ 
    Add a timeline when versions or items were added to the game.
    
    Examples:
    = p.timeline{
        tables = 'versions',
        fields = 'versions.version, versions.release_date',
        q_where = 'versions.version <> ""',
        q_orderBy = 'versions.version DESC, versions.release_date ASC'
    }
    
    = p.timeline{
        tables = 'versions, items',        
        fields = 'versions.version, versions.release_date, versions._pageName, items.class, items._pageName, items.name, items.release_version, items.inventory_icon, items.html',
        q_join = 'versions.version=items.release_version',
        q_where = 'versions.version IS NOT NULL AND items.release_version IS NOT NULL AND items.rarity = "Unique"',
        q_orderBy = 'versions.version DESC, versions.release_date ASC, items.name ASC',
        q_groupBy = 'versions._pageID, items.name',
        q_limit = 5000,
    }
    --]]

    -- Query results:
    local results = h.cargo_query(args)
    
    -- Preallocate:
    local out = {}
    local last_main_version 
    local last_minor_version
    local current_version
    local result_list
    
    -- Loop through all the results from the query:
    for i, result in ipairs(results) do 
        local release_version = result['versions.version']
        
        local v = m_util.cast.version(release_version)
        local version_h2 = table.concat({v[1], v[2]}, '.')
        
        if release_version ~= last_minor_version then
            
            if version_h2 ~= last_main_version then 
                if current_version ~= nil then
                    out[#out + 1] = tostring(current_version)
                end
                
                out[#out+1] = string.format(
                    '===%s %s===', 
                    i18n.timeline.version, 
                    table.concat({v[1], v[2], 0}, '.')
                ) 
                current_version = mw.html.create('ul')
            end
        
            current_version
                :tag('li')
                    :wikitext(string.format(
                        '%s - [[%s %s]]', 
                        h.date(result['versions.release_date']), 
                        i18n.timeline.version, 
                        release_version, 
                        result['versions.version'])
                    )
            result_list = current_version:tag('ol')
        end 
        
              
        -- If the result has an item class, then add another list with 
        -- the results.
        if result['items.class'] ~= nil then 
            -- result_list:tag('li'):wikitext(string.format('%s', 
                -- f_item_link{
                    -- page=result['items._pageName'], 
                    -- name=result['items.name'],
                    -- inventory_icon=result['items.inventory_icon'] or '', 
                    -- html=result['items.html'] or '', 
                    -- skip_query=true
                    -- }
                -- )
            -- )
            result_list:tag('li'):wikitext(string.format('[[%s]]', 
                result['items._pageName']
                )
            )
        end
        
        -- Save the last list:
        if (i == #results) and (current_version ~= nil) then 
            out[#out + 1] = tostring(current_version)
        end
        
        last_main_version = version_h2
        last_minor_version = release_version
    end
    
    -- Add categories:
    local cats = {
        i18n.categories.timelines,
    }
    
    return table.concat(out, '\n') .. m_util.misc.add_category(cats)
end

-- ----------------------------------------------------------------------------
-- Exported functions
-- ----------------------------------------------------------------------------

local p = {}

p.table_versions = m_cargo.declare_factory{data=tables.versions}

--
-- Template:Version
--
p.version = m_util.misc.invoker_factory(_version, {
    parentFirst = true,
})

--
-- Template:Timeline
--
p.timeline = m_util.misc.invoker_factory(_timeline, {
    parentFirst = true,
})

return p