Module:Tennis performance timeline
local p = {}
local concat = table.concat
local insert = table.insert
local format = mw.ustring.format
local tConfig = mw.loadData("Module:Tennis performance timeline/data")
local rounds = tConfig.rounds
local tournaments = tConfig.tournaments
local environments = tConfig.environments
local surfaces = tConfig.surfaces
local tOrders = tConfig.orders
local curYear = os.date("!*t").year
local calendar = mw.loadData("Module:Tennis performance timeline/calendar")
local genders = {
men = "Men's",
women = "Women's"
}
local matchTypes = {
singles = "Singles",
doubles = "Doubles"
}
--[[Utility functions]]
local function checkNonNil(value, type)
if value == nil then
error("Expected " .. type .. ", but is nil", 2)
end
return value
end
local function checkFormat(str, pattern, type)
if str == mw.ustring.match(str, pattern) then
return str
else
error("Invalid " .. type .. ": " .. str, 2)
end
end
local function checkYear(year)
checkNonNil(year, "year")
return checkFormat(year, "%d%d%d%d", "year")
end
local function checkNum(num, diagMsg)
diagMsg = "number" .. (diagMsg and " for " .. diagMsg or "")
checkNonNil(num, diagMsg);
return checkFormat(num, "%d+", diagMsg)
end
local function checkMember(elem, arr, type, diagMsg)
if arr[elem] then
return elem
else
diagMsg = type .. (diagMsg and " for " .. diagMsg or "")
checkNonNil(elem, diagMsg)
local message = {}
insert(message, "Invalid ")
insert(message, diagMsg)
insert(message, ": ")
insert(message, elem)
error(concat(message))
end
end
-- Format an HTML element with link, tooltip, and colors.
local function tooltip(tag, link, tooltip, text, spec)
spec = spec or {}
if spec.color then
tag:css('color', spec.color)
end
if spec.italic then
tag:wikitext("''");
end
if spec.bold then
tag:wikitext("'''");
end
if link then
tag:wikitext("[[" .. link .. "|")
end
if tooltip then
if spec.abbr then
tag:tag('abbr'):attr('title', tooltip):wikitext(text)
else
tag:attr('title', tooltip):wikitext(text)
end
else
tag:wikitext(text)
end
if link then tag:wikitext("]]") end
if spec.bold then
tag:wikitext("'''");
end
if spec.italic then
tag:wikitext("''");
end
end
-- Substitute "$[`param`]$" appearing in `str` with `value`.
-- For example, subst("$year$ ATP Tour", "year", "1990") -> "1990 ATP Tour"
local function subst(str, param, value)
if str == nil then return str end
return str:gsub("%$" .. param .. "%$", value)
end
local function tr()
return mw.html.create('tr')
end
local function th(row)
return row:tag('th')
end
local function td(row)
return row:tag('td')
end
local years
local data
local usedRounds
-- parser for data tables
local function parse(entry, year, tStats)
local entryType = type(entry)
if entryType == "string" then
return entry
elseif entryType == "table" then
if entry.type == "chrono" then
local numericYear = tonumber(year)
for _,elem in ipairs(entry) do
if type(elem) == "table" and numericYear >= elem[1] then
return parse(elem[2], year, tStats)
end
end
return parse(entry.default, year, tStats)
elseif entry.type == "switch" then
local param = entry.param
local arg = param == "year" and year or param == "gender" and data.gender or tStats[param]
if entry[arg] then return parse(entry[arg], year, tStats) end
return parse(entry.default, year, tStats)
else
return entry
end
end
end
-- Transform `param` entry in `array` with supplied data.
local function transform(array, param, data, tournament, year, country)
local entry = array[param]
entry = subst(entry, "gender", genders[data.gender])
entry = subst(entry, "matchType", matchTypes[data.matchType])
entry = subst(entry, "year", year)
if data[tournament] and data[tournament][year] then
local tStats = data[tournament][year]
if tournaments[tournament] and tournaments[tournament].region then
entry = subst(entry, "region",
parse(tournaments[tournament].region[country]
or tournaments[tournament].region.default, year, tStats))
end
if tournaments[tournament].group then
entry = subst(entry, "group", tournaments[tournament].group[tStats.group] or "")
end
end
array[param] = entry
end
-- Return wiki page title for the tournament in a given year.
local function annualTournamentLink(year, tStats, tInfo)
local annualLink = tInfo.annualLink
return parse(annualLink, year, tStats)
end
--[[-
Prepare the header row of the performance timeline table.
@return three elements:
- table header wikitext
- header row
- the first cell in the header row
]]
local function header()
local rows = {}
local row = tr()
local headerCell = th(row):attr('scope', 'col'):addClass('unsortable')
local tooltipSpec = {abbr = true}
if years.amateur or years.professional then
headerCell:attr('rowspan', 2)
local eras = {}
if years.amateur then
eras[#eras+1] = {years.amateur, "Amateur"}
end
if years.professional then
eras[#eras+1] = {years.professional, "Professional"}
end
eras[#eras+1] = {"1968", "Open Era"}
eras[#eras+1] = {tostring(years.last + 1)}
local lastEra = "?"
local idx = 1
for _,era in ipairs(eras) do
local count = 0
while years[idx] and years[idx] ~= era[1] do
count = count + 1
idx = idx + 1
end
if count > 0 then
th(row):attr('scope', 'col'):attr('colspan', count):wikitext(lastEra)
end
lastEra = era[2]
end
tooltip(th(row):attr('scope', 'col'):attr('rowspan', 2), nil, "Strike rate", "SR", tooltipSpec)
tooltip(th(row):attr('scope', 'col'):attr('rowspan', 2), nil, "Win–Loss", "W–L", tooltipSpec)
th(row):attr('scope', 'col'):attr('rowspan', 2):wikitext("Win %")
insert(rows, row)
row = tr()
end
if data.categories.count > 1 then
headerCell:attr('colspan', 2):wikitext("Tournament")
end
for _,year in ipairs(years) do
local link = subst(parse(tConfig.tours.link, year), "year", year)
th(row):attr('scope', 'col'):addClass('unsortable')
:wikitext(link and format("[[%s|%s]]", link, year) or year)
end
if #rows == 0 then
tooltip(th(row):attr('scope', 'col'), nil, "Strike rate", "SR", tooltipSpec)
tooltip(th(row):attr('scope', 'col'), nil, "Win–Loss", "W–L", tooltipSpec)
th(row):attr('scope', 'col'):wikitext("Win %")
end
insert(rows, row)
-- Add hatnote if needed.
local headerText = {}
if years.last and data.last and tournaments[data.last] then
local tInfo = tournaments[data.last]
local annualLink = {link = annualTournamentLink(years.last, nil, tInfo)}
transform(annualLink, "link", data, data.last, years.last)
local hatnote = {}
insert(hatnote, "''This table includes results through the conclusion of the [[")
annualLink = annualLink.link:gsub(" – .*$", "")
local annualName = annualLink
local annualSubst = tInfo.annualSubst or {}
for _,subst in ipairs(annualSubst) do
annualName = annualName:gsub(subst[1], subst[2])
end
if annualLink ~= annualName then
insert(hatnote, annualLink)
insert(hatnote, "|")
end
insert(hatnote, annualName)
insert(hatnote, "]].''")
insert(headerText, concat(hatnote))
end
insert(headerText, "{|class=\"plainrowheaders wikitable sortable\" style=text-align:center")
return concat(headerText, "\n"), rows, headerCell
end
local function outputSpan(row, span, seenRounds)
local cell = td(row):attr('colspan', span.span)
tooltip(cell, nil,
span.span < span.info.minSpellCols and span.info.tooltip,
span.span >= span.info.minSpellCols and span.info.tooltip or span.round,
span.info)
if seenRounds and span.span < span.info.minSpellCols then
seenRounds[span.info.round] = span.info
end
end
local footnoteCount
-- Return the year a given tournament was first held.
local function eventFirstYear(entries, yearInfos, year, yearEntry)
year = tonumber(year)
local startYear
if #entries > 0 then
local endYear = entries[#entries][4]
for testYear = endYear + 1, year do
local testYearTournament = parse(yearInfos, testYear)
if testYearTournament ~= entries[#entries][1] then
entries[#entries][4] = testYear - 1
break;
end
end
for testYear = year, endYear, -1 do
local testYearTournament = parse(yearInfos, testYear)
if testYearTournament ~= yearEntry then
startYear = testYear + 1
break;
end
end
end
return startYear
end
local frame_
-- Format a footnote.
local function footnoteText(footnoteChunks, wikilinkFn)
if #footnoteChunks > 1 then
local footnote = {}
local footnoteChunk = {"Held as"}
for pos,entry in ipairs(footnoteChunks) do
local link, name, abbr = wikilinkFn(entry)
insert(footnoteChunk, format("[[%s%s]]",
link and link .. "|" or "", name))
-- Add abbr if doesn't appear in name
if abbr and not name:match(abbr) then
insert(footnoteChunk, "(" .. abbr .. ")")
end
if entry[3] == entry[4] then
-- One-year event
insert(footnoteChunk, "in")
insert(footnoteChunk, entry[3])
else
if pos > 1 then
insert(footnoteChunk, "from")
insert(footnoteChunk, entry[3])
end
if pos < #footnoteChunks then
insert(footnoteChunk, pos == 1 and "until" or "to")
insert(footnoteChunk, entry[4])
end
end
insert(footnote, concat(footnoteChunk, (" ")))
footnoteChunk = {}
end
footnote[#footnote] = "and " .. footnote[#footnote]
return frame_:callParserFunction{name = '#tag:ref', args = {
concat(footnote, #footnote > 2 and ", " or " "),
group = "lower-alpha"
}}
end
end
local function winLossStatsSummary(row, stats, boldmarkup, summaryRowspan)
boldmarkup = boldmarkup or ""
-- − is (nonbreaking) minus sign.
local srCell = td(row):attr('data-sort-value', stats.count > 0 and stats.champs / stats.count or -1)
:addClass('nowrap')
:wikitext(format("%s%d / %d%s", boldmarkup, stats.champs, stats.count, boldmarkup))
local matches = stats.wins + stats.losses
local wlCell = td(row):attr('data-sort-value', matches > 0 and stats.wins / matches or -1)
local wrCell = td(row)
if summaryRowspan then
wlCell:attr('rowspan', summaryRowspan)
wrCell:attr('rowspan', summaryRowspan)
srCell:attr('rowspan', summaryRowspan)
end
if matches > 0 then
wlCell:wikitext(format("%s%d–%d%s", boldmarkup, stats.wins, stats.losses, boldmarkup))
wlCell:addClass("nowrap")
wrCell:wikitext(format("%s%.2f%%%s", boldmarkup, stats.wins * 100 / matches, boldmarkup))
else
wlCell:wikitext(format("%s–%s", boldmarkup, boldmarkup))
wrCell:attr('data-sort-value', '-1%')
:wikitext(format("%s–%s", boldmarkup, boldmarkup))
end
end
-- Prepare a Win-Loss row in the performance timeline table.
local function winLossStatsRow(row, info, stats, bold, summaryRowspan)
local boldmarkup = bold and "'''" or ""
local headerName = info.name and info.name .. " " or ""
th(row):attr('scope', 'row')
:wikitext(format("%s%sWin–Loss%s", boldmarkup, headerName, boldmarkup))
local span
for _,year in ipairs(years) do
local yStats = stats[year]
local display = {}
if yStats and yStats.wins + yStats.losses > 0 then
display.text = format("%s%d–%d%s", boldmarkup, yStats.wins, yStats.losses, boldmarkup)
bg_col = 'background-color:#EAECF0'
else
display.text = format("%s–%s", boldmarkup, boldmarkup)
bg_col = 'background-color:#EAECF0'
if info.absence then
local aConfig = parse(info.absence, year)
if aConfig then
display.text = aConfig.round
display.config = aConfig
end
end
end
if span then
if span.info.round == display.text then
span.span = span.span + 1
else
outputSpan(row, span)
span = nil
end
end
if not span then
if display.config and display.config.span then
span = {round = display.text, span = 1, info = display.config}
else
td(row):wikitext(display.text)
:addClass("nowrap")
end
end
end
if span then
outputSpan(row, span)
span = nil
end
winLossStatsSummary(row, stats, boldmarkup, summaryRowspan)
end
-- Return true if the player appears in a given tournament.
local function hasTournamentAppearance(tournament)
local tournamentType = type(tournament)
if tournamentType == "string" then
if data[tournament] then
return true
end
elseif tournamentType == "table" then
if tournament.type == "chrono" then
for _,entry in ipairs(tournament) do
if data[entry[2]] then
return true
end
end
if data[tournament.default] then
return true
end
else
-- TODO other table type
end
end
return false
end
-- Create a fresh statistics table.
local function statsFactory()
return {count = 0, champs = 0, finals = 0, wins = 0, losses = 0}
end
-- Generate performance timeline rows for a given tournament level.
local function body(level, levelHeaderCell)
local entries = {}
local stats = statsFactory()
local levelInfo = parse(tOrders[level])
local levelInfos = {}
local levelLastAppearance
for pos,tournament in ipairs(levelInfo) do
if hasTournamentAppearance(tournament) then
local tStats = statsFactory()
local row = tr()
if #entries == 0 and data.categories.count > 1 then
levelHeaderCell = th(row):attr('scope', 'row')
:css('width', '6.5em')
:css('max-width', '10em')
end
local headerCell = th(row):attr('scope', 'row')
local tInfos = {}
local lastAppearance
local seenRounds = {}
local span
local country = data.country.default
for _,year in ipairs(years) do
country = data.country[year] or country
local yearLevelName = parse(levelInfo.name or levelInfo.tooltip, year)
if #entries == 0 and (#levelInfos == 0 or levelInfos[#levelInfos][1] ~= yearLevelName) then
-- Add footnote noting series transition.
local startYear = eventFirstYear(levelInfos, levelInfo.name or levelInfo.tooltip, year, yearLevelName)
insert(levelInfos, {
yearLevelName,
{link = parse(levelInfo.link, year), abbr = parse(levelInfo.abbr, year)},
startYear, tonumber(year)
})
else
levelInfos[#levelInfos][4] = tonumber(year)
end
local yearTournament = parse(tournament, year)
local tInfo = checkNonNil(tournaments[yearTournament], "entry for " .. yearTournament)
if #tInfos == 0 or tInfos[#tInfos][2] ~= tInfo then
-- Add footnote noting tournament transition.
local startYear = eventFirstYear(tInfos, tournament, year, yearTournament)
insert(tInfos, {yearTournament, tInfo, startYear, tonumber(year)})
else
tInfos[#tInfos][4] = tonumber(year)
end
local display = {}
if data[yearTournament] and data[yearTournament][year] then
if not levelLastAppearance or levelLastAppearance < year then
levelLastAppearance = year
end
lastAppearance = tInfo
local tyStats = data[yearTournament][year]
if not rounds[tyStats.round].nocount then
tStats.count = tStats.count + 1
stats.count = stats.count + 1
end
if rounds[tyStats.round].strike then
tStats.champs = tStats.champs + 1
stats.champs = stats.champs + 1
end
if not stats[year] then
stats[year] = statsFactory()
end
tStats.wins = tStats.wins + tyStats.wins
stats[year].wins = stats[year].wins + tyStats.wins
stats.wins = stats.wins + tyStats.wins
tStats.losses = tStats.losses + tyStats.losses
stats[year].losses = stats[year].losses + tyStats.losses
stats.losses = stats.losses + tyStats.losses
local annualLink = annualTournamentLink(year, tyStats, tInfo)
display.round = tyStats.round
display.group = tyStats.group
display.link = annualLink
transform(display, "link", data, yearTournament, year, country)
else
display.round = parse(tInfo.absence, year) or "A"
end
local round = rounds[display.round]
if round.absence then
local absence = false
if year < years.last or not data.last and tonumber(year) < curYear then
absence = true
elseif year == years.last and data.last and calendar[year] and calendar[year][data.gender] then
local tWeek = calendar[year][data.gender].week[yearTournament]
local lWeek = calendar[year][data.gender].week[data.last]
if tWeek and lWeek and tWeek <= lWeek then
absence = true
end
end
if not absence then display.round = nil end
end
local roundInfo = {}
setmetatable(roundInfo, {__index = rounds[display.round]})
transform(roundInfo, "tooltip", data, yearTournament, year)
if roundInfo.group then
roundInfo.round = display.round .. display.group
else
roundInfo.round = display.round
end
display.round = roundInfo.name or roundInfo.round
if span then
if span.round == display.round then
span.span = span.span + 1
else
outputSpan(row, span, seenRounds)
span = nil
end
end
if not span then
if roundInfo.span then
span = {round = display.round, span = 1, info = roundInfo}
else
local cell = td(row)
if roundInfo.round then
if roundInfo.bgcolor then
cell:css('background', roundInfo.bgcolor)
end
tooltip(cell, display.link, roundInfo.tooltip, display.round, roundInfo)
seenRounds[roundInfo.round] = roundInfo
end
end
end
end
if span then
outputSpan(row, span, seenRounds)
span = nil
end
if lastAppearance then
headerCell:wikitext(
format("[[%s%s]]",
lastAppearance.link and lastAppearance.link .. "|" or
lastAppearance.abbr and lastAppearance.name .. "|" or "",
lastAppearance.abbr or lastAppearance.name))
if #tInfos > 1 then
local footnote = footnoteText(tInfos,
function(entry)
local tInfo = entry[2]
return tInfo.link, tInfo.name, tInfo.abbr
end)
headerCell:wikitext(footnote)
footnoteCount = footnoteCount + 1
end
winLossStatsSummary(row, tStats)
insert(entries, row)
for _,roundInfo in pairs(seenRounds) do
local round = roundInfo.name and not usedRounds[roundInfo.name] and roundInfo.name or roundInfo.round
usedRounds[round] = roundInfo
end
end
end
end
if #entries == 0 then return nil end
if levelHeaderCell then
local levelLink = parse(levelInfo.link, levelLastAppearance)
local levelName = parse(levelInfo.name, levelLastAppearance)
local levelAbbr = parse(levelInfo.abbr, levelLastAppearance)
local levelTooltip = parse(levelInfo.tooltip, levelLastAppearance)
local levelString = levelAbbr or levelName
if data.categories.count > 1 then
levelHeaderCell:attr('rowspan', #entries + 1)
end
tooltip(levelHeaderCell, levelLink or levelAbbr and levelName,
levelTooltip, levelString, {bold = true, abbr = true})
if #levelInfos > 1 then
local footnote = footnoteText(levelInfos,
function(entry)
return entry[2].link, entry[1], entry[2].abbr
end)
levelHeaderCell:wikitext(footnote)
footnoteCount = footnoteCount + 1
end
end
local row = tr()
winLossStatsRow(row, {}, stats, true)
insert(entries, row)
local result = {}
for _,entry in ipairs(entries) do
insert(result, tostring(entry))
end
return concat(result, "\n")
end
-- Generate rows for career performance timeline.
local function summary(envSummary, headerCell)
local entries = {}
local stats = statsFactory()
local environmentInfo = tOrders.environments
local surfaceInfo = tOrders.surfaces
local surfaceCount = 0
for _,environment in ipairs(environmentInfo) do
if data[environment] then
for _,surface in ipairs(surfaceInfo) do
if data[environment][surface] then
surfaceCount = surfaceCount + 1
end
end
end
end
if surfaceCount == 0 then return nil end
-- Aggregate data.
local eStats = {}
local sStats = {}
for _,env in ipairs(environmentInfo) do
if data[env] then
for _,surface in ipairs(surfaceInfo) do
if data[env][surface] then
for _,year in ipairs(years) do
local esyStats = data[env][surface][year]
if esyStats then
if not eStats[env] then
eStats[env] = statsFactory()
end
if not eStats[env][year] then
eStats[env][year] = statsFactory()
end
if not sStats[surface] then
sStats[surface] = statsFactory()
end
if not sStats[surface][year] then
sStats[surface][year] = statsFactory()
end
if not stats[year] then
stats[year] = statsFactory()
end
for key,value in pairs(esyStats) do
sStats[surface][year][key] = sStats[surface][year][key] + value
sStats[surface][key] = sStats[surface][key] + value
eStats[env][year][key] = eStats[env][year][key] + value
eStats[env][key] = eStats[env][key] + value
stats[year][key] = stats[year][key] + value
stats[key] = stats[key] + value
end
end
end
end
end
end
end
local function venueWinLossStatsRow(venues, vInfo, vStats)
for _,venue in ipairs(venues) do
if vStats[venue] then
local row = tr()
if #entries == 0 and data.categories.count > 1 then
-- Add header cell.
headerCell = th(row):attr('scope', 'row')
end
winLossStatsRow(row, vInfo[venue], vStats[venue])
insert(entries, row)
end
end
end
venueWinLossStatsRow(surfaceInfo, surfaces, sStats)
if envSummary then
venueWinLossStatsRow(environmentInfo, environments, eStats)
end
local row = tr()
winLossStatsRow(row, {name = "Overall"}, stats, true, 2)
insert(entries, row)
local row = tr()
local wrHdrCell = th(row):attr('scope', 'row'):wikitext("'''Win %'''")
for _,year in ipairs(years) do
local cellContent = "'''–'''"
if stats[year] then
local wins = stats[year].wins
local losses = stats[year].losses
local matches = wins + losses
if matches > 0 then
cellContent = format("'''%.1f%%'''", wins * 100 / matches)
end
end
td(row):wikitext(cellContent)
end
insert(entries, row)
if headerCell then
if data.categories.count > 1 then
headerCell:attr('rowspan', #entries)
end
headerCell:wikitext("'''Career'''")
end
local function counterStatsRow(row, name, type, bold)
local boldmarkup = bold and "'''" or ""
local rowHdrCell = th(row):attr('scope', 'row'):css('text-align', 'right')
:wikitext(format("%s%s%s", boldmarkup, name, boldmarkup))
if data.categories.count > 1 then rowHdrCell:attr('colspan', 2) end
for _,year in ipairs(years) do
td(row):wikitext(format("%s%s%s",
boldmarkup,
stats[year] and tostring(stats[year][type]) or "–",
boldmarkup))
end
td(row):attr('colspan', 2)
:wikitext(format("%s%d total%s", boldmarkup, stats[type], boldmarkup))
end
local row = tr():addClass('sortbottom')
counterStatsRow(row, "Tournaments played", "count")
td(row):wikitext(format("'''%.1f%%'''", stats.champs * 100 / stats.count))
insert(entries, row)
if stats.finals > 0 then
local row = tr():addClass('sortbottom')
counterStatsRow(row, "Finals reached", "finals")
td(row):attr('rowspan', 2)
:wikitext(format("'''%.1f%%'''", stats.champs * 100 / stats.finals))
insert(entries, row)
local row = tr():addClass('sortbottom')
counterStatsRow(row, "Titles", "champs", true)
insert(entries, row)
end
local row = tr():addClass('sortbottom')
local yearEndHdrCell = th(row):attr('scope', 'row'):css('text-align', 'right')
:wikitext("'''Year-end ranking'''")
if data.categories.count > 1 then yearEndHdrCell:attr('colspan', 2) end
for _,year in ipairs(years) do
local cell = td(row)
if data.rank and data.rank[year] then
local rank = data.rank[year]
local rankConfig = tConfig.rankings[rank] or {}
if rankConfig.bgcolor then
cell:css('background', rankConfig.bgcolor)
end
local boldmarkup = rankConfig.bold and "'''" or ""
cell:wikitext(format("%s%s%s", boldmarkup, rank, boldmarkup))
end
end
local cell = td(row):attr('colspan', 3)
if data.prizemoney then
tooltip(cell, nil, "Career prize money", data.prizemoney, {bold = true, abbr = true})
end
insert(entries, row)
local result = {}
for _,entry in ipairs(entries) do
insert(result, tostring(entry))
end
return concat(result, "\n")
end
-- Generate wikitext to conclude performance timeline table, including footnotes.
local function footer()
local result = {}
insert(result, "|-\n|}")
if footnoteCount > 0 then
local reflistTag = mw.html.create("div")
reflistTag:addClass("reflist"):css('list-style-type', 'lower-alpha')
:wikitext(frame_:callParserFunction{
name = '#tag:references', args = {"", group = "lower-alpha"}
})
insert(result, tostring(reflistTag))
end
return concat(result, "\n")
end
-- Return true if the player appears in a given tournament level.
local function hasLevelAppearance(level)
local levelInfo = parse(tOrders[level])
for _,tournament in ipairs(levelInfo) do
if hasTournamentAppearance(tournament) then return true end
end
return false
end
function p._main(args, frame)
frame_ = frame
data = {}
years = {}
usedRounds = {}
footnoteCount = 0
data.gender = args.gender or "men"
data.matchType = args.matchType or "singles"
data.country = {}
data.country.default = args.country or "UNK"
local idx = 1
local environmentSummary = true
local year
while args[idx] do
local arg = args[idx]
if arg == "year" then
idx = idx + 1
year = checkYear(args[idx])
if years.last and year <= years.last then
error(format("Nonincreasing year: %s appears after %s", year, years.last))
end
years.last = year
insert(years, year)
elseif arg == "country" then
idx = idx + 1
local country = checkNonNil(args[idx], year .. " country")
data.country[year] = args[idx]
elseif arg == "amateur" then
years.amateur = year
elseif arg == "professional" then
years.professional = year
elseif tournaments[arg] then
local tournament = arg
local diagMsg = year .." " .. tournament
local tStats = {}
idx = idx + 1
local round = args[idx]
-- Handle zones for Davis Cup.
if round and round:sub(1, 2) == "WG" then
tStats.round = "WG"
tStats.group = round:sub(3)
elseif round and round:sub(1, 2) == "PO" then
tStats.round = "PO"
tStats.group = round:sub(3)
elseif round and round:sub(1, 1) == "Z" then
tStats.round = "Z"
tStats.group = checkNum(round:sub(2), diagMsg .. " zone")
else
tStats.round = round
end
checkMember(tStats.round, rounds, "round", diagMsg)
idx = idx + 1
tStats.wins = checkNum(args[idx], diagMsg)
idx = idx + 1
tStats.losses = checkNum(args[idx], diagMsg)
if data[tournament] == nil then data[tournament] = {} end
data[tournament][year] = tStats
elseif environments[arg] then
local environment = arg
local diagMsg = year .. " " .. " " .. environment
idx = idx + 1
local surface = checkNonNil(args[idx], diagMsg .. " surface")
if surfaces[surface] then
diagMsg = diagMsg .. " " .. surface
local sStats = {}
idx = idx + 1
sStats.count = checkNum(args[idx], diagMsg)
idx = idx + 1
sStats.wins = checkNum(args[idx], diagMsg)
idx = idx + 1
sStats.losses = checkNum(args[idx], diagMsg)
idx = idx + 1
sStats.champs = checkNum(args[idx], diagMsg)
idx = idx + 1
sStats.finals = sStats.champs + checkNum(args[idx], diagMsg)
if data[environment] == nil then data[environment] = {} end
if data[environment][surface] == nil then
data[environment][surface] = {}
end
data[environment][surface][year] = sStats
else
error(format("Unknown surface (%s %s): %s", year, environment, arg))
end
elseif surfaces[arg] then
local surface = arg
local diagMsg = year .. " " .. surface
local sStats = {}
idx = idx + 1
sStats.count = checkNum(args[idx], diagMsg)
idx = idx + 1
sStats.wins = checkNum(args[idx], diagMsg)
idx = idx + 1
sStats.losses = checkNum(args[idx], diagMsg)
idx = idx + 1
sStats.champs = checkNum(args[idx], diagMsg)
idx = idx + 1
sStats.finals = sStats.champs + checkNum(args[idx], diagMsg)
if data.outdoor == nil then data.outdoor = {} end
if data["outdoor"][surface] == nil then data["outdoor"][surface] = {} end
data["outdoor"][surface][year] = sStats
-- Disable summary by environment.
environmentSummary = false
elseif arg == "rank" then
idx = idx + 1
if data.rank == nil then data.rank = {} end
data.rank[year] = checkNum(args[idx], year .. " rank")
else
error(format("Unknown argument at position %d (%s): %s", idx, year, arg))
end
idx = idx + 1
end
data.prizemoney = args.prizemoney
data.last = args.last
data.categories = {}
if args.types then
local count = 0
for _,type in ipairs(mw.text.split(args.types, ",")) do
if type == "Career" or tConfig.orders[type] and hasLevelAppearance(type) then
data.categories[type] = true
count = count + 1
end
end
data.categories.count = count
else
local count = 0
for _,type in ipairs(parse(tConfig.orders.order)) do
if hasLevelAppearance(type) then
data.categories[type] = true
count = count + 1
end
end
data.categories.Career = true
data.categories.count = count + 1
end
local result = {}
local tableHeader, headerRows, headerCell = header()
insert(result, tableHeader)
local function insertHeaderRowsIfNeeded()
if #result == 1 then
for _,headerRow in ipairs(headerRows) do
insert(result, tostring(headerRow))
end
end
end
for _,level in ipairs(parse(tConfig.orders.order)) do
if data.categories[level] then
local levelRows = body(level, data.categories.count == 1 and headerCell)
insertHeaderRowsIfNeeded()
insert(result, levelRows)
end
end
if data.categories.Career then
local careerRows = summary(environmentSummary, data.categories.count == 1 and headerCell)
insertHeaderRowsIfNeeded()
if careerRows then insert(result, careerRows) end
end
insert(result, footer())
return concat(result, "\n")
end
function p.main(frame)
-- Import module function to work with passed arguments
local getArgs = require('Module:Arguments').getArgs
local args = getArgs(frame)
return p._main(args, frame)
end
return p
Content Disclaimer
Informasi ini disarikan dari Wikipedia dan disajikan kembali untuk tujuan edukasi. Konten tersedia di bawah lisensi CC BY-SA 3.0. Kami tidak bertanggung jawab atas ketidakakuratan data yang bersumber dari kontribusi publik tersebut.
- The information displayed on this website is sourced in part or in whole from Wikipedia and has been adapted for the purpose of restating it. We strive to provide accurate and relevant information, however:
- There is no guarantee of absolute accuracy. Wikipedia is an open, collaborative project that can be edited by anyone, so information is subject to change.
- It is not intended to constitute professional advice. The content displayed is for informational and educational purposes only. For important decisions (e.g., medical, legal, or financial), please consult a professional.
- Content copyright. Wikipedia is licensed under the Creative Commons Attribution-ShareAlike License (CC BY-SA). This means that content may be reused with appropriate attribution and shared under a similar license.
- Responsible use. Any risk arising from the use of information from this website is entirely the responsibility of the user.