模組:沙盒/TimWu007/RailSystems
调用
本模块支持调用下列函数来获取数据(或 MediaWiki 内容)。
调用名称 | 参数 | 说明 |
---|---|---|
stationLink |
{{#invoke:RailSystems | stationLink |
通过车站名获取车站在铁道系统中的链接,用于处理条目名不规则或消歧义后的条目。
例如从“朝阳门”转换至“[[朝阳门站 (北京)|朝阳门]]”。 |
lineColor |
{{#invoke:RailSystems | lineColor |
通过线路编号获取线路颜色。若 prefix=1 则加上“#”前缀。 |
lineTitle |
{{#invoke:RailSystems | lineTitle |
通过线路编号获取线路条目链接。若 link=0 则只返回条目名,否则返回条目链接 Wiki Text,默认 link=1。 |
lineRichTitle |
{{#invoke:RailSystems | lineRichTitle |
通过线路编号获取带有标志色装饰(默认为前置彩色小方块)的线路条目链接。若 degrade 为 1,则在标志色未定义时自动返回 lineTitle 的结果。 |
lineTerminal |
{{#invoke:RailSystems | lineTerminal |
通过线路编号、方向、类型(可选)及支线(可选)获得线路终点站链接。 |
lineTerminalName |
{{#invoke:RailSystems | lineTerminalName |
通过线路编号、方向、类型(可选)及支线(可选)获得线路终点站站名。 |
trainTime |
{{#invoke:RailSystems | trainTime |
通过线路编号、运营方向(预定义)、首/末班及始发站到达本站的时间获得车站首班/末班车时间。 |
trainDirectionTime |
{{#invoke:RailSystems | trainDirectionTime |
通过线路编号、运营方向(预定义)、首/末班及始发站到达本站的时间获得车站首班/末班车终点站名链接及时间组成的 WikiText。 |
lineDateMessage |
{{#invoke:RailSystems | lineDateMessage |
通过线路编号和期数,显示距离开通需要的时间。本函数支持自定义日期格式,year、month、day分别表示年、月、日(含单位),ym、date分别表示年月组合、日期,可通过管道符号“|”和其他自定义字符相连。如果 auto_hide 未指定或值为“1”,当该日期已过时,文字将自动隐藏,条目会被加入Category:需要去除时间判断模板的页面。 |
使用建议
不建议直接在条目空间中使用 #invoke 语法调用本模块,而是为每个系统建立自己的系列模板,在模板中引用本模块。例如,北京地铁系列条目使用 {{BJS color}} 调用本模块的 lineColor 方法。
一部分其他模板也集成了本模板,包括参数 lua=1 条件下的 {{铁道路线}} 模板(参见 Module:RouteSequence)以及 {{S-line}} 中的一部分功能。{{RenderStations}} 利用了模块中的 renderStationLinks 方法,可以方便地对一段 WikiText 中的站名进行转换。
子模块列表
下面为已有的子模块列表,支持不同的铁道系统。注意,以“/doc”为后缀的是文档,而“UseCase”是测试用例。
建立系统模板
将本模块引入铁道系统前,你需要首先创建一个系统数据模块。该模块为本模块的子模块,名称为 Module:RailSystems/<系统名> ,基本框架为
local p = {}
p.lines = {
-- 线路列表
}
-- 开通日期,用于为未开通线路标记日期,可有多行,非必需
p.lines['线路名'].openDates = { ['#default'] = '默认开通日期', ['2'] = '二期开通日期' }
p.lines['线路名2'].openDates = '单一开通日期'
local lineAliases = {
-- 线路别名(用于简繁转换)
}
-- 用于处理别名的代码,勿删
for k, v in pairs(lineAliases) do p.lines[k] = p.lines[v] end
-- 起讫站信息必需代码,勿删
for k, v in pairs(p.lines) do v.terminals = {} end
-- 首末班车信息必需代码,勿删
for k, v in pairs(p.lines) do v.trainTime = {} end
p.stationNames = {
-- 站名链接
}
local stationAliases = {
-- 车站别名
}
-- 用于处理别名的代码,勿删
for k, v in pairs(stationAliases) do p.stationNames[k] = p.stationNames[v] end
-- 起讫站
p.lines['1'].terminals = { left = 'XXX', right = 'XXX' }
p.lines['2'].terminals = {
left = { ['#field'] = 'type', ['#default'] = 'XXX', F = 'XXX'},
right = { 'XXX' },
}
-- 首末班车
p.lines['1'].trainTime = {
['SE'] = { first = '06:00', last = '23:00', startService = 'XXX', endService = 'XXX' },
['ES'] = { first = '06:00', last = '23:00', startService = 'XXX', endService = 'XXX' },
}
return p
线路列表
本模块线路列表的例子如下
p.lines = {
['1'] = { title = "宁波轨道交通1号线|1号线", color = '3180b7' },
['2'] = { title = "宁波轨道交通2号线|2号线", color = 'cc0000' },
}
其中,title 表示线路的条目名称及显示名称,使用“|”分隔。color 表示线路的标志色。
线路开通日期
本模块线路开通日期的例子如下。如果系统中无此需求可省略这部分。
p.lines['1'].openDates = { ['#default'] = '2014-05-30', ['2'] = '2016-03-19' }
p.lines['2'].openDates = '2020-12-28'
对于不分期线路,可采用“yyyy-mm-dd”字符串。对于分期线路,可以使用一个 Lua Table 指定各个分期的开通日期。
站名链接
本模块站名链接的例子如下
p.lines = {
['朝阳门'] = '朝阳门站 (北京)|朝阳门',
['北京南站'] = '北京南站|北京南站',
['大兴线天宫院'] = {'天宫院站|天宫院', '([[北京地铁大兴线|大兴线]])'},
}
第一例中,站名“朝阳门”对应了链接 [[朝阳门站 (北京)|朝阳门]],使得调用 {{#invoke:RailSystems|stationLink|name=朝阳门|system=BJS}} 时能够返回重定向后正确的连接(而非 [[朝阳门站|朝阳门]])。
第二例中,站名“北京南站”对应的条目名为自身,因而 stationLink 返回“[[北京南站|北京南站]]”而非“[[北京南站站|北京南站]]”。需要注意的是,stationLink 根据字符串中是否存在“[[”外的“|”符号判断是否需要将字符串转换为链接。因而,单写“['北京南站'] = '北京南站',”只会返回“北京南站”的文字。
第三例中,站名“大兴线天宫院”是为了在北京地铁4号线车站条目中显示终点站为“[[天宫院站|天宫院]]([[北京地铁大兴线|大兴线]])”。这里使用了 Lua table 取代字符串。模块会自动扫描 table,将其中属于链接的部分组装为链接,并将所有部分拼装在一起。
起讫站
本模块起讫站的例子如下
p.lines['1'].terminals = {
left = '湘湖',
right = { ['#field'] = 'branch',
['#default'] = { ['#field'] = 'type', ['#default'] = '临平、文泽路', future = '临平、下沙江滨' },
['临平'] = '临平',
['下沙'] = { ['#field'] = 'type', ['#default'] = '文泽路', future = '下沙江滨' },
},
}
p.lines['2'].terminals = {
left = '朝阳',
right = { ['#field'] = 'type', ['#default'] = '钱江路', ['future'] = '良渚'},
}
起讫站名中,terminals 中的 left 属性表示起点站,right 属性表示终点站。两个属性的值均为迭代定义。若值为字符串,则返回站名。否则根据 '#field' 的值,判定输入参数是否符合当前 table 的某一个属性名。如果符合,取出值。否则取出 '#default' 属性的值。最后,对该值重复上述处理,直至返回结果为字符串为止。
首末班车
本模块起讫站的例子如下
p.lines['1'].trainTime = {
-- 东环南路首班车
['东高'] = { first = '06:00', startService = '东环南路', endService = '高桥西' },
['东霞'] = { first = '06:00', startService = '东环南路', endService = '霞浦' },
-- 起讫站首末班车
['霞高'] = { first = '06:00', last = '22:00', startService = '霞浦', endService = '高桥西' },
['高霞'] = { first = '06:00', last = '22:00', startService = '高桥西', endService = '霞浦' },
}
其中,trainTime 的每个属性值表示线路上的一种服务,名称可自定。此后 first 和 last 分别表示该服务首末班车的时间,而 startService 和 endService 表示服务的始发/终点站。
增加新功能
编者可为本模块增加新功能。但需要注意,新增的功能不应影响原有功能。为了便于维护和复用,请在完成功能编写后更新用例和帮助文档。本模块的用例位于 p.testCase,请在提交前先执行用例,确保所有用例均通过再行提交。
可用性
如果下方显示错误信息,则相应功能不可用,请联系最近一次修改者修复该错误。查看编辑历史
Category:铁路模板
-------------------------------------------------------------------
-- 重要:在保存编辑前,请务必跑完所有测试用例并保证没有错误输出。
-- 测试方法为:在调试控制台输入
--
-- print(p.testCase())
--
-- 如果输出为“所有用例均通过!”,说明可以保存编辑,否则请消除错误。
-------------------------------------------------------------------
local p = {
DEFAULT_LINE_COLOR = 'cccccc',
DEFAULT_STATION_SUFFIX = '站',
DEFAULT_PLAIN_LINK_TEMPLATE = '[[{line_link}|{line_title}]]',
DEFAULT_RICH_LINK_TEMPLATE = ' <span style="color:#{line_color}"><big>■</big></span> [[{line_link}|{line_title}]]',
DEFAULT_TRAIN_TIME_TEMPLATE = '往{station_link}方向:{train_time|H:i|次日}',
DEFAULT_STATION_TRAIN_TIME_TEMPLATE = '{start_time|H:i|次日}-{end_time|H:i|次日}',
DEFAULT_DATE_ELAPSED_TEXT = '[[Category:需要去除时间判断模板的页面]]',
DEFAULT_LINE_DATE_ABSENT_TEXT = '规划中',
}
local function plain_replace(src, substr, repl)
function literalize(str)
return str:gsub("[%(%)%.%%%+%-%*%?%[%]%^%$]", function(c) return "%" .. c end)
end
substr = literalize(substr)
local rstr, _ = src:gsub(substr, repl)
return rstr
end
local function _loadSystemData(system, raises)
local system_data = nil
local state
if raises == nil then
raises = true
end
if system ~= nil then
state, system_data = pcall(mw.loadData, "Module:RailSystems/" .. system)
if not state then
if raises then
error(string.format('铁道系统“%s”的数据模块不存在', system))
else
system_data = nil
end
end
end
return system_data
end
local function _safeExpandTemplate(frame, title, args)
local function _caller(frame, t, a)
return frame:expandTemplate{ ['title'] = t, ['args'] = a }
end
local state, result = pcall(_caller, frame, title, args)
if not state then
return string.format('[[:Template:%s]]', title)
else
return result
end
end
local function _convBool(val, default)
if val == nil then
return default
elseif type(val) == 'boolean' then
return val
elseif mw.text.trim(val) == '' then
return default
end
return (mw.text.trim(val) == '1')
end
function p._internalStationLink(name, style, short, system_data, frame)
local station_parts, station_data
local station_link, station_name
local repr = ''
if short == nil then short = true end
local function marshalStation(station_link, station_name, style)
if not short then
local link_pos, _ = string.find(station_link, ' %(')
if link_pos ~= nil then
station_name = string.sub(station_link, 1, link_pos - 1)
else
station_name = station_link
end
end
if style == nil or mw.text.trim(style) == '' then
return '[[' .. station_link .. '|' .. station_name .. ']]'
else
return '[[' .. station_link .. '|<span style="' .. style .. '">' .. station_name .. '</span>]]'
end
end
if type(system_data) == 'string' then
system_data = _loadSystemData(system_data, false)
end
if system_data == nil then
return nil
end
if system_data.stationNames[name] ~= nil then
station_data = system_data.stationNames[name]
if type(station_data) ~= 'table' then
station_data = {station_data, }
end
for i, datum in ipairs(station_data) do
if string.find(datum, '|', 1, true) == nil or string.find(datum, '[[', 1, true) ~= nil or string.find(datum, '{{', 1, true) ~= nil then
repr = repr .. datum
else
station_parts = mw.text.split(datum, '|')
station_link = station_parts[1]
station_name = table.concat(station_parts, '|', 2)
if frame ~= nil and string.find(station_name, '{', 1, true) ~= nil then
station_name = frame:preprocess(table.concat(station_parts, '|', 2))
end
repr = repr .. marshalStation(station_link, station_name, style)
end
end
return repr
else
station_link = name .. p.DEFAULT_STATION_SUFFIX
return marshalStation(station_link, name, style)
end
end
function p._internalLineColor(code, prefix, system)
local color_prefix = ''
local system_data = _loadSystemData(system, false)
if system_data == nil then
return nil
end
if prefix then
color_prefix = '#'
end
if system_data.lines[code] == nil then
return color_prefix .. p.DEFAULT_LINE_COLOR
elseif system_data.lines[code].color == nil then
return color_prefix .. p.DEFAULT_LINE_COLOR
else
return color_prefix .. system_data.lines[code].color
end
end
function p._internalLineTitle(line_code, link, system)
local system_data = _loadSystemData(system, false)
local tmpl = p.DEFAULT_PLAIN_LINK_TEMPLATE
local raw_title, link_parts, link_str, _match
if link == nil then
link = true
end
if system_data == nil then
return nil
end
raw_title = system_data.lines[line_code].title
link_parts = mw.text.split(raw_title, '|')
if string.find(raw_title, '[[', 1, true) ~= nil then
return raw_title
end
if system_data.plain_link_template ~= nil then
tmpl = system_data.plain_link_template
end
if link then
if table.getn(link_parts) == 1 then
link_str, _match = string.gsub(string.gsub(tmpl, '\{line_link\}', link_parts[1]), '\{line_title\}', link_parts[1])
else
link_str, _match = string.gsub(string.gsub(tmpl, '\{line_link\}', link_parts[1]), '\{line_title\}', link_parts[2])
end
else
link_str = link_parts[1]
end
return link_str
end
function p._internalLineRichTitle(line_code, auto_degrade, system)
local system_data = _loadSystemData(system)
local raw_title = system_data.lines[line_code].title
local line_color = system_data.lines[line_code].color
local link_parts = mw.text.split(raw_title, '|')
local tmpl = p.DEFAULT_RICH_LINK_TEMPLATE
local link, _match
if string.find(raw_title, '[[', 1, true) ~= nil then
return raw_title
end
if system_data.rich_link_template ~= nil then
tmpl = system_data.rich_link_template
end
if line_color == nil then
if auto_degrade then
return p._internalLineTitle(line_code, true, system)
else
line_color = p.DEFAULT_LINE_COLOR
end
end
link, _match = string.gsub(tmpl, '\{line_link\}', link_parts[1])
link, _match = string.gsub(link, '\{line_title\}', link_parts[2])
link, _match = string.gsub(link, '\{line_color\}', line_color)
return link
end
function p._internalLineTerminal(line_code, side, type_, branch, system)
local system_data = _loadSystemData(system, false)
local side_data, conds
function get_condition(cond, data)
local c
if type(data) == 'table' then
c = cond[data['#field']]
if data[c] == nil then
return get_condition(cond, data['#default'])
else
return get_condition(cond, data[c])
end
else
return data
end
end
if system_data ~= nil and system_data.lines[line_code] ~= nil and system_data.lines[line_code].terminals ~= nil then
side_data = system_data.lines[line_code].terminals[side]
conds = { ['type'] = type_, ['branch'] = branch }
return get_condition(conds, side_data)
else
return nil
end
end
function p._parseTime(time_str)
local base_parts = mw.text.split(time_str, ':')
local parts_size = table.getn(base_parts)
local hour = 0
local minute = 0
local sec = 0
if parts_size == 1 then
minute = tonumber(mw.text.trim(base_parts[1]))
elseif parts_size == 2 then
hour = tonumber(mw.text.trim(base_parts[1]))
minute = tonumber(mw.text.trim(base_parts[2]))
elseif parts_size == 3 then
hour = tonumber(mw.text.trim(base_parts[1]))
minute = tonumber(mw.text.trim(base_parts[2]))
sec = tonumber(mw.text.trim(base_parts[3]))
else
error('非法的时间输入')
end
return hour, minute, sec
end
function p._extractSeconds(totalsec)
local hour = math.floor(totalsec / 3600)
local minute = math.floor(totalsec % 3600 / 60)
local sec = totalsec % 60
return hour, minute, sec
end
function p._internalTrainTime(line_code, dir, type_, delta, system)
local system_data = _loadSystemData(system)
local dir_info = system_data.lines[line_code].trainTime[dir]
local bhour, bmin, bsec = p._parseTime(dir_info[type_])
local dhour, dmin, dsec = p._parseTime(delta)
local totalsec = (bhour * 60 + bmin) * 60 + bsec + (dhour * 60 + dmin) * 60 + dsec
local hour, minute, sec = p._extractSeconds(totalsec)
return dir_info.endService, hour, minute, sec
end
function p._formatTemplateTime(frame, sparts, hour, minute, second)
local tomorrow_prefix = ''
if table.getn(sparts) >= 3 then
if hour >= 24 then
tomorrow_prefix = mw.text.trim(sparts[3])
if tomorrow_prefix == '' then tomorrow_prefix = '次日' end
hour = hour - 24
end
end
local time_str = string.format('%d:%d:%d', hour, minute, second)
return tomorrow_prefix .. frame:callParserFunction{ name = '#time', args = { sparts[2], time_str } }
end
function p._internalLineDateMessage(line_code, type_, reprs, options, system)
local system_data = _loadSystemData(system)
local frame = mw.getCurrentFrame()
local lowest_level = 0 -- 0: everything is ok, 1: year, 2: month, 3: day
local cur_time = frame:callParserFunction{ name = '#time', args = { 'U' } }
local open_date, year, month, day, new_year, new_month
local ret_str = ''
for i, repr in ipairs(reprs) do
repr = mw.text.trim(repr)
if lowest_level < 1 and (repr == 'year') then
lowest_level = 1
elseif lowest_level < 2 and (repr == 'month' or repr == 'ym') then
lowest_level = 2
elseif lowest_level < 3 and (repr == 'day' or repr == 'date' or repr == 'ymd') then
lowest_level = 3
end
end
if options == nil then options = {} end
options.auto_hide = _convBool(options.auto_hide, false)
options.auto_defer = _convBool(options.auto_defer, true)
if options.cur_time ~= nil then -- for testing purpose
cur_time = frame:callParserFunction{ name = '#time', args = { 'U', options.cur_time } }
end
if system_data.lines[line_code] == nil then return nil end
open_date = system_data.lines[line_code].openDates
if open_date == nil then open_date = {} end
if type(open_date) == 'table' then
if open_date[type_] ~= nil then
open_date = open_date[type_]
elseif open_date['#default'] ~= nil then
open_date = open_date['#default']
elseif lowest_level == 0 then
for i, repr in ipairs(reprs) do ret_str = ret_str .. repr end
return ret_str
else
return p.DEFAULT_LINE_DATE_ABSENT_TEXT
end
end
year = tonumber(frame:callParserFunction{ name = '#time', args = { 'Y', open_date } })
month = tonumber(frame:callParserFunction{ name = '#time', args = { 'm', open_date } })
day = tonumber(frame:callParserFunction{ name = '#time', args = { 'd', open_date } })
if options.auto_defer then
if lowest_level == 1 then
open_date = string.format('%04d-01-01', year + 1)
elseif lowest_level == 2 then
new_year, new_month = year, month
if month == 12 then
new_year = new_year + 1
new_month = 1
else
new_month = new_month + 1
end
open_date = string.format('%04d-%02d-01', new_year, new_month)
end
end
open_date = frame:callParserFunction{ name = '#time', args = { 'U', open_date } }
if options.auto_hide and cur_time >= open_date then
return p.DEFAULT_DATE_ELAPSED_TEXT
end
for i, repr in ipairs(reprs) do
if repr == 'year' then
ret_str = ret_str .. year .. '年'
elseif repr == 'month' then
ret_str = ret_str .. month .. '月'
elseif repr == 'day' then
ret_str = ret_str .. day .. '日'
elseif repr == 'ym' then
ret_str = ret_str .. string.format('%s年%s月', year, month)
elseif repr == 'date' or repr == 'ymd' then
ret_str = ret_str .. string.format('%s年%s月%s日', year, month, day)
else
ret_str = ret_str .. repr
end
end
return ret_str
end
function p.stationLink(frame)
local a = frame.args
local short = _convBool(a.short, true)
local system_data = _loadSystemData(a.system, false)
local ret = p._internalStationLink(a.name, a.style, short, system_data, frame)
if ret == nil then
return _safeExpandTemplate(frame, string.format('%s stations', a.system), {line=a.link, branch=a.branch, station=a.name, state=a.state})
else
return ret
end
end
function p.lineColor(frame)
local a = frame.args
a.prefix = _convBool(a.prefix, false)
local ret = p._internalLineColor(a.name, a.prefix, a.system)
if ret == nil then
ret = _safeExpandTemplate(frame, string.format('%s color', a.system), {a.name})
if a.prefix then ret = '#' .. ret end
return ret
else
return ret
end
end
function p.lineTitle(frame)
local a = frame.args
local link = _convBool(a.link, true)
local rev = p._internalLineTitle(a.name, link, a.system)
if rev == nil then
return _safeExpandTemplate(frame, string.format('%s lines', a.system), {a.name})
else
return rev
end
end
function p.lineRichTitle(frame)
local a = frame.args
local degrade = _convBool(a.degrade, true)
return p._internalLineRichTitle(a.name, degrade, a.system)
end
function p.lineTerminal(frame)
local a = frame.args
local term = p._internalLineTerminal(a.name, a.side, a['type'], a.branch, a.system)
if term == nil then
term = _safeExpandTemplate(frame, string.format('S-line/%s %s/%s', a.system, a.side, a.name), { ['type']=a['type'], branch=a.branch })
return _safeExpandTemplate(frame, string.format('%s stations', a.system), {line=a.name, branch=a.branch, station=term, state=a.state})
else
return p._internalStationLink(term, '', true, a.system, frame)
end
end
function p.lineTerminalName(frame)
local a = frame.args
local terminal_str = p._internalLineTerminal(a.name, a.side, a['type'], a.branch, a.system)
if terminal_str == nil then
return _safeExpandTemplate(frame, string.format('S-line/%s %s/%s', a.system, a.side, a.name), { ['type']=a['type'], branch=a.branch })
else
return terminal_str
end
end
function p.trainTime(frame)
local a = frame.args
if a['type'] == 'f' or a['type'] == 'F' then a['type'] = 'first' end
if a['type'] == 'l' or a['type'] == 'L' then a['type'] = 'last' end
local terminal, hour, minute, sec = p._internalTrainTime(a.name, a.dir, a['type'], a.delta, a.system)
return string.format('%02d:%02d', hour, minute)
end
function p.trainDirectionTime(frame)
local str = frame.template
if str == nil then str = p.DEFAULT_TRAIN_TIME_TEMPLATE end
local a = frame.args
if a['type'] == 'f' or a['type'] == 'F' then a['type'] = 'first' end
if a['type'] == 'l' or a['type'] == 'L' then a['type'] = 'last' end
local terminal, hour, minute, sec = p._internalTrainTime(a.name, a.dir, a['type'], a.delta, a.system)
local l = 0
while true do
local l, r, c = string.find(str, '\{([^\}]+)\}', l + 1)
if l == nil then break end
local sparts = mw.text.split(c, '|')
if sparts[1] == 'station_name' then
str = plain_replace(str, '{' .. c .. '}', terminal)
elseif sparts[1] == 'station_link' then
str = plain_replace(str, '{' .. c .. '}', p._internalStationLink(terminal, '', true, a.system, frame))
elseif sparts[1] == 'train_time' then
str = plain_replace(str, '{' .. c .. '}', p._formatTemplateTime(frame, sparts, hour, minute, sec))
end
end
return str
end
function p.stationTrainTime(frame)
local a = frame.args
local dir_reprs = {}
local system_data = _loadSystemData(a.system)
local min_time_data = 999999
local max_time_data = -999999
local line_names = mw.text.split(a.name, ',')
for k, v in pairs(a) do
if type(k) == 'number' then
dir_reprs[k] = v
end
end
for k, line_name in ipairs(line_names) do
for i = 1, table.getn(dir_reprs), 2 do
local dir = dir_reprs[i]
local diff = dir_reprs[i + 1]
local dir_times = system_data.lines[line_name].trainTime[dir]
if dir_times ~= nil then
local dh, dm, ds = p._parseTime(diff)
for _, dir_time in ipairs({dir_times.first, dir_times.last}) do
if dir_time ~= nil then
local h, m, s = p._parseTime(dir_time)
local t = (h * 60 + m) * 60 + s + (dh * 60 + dm) * 60 + ds
min_time_data = math.min(min_time_data, t)
max_time_data = math.max(max_time_data, t)
end
end
end
end
end
local min_h, min_m, min_s = p._extractSeconds(min_time_data)
local max_h, max_m, max_s = p._extractSeconds(max_time_data)
local str = frame.template
if str == nil then str = p.DEFAULT_STATION_TRAIN_TIME_TEMPLATE end
local l = 0
while true do
local l, r, c = string.find(str, '\{([^\}]+)\}', l + 1)
if l == nil then break end
local sparts = mw.text.split(c, '|')
if sparts[1] == 'start_time' then
str = plain_replace(str, '{' .. c .. '}', p._formatTemplateTime(frame, sparts, min_h, min_m, min_s))
elseif sparts[1] == 'end_time' then
str = plain_replace(str, '{' .. c .. '}', p._formatTemplateTime(frame, sparts, max_h, max_m, max_s))
end
end
return str
end
function p.lineDateMessage(frame)
local a = frame.args
local name, type_, system = a.name, a['type'], a.system
local reprs, options = {}, {}
local preserved = { name = true, ['type'] = true, system = true }
for k, v in pairs(a) do
if preserved[k] == nil then
if type(k) == 'number' then
reprs[k] = v
else
options[k] = v
end
end
end
return p._internalLineDateMessage(name, type_, reprs, options, system)
end
function p.renderStationLinks(frame)
local a = frame:getParent().args
local short = _convBool(a.short, true)
local spos, epos, station_name = 1, 1, nil
local pos = 1
local system_data = _loadSystemData(a.system)
local sys_args = { system=true, short=true }
local text = ''
for k, v in frame:getParent():argumentPairs() do
if type(k) == 'number' then
if string.len(text) > 0 then
text = text .. '|' .. v
else
text = v
end
else
if sys_args[k] == nil then
if string.len(text) > 0 then
text = text .. '|' .. k .. '=' .. v
else
text = k .. '=' .. v
end
end
end
end
text = mw.text.trim(text)
local out_text = ''
while spos ~= nil do
spos, epos, station_name = string.find(text, '%$%$([^%$]+)%$%$', pos)
if spos ~= nil then
out_text = out_text .. string.sub(text, pos, spos - 1) .. p._internalStationLink(station_name, '', short, system_data, frame)
pos = epos + 1
else
out_text = out_text .. string.sub(text, pos, string.len(text))
end
end
return out_text
end
function p.testCase(frame)
local frame = mw.getCurrentFrame()
local lineTerminalNameFrame
local link, _match
local function mkfp(func_name)
local fun = p[func_name]
local function call_frame(frame)
return fun(mw.getCurrentFrame():newChild(frame))
end
return call_frame
end
local fp = {
stationLink = mkfp('stationLink'),
lineTitle = mkfp('lineTitle'),
lineColor = mkfp('lineColor'),
lineTerminal = mkfp('lineTerminal'),
lineTerminalName = mkfp('lineTerminalName'),
trainDirectionTime = mkfp('trainDirectionTime'),
stationTrainTime = mkfp('stationTrainTime'),
}
-- stationLink test
if fp.stationLink{args={name='宁波站', system='UseCase'}} ~= '[[宁波站|宁波站]]' then
error('stationLink test failed: name=宁波站.')
end
if fp.stationLink{args={name='寧波站', system='UseCase'}} ~= '[[宁波站|宁波站]]' then
error('stationLink test failed: name=宁波站.')
end
if fp.stationLink{args={name='高桥西', system='UseCase'}} ~= '[[高桥西站 (宁波)|高桥西]]' then
error('stationLink test failed: name=高桥西.')
end
if fp.stationLink{args={name='高桥西', short='0', system='UseCase'}} ~= '[[高桥西站 (宁波)|高桥西站]]' then
error('stationLink test failed: name=高桥西, short=0.')
end
if fp.stationLink{args={name='泽民', system='UseCase'}} ~= '[[泽民站|泽民]]' then
error('stationLink test failed: name=泽民.')
end
if fp.stationLink{args={name='内嵌模板', system='UseCase'}} ~= '[[内嵌模板站|内嵌|模板]]' then
error('stationLink test failed: name=内嵌模板.')
end
-- lineColor test
if p.lineColor{args={name='1', system='UseCase'}} ~= '3180b7' then
error('lineColor test failed: name=1.')
end
if p.lineColor{args={name='1', prefix='1', system='UseCase'}} ~= '#3180b7' then
error('lineColor test failed: name=1.')
end
if fp.lineColor{args={name='1', system='TestComp'}} ~= 'cc0000' then
error('lineColor compatibility test failed: name=1.')
end
if fp.lineColor{args={name='1', prefix='1', system='NonExist'}} ~= '#[[:Template:NonExist color]]' then
error('lineColor compatibility test failed: name=1.')
end
-- lineTitle test
if p.lineTitle{args={name='1', system='UseCase'}} ~= "'''[[宁波轨道交通1号线|1号线]]'''" then
error('lineTitle test failed: name=1.')
end
if p.lineTitle{args={name='WRL', system='UseCase'}} ~= "'''[[西鐵綫|西鐵綫]]'''" then
error('lineTitle test failed: name=WRL.')
end
if p.lineTitle{args={name='WRL', link='0', system='UseCase'}} ~= "西鐵綫" then
error('lineTitle test failed: name=WRL, link=0.')
end
if fp.lineTitle{args={ name='1', system='TestComp'}} ~= '[[哈尔滨地铁1号线|1号线]]' then
error('lineTitle compatibility test failed.')
end
if fp.lineTitle{args={ name='1', system='NonExist'}} ~= '[[:Template:NonExist lines]]' then
error('lineTitle non compatibility test failed.')
end
-- lineRichTitle test
link, _match = string.gsub(p.DEFAULT_RICH_LINK_TEMPLATE, '\{line_link\}', '宁波轨道交通1号线')
link, _match = string.gsub(link, '\{line_title\}', '1号线')
link, _match = string.gsub(link, '\{line_color\}', '3180b7')
if p.lineRichTitle{args={name='1', system='UseCase'}} ~= link then
error('lineRichTitle test failed: name=1.')
end
-- degrade case
if p.lineRichTitle{args={name='S1', system='UseCase'}} ~= "'''[[宁波至余慈城际铁路|余慈线]]'''" then
error('lineRichTitle test failed: name=1.')
end
-- lineTerminal test
-- 仅此一例,其余测试覆盖由 lineTerminalName 完成
if fp.lineTerminal{args={name='S1', side='left', type='F', system='UseCase'}} ~= '[[宁波东站|宁波东站]]' then
error('lineTerminal test failed: name=S1, side=left, type=F.')
end
if fp.lineTerminal{args={ name='1', side='left', type='s1', branch='b1', system='TestComp'}} ~= '[[S1B1]]' then
error('lineTerminal compatibility test failed: name=S1, side=left, type=F.')
end
-- lineTerminalName test
if fp.lineTerminalName{args={ name='1', side='left', system='UseCase'}} ~= '高桥西' then
error('lineTerminalName test failed: name=1, side=left.')
end
if fp.lineTerminalName{args={ name='S1', side='left', system='UseCase'}} ~= '宁波站' then
error('lineTerminalName test failed: name=1, side=left.')
end
if fp.lineTerminalName{args={ name='S1', side='left', type='F', system='UseCase'}} ~= '宁波东站' then
error('lineTerminalName test failed: name=1, side=left.')
end
if fp.lineTerminalName{args={ name='S1', side='right', system='UseCase'}} ~= '余姚' then
error('lineTerminalName test failed: name=1, side=left.')
end
if fp.lineTerminalName{args={ name='S1', side='right', type='F', system='UseCase'}} ~= '马渚' then
error('lineTerminalName test failed: name=1, side=left.')
end
if fp.lineTerminalName{args={ name='S1', side='right', type='F', branch='HZW', system='UseCase'}} ~= '杭州湾' then
error('lineTerminalName test failed: name=1, side=left.')
end
-- lineTerminalName compatibility test
if fp.lineTerminalName{args={ name='1', side='left', ['type']='s1', branch='b1', system='TestComp'}} ~= 'S1B1' then
error('lineTerminalName compatibility test failed: type=s1, branch=b1.')
end
if fp.lineTerminalName{args={ name='1', side='left', ['type']='s1', system='TestComp'}} ~= 'S1DEFAULT' then
error('lineTerminalName compatibility test failed: type=s1.')
end
if fp.lineTerminalName{args={ name='1', side='left', ['type']='s2', system='TestComp'}} ~= 'DEFAULT' then
error('lineTerminalName compatibility test failed: type=s2.')
end
if fp.lineTerminalName{args={ name='1', side='left', system='NonExist'}} ~= '[[:Template:S-line/NonExist left/1]]' then
error('lineTerminalName compatibility test failed: type=NonExist.')
end
-- trainTime test
if p.trainTime{args={name='1', dir='霞高', ['type']='L', delta='0:3', system='UseCase'}} ~= '22:06' then
error('trainTime test failed: dir=霞高, type=L, delta=0:3.')
end
if p.trainTime{args={name='1', dir='高霞', ['type']='F', delta='0:4', system='UseCase'}} ~= '06:08' then
error('trainTime test failed: dir=高霞, type=F, delta=0:4.')
end
-- trainDirectionTime test
if fp.trainDirectionTime{args={name='1', dir='霞高', ['type']='L', delta='0:3', system='UseCase'}} ~= "往[[高桥西站 (宁波)|高桥西]]方向:22:06" then
error('trainDirectionTime test failed: dir=霞高, type=L, delta=0:3.')
end
if fp.trainDirectionTime{args={name='1', dir='高霞', ['type']='F', delta='0:4', system='UseCase'}} ~= "往[[霞浦站 (宁波)|霞浦]]方向:06:08" then
error('trainDirectionTime test failed: dir=高霞, type=F, delta=0:4.')
end
-- stationTrainTime test
if fp.stationTrainTime{args={'高霞', '0:3', name='1', system='UseCase'}} ~= "06:07-22:08" then
error('stationTrainTime test failed: 高霞, 0:3, name=1.')
end
if fp.stationTrainTime{args={'高霞', '0:3', '栎清', '0:1', name='1,2', system='UseCase'}} ~= "06:07-22:10" then
error('stationTrainTime test failed: 高霞, 0:3, name=1,2.')
end
-- lineDateMessage test
if p.lineDateMessage{args = {'在', 'year', '的', 'month', '的', 'day', ',月份', 'ym', ',日期', 'date', '开通', name='1', ['type']='2', cur_time = '2016-1-1', auto_hide=true, system='UseCase'}} ~= '在2016年的3月的19日,月份2016年3月,日期2016年3月19日开通' then
error('lineDateMessage test failed.')
end
if p.lineDateMessage{args = {'year', name='1', ['type']='2', cur_time = '2016-5-19', auto_hide=true, system='UseCase'}} ~= '2016年' then
error('lineDateMessage test failed.')
end
if p.lineDateMessage{args = {'year', name='1', ['type']='2', cur_time = '2017-3-19', auto_hide=true, system='UseCase'}} ~= p.DEFAULT_DATE_ELAPSED_TEXT then
error('lineDateMessage test failed.')
end
if p.lineDateMessage{args = {'ym', name='1', ['type']='2', cur_time = '2016-3-30', auto_hide=true, system='UseCase'}} ~= '2016年3月' then
error('lineDateMessage test failed.')
end
if p.lineDateMessage{args = {'ym', name='1', ['type']='2', cur_time = '2016-4-19', auto_hide=true, system='UseCase'}} ~= p.DEFAULT_DATE_ELAPSED_TEXT then
error('lineDateMessage test failed.')
end
if p.lineDateMessage{args = {'ymd', name='1', ['type']='2', cur_time = '2016-3-18', auto_hide=true, system='UseCase'}} ~= '2016年3月19日' then
error('lineDateMessage test failed.')
end
if p.lineDateMessage{args = {'ymd', name='1', ['type']='2', cur_time = '2016-3-19', auto_hide=true, system='UseCase'}} ~= p.DEFAULT_DATE_ELAPSED_TEXT then
error('lineDateMessage test failed.')
end
if p.lineDateMessage{args = {'ymd', name='S1', cur_time = '2016-3-18', auto_hide=true, system='UseCase'}} ~= '2019年12月28日' then
error('lineDateMessage test failed.')
end
if p.lineDateMessage{args = {'二期规划中', name='2', auto_hide=true, system='UseCase'}} ~= '二期规划中' then
error('lineDateMessage test failed.')
end
if p.lineDateMessage{args = {'ymd', name='2', auto_hide=true, system='UseCase'}} ~= p.DEFAULT_LINE_DATE_ABSENT_TEXT then
error('lineDateMessage test failed.')
end
local child_frame = frame:newChild{title='RenderStations', args={system='UseCase', '1号线从$$高桥西$$\n', '到$$霞浦$$\n'}}
if p.renderStationLinks(child_frame:newChild{title='renderStationLinks', args={}}) ~= '1号线从[[高桥西站 (宁波)|高桥西]]\n|到[[霞浦站 (宁波)|霞浦]]' then
error('renderStationLinks test failed: short=true.')
end
child_frame = frame:newChild{title='RenderStations', args={system='UseCase', short='0', '1号线从$$高桥西$$\n', '到$$霞浦$$\n'}}
if p.renderStationLinks(child_frame:newChild{title='renderStationLinks', args={}}) ~= '1号线从[[高桥西站 (宁波)|高桥西站]]\n|到[[霞浦站 (宁波)|霞浦站]]' then
error('renderStationLinks test failed: short=false.')
end
return '所有用例均通过!'
end
return p