Разница между страницами «Модуль:Cargo» и «Модуль:Cargo/sandbox»

(Различия между страницами)
Перейти к: навигация, поиск
Первая страница
Вторая страница
(Translation strings moved to Module:Cargo/config for easier porting.)
 
(Новая страница: «-- ---------------------------------------------------------------------------- -- Utility and helper functions for the cargo extension -- -----------------------…»)
 
Строка 1: Строка 1:
-------------------------------------------------------------------------------
+
-- ----------------------------------------------------------------------------
  +
-- Utility and helper functions for the cargo extension
--
 
  +
-- ----------------------------------------------------------------------------
-- Module:Cargo
 
  +
-- * Common cargo tasks should be generalized into functions for this module
--
 
  +
-- * This module should remain atomic so it can be copy-pasted more easily to
-- Common tasks for the cargo extension are generalized into handy functions
 
-- in this meta module
+
-- other wikis
-------------------------------------------------------------------------------
 
 
local getArgs = require('Module:Arguments').getArgs
 
local m_util = require('Module:Util')
 
   
 
local cargo = mw.ext.cargo
 
local cargo = mw.ext.cargo
   
  +
-- ----------------------------------------------------------------------------
-- The cfg table contains all localisable strings and configuration, to make it
 
  +
-- Strings
-- easier to port this module to another wiki.
 
  +
-- ----------------------------------------------------------------------------
local cfg = mw.loadData('Module:Cargo/config')
 
   
local i18n = cfg.i18n
+
local i18n = {
  +
bool_false = {'false', '0', 'disabled', 'off', 'no', '', 'deactivated'},
  +
  +
errors = {
  +
-- util.cast.boolean
  +
not_a_boolean = 'value "%s" of type "%s" is not a boolean',
  +
  +
-- util.args.from_cargo_map
  +
missing_key_in_fields = 'Key "%s" not found in the fields mapping of table "%s"',
  +
table_object_as_default = 'Warning: table object as default value on key "%s" in mapping of table "%s"',
  +
missing_key_in_order = 'Fields mapping of table "%s" has the following extra keys that are not handled by order:\n%s',
  +
handler_returned_nil = 'Handler for "%s.fields.%s" returned nil for argument "%s". Check whether the value is correct for the given field type "%s".',
  +
argument_required = 'Argument "%s" is required',
  +
  +
-- util.cargo.array_query
  +
duplicate_ids = 'Found duplicates for field "%s":\n %s',
  +
missing_ids = 'Missing results for "%s" field with values: \n%s',
  +
  +
-- cargo.table_query
  +
no_results = 'No results found for the given query.',
  +
no_join = 'No table join set in data.tables[%s].join',
  +
missing_unique_field_in_result_row = 'Unique identifier field "%s" was not found in result set - field is either missing or empty. Current row data: <br>%s',
  +
},
  +
}
  +
  +
-- ----------------------------------------------------------------------------
  +
-- Constants
  +
-- ----------------------------------------------------------------------------
  +
  +
local c = {}
  +
c.limit = 5000
  +
  +
-- ----------------------------------------------------------------------------
  +
-- Internal helper functions
  +
-- ----------------------------------------------------------------------------
  +
  +
local util = {}
  +
function util.is_frame(frame)
  +
return not(frame == nil or type(frame) ~= 'table' or (frame.argumentPairs == nil and frame.callParserFunction == nil))
  +
end
  +
  +
function util.get_frame(frame)
  +
if util.is_frame(frame) then
  +
return frame
  +
end
  +
return mw.getCurrentFrame()
  +
end
  +
  +
function util.strip(str, pattern)
  +
pattern = pattern or '%s'
  +
return string.gsub(str, "^" .. pattern .. "*(.-)" .. pattern .. "*$", "%1")
  +
end
  +
  +
function util.split(str, pattern)
  +
-- Splits string into a table
  +
--
  +
-- str: string to split
  +
-- pattern: pattern to use for splitting
  +
local out = {}
  +
local i = 1
  +
local split_start, split_end = string.find(str, pattern, i)
  +
while split_start do
  +
out[#out+1] = string.sub(str, i, split_start-1)
  +
i = split_end+1
  +
split_start, split_end = string.find(str, pattern, i)
  +
end
  +
out[#out+1] = string.sub(str, i)
  +
return out
  +
end
  +
  +
function util.to_boolean(value, args)
  +
-- Takes an abitary value and casts it to a bool value
  +
--
  +
-- for strings false will be according to i18n.bool_false
  +
--
  +
-- args:
  +
-- cast_nil - if set to false, it will not cast nil values
  +
args = args or {}
  +
local t = type(value)
  +
if t == 'nil' then
  +
if args.cast_nil == nil or args.cast_nil == true then
  +
return false
  +
else
  +
return
  +
end
  +
elseif t == 'boolean' then
  +
return value
  +
elseif t == 'number' then
  +
if value == 0 then return false end
  +
return true
  +
elseif t == 'string' then
  +
local tmp = string.lower(value)
  +
for _, v in ipairs(i18n.bool_false) do
  +
if v == tmp then
  +
return false
  +
end
  +
end
  +
return true
  +
else
  +
error(string.format(i18n.errors.not_a_boolean, tostring(value), t))
  +
end
  +
  +
end
   
 
-- ----------------------------------------------------------------------------
 
-- ----------------------------------------------------------------------------
Строка 24: Строка 122:
 
local m_cargo = {}
 
local m_cargo = {}
   
--
+
--
 
-- Cargo function wrappers
 
-- Cargo function wrappers
--
+
--
   
 
function m_cargo.declare(frame, args)
 
function m_cargo.declare(frame, args)
Строка 39: Строка 137:
 
-- Calls the cargo_store parser function and ensures the values passed are casted properly
 
-- Calls the cargo_store parser function and ensures the values passed are casted properly
 
--
 
--
-- Value handling:
+
-- Value handling:
 
-- tables - automatically concat
 
-- tables - automatically concat
 
-- booleans - automatically casted to 1 or 0 to ensure they're stored properly
 
-- booleans - automatically casted to 1 or 0 to ensure they're stored properly
Строка 46: Строка 144:
 
-- frame - frame object
 
-- frame - frame object
 
-- values - table of field/value pairs to store
 
-- values - table of field/value pairs to store
-- args - any additional arguments
+
-- args - any additional arguments
 
-- sep - separator to use for concat
 
-- sep - separator to use for concat
 
-- store_empty - if specified, allow storing empty rows
 
-- store_empty - if specified, allow storing empty rows
-- debug - send the converted values to the lua debug log
+
-- debug - send the converted values to the lua debug log
 
args = args or {}
 
args = args or {}
 
args.sep = args.sep or {}
 
args.sep = args.sep or {}
Строка 79: Строка 177:
 
end
 
end
 
end
 
end
  +
  +
-- results = m_cargo.query({'skill_stats_per_level '}, {'skill_stats_per_level.id'}, {where='skill_stats_per_level.id IS NOT NULL'})
   
 
function m_cargo.query(tables, fields, query, args)
 
function m_cargo.query(tables, fields, query, args)
Строка 92: Строка 192:
 
-- args
 
-- args
 
-- args.keep_empty
 
-- args.keep_empty
  +
 
 
-- Cargo bug workaround
 
-- Cargo bug workaround
 
args = args or {}
 
args = args or {}
Строка 101: Строка 201:
 
end
 
end
 
end
 
end
  +
 
query.limit = query.limit or cfg.limit*100
+
query.limit = query.limit or c.limit*100
 
query.offset = query.offset or 0
 
query.offset = query.offset or 0
local results = {}
+
local results = {}
 
repeat
 
repeat
 
local result = cargo.query(table.concat(tables, ','), table.concat(fields, ','), query)
 
local result = cargo.query(table.concat(tables, ','), table.concat(fields, ','), query)
Строка 112: Строка 212:
 
results[#results + 1] = v
 
results[#results + 1] = v
 
end
 
end
until (#result < cfg.limit) or (#results >= query.limit)
+
until (#result < c.limit) or (#results >= query.limit)
  +
 
 
if args.keep_empty == nil then
 
if args.keep_empty == nil then
 
for _, row in ipairs(results) do
 
for _, row in ipairs(results) do
Строка 126: Строка 226:
 
end
 
end
   
--
+
--
 
-- Extended cargo functions
 
-- Extended cargo functions
--
+
--
 
function m_cargo.store_from_lua(args)
 
-- Factory for function that stores data from lua data into a cargo table from a template call
 
--
 
-- Arguments:
 
-- module: Name of the module where the data is located, without the module prefix
 
-- tables: Mapping of the table data
 
--
 
-- Return:
 
-- function that takes frame argument
 
--
 
--
 
-- The function created takes the following arguments:
 
-- REQURIED:
 
-- tbl: table to store
 
-- src: source wiki path after the module if it differs from the table name
 
-- index_start: Starting index (default: 1)
 
-- index_end: Ending index (default: data length, i.e. all data)
 
args = args or {}
 
if args.module == nil or args.tables == nil then
 
error(i18n.errors.store_from_lua_missing_arguments)
 
end
 
 
return function (frame)
 
-- Get args
 
tpl_args = getArgs(frame, {
 
parentFirst = true
 
})
 
frame = m_util.misc.get_frame(frame)
 
 
if args.tables[tpl_args.tbl] == nil then
 
error(string.format(i18n.errors.store_from_lua_invalid_table, tostring(tpl_args.tbl)))
 
end
 
 
-- mw.loadData has some problems...
 
local data = require(string.format('%s:%s/%s', i18n.module, args.module, tpl_args.src or tpl_args.tbl))
 
 
tpl_args.index_start = math.max(tonumber(tpl_args.index_start) or 1, 1)
 
tpl_args.index_end = math.min(tonumber(tpl_args.index_end) or #data, #data)
 
 
for i=tpl_args.index_start, tpl_args.index_end do
 
local row = data[i]
 
if row == nil then
 
break
 
end
 
-- get full table name
 
row._table = args.tables[tpl_args.tbl].table
 
m_cargo.store(frame, row)
 
end
 
 
return string.format(i18n.tooltips.store_rows, tpl_args.index_start, tpl_args.index_end, tpl_args.index_end-tpl_args.index_start+1, tpl_args.tbl)
 
end
 
end
 
 
 
--[[test = {
 
--[[test = {
 
tables = {
 
tables = {
Строка 270: Строка 316:
 
-- empty_cell
 
-- empty_cell
 
-- table_css
 
-- table_css
  +
 
 
-- TPL_ARGS:
 
-- TPL_ARGS:
 
-- q_***
 
-- q_***
Строка 278: Строка 324:
 
-- *** - as defined in data
 
-- *** - as defined in data
 
local tpl_args = args.tpl_args
 
local tpl_args = args.tpl_args
local frame = m_util.misc.get_frame(args.frame)
+
local frame = util.get_frame(args.frame)
 
args.data.tables = args.data.tables or {}
 
args.data.tables = args.data.tables or {}
 
args.row_unique_fields = args.row_unique_fields or {string.format('%s._pageID', args.main_table)}
 
args.row_unique_fields = args.row_unique_fields or {string.format('%s._pageID', args.main_table)}
 
args.empty_cell = args.empty_cell or '<td></td>'
 
args.empty_cell = args.empty_cell or '<td></td>'
  +
 
 
local row_infos = {}
 
local row_infos = {}
 
for _, row_info in ipairs(args.data) do
 
for _, row_info in ipairs(args.data) do
Строка 288: Строка 334:
 
if row_info.args == nil then
 
if row_info.args == nil then
 
enabled = true
 
enabled = true
elseif type(row_info.args) == 'string' and m_util.cast.boolean(tpl_args[row_info.args]) then
+
elseif type(row_info.args) == 'string' and util.to_boolean(tpl_args[row_info.args]) then
 
enabled = true
 
enabled = true
elseif type(row_info.args) == 'table' then
+
elseif type(row_info.args) == 'table' then
 
for _, argument in ipairs(row_info.args) do
 
for _, argument in ipairs(row_info.args) do
if m_util.cast.boolean(tpl_args[argument]) then
+
if util.to_boolean(tpl_args[argument]) then
 
enabled = true
 
enabled = true
 
break
 
break
Строка 298: Строка 344:
 
end
 
end
 
end
 
end
  +
 
 
if enabled then
 
if enabled then
 
row_info.options = row_info.options or {}
 
row_info.options = row_info.options or {}
Строка 304: Строка 350:
 
end
 
end
 
end
 
end
  +
 
 
-- sort the rows
 
-- sort the rows
 
table.sort(row_infos, function (a, b)
 
table.sort(row_infos, function (a, b)
 
return (a.order or 0) < (b.order or 0)
 
return (a.order or 0) < (b.order or 0)
 
end)
 
end)
  +
 
 
-- Set tables
 
-- Set tables
 
local tables_assoc = {
 
local tables_assoc = {
Строка 315: Строка 361:
 
}
 
}
 
if tpl_args.q_tables then
 
if tpl_args.q_tables then
for _, tbl_name in ipairs(m_util.string.split(tpl_args.q_tables, ',%s*')) do
+
for _, tbl_name in ipairs(util.split(tpl_args.q_tables, ',%s*')) do
 
tables_assoc[tbl_name] = true
 
tables_assoc[tbl_name] = true
 
end
 
end
 
end
 
end
  +
 
  +
 
-- Set required fields
 
-- Set required fields
 
local fields_assoc = {
 
local fields_assoc = {
Строка 330: Строка 377:
 
for index, field in ipairs(rowinfo.fields) do
 
for index, field in ipairs(rowinfo.fields) do
 
rowinfo.options[index] = rowinfo.options[index] or {}
 
rowinfo.options[index] = rowinfo.options[index] or {}
  +
tables_assoc[util.split(field, '%.')[1]] = true
-- Support using functions such as CONCAT() in fields:
 
local f = string.match(
 
field, m_util.string.pattern.valid_var_name() .. '%.'
 
)
 
if f ~= nil then
 
tables_assoc[f] = true
 
end
 
 
fields_assoc[field] = true
 
fields_assoc[field] = true
 
-- The results from the cargo query will use the aliased field:
 
field = m_util.string.split(field, '%s*=%s*')
 
rowinfo.fields[index] = field[2] or field[1]
 
 
end
 
end
 
end
 
end
  +
 
for _, field in ipairs(args.row_unique_fields) do
+
for _, field_name in ipairs(args.row_unique_fields) do
fields_assoc[field] = true
+
fields_assoc[field_name] = true
 
end
 
end
  +
 
 
-- Parse query arguments
 
-- Parse query arguments
 
local query = {
 
local query = {
 
}
 
}
for key, value in pairs(tpl_args) do
+
for key, value in pairs(tpl_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
 
end
 
end
 
end
 
end
  +
 
 
if tpl_args.q_fields then
 
if tpl_args.q_fields then
  +
for _, field_name in ipairs(util.split(tpl_args.q_fields, ',%s*')) do
local _extra_fields = m_util.string.split_outer(
 
tpl_args.q_fields,
+
fields_assoc[field_name] = true
',%s*',
 
{'%(', '%)'}
 
)
 
for _, field in ipairs(_extra_fields) do
 
fields_assoc[field] = true
 
 
end
 
end
 
end
 
end
  +
 
 
--
 
--
 
-- Query
 
-- Query
Строка 379: Строка 411:
 
joins[#joins+1] = args.data.tables[tbl_name].join
 
joins[#joins+1] = args.data.tables[tbl_name].join
 
tables[#tables+1] = tbl_name
 
tables[#tables+1] = tbl_name
elseif string.match(tpl_args.q_join or '', '.*' .. tbl_name .. '%..*') ~= nil then
+
elseif string.match(tpl_args.q_join, '.*' .. tbl_name .. '%..*') ~= nil then
 
tables[#tables+1] = tbl_name
 
tables[#tables+1] = tbl_name
 
elseif tbl_name ~= args.main_table then
 
elseif tbl_name ~= args.main_table then
Строка 385: Строка 417:
 
end
 
end
 
end
 
end
  +
 
 
local fields = {}
 
local fields = {}
 
for field, _ in pairs(fields_assoc) do
 
for field, _ in pairs(fields_assoc) do
 
fields[#fields+1] = field
 
fields[#fields+1] = field
 
end
 
end
  +
 
 
if #joins > 0 then
 
if #joins > 0 then
 
if query.join then
 
if query.join then
Строка 421: Строка 453:
 
end
 
end
 
end
 
end
  +
 
 
if #results_order == 0 then
 
if #results_order == 0 then
 
if tpl_args.default ~= nil then
 
if tpl_args.default ~= nil then
Строка 429: Строка 461:
 
end
 
end
 
end
 
end
  +
 
 
--
 
--
 
-- Display
 
-- Display
 
--
 
--
  +
 
 
-- Preformance optimization
 
-- Preformance optimization
 
if tpl_args.q_fields then
 
if tpl_args.q_fields then
tpl_args._extra_fields = m_util.string.split_outer(
+
tpl_args._extra_fields = m_util.string.split(tpl_args.q_fields, ',')
tpl_args.q_fields,
 
',%s*',
 
{'%(', '%)'}
 
)
 
 
for index, field in ipairs(tpl_args._extra_fields) do
 
for index, field in ipairs(tpl_args._extra_fields) do
field = m_util.string.split(field, '%s*=%s*')
+
field = m_util.string.split(field, '=')
 
-- field[2] will be nil if there is no alias
 
-- field[2] will be nil if there is no alias
 
tpl_args._extra_fields[index] = field[2] or field[1]
 
tpl_args._extra_fields[index] = field[2] or field[1]
Строка 449: Строка 477:
 
tpl_args._extra_fields = {}
 
tpl_args._extra_fields = {}
 
end
 
end
  +
 
 
local tbl = mw.html.create('table')
 
local tbl = mw.html.create('table')
 
tbl:attr('class', 'wikitable sortable ' .. (args.table_css or ''))
 
tbl:attr('class', 'wikitable sortable ' .. (args.table_css or ''))
 
 
-- Header
 
-- Header
  +
 
local tr = tbl:tag('tr')
 
local tr = tbl:tag('tr')
 
for _, row_info in ipairs(row_infos) do
 
for _, row_info in ipairs(row_infos) do
Строка 462: Строка 490:
 
:done()
 
:done()
 
end
 
end
  +
 
 
for _, field in ipairs(tpl_args._extra_fields) do
 
for _, field in ipairs(tpl_args._extra_fields) do
 
tr
 
tr
Строка 468: Строка 496:
 
:wikitext(field)
 
:wikitext(field)
 
end
 
end
  +
 
 
-- Body
 
-- Body
  +
 
 
for _, unique_key in ipairs(results_order) do
 
for _, unique_key in ipairs(results_order) do
 
local rows = results[unique_key]
 
local rows = results[unique_key]
 
tr = tbl:tag('tr')
 
tr = tbl:tag('tr')
  +
 
 
for _, rowinfo in ipairs(row_infos) do
 
for _, rowinfo in ipairs(row_infos) do
 
local display_fields = {}
 
local display_fields = {}
Строка 488: Строка 516:
 
end
 
end
 
end
 
end
  +
 
 
local display = true
 
local display = true
 
for key, value in pairs(display_fields) do
 
for key, value in pairs(display_fields) do
Строка 496: Строка 524:
 
end
 
end
 
end
 
end
  +
 
 
if display then
 
if display then
 
rowinfo.display(tpl_args, frame, tr, rows, rowinfo)
 
rowinfo.display(tpl_args, frame, tr, rows, rowinfo)
Строка 503: Строка 531:
 
end
 
end
 
end
 
end
  +
 
-- Add extra columns specified by tpl_args.q_fields:
 
 
for _, field in ipairs(tpl_args._extra_fields) do
 
for _, field in ipairs(tpl_args._extra_fields) do
local extra_col = {}
+
if row[field] then
for _, row in ipairs(rows) do
 
if row[field] then
 
extra_col[#extra_col+1] = row[field]
 
end
 
end
 
if #extra_col > 0 then
 
 
tr
 
tr
 
:tag('td')
 
:tag('td')
:wikitext(table.concat(extra_col, '<br>'))
+
:wikitext(row[field])
 
else
 
else
 
tr:wikitext(args.empty_cell)
 
tr:wikitext(args.empty_cell)
Строка 521: Строка 542:
 
end
 
end
 
end
 
end
  +
 
 
return (tpl_args.before or '') .. tostring(tbl) .. (tpl_args.after or '')
 
return (tpl_args.before or '') .. tostring(tbl) .. (tpl_args.after or '')
 
end
 
end
Строка 536: Строка 557:
 
-- map.fields[id].func - OPTIONAL - Function to handle the arguments. It will be passed tpl_args and frame.
 
-- map.fields[id].func - OPTIONAL - Function to handle the arguments. It will be passed tpl_args and frame.
 
-- The function should return the parsed value.
 
-- The function should return the parsed value.
  +
--
--
 
 
-- If no function is specified, default handling depending on the cargo field type will be used
 
-- If no function is specified, default handling depending on the cargo field type will be used
 
-- map.fields[id].default - OPTIONAL - Default value if the value is not set or returned as nil
 
-- map.fields[id].default - OPTIONAL - Default value if the value is not set or returned as nil
Строка 544: Строка 565:
 
-- Note: With a default value the field will never be empty
 
-- Note: With a default value the field will never be empty
 
-- map.fields[id].skip - OPTIONAL - Skip field if missing from order
 
-- map.fields[id].skip - OPTIONAL - Skip field if missing from order
--
+
--
 
--
 
--
 
-- Expects argument table.
 
-- Expects argument table.
Строка 550: Строка 571:
 
-- tpl_args - arguments passed to template after preprecessing
 
-- tpl_args - arguments passed to template after preprecessing
 
-- frame - frame object
 
-- frame - frame object
-- table_map - table mapping object
+
-- table_map - table mapping object
 
-- rtr - if set return cargo props instead of storing them
 
-- rtr - if set return cargo props instead of storing them
 
local tpl_args = args.tpl_args
 
local tpl_args = args.tpl_args
 
local frame = args.frame
 
local frame = args.frame
 
local map = args.table_map
 
local map = args.table_map
  +
 
 
local cargo_values = {_table = map.table}
 
local cargo_values = {_table = map.table}
  +
 
 
-- for checking missing keys in order
 
-- for checking missing keys in order
 
local available_fields = {}
 
local available_fields = {}
Строка 565: Строка 586:
 
end
 
end
 
end
 
end
  +
 
 
-- main loop
 
-- main loop
 
for _, key in ipairs(map.order) do
 
for _, key in ipairs(map.order) do
Строка 586: Строка 607:
 
if field.type ~= nil then
 
if field.type ~= nil then
 
value = tpl_args[args_key]
 
value = tpl_args[args_key]
  +
 
 
local cfield = m_cargo.parse_field{field=field.type}
 
local cfield = m_cargo.parse_field{field=field.type}
 
local handler
 
local handler
Строка 592: Строка 613:
 
handler = tonumber
 
handler = tonumber
 
elseif cfield.type == 'Boolean' then
 
elseif cfield.type == 'Boolean' then
handler = function (value)
+
handler = function (value)
return m_util.cast.boolean(value, {cast_nil=false})
+
return util.to_boolean(value, {cast_nil=false})
 
end
 
end
 
end
 
end
  +
 
 
if cfield.list and value ~= nil then
 
if cfield.list and value ~= nil then
 
-- ingore whitespace between separator and values
 
-- ingore whitespace between separator and values
value = m_util.string.split(value, cfield.list .. '%s*')
+
value = util.split(value, cfield.list .. '%s*')
 
if handler then
 
if handler then
 
for index, v in ipairs(value) do
 
for index, v in ipairs(value) do
Строка 642: Строка 663:
 
end
 
end
 
end
 
end
  +
 
 
-- check for missing keys and return error if any are missing
 
-- check for missing keys and return error if any are missing
 
local missing = {}
 
local missing = {}
Строка 651: Строка 672:
 
error(string.format(i18n.errors.missing_key_in_order, map.table, table.concat(missing, '\n')))
 
error(string.format(i18n.errors.missing_key_in_order, map.table, table.concat(missing, '\n')))
 
end
 
end
  +
 
 
-- finally store data in DB
 
-- finally store data in DB
 
if args.rtr ~= nil then
 
if args.rtr ~= nil then
Строка 670: Строка 691:
 
-- type: type of the field
 
-- type: type of the field
 
return function (frame)
 
return function (frame)
frame = m_util.misc.get_frame(frame)
+
frame = util.get_frame(frame)
  +
 
 
local dcl_args = {}
 
local dcl_args = {}
 
dcl_args._table = args.data.table
 
dcl_args._table = args.data.table
Строка 679: Строка 700:
 
end
 
end
 
end
 
end
  +
 
 
return m_cargo.declare(frame, dcl_args)
 
return m_cargo.declare(frame, dcl_args)
 
end
 
end
Строка 694: Строка 715:
 
-- type: type of the field
 
-- type: type of the field
 
return function (frame)
 
return function (frame)
frame = m_util.misc.get_frame(frame)
+
frame = util.get_frame(frame)
  +
 
 
local attach_args = {}
 
local attach_args = {}
 
attach_args._table = args.data.table
 
attach_args._table = args.data.table
  +
 
 
return m_cargo.attach(frame, attach_args)
 
return m_cargo.attach(frame, attach_args)
 
end
 
end
 
end
 
end
   
-- mw.logObject(m_cargo.map_results_to_id{results=mw.ext.cargo.query('mods,spawn_weights', 'mods._pageID, spawn_weights.tag', {where='mods.id="Strength1"', join='mods._pageID=spawn_weights._pageID'}), field='mods._pageID'})
+
--mw.logObject(p.cargo.map_results_to_id{results=mw.ext.cargo.query('mods,spawn_weights', 'mods._pageID,spawn_weights.tag', {where='mods.id="Strength1"', join='mods._pageID=spawn_weights._pageID'}), table_name='mods'})
 
function m_cargo.map_results_to_id(args)
 
function m_cargo.map_results_to_id(args)
 
-- Maps the results passed to a table containing the specified field as key and a table of rows for the particular page as values.
 
-- Maps the results passed to a table containing the specified field as key and a table of rows for the particular page as values.
 
--
 
--
 
-- args
 
-- args
-- results : Table of results returned from mw.ext.cargo.query to map to the specified id field.
+
-- results : table of results returned from mw.ext.cargo.query to map to the specified id field
-- field : Name of the id field to map results to
+
-- field : name of the id field to map results to
-- the field has to be in the fields list of the original query or it will cause errors.
+
-- the field has to be in the fields list of the original query or it will cause errors
-- keep_id_field : If set then don't delete _pageID.
+
-- keep_id_field : if set, don't delete _pageID
-- append_id_field : If set then append the id to the table sequentially as well which allows preserving
 
-- the id order they were found in.
 
 
--
 
--
 
-- return
 
-- return
 
-- table
 
-- table
-- key : The specified id field
+
-- key : the specified field
-- value : Array containing the found rows (in the order that they were found)
+
-- value : array containing the found rows (in the order that they were found)
 
local out = {}
 
local out = {}
 
for _, row in ipairs(args.results) do
 
for _, row in ipairs(args.results) do
local key = row[args.field]
+
local pid = row[args.field]
if out[key] then
+
if out[pid] then
out[key][#out[key]+1] = row
+
out[pid][#out[pid]+1] = row
 
else
 
else
out[key] = {row}
+
out[pid] = {row}
 
-- Append the ids sequentially, this allows preserving the order
 
-- the ids were found:
 
if args.append_id_field ~= nil then
 
out[#out+1] = key
 
end
 
 
end
 
end
  +
-- discard the pageID, don't need this any longer in most cases
 
--Discard the pageID, don't need this any longer in most cases:
 
 
if args.keep_id_field == nil then
 
if args.keep_id_field == nil then
 
row[args.field] = nil
 
row[args.field] = nil
 
end
 
end
 
end
 
end
  +
 
 
return out
 
return out
 
end
 
end
Строка 745: Строка 757:
 
function m_cargo.array_query(args)
 
function m_cargo.array_query(args)
 
-- Performs a long "OR" query from the given array and field validating that there is only exactly one match returned
 
-- Performs a long "OR" query from the given array and field validating that there is only exactly one match returned
--
+
--
 
-- args:
 
-- args:
 
-- REQUIRED:
 
-- REQUIRED:
Строка 761: Строка 773:
 
-- msg - any error messages if it was used as warning
 
-- msg - any error messages if it was used as warning
 
args.query = args.query or {}
 
args.query = args.query or {}
  +
 
 
args.fields[#args.fields+1] = args.id_field
 
args.fields[#args.fields+1] = args.id_field
  +
 
 
if #args.id_array == 0 then
 
if #args.id_array == 0 then
 
return {}
 
return {}
 
end
 
end
  +
 
 
-- remove blanks
 
-- remove blanks
 
local id_array = {}
 
local id_array = {}
 
for _, value in ipairs(args.id_array) do
 
for _, value in ipairs(args.id_array) do
if value ~= '' then
+
if value ~= '' then
 
id_array[#id_array+1] = value
 
id_array[#id_array+1] = value
 
end
 
end
 
end
 
end
  +
 
 
-- for error returning
 
-- for error returning
 
local msg = {}
 
local msg = {}
  +
 
 
local where = string.format('%s IN ("%s")', args.id_field, table.concat(id_array, '","'))
 
local where = string.format('%s IN ("%s")', args.id_field, table.concat(id_array, '","'))
 
if args.query.where then
 
if args.query.where then
Строка 785: Строка 797:
 
args.query.where = where
 
args.query.where = where
 
end
 
end
  +
 
 
--
 
--
 
-- Prepare query
 
-- Prepare query
 
--
 
--
  +
 
 
local results = m_cargo.query(
 
local results = m_cargo.query(
 
args.tables,
 
args.tables,
Строка 795: Строка 807:
 
args.query
 
args.query
 
)
 
)
  +
 
 
--
 
--
 
-- Check missing results
 
-- Check missing results
Строка 814: Строка 826:
 
where=args.query.where,
 
where=args.query.where,
 
groupBy=args.id_field,
 
groupBy=args.id_field,
having=string.format('COUNT(DISTINCT %s._pageID) > 1', args.tables[1]),
+
having=string.format('COUNT(DISTINCT %s._pageID) > 1', args.tables[1]),
 
}
 
}
 
)
 
)
  +
 
 
if #dupes > 0 then
 
if #dupes > 0 then
 
out = {}
 
out = {}
Строка 825: Строка 837:
 
error(string.format(i18n.errors.duplicate_ids, args.id_field, table.concat(out, '\n')))
 
error(string.format(i18n.errors.duplicate_ids, args.id_field, table.concat(out, '\n')))
 
end
 
end
  +
 
 
local dupes = m_cargo.query(
 
local dupes = m_cargo.query(
 
args.tables,
 
args.tables,
Строка 839: Строка 851:
 
}
 
}
 
)
 
)
  +
 
 
if #dupes > 0 then
 
if #dupes > 0 then
 
out = {}
 
out = {}
Строка 847: Строка 859:
 
error(string.format(i18n.errors.duplicate_ids, args.id_field, table.concat(out, '\n')))
 
error(string.format(i18n.errors.duplicate_ids, args.id_field, table.concat(out, '\n')))
 
end
 
end
  +
 
 
if not args.ignore_missing then
 
if not args.ignore_missing then
 
local missing = {}
 
local missing = {}
Строка 856: Строка 868:
 
missing[row[args.id_field]] = nil
 
missing[row[args.id_field]] = nil
 
end
 
end
  +
 
 
local missing_ids = {}
 
local missing_ids = {}
 
for k, _ in pairs(missing) do
 
for k, _ in pairs(missing) do
 
missing_ids[#missing_ids+1] = k
 
missing_ids[#missing_ids+1] = k
 
end
 
end
  +
 
 
msg[#msg+1] = string.format(i18n.errors.missing_ids, args.id_field, table.concat(missing_ids, '\n'))
 
msg[#msg+1] = string.format(i18n.errors.missing_ids, args.id_field, table.concat(missing_ids, '\n'))
 
if args.warning_on_missing == nil then
 
if args.warning_on_missing == nil then
Строка 870: Строка 882:
 
end
 
end
 
end
 
end
  +
 
 
return results, msg
 
return results, msg
 
end
 
end
Строка 895: Строка 907:
 
if args.mode == 'like' or args.mode == nil then
 
if args.mode == 'like' or args.mode == nil then
 
return string.gsub(
 
return string.gsub(
args.string,
+
args.string,
 
string.format('(%s) HOLDS ([NOT ]*)([LIKE ]*)"([^"]+)"', args.field),
 
string.format('(%s) HOLDS ([NOT ]*)([LIKE ]*)"([^"]+)"', args.field),
 
'%1__full %2LIKE "%%%4%%"'
 
'%1__full %2LIKE "%%%4%%"'
Строка 902: Строка 914:
 
args.separator = args.separator or ','
 
args.separator = args.separator or ','
 
return string.gsub(
 
return string.gsub(
args.string,
+
args.string,
string.format('(%s) HOLDS ([NOT ]*)"([^"]+)"', args.field),
+
string.format('(%s) HOLDS ([NOT ]*)"([^"]+)"', args.field),
 
string.format('%%1__full %%2REGEXP "(%s|^)%%3(%s|$)"', args.separator, args.separator)
 
string.format('%%1__full %%2REGEXP "(%s|^)%%3(%s|$)"', args.separator, args.separator)
 
)
 
)
Строка 924: Строка 936:
 
local results = {}
 
local results = {}
 
local match
 
local match
  +
 
 
match = { string.match(field, 'List %(([^%(%)]+)%) of (.*)') }
 
match = { string.match(field, 'List %(([^%(%)]+)%) of (.*)') }
 
if #match > 0 then
 
if #match > 0 then
Строка 930: Строка 942:
 
field = match[2]
 
field = match[2]
 
end
 
end
  +
 
 
match = { string.match(field, '%s*(%a+)%s*%(([^%(%)]+)%)') }
 
match = { string.match(field, '%s*(%a+)%s*%(([^%(%)]+)%)') }
 
if #match > 0 then
 
if #match > 0 then
Строка 936: Строка 948:
 
field = match[2]
 
field = match[2]
 
results.parameters = {}
 
results.parameters = {}
for _, param_string in ipairs(m_util.string.split(field, ';')) do
+
for _, param_string in ipairs(util.split(field, ';')) do
 
local index = { string.find(param_string, '=') }
 
local index = { string.find(param_string, '=') }
 
local key
 
local key
Строка 942: Строка 954:
 
if #index > 0 then
 
if #index > 0 then
 
key = string.sub(param_string, 0, index[1]-1)
 
key = string.sub(param_string, 0, index[1]-1)
value = m_util.string.strip(string.sub(param_string, index[1]+1))
+
value = util.strip(string.sub(param_string, index[1]+1))
 
else
 
else
 
key = param_string
 
key = param_string
 
value = true
 
value = true
 
end
 
end
results.parameters[m_util.string.strip(key)] = value
+
results.parameters[util.strip(key)] = value
 
end
 
end
 
else
 
else
Строка 953: Строка 965:
 
results.type = string.match(field, '%s*(%a+)%s*')
 
results.type = string.match(field, '%s*(%a+)%s*')
 
end
 
end
  +
 
 
return results
 
return results
 
end
 
end