Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 29 additions & 10 deletions lua/wikis/commons/Standings/Parse/Lpdb.lua
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ local ColumnName = Condition.ColumnName

local StandingsParseLpdb = {}

---Slim match struct carrying only the fields consumed by standings tiebreakers and import.
---Dropped: bracketData, extradata, full game payloads.
---@class StandingsImportMatch
---@field matchId string
---@field finished boolean
---@field winner integer?
---@field opponents standardOpponent[] -- by reference from matchFromRecord
---@field games {winner: integer?, status: string?, scores: table?}[]

---@param rounds {roundNumber: integer, matches: string[]}[]
---@param scoreMapper fun(opponent: match2opponent): number|nil
---@return StandingTableOpponentData[]
Expand Down Expand Up @@ -59,10 +68,21 @@ function StandingsParseLpdb.importFromMatches(rounds, scoreMapper)
{
conditions = tostring(conditions),
},
function(match2)
local roundNumbers = matchIdToRound[match2.match2id]
function(record)
local match2 = MatchGroupUtil.matchFromRecord(record)
---@type StandingsImportMatch
local slimMatch = {
matchId = match2.matchId,
finished = match2.finished,
winner = match2.winner,
opponents = match2.opponents,
games = Array.map(match2.games, function(game)
return {winner = game.winner, status = game.status, scores = game.scores}
end),
}
local roundNumbers = matchIdToRound[record.match2id]
Array.forEach(roundNumbers, function(roundNumber)
StandingsParseLpdb.parseMatch(roundNumber, match2, opponents, scoreMapper, #rounds)
StandingsParseLpdb.parseMatch(roundNumber, slimMatch, opponents, scoreMapper, #rounds)
end)
end
)
Expand Down Expand Up @@ -114,13 +134,12 @@ function StandingsParseLpdb.newOpponent(opponentData, maxRounds)
end

---@param roundNumber integer
---@param match match2
---@param slimMatch StandingsImportMatch
---@param opponents StandingTableOpponentData[]
---@param scoreMapper fun(opponent: standardOpponent): number?
---@param maxRounds integer
function StandingsParseLpdb.parseMatch(roundNumber, match, opponents, scoreMapper, maxRounds)
local match2 = MatchGroupUtil.matchFromRecord(match)
Array.forEach(match2.opponents, function(opponent)
function StandingsParseLpdb.parseMatch(roundNumber, slimMatch, opponents, scoreMapper, maxRounds)
Array.forEach(slimMatch.opponents, function(opponent)
---Find matching opponent
local standingsOpponentData = Array.find(opponents, function(opponentData)
return Opponent.same(opponentData.opponent, opponent)
Expand All @@ -137,11 +156,11 @@ function StandingsParseLpdb.parseMatch(roundNumber, match, opponents, scoreMappe
opponentRoundData.scoreboard.points = (opponentRoundData.scoreboard.points or 0) + points
end
opponentRoundData.specialstatus = ''
opponentRoundData.match = match2
if not match2.finished then
opponentRoundData.match = slimMatch
if not slimMatch.finished then
return
end
local matchResult = match2.winner == 0 and 'd' or opponent.placement == 1 and 'w' or 'l'
local matchResult = slimMatch.winner == 0 and 'd' or opponent.placement == 1 and 'w' or 'l'
opponentRoundData.scoreboard.match[matchResult] = (opponentRoundData.scoreboard.match[matchResult] or 0) + 1
end)
end
Expand Down
86 changes: 78 additions & 8 deletions lua/wikis/commons/Standings/Table.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ local Arguments = Lua.import('Module:Arguments')
local Array = Lua.import('Module:Array')
local FnUtil = Lua.import('Module:FnUtil')
local Logic = Lua.import('Module:Logic')
local Table = Lua.import('Module:Table')

local StandingsParseWiki = Lua.import('Module:Standings/Parse/Wiki')
local StandingsParseLpdb = Lua.import('Module:Standings/Parse/Lpdb')
Expand All @@ -32,7 +31,7 @@ local StandingsTable = {}

---@class StandingTableOpponentData
---@field rounds {tiebreakerPoints: number?, specialstatus: string, scoreboard: Scoreboard?,
---match: MatchGroupUtilMatch?, matches: MatchGroupUtilMatch[], matchId: string}[]?
---match: StandingsImportMatch?, matches: StandingsImportMatch[], matchId: string}[]?
---@field opponent standardOpponent
---@field startingPoints number?

Expand Down Expand Up @@ -73,6 +72,27 @@ function StandingsTable.fromTemplate(frame)
return StandingsDisplay{pageName = mw.title.getCurrentTitle().text, standingsIndex = standingsTable.standingsindex}
end

---Merge a single imported opponent round into a manual opponent round.
---Manual values take priority; imported-only fields (matches, matchId, scoreboard.match) survive.
---@param importedRound table
---@param manualRound table
---@return table
local function mergeRound(importedRound, manualRound)
local importedScoreboard = importedRound.scoreboard or {}
local manualScoreboard = manualRound.scoreboard or {}
return {
scoreboard = {
points = manualScoreboard.points ~= nil and manualScoreboard.points or importedScoreboard.points,
match = importedScoreboard.match,
},
specialstatus = manualRound.specialstatus ~= nil and manualRound.specialstatus or importedRound.specialstatus,
tiebreakerPoints = manualRound.tiebreakerPoints ~= nil
and manualRound.tiebreakerPoints or importedRound.tiebreakerPoints,
matches = importedRound.matches,
matchId = importedRound.matchId,
}
end

---@param manualOpponents StandingTableOpponentData[]
---@param importedOpponents StandingTableOpponentData[]
---@param addNewOpponents boolean
Expand All @@ -81,21 +101,71 @@ function StandingsTable.mergeOpponentsData(manualOpponents, importedOpponents, a
--- Add all manual opponents to the new opponents list
local newOpponents = Array.map(manualOpponents, FnUtil.identity)

--- Build a name-keyed index of newOpponents for O(1) lookup.
--- For team opponents a renamed-team fallback (Opponent.same) is used on miss.
local opponentIndex = {}
Array.forEach(newOpponents, function(opponentData, idx)
local name = Opponent.toName(opponentData.opponent)
if name then
opponentIndex[name] = idx
end
end)

--- Find all imported opponents
Array.forEach(importedOpponents, function(importedOpponent)
--- Find the matching manual opponent
local manualOpponentId = Array.indexOf(newOpponents, function(manualOpponent)
return Opponent.same(manualOpponent.opponent, importedOpponent.opponent)
end)
local importedName = Opponent.toName(importedOpponent.opponent)
local manualOpponentId = importedName and opponentIndex[importedName] or 0

--- On miss, do one fallback linear scan (handles renamed teams via historicaltemplate)
if manualOpponentId == 0 then
manualOpponentId = Array.indexOf(newOpponents, function(manualOpponent)
return Opponent.same(manualOpponent.opponent, importedOpponent.opponent)
end)
--- Alias the name so future lookups hit the fast path
if manualOpponentId ~= 0 and importedName then
opponentIndex[importedName] = manualOpponentId
end
end

--- If there isn't one, means this is a new opponent
if manualOpponentId == 0 then
if addNewOpponents then
table.insert(newOpponents, importedOpponent)
local name = Opponent.toName(importedOpponent.opponent)
if name then
opponentIndex[name] = #newOpponents
end
end
return
end
--- Manual data has priority over imported data
newOpponents[manualOpponentId] = Table.deepMerge(importedOpponent, newOpponents[manualOpponentId])

--- Manual data has priority over imported data; build a new merged opponent table
local manualOpponent = newOpponents[manualOpponentId]
local importedRounds = importedOpponent.rounds or {}
local manualRounds = manualOpponent.rounds or {}

-- Determine the union of round indices present on either side
local maxRoundIndex = math.max(#importedRounds, #manualRounds)
local mergedRounds = {}
for i = 1, maxRoundIndex do
local importedRound = importedRounds[i]
local manualRound = manualRounds[i]
if importedRound and manualRound then
mergedRounds[i] = mergeRound(importedRound, manualRound)
elseif manualRound then
mergedRounds[i] = manualRound
else
mergedRounds[i] = importedRound
end
end

newOpponents[manualOpponentId] = {
opponent = manualOpponent.opponent,
startingPoints = manualOpponent.startingPoints ~= nil
and manualOpponent.startingPoints
or importedOpponent.startingPoints,
rounds = mergedRounds,
}
end)

return newOpponents
Expand Down
2 changes: 1 addition & 1 deletion lua/wikis/commons/Standings/Tiebreaker/Interface.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ local Lua = require('Module:Lua')

local Class = Lua.import('Module:Class')

---@alias TiebreakerOpponent {opponent: standardOpponent, points: number, matches: MatchGroupUtilMatch[],
---@alias TiebreakerOpponent {opponent: standardOpponent, points: number, matches: StandingsImportMatch[],
---match: {w: integer, d: integer, l:integer}, extradata: table}

---@class StandingsTiebreaker
Expand Down
Loading