Path of Exile Wiki

Please consider helping keep the wiki up to date. Check the to-do list of updates needed for version 3.14.0.

Game data exports will becoming later as the technical changes in addition to regular changes take some more time.

READ MORE

Path of Exile Wiki
mNo edit summary
(Module:Cargo contains the most up-to-date cargo functions. Also DRY principle...)
 
(19 intermediate revisions by 4 users not shown)
Line 1: Line 1:
 
local getArgs = require('Module:Arguments').getArgs
 
local getArgs = require('Module:Arguments').getArgs
local util = require('Module:Util')
+
local m_util = require('Module:Util')
  +
local m_cargo = require('Module:Cargo')
 
  +
local f_item_link = require('Module:Item link').item_link
local error = error
 
local tostring = tostring
+
local cargo = mw.ext.cargo
local pairs = pairs
 
   
 
local string_format = string.format
 
local string_format = string.format
Line 10: Line 9:
   
 
local mw_html = mw.html
 
local mw_html = mw.html
  +
  +
local mw_language = mw.getLanguage('en')
   
 
local p = {}
 
local p = {}
   
  +
local i18n = {
-----
 
  +
categories = {
 
  +
versions = 'Versions',
------------------------------------------------------------------------------------------------------
 
  +
timelines = 'Timelines',
-- Template: Version
 
 
local version_map = {
 
version = {
 
datatype = 'String',
 
property = 'Is version',
 
 
},
 
},
  +
release_date = {
 
datatype = 'String',
+
show_date = {
property = 'Has release date',
+
before = ' [[Version %s|%s]]<br>%s',
  +
after = '[[Version %s|%s]] →<br>%s',
 
},
 
},
major_part = {
+
version = {
  +
required_args = 'Arguments "patch" and "patchdate" are required',
datatype = 'Integer',
 
property = 'Has major version part',
+
multiple_versions = 'There are multiple versions with the same name',
  +
header = '[[Version history|Version History]]',
 
},
 
},
minor_part = {
+
timeline = {
datatype = 'Integer',
+
version = 'Version',
property = 'Has minor version part',
 
},
 
patch_part = {
 
datatype = 'Integer',
 
property = 'Has patch version part',
 
},
 
revision_part = {
 
datatype = 'String',
 
property = 'Has revision version part',
 
},
 
before = {
 
datatype = 'String',
 
property = 'Has version before',
 
show = function(this)
 
local version = this.value
 
local date = this.release_date
 
if version and date then
 
return string_format('← [[Version %s|%s]]<br>%s', version, version, date)
 
else
 
return ''
 
end
 
end,
 
},
 
after = {
 
datatype = 'String',
 
property = 'Has version after',
 
show = function(this)
 
local version = this.value
 
local date = this.release_date
 
if version and date then
 
return string_format('[[Version %s|%s]] →<br>%s', version, version, date)
 
else
 
return ''
 
end
 
end,
 
 
},
 
},
 
}
 
}
   
  +
-- ---------------------------------------------------------------------
local temp_map_for_cargo = {'version', 'release_date' }
 
  +
-- Helper functions
  +
-- ---------------------------------------------------------------------
   
  +
h = {}
   
  +
function h.cargo_query(tpl_args)
p.version = function(frame)
 
  +
--[[
local args = getArgs(frame, {parentFirst = true})
 
  +
Returns a Cargo query of all the results.
local frame = util.misc.get_frame(frame)
 
  +
  +
tpl_args should include these keys:
  +
tpl_args.tables
  +
tpl_args.fields
  +
tpl_args.q_*
  +
  +
]]
  +
  +
local tables = m_util.string.split(tpl_args.tables, ', ')
  +
local fields = m_util.string.split(tpl_args.fields, ', ')
  +
  +
-- Parse query arguments
  +
local query = {
  +
}
  +
for key, value in pairs(tpl_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, args)
  +
  +
return results
  +
end
   
  +
function h.date(value, args)
 
--[[
 
--[[
  +
Format dates in correct and useable form.
= p.version({
 
  +
before = '2.4.1a',
 
  +
Parameters
patch = '2.4.1b',
 
  +
----------
patchdate = 'October 18, 2016',
 
  +
value : String, required
after = '2.4.2',
 
})
+
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 date_format = arg_list['format']['default']
  +
local timestamp = mw_language:formatDate(date_format, value)
  +
  +
-- 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
  +
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 = mw_language:formatDate(date_format, timestamp)
  +
end
  +
  +
return out
  +
end
   
   
  +
-- ---------------------------------------------------------------------
if not args.patch or not args.patchdate then
 
  +
-- Template: Version
error('Arguments "patch" and "patchdate" are required')
 
  +
-- ---------------------------------------------------------------------
  +
  +
  +
function validate_version(value)
  +
if value == nil then
  +
return value
  +
else
  +
return m_util.cast.version(value, {return_type='string'})
 
end
 
end
  +
end
   
  +
function show_date(args)
local version = util.cast.version(args.patch)
 
  +
return function(tpl_args, frame)
  +
local version = tpl_args[args.key]
  +
local date = tpl_args[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
   
local version_struct = util.Struct(version_map)
 
   
  +
local version_map = {
version_struct:set('version', args.patch)
 
  +
table = 'versions',
version_struct:set('release_date', args.patchdate)
 
  +
fields = {
  +
patch = {
  +
field = 'version',
  +
type = 'String',
  +
validate = 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 = validate_version,
  +
show = show_date{key='before'},
  +
},
  +
after = {
  +
field = 'after',
  +
type = 'String',
  +
validate = validate_version,
  +
show = show_date{key='after'},
  +
},
  +
},
  +
}
   
  +
p.table_versions = m_cargo.declare_factory{data=version_map}
local part_names = {'major_part', 'minor_part', 'patch_part', 'revision_part'}
 
for i = 1, #part_names do
 
local part = version[i]
 
   
  +
p.version = function(frame)
if part then
 
  +
--[[
version_struct:set(part_names[i], part)
 
  +
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',
  +
}
  +
--]]
  +
  +
local tpl_args = getArgs(frame, {parentFirst = true})
  +
local frame = m_util.misc.get_frame(frame)
  +
  +
for k, data in pairs(version_map.fields) do
  +
if data.validate ~= nil then
  +
tpl_args[k] = data.validate(tpl_args[k])
 
end
 
end
  +
end
  +
  +
-- Workaround for patchdate returning the string 'nil' when it's nil,
  +
-- not sure if that's intentional:
  +
if not tpl_args.patch or not tpl_args.patchdate or tpl_args.patchdate == 'nil' then
  +
error(i18n.version.required_args)
  +
end
  +
  +
local version_parts = m_util.cast.version(tpl_args.patch, {return_type='table'})
  +
tpl_args.major_part = tonumber(version_parts[1])
  +
tpl_args.minor_part = tonumber(version_parts[2])
  +
tpl_args.patch_part = tonumber(version_parts[3])
  +
if version_parts[4] then
  +
tpl_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 i = 1, #edge_names do
+
for _, key in ipairs(edge_names) do
local v = args[edge_names[i]]
+
local v = tpl_args[key]
 
if v then
 
if v then
local edge_version = util.cast.version(v, {return_type = 'string'})
+
local results = cargo.query(
  +
'versions',
 
local query = {
+
'versions.release_date',
  +
{
string_format('[[Is version::%s]] [[Has release date::+]]', edge_version),
 
'?Has release date',
+
where=string.format('version="%s"', v),
}
+
-- Cargo bug work around
local results = util.smw.query(query, frame)
+
groupBy='versions._pageID',
  +
}
  +
)
 
if #results == 1 then
 
if #results == 1 then
  +
tpl_args[string.format('%s_date', key)] = results[1]['versions.release_date']
version_struct:set(edge_names[i], edge_version)
 
version_struct:set_prop(edge_names[i], 'release_date', results[1]['Has release date'])
 
 
elseif #results > 1 then
 
elseif #results > 1 then
error('There are versions with the same name')
+
error(i18n.version.multiple_versions)
 
end
 
end
 
end
 
end
 
end
 
end
   
  +
 
-- Set SMW and Cargo data
+
-- Set Cargo data
local props_smw = {}
+
local _properties = {
  +
_table = version_map.table,
local props_cargo = {
 
_table = 'Versions',
 
 
}
 
}
for i, _ in pairs(version_struct.map) do
+
for key, data in pairs(version_map.fields) do
local value = version_struct:get(i)
+
if tpl_args[key] ~= nil then
  +
_properties[data.field] = tpl_args[key]
if value then
 
  +
end
props_smw[version_struct:get_prop(i, 'property')] = value
 
end
 
--props_cargo[i] = value or ''
 
 
end
 
end
  +
 
util.smw.set(frame, props_smw)
+
m_cargo.store(frame, _properties)
 
 
for i = 1, #temp_map_for_cargo do
 
local v = temp_map_for_cargo[i]
 
props_cargo[v] = version_struct:get(v) or ''
 
end
 
 
-- mw.logObject(props_cargo)
 
util.cargo.store(frame, props_cargo)
 
 
   
 
-- Generate output
 
-- Generate output
  +
local release_date = h.date(tpl_args.patchdate)
-- todo: rework it somehow
 
local release_date = frame:callParserFunction('#show: Version ' .. version_struct:get('version'), {'?Has release date'})
 
 
 
local tbl = mw_html.create('table')
 
local tbl = mw_html.create('table')
 
tbl
 
tbl
Line 166: Line 274:
 
:tag('th')
 
:tag('th')
 
:attr('colspan', 3)
 
:attr('colspan', 3)
:wikitext('[[Version history|Version History]]')
+
:wikitext(i18n.version.header)
 
:done()
 
:done()
 
:done()
 
:done()
Line 172: Line 280:
 
:tag('td')
 
:tag('td')
 
:cssText('width: 30%')
 
:cssText('width: 30%')
:wikitext(version_struct:show('before'))
+
:wikitext(version_map.fields.before.show(tpl_args, frame))
 
:done()
 
:done()
 
:tag('td')
 
:tag('td')
 
:cssText('width: 40%')
 
:cssText('width: 40%')
:wikitext(string_format('[[Version %s]]<br>%s', version_struct:get('version'), release_date))
+
:wikitext(string_format('<b>%s</b><br>%s', tpl_args.patch, release_date))
 
:done()
 
:done()
 
:tag('td')
 
:tag('td')
 
:cssText('width: 30%')
 
:cssText('width: 30%')
:wikitext(version_struct:show('after'))
+
:wikitext(version_map.fields.after.show(tpl_args, frame))
   
 
local cats = {
 
local cats = {
'Versions',
+
i18n.categories.versions,
 
}
 
}
   
return tostring(tbl) .. util.misc.add_category(cats)
+
return tostring(tbl) .. m_util.misc.add_category(cats)
 
end
 
end
   
Line 193: Line 301:
 
p.version_declare = function(frame)
 
p.version_declare = function(frame)
 
-- local args = getArgs(frame, {parentFirst = true})
 
-- local args = getArgs(frame, {parentFirst = true})
local frame = util.misc.get_frame(frame)
+
local frame = m_util.misc.get_frame(frame)
   
 
local props = {
 
local props = {
Line 211: Line 319:
 
--mw.logObject(props)
 
--mw.logObject(props)
   
return util.cargo.declare(frame, props)
+
return m_cargo.declare(frame, props)
 
end
 
end
   
-----
 
   
------------------------------------------------------------------------------------------------------
+
-- ---------------------------------------------------------------------
  +
-- Timeline
-- Template: Version history list
 
  +
-- ---------------------------------------------------------------------
   
p.version_history_list = function(frame)
 
local args = getArgs(frame, {parentFirst = true})
 
local frame = util.misc.get_frame(frame)
 
   
  +
function p.timeline(frame)
-- = p.version_history_list({conditions='[[Is version::~1*||~2*]]'})
 
  +
--[[
-- = p.version_history_list({conditions='[[Is version::~0.9*]]'})
 
  +
Add a timeline when versions or items were added to the game.
-- = p.version_history_list({conditions='[[Is version::~0.5*]]'})
 
  +
 
  +
Examples:
if args.conditions then
 
  +
= p.timeline{
args.conditions = args.conditions .. '[[Has release date::+]]'
 
  +
tables = 'versions',
else
 
args.conditions = '[[Is version::+]][[Has release date::+]]'
+
fields = 'versions.version, versions.release_date',
  +
q_where = 'versions.version <> ""',
end
 
  +
q_orderBy = 'versions.version DESC, versions.release_date ASC'
 
local query = {
 
args.conditions,
 
'?Is version',
 
'?Has release date',
 
sort = 'Has release date, Is version',
 
order = 'desc, desc',
 
link = 'none',
 
offset = 0,
 
 
}
 
}
  +
  +
= 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,
  +
}
  +
  +
]]
  +
  +
  +
-- Get args:
  +
local tpl_args = getArgs(frame, {parentFirst = true})
  +
local frame = m_util.misc.get_frame(frame)
   
local results = {}
+
-- Query results:
  +
local results = h.cargo_query(tpl_args)
repeat
 
  +
local result = util.smw.query(query, frame)
 
  +
-- Preallocate:
local length = #result
 
query.offset = query.offset + length
 
 
for i = 1, length do
 
results[#results + 1] = result[i]
 
end
 
until length < 1000
 
 
 
local out = {}
 
local out = {}
local last_minor_version, current_list
+
local last_main_version
  +
local last_minor_version
 
  +
local current_version
for i = 1, #results do
 
local result = results[i]
+
local result_list
  +
local date = result['Has release date']
 
  +
-- Loop through all the results from the query:
local version = result['Is version']
 
  +
for i, result in ipairs(results) do
 
local v = util.cast.version(result['Is version'])
+
release_version = result['versions.version']
  +
local minor_version = table_concat({v[1], v[2], v[3]}, '.') -- todo: rework it
 
  +
local v = m_util.cast.version(release_version)
 
if minor_version ~= last_minor_version then
+
local version_h2 = table.concat({v[1], v[2]}, '.')
if current_list ~= nil then
+
  +
if release_version ~= last_minor_version then
out[#out + 1] = tostring(current_list)
 
  +
  +
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
 
end
  +
 
  +
current_version
out[#out + 1] = string_format('===Version %s===', minor_version)
 
current_list = mw_html.create('ul')
+
: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
 
end
  +
 
  +
-- Save the last list:
current_list:tag('li'):wikitext(string_format('%s &ndash; [[Version %s]]', date, version))
 
  +
if (i == #results) and (current_version ~= nil) then
 
  +
out[#out + 1] = tostring(current_version)
-- save the last list
 
if i == #results and current_list ~= nil then
 
out[#out + 1] = tostring(current_list)
 
 
end
 
end
  +
 
last_minor_version = minor_version
+
last_main_version = version_h2
  +
last_minor_version = release_version
 
end
 
end
  +
 
  +
-- Add categories:
return table_concat(out, '\n')
 
  +
local cats = {
end
 
  +
i18n.categories.timelines,
 
-----
 
 
p.version_history_list_2 = function(frame)
 
local args = getArgs(frame, {parentFirst = true})
 
local frame = util.misc.get_frame(frame)
 
 
-- = p.version_history_list({conditions='[[Is version::~1*||~2*]]'})
 
-- = p.version_history_list({conditions='[[Is version::~0.9*]]'})
 
-- = p.version_history_list({conditions='[[Is version::~0.5*]]'})
 
 
if args.conditions then
 
args.conditions = args.conditions .. '[[Has release date::+]]'
 
else
 
args.conditions = '[[Is version::+]] [[Has release date::+]]'
 
end
 
 
local query = {
 
args.conditions,
 
'?Is version',
 
'?Has release date',
 
'?Has major version part',
 
'?Has minor version part',
 
'?Has patch version part',
 
-- '?Has revision version part',
 
sort = 'Has major version part, Has minor version part, Has patch version part, Is version',
 
order = 'desc, desc, desc, desc',
 
link = 'none',
 
offset = 0,
 
 
}
 
}
  +
 
  +
return table.concat(out, '\n') .. m_util.misc.add_category(cats)
local results = {}
 
repeat
 
local result = util.smw.query(query, frame)
 
local length = #result
 
query.offset = query.offset + length
 
 
for i = 1, length do
 
results[#results + 1] = result[i]
 
end
 
until length < 1000
 
 
-- mw.logObject(results)
 
 
local out = {}
 
local last_minor_version, current_list
 
 
for i = 1, #results do
 
local result = results[i]
 
local date = result['Has release date']
 
local version = result['Is version']
 
 
local patch_version = string_format('%s.%s.%s',
 
result['Has major version part'], result['Has minor version part'], result['Has patch version part'])
 
 
if patch_version ~= last_minor_version then
 
if current_list ~= nil then
 
out[#out + 1] = tostring(current_list)
 
end
 
 
out[#out + 1] = string_format('===Version %s===', patch_version)
 
current_list = mw_html.create('ul')
 
end
 
 
current_list:tag('li'):wikitext(string_format('%s &ndash; [[Version %s]]', date, version))
 
 
-- save the last list
 
if i == #results and current_list ~= nil then
 
out[#out + 1] = tostring(current_list)
 
end
 
 
last_minor_version = patch_version
 
end
 
 
return table_concat(out, '\n')
 
 
end
 
end
   

Latest revision as of 01:34, 2 May 2021

Template info icon Module documentation[view] [edit] [history] [purge]

Templates[]

local getArgs = require('Module:Arguments').getArgs
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

local string_format = string.format
local table_concat = table.concat

local mw_html = mw.html

local mw_language = mw.getLanguage('en')

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',
    },
}

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

h = {}

function h.cargo_query(tpl_args)
    --[[
    Returns a Cargo query of all the results.
    
    tpl_args should include these keys:
    tpl_args.tables
    tpl_args.fields
    tpl_args.q_*
    
    ]]
    
    local tables = m_util.string.split(tpl_args.tables, ', ')
    local fields = m_util.string.split(tpl_args.fields, ', ')
    
    -- Parse query arguments
    local query = {
    }
    for key, value in pairs(tpl_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, args)
    
    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 date_format = arg_list['format']['default']
    local timestamp = mw_language:formatDate(date_format, value)
    
    -- 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 
        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 = mw_language:formatDate(date_format, timestamp)
    end
    
    return out
end


-- ---------------------------------------------------------------------
-- Template: Version
-- ---------------------------------------------------------------------


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

function show_date(args)
    return function(tpl_args, frame)
        local version = tpl_args[args.key]
        local date = tpl_args[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


local version_map = {
    table = 'versions',
    fields = {
        patch = {
            field = 'version',
            type = 'String',
            validate = 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 = validate_version,
            show = show_date{key='before'},
        },
        after = {
            field = 'after',
            type = 'String',
            validate = validate_version,
            show = show_date{key='after'},
        },
    },
}

p.table_versions = m_cargo.declare_factory{data=version_map}

p.version = function(frame)
    --[[
    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',
    }
    --]]

    local tpl_args = getArgs(frame, {parentFirst = true})
    local frame = m_util.misc.get_frame(frame)

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

    -- Check and set 'before' and 'after' tpl_args
    local edge_names = {'before', 'after'}
    for _, key in ipairs(edge_names) do
        local v = tpl_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
                tpl_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 = version_map.table,
    }
    for key, data in pairs(version_map.fields) do
        if tpl_args[key] ~= nil then
            _properties[data.field] = tpl_args[key]
       end
    end
    
    m_cargo.store(frame, _properties)

    -- Generate output
    local release_date = h.date(tpl_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(version_map.fields.before.show(tpl_args, frame))
                :done()
            :tag('td')
                :cssText('width: 40%')
                :wikitext(string_format('<b>%s</b><br>%s', tpl_args.patch, release_date))
                :done()
            :tag('td')
                :cssText('width: 30%')
                :wikitext(version_map.fields.after.show(tpl_args, frame))

    local cats = {
        i18n.categories.versions,
    }

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

-----

p.version_declare = function(frame)
    -- local args = getArgs(frame, {parentFirst = true})
    local frame = m_util.misc.get_frame(frame)

    local props = {
        _table = 'Versions',
    }

--    for i, _ in pairs(version_map) do
--        props[i] = _.datatype
--    end

    for i = 1, #temp_map_for_cargo do
        local v = temp_map_for_cargo[i]
        local _ = version_map[v]
        props[v] = _.datatype
    end

    --mw.logObject(props)

    return m_cargo.declare(frame, props)
end


-- ---------------------------------------------------------------------
-- Timeline 
-- ---------------------------------------------------------------------


function p.timeline(frame) 
    --[[ 
    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,
    }
    
    ]]
    
    
    -- Get args:
    local tpl_args = getArgs(frame, {parentFirst = true})
    local frame = m_util.misc.get_frame(frame)

    -- Query results:
    local results = h.cargo_query(tpl_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 
        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

-----

return p