Mô đun:Goalscorers

require('strict');
local yesno = require('Module:Yesno')

local p = {}
local g = {} -- for parameters with global scope in this module
g.goalscorers = {} -- table where selected and sorted players will be place
g.args = {}
g.totalGoals = 0
local data = {} -- module subpage data -- require('Module:Goalscorers/data/UEFA Euro 2016 qualifying');

p.errorString = ""
function p.error_msg()
if p.errorString ~= "" then
return
-- '&124;_template='
.. p.errorString .. ;
end
end
-- data for goals scored held in module subpages, e.g. "Module:Goalscorers/data/UEFA Euro 2016 qualifying"
-- parameters containing data help in three tables
data.rounds = {} -- group, play-off
data.goalscorers = {} -- player, country, goals in each round)
data.owngoalscorers = {} -- player, country, goals in each round)
data.updated = {} -- date of latest update (month, day, year)
--

-- Parameter handing
p.getArgs() - gets arguments from frame (invoke) or parent frame (template)

local function getArgs(frame)
local parents = mw.getCurrentFrame():getParent()

for k,v in pairs(parents.args) do
--check content
if v and v ~= "" then
g.args[k]=mw.text.trim(v) --parents.args[k]
end
end
for k,v in pairs(frame.args) do
--check content
if v and v ~= "" then
g.args[k]= mw.text.trim(v) --parents.args[k]
end
end
-- allow empty caption to blank default
--if parents.args['caption'] then templateArgs['caption'] = parents.args['caption'] end
end

-- Main function and other functions
p.main() - simple output of the data in the module in list form
p.addIntroductorySentence() - add sentence on number of goals and matches, with goals per match
p.addFooterSentence() - add footnote
p.getNumberMatches()
p.owngoals() - get own goals (no longer used?)
p._owngoals() - core functionality for p.owngoals()

function p.main(frame)
getArgs(frame)
local dataTarget = g.args[1] or g.args['data']
if dataTarget then
data = require('Module:Goalscorers/data/'.. dataTarget) --or 'UEFA Euro 2016 qualifying'
return p.useModuleData(frame) -- data on goals taken from module subpage
else
return p.useTemplateData(frame) -- data on goals/assists taken from template
end

end
function p.useModuleData(frame)

--p.goalscorers = {} -- table where selected and sorted players will be place
g.totalGoals = 0
local ok = p.selectGoalscorers() -- selected goalscorers meeting round and group criteris
if not ok then return p.error_msg() end

-- CHANGE: append own goals to list (data will now include goals and own goals (negative))
p.selectGoalscorers("OG")

p.sortGoalscorers() -- sort selected goalscorers by number of goal, then country

local outputString = p.addIntroductorySentence() .. p.outputGoalscorers(frame) .. p.addFooterSentence()
-- .. "" --TODO add intermediate heading?
-- .. p._owngoals(frame) -- output list of goalscorers

return p.error_msg() or outputString
end

function p.addIntroductorySentence() -- add introductory text

local totalGoalString = "Tổng cộng có " .. g.totalGoals .. " bàn thắng đã ghi được."
--[Đang|Đang|Đã|Đã] có GOALS bàn thắng ghi được trong MATCHES trận đấu, trung bình GOALS/MATCHES bàn thắng mỗi trận đấu.

local matches, dateUpdated = p.getNumberMatches()
local mdyFormat = yesno(g.args['dmy'])

local Date = require('Module:Date')._Date

local pluralGoals = ""
local text1 = ""
if g.totalGoals == 1 then
pluralGoals = ""
if dateUpdated == 'complete' then text1 = "Đã" else text1 = "Đang" end
else
if dateUpdated == 'complete' then text1 = "Đã" else text1 = "Đang" end
end
local text = string.format("%s có %s bàn thắng%s ghi được", text1, mw.getLanguage('vi'):formatNum(g.totalGoals), pluralGoals)

local pluralMatches = ""
if matches==1 then pluralMatches = "" end
if matches then
local average = g.totalGoals/tonumber(matches)
local precision = 3 -- display d.dd (three significant disgits)
if average ' .. footerSentence ..
end
return footerSentence
end
function p.getNumberMatches()
local matches = g.args['matches']
local dateUpdated = data.updated['date'] or "1700-01-01" --'complete' -- assume completed if missing

--local round = g.args['round'] or "all" -- round = all(empty)|group|playoffs
--local group = g.args['group'] or "all" -- group = all(empty), A,B,C etc
local round, group = p.getRoundAndGroup()

local allGroupGames = 0
local latestGroupDate = "1800-01-01"
if group and (round "all" or group "all") then -- count all the group games
for k,v in pairs(data.updated.group) do
allGroupGames = allGroupGames + v[1]
if v[2] ~= "complete" and v[2] > latestGroupDate then latestGroupDate = v[2] end -- update if later date
end
if latestGroupDate == "1800-01-01" then latestGroupDate = "complete" end -- no dates so must be complete
end
if group and (round == "all" and group ~= "all") then -- for totals of all rounds with only one group
allGroupGames = data.updated.group[group][1] -- number matches
latestGroupDate = data.updated.group[group][2] -- update date or completed
end
if round == "all" then -- all rounds and goals
matches=0
for k,v in pairs(data.updated) do
if k == "group" then
matches = matches + allGroupGames
if latestGroupDate ~= "complete" and latestGroupDate > dateUpdated then
dateUpdated = latestGroupDate -- update if later date
end
elseif p.validateRound(k) then
matches = matches + v[1]
if v[2] ~= "complete" and v[2] > dateUpdated then dateUpdated = v[2] end -- update if later date
end

end
elseif round "group" then -- group round only
if group "all" then
matches = allGroupGames
dateUpdated = latestGroupDate
else -- single group only
matches = data.updated.group[group][1] -- number matches
dateUpdated = data.updated.group[group][2] -- update date or completed
end
else -- any other round
matches = data.updated[round][1] -- number matches
dateUpdated = data.updated[round][2] -- update date or completed
end

if dateUpdated == "1700-01-01" then dateUpdated = "complete" end -- no dates so must be complete

return matches, dateUpdated
end

function p.owngoals(frame) -- need to check parameters if external call
getArgs(frame)
data = require('Module:Goalscorers/data/'.. g.args[1]) --or 'UEFA Euro 2016 qualifying'

local outputString = p._owngoals(frame)
return p.error_msg() or outputString
end
function p._owngoals(frame) -- internal call for own goals

--p.goalscorers = {} -- table where selected and sorted players will be place

p.selectGoalscorers("OG") -- selected goalscorers meeting round and group criteris

p.sortGoalscorers() -- sort selected goalscorers by number of goal, then country

return p.outputGoalscorers(frame, "OG") -- output list of goalscorers

end
function p.validateRound(round)
local validateRound = false
for k,v in pairs(data.rounds) do
if k == round then validateRound = true end -- data for this round exists
end
return validateRound
end

-- functions to select goalscorers
p.selectGoalscorers() - select goals scoreers required for list (rounds, groups)
p.getRoundAndGroup()
p.getGoalsCol(round) - get column containing round data or first data column if round = all
(country, possibleGroup)
p.getGoals (u, player)
p.parseComment(comment)
p.getPlayer(u)

-- p.selectGoalscorers()
- select players meeting round and group criteria from goalscoreres list
- gets goals and comments

function p.selectGoalscorers(og)

local round, group = p.getRoundAndGroup()
if not round then return false end -- exit if no valid round

local goalMinimum = tonumber(g.args['minimum']) or -5 -- assume 5 own goals is maximum
local goalsCol = p.getGoalsCol(round) -- first column for goals

-- select players who have scored in rounds/groups requested
local goalscorerData = data.goalscorers
if og == "OG" then goalscorerData = data.owngoalscorers end

for k,v in pairs(goalscorerData) do
local goals, comment = 0, "" -- goals > 0 is the flag to include the player
local playerName, playerAlias = p.getPlayer(v[1]) -- player name
local goalsByRound, commentByRound = 0, ""

if round == "all" then -- goals in all rounds and all groups

for i = goalsCol, v, 1 do
if group and group ~= "all" and i == p.getGoalsCol("group") and group ~= p.getGroup(v[2], v[3]) then
goalsByRound = 0
commentByRound = ""
else
goalsByRound, commentByRound = p.getGoals( v[i] , playerName)
end

goals = goals + goalsByRound --TODO use getGoals on round options
if commentByRound ~= "" then
if comment == "" then
comment = commentByRound
else
comment = comment .. "," .. commentByRound --TODO decide on comma or semi-colon
end
end
i = i+1
end
elseif round == "all2" and group ~= "all" then -- goals in all rounds but only from one group

--TODO code to go through all rounds but only include goals in specified group [TODO merge with above option]
--mw.addWarning( g.args[1] .. ":Mix:round=all and group=" .. group .. "/" .. p.getGroup(v[2], v[3] ) )
for i = goalsCol, v, 1 do
if i == p.getGoalsCol("group") and group ~= p.getGroup(v[2], v[3]) then
goalsByRound = 0
commentByRound = ""
else
goalsByRound, commentByRound = p.getGoals( v[i] , playerName)
end
goals = goals + goalsByRound
if commentByRound ~= "" then
if comment == "" then
comment = commentByRound
else
comment = comment .. "," .. commentByRound --TODO decide on comma or semi-colon
end
end
i = i+1
end
elseif round "group" then -- group round only
if group p.getGroup(v[2], v[3]) then -- single group only
goals, comment = p.getGoals( v[goalsCol] , playerName)
elseif group == "all" then -- any group
goals, comment = p.getGoals( v[goalsCol] , playerName)
else
-- do nothing for other groups
end
--elseif round == "playoffs" then -- playoff round (redunant?)
-- goals = v[goalsCol]
else -- any other round
goals, comment = p.getGoals( v[goalsCol] , playerName) -- should also handle playoffs
end
if goals >= goalMinimum and goals ~= 0 then
if comment ~= "" then
if og == "OG" then
comment = ' (' .. p.sortComment(comment) .. ')'
else
comment = .. comment .. -- no parenthesis when using notes
end
end

if og == "OG" then goals = -goals end -- make owngoals negative numbers

g.goalscorers[g.goalscorers+1] = { player=playerName, alias=playerAlias,
country=v[2],
goals=goals,
comment=p.parseComment(comment)}
--g.totalGoals = g.totalGoals + math.abs(goals) -- increment total goal counter
end
g.totalGoals = g.totalGoals + math.abs(goals) -- increment total goal counter
end
return true -- data collected for selected goalscorers
end
-- p.getRoundAndGroup()

function p.getRoundAndGroup()
local round = g.args['round'] or "all" -- round = all(empty)|group|playoffs
local group = g.args['group'] or "all" -- group = all(empty), A,B,C etc

local validateRound = false
local validateGroupRound = false
for k,v in pairs(data.rounds) do
if k == round then validateRound = true end -- data for this round exists
if k == "group" then validateGroupRound = true end -- there is a group round
end
if validateRound == false and round ~= "all" then
local message = 'Invalid round "' .. round .. '" specified. No data found for that round. '
mw.addWarning( message )
p.errorString = p.errorString .. message
round = nil
end
if validateGroupRound == false then group = false end -- there is no group round

-- TODO add group error checking
-- Could merge with getGoalsCol() and also return goalsCol
return round, group
end

-- p.getGoalsCol(round)
- get column containing round data or first data column if round = "all"
- allows group column to be omitted from player table when group table provided

function p.getGoalsCol(round)

local minimum = 1000
if round == "all" then -- if all need column of first round
for k,v in pairs(data.rounds) do
if v 1 then
p.errorString = p.errorString .. "\n\nWe have u[1]=" .. u[1] .. " and u[2]=" .. u[2]
end]]
return u[1], u[2] -- return player name, player sorting alias
else
p.errorString = p.errorString .. " Nhập tên không hợp lệ cho cầu thủ " .. u[1] .. ", " .. u[2]
return "", "" --TODO errroer
end
elseif type(u) == 'string' then
return u, "" -- return player name
else
p.errorString = p.errorString .. " Nhập tên không hợp lệ cho cầu thủ " .. u or u[1] or "chưa biết"
return "", ""
end
end

-- functions to sort goalscorers
p.preprocessSortName (name)
p.getPlayerSortName (playerName, sortName, countryName)
p.sortComment(comment)
p.getCountryName(country)
p.sortGoalscorers() -- the main sort funtion


--[=[ function p.preprocessSortName()
stripp off wikitext and
force to lowercase
change special characters to standard letters
]=]
function p.preprocessSortName (name)
name = string.gsub(name, "%[%[", "") -- strip off and
name = string.gsub(name, "%]%]", "")
--name =string.lower(name) -- force lower case and return
name = mw.ustring.lower(name) -- use unicode function

local specialChars = { -- list of special characters and replacement pairs
{ "ı", "i" } , { "İ", "i" } , { "ß", "ss" },
{ "ý", "y" } , { "ř", "r" } , { "ő", "o" },
{ "é", "e" } , { "è", "e" } , { "þ", "th" },
{ "ē", "e" } , { "ņ", "n" } , { "č", "c" },
{ "ū", "u" } , { "ž", "z" } , { "æ", "ae" },
{ "å", "a" } , { "ø", "o" } , { "ą", "a" },
{ "ń", "n" } , { "ł", "l" } , { "ã", "a" },
{ "ș", "s" } , { "š", "s" } , { "í", "i" },
{ "á", "a" } , { "ä", "a" } , { "ć", "c" },
{ "ç", "c" } , { "ğ", "g" } , { "ö", "o" },
{ "ë", "e" } , { "ú", "u" } , { "ó", "o" },
{ "ð", "d" } , { "ü", "u" } , { "ű", "u" },
{ "ā", "a" } , { "ī", "i" } , { "đ", "d" },
{ "ă", "a" } , { "â", "a" } , { "ż", "z" },
{ "ț", "t" } , { "ş", "s" } , { "ś", "s" },
{ "ǎ", "a" } , { "ě", "e" } , { "ů", "u" },
{ "ĕ", "e" } , { "ñ", "n" } , { "ď", "d" },
{ "ï", "i" } , { "ź", "z" } , { "ô", "o" },
{ "ė", "e" } , { "ľ", "l" } , { "ģ", "g" },
{ "ļ", "l" } , { "ę", "e" } , { "ň", "n" },
{ "ò", "o" }
}
for k,v in pairs(specialChars) do -- replace special characters from supplied list
name = string.gsub(name, v[1], v[2])
end

return name
end
--[[ return the name for sorting
return supplied alias name for sorting
otherwise
checks for pipe (redirect) and uses name after pipe
splits name into words
returns first name if only name (e.g. Nani)
otherwise returns name in format second_name [.. last name], firstname
]]
function p.getPlayerSortName (playerName, sortName, countryName)

--dewikify all names before sorting, also forces lowercase
playerName = p.preprocessSortName(playerName)
sortName = p.preprocessSortName(sortName)

if sortName ~= "" then -- if we have a sort name supplied
return sortName -- then return it
end

-- players from certain countries will use name in order supplied
local noSort = { "CAM", "CHN", "TPE", "MYA", "PRK", "KOR", "VIE" }
for k,v in pairs(noSort) do
if v == countryName then
return playerName
end
end


-- else work it out from the supplied player name

-- we don't want to test the name in a redirect, so get name after pipe if there is one
if string.find (playerName, "|") then -- test for redirect
local names = mw.text.split( playerName, "|")
playerName = names[2] -- get name after pipe
end

local names = mw.text.split( playerName, " ") -- we don't want to sort on first name

if names == 1 then
return names[1] -- return name of single name player
else
-- we will assume the second name is the sort name e.g, Joe Bloggs, Jan van Bloggen
local name = names[2] -- set name to second name e.g. Bloggs or van
local i=3
while i b.goals) then -- primary sort on 'goals' -> a before b
return true
elseif (a.goals b before a
return false
else -- a.goals b.goals -- primary sort tied,

--return a.country country_b) then -- secondary sort on 'country'
return false
else -- a.country b.country -- secondary sort tied,

--return a.player ' .. p.addLinkedIcon(frame, u['country']) -- linked flag icon
if playerActive and g.args['bold']~='no' then
goalscorerString = goalscorerString .. " " .. u['player'] .. "" -- bolded name
else
goalscorerString = goalscorerString .. " " .. u['player'] -- name
end
goalscorerString = goalscorerString .. u['comment'] .. -- comment for o.g.

outputString = outputString .. goalscorerString -- .. " " .. tostring(u['goals'])

end -- reached end of list of goalscorers

if outputString ~= "" then
outputString = outputString .. p.closeList(frame)

return outputString
else
return ("Không có bàn thắng nào khớp với tiêu chí được yêu cầu.")
end
end

-- output icon linked to national team page
function p.addLinkedIcon(frame, country)
local icon = data.templates['flag_icon_linked'] -- fbicon etc set in data module
local level = data.templates['youth_level'] or "" -- parameter for youth level, ie under-21
-- equivalent to
local flagVariant = ""
if data.templates.flagvar and data.templates.flagvar[country] then
flagVariant = data.templates.flagvar[country]
end
if level ~= "" then
return frame:expandTemplate{ title = icon , args = { level, country, flagVariant } }
else
return frame:expandTemplate{ title = icon , args = { country, flagVariant } } -- flag icon
end
end
-- formatting of list under each number of goals
function p.openList(frame,og)

return mw.getCurrentFrame():extensionTag{
name = 'templatestyles', args = { src = 'Div col/styles.css' }
} .. -- perhaps add "column-count:3;"" to limit max number of columns?
end
function p.closeList(frame)
return
end
function p.firstToUpper(str)
return (str:gsub("^%l", string.upper))
end

-- handles parameters bold, further, extra
function p.addAdditionHeaderText(text, dateUpdated)
if g.args['inlineref'] then
text = text .. g.args['inlineref']
end
if g.args['bold'] and g.args['bold']~='no' then
text = text .. " Các cầu thủ được thể hiện bằng chữ in đậm vẫn đang thi đấu tại giải."
end
if g.args['further'] then
if text ~= "" then text = text .. " " end
text = text .. g.args['further']
end
if g.args['extra'] then
text = text .. "\n\n" .. g.args['extra']
end
return text
end
-- count number of goals for data in template
function p.countGoals(list, number, totalGoals)

local split = mw.text.split( list, "\n", true ) -- split the list for number of goals scorers with N goals
local count = split math.abs(number) -- calculate number of goals (including own goals)
totalGoals = totalGoals + count

--mw.addWarning( "Nhập: " .. list .. "[" .. count .. "]")

return totalGoals
end

-- use data supplied by template

--function p.list(frame)
function p.useTemplateData(frame)
--getArgs(frame)

--|
}|}=1|Đang|Đang}}
|}=1|Đã|Đã}}}} }
}=1|bàn thắng|bàn thắng}} ghi được}|&nbsp;bởi }
}=1|cầu thủ|cầu thủ khác nhau}}
}|&nbsp;(với } trong số đó được ghi nhận là }=1|bàn phản lưới nhà|bàn phản lưới nhà}})|}}|}} trong }
}=1|trận đấu|trận đấu}}, trung bình }/} round 2}}
}/} round 2)=1|bàn thắng|bàn thắng}} mỗi trận đấu
}|&nbsp;(tính đến ngày })}}.}}}|}||&nbsp;}}
Các cầu thủ được thể hiện bằng chữ đậm vẫn đang thi đấu ở giải.
|}}}|}||&nbsp;}}}|}}
}|}|}}
--
local statNumber = g.args['goals'] or g.args['assists'] or 0
local matches = g.args['matches']
local statType = "bàn thắng"
if g.args['assists'] then statType = "kiến tạo" end
if g.args['clean sheets'] then statType = "giữ sạch lưới" end
local ongoing = g.args['ongoing']
local text1 = ""
if g.args['lc'] then text1 = "" end
local text2 = "Đã"
if ongoing then text2 = "Đang" end
local updateString = ""
local averageString = ""
local goalPlural = "" -- goal(s)
if g.args['goals'] and tonumber(g.args['goals']) == 1 then
goalPlural = ""
text2 = "Đã"
if ongoing then text2 = "Đang" end
end
local matchPlural = "" -- match(es)
if g.args['matches'] and tonumber(g.args['matches']) == 1 then matchPlural = "" end


-- auto version: string.format(" trong %d trận đấu%s, trung bình %."..precision.."g bàn thắng%s mỗi trận đấu", matches, pluralMatches, average, pluralAverage)
if g.args['goals'] and g.args['matches'] then
local averageGoals = g.args['goals']/g.args['matches']
local avGoalPlural = ""
if averageGoals == 1 then avGoalPlural = "" end
averageString = string.format(" trong %d trận đấu%s, trung bình %.3g bàn thắng%s mỗi trận đấu", g.args['matches'], matchPlural, averageGoals, avGoalPlural)
end
if g.args['updated'] and g.args['updated'] ~= "complete" then
updateString = "&nbsp;(tính đến ngày " ..g.args['updated'] .. ")"
end
local sep = "."
if g.args['sep'] then sep = g.args['sep'] end
local text = ""
if g.args['goals'] then
text = string.format("%s có %d %s%s ghi được%s",
text2, statNumber, statType, goalPlural, averageString..updateString..sep)
end
text = p.addAdditionHeaderText(text) -- handles template parameters bold, further, extra

--30 }|kiến tạo|bàn thắng}}

}|}|}}}|}}
local output = "\n"
local number = 30

local totalGoals = 0

while number > -4 do -- for the each goals/assists

local entry = g.args[number .. ' goals'] or g.args[number .. ' goal']
or g.args[number .. ' assists'] or g.args[number .. ' assist']
or g.args[number .. ' clean sheets'] or g.args[number .. ' clean sheet']

if number }|}}}|}}}|}}
local footerText = g.args['footer-text'] or g.args['bottom'] or ""
local footerHeading = g.args['footer-heading'] or g.args['bottom-text'] or ""
local footer = ""
if footerText ~= "" then
local heading = ""
if footerHeading ~= "" then
heading = .. footerHeading ..
end
footer = '\n' .. heading .. p.openList(frame) .. '\n' .. footerText .. p.closeList(frame)
end


--}|}}}|}}
local source = g.args['source'] or ""
if source ~= "" then source = "Nguồn: " .. source .. "" end

return text .. output .. footer .. source
end
return p