diff --git a/lua/spec/participant_table_spec.lua b/lua/spec/participant_table_spec.lua index 8a703e0abe9..7addf2960c9 100644 --- a/lua/spec/participant_table_spec.lua +++ b/lua/spec/participant_table_spec.lua @@ -196,9 +196,9 @@ describe('Participant Table', function() { { entries = { - Table.merge({inputIndex = 1}, parsedBaseEntries.clem), - Table.merge({inputIndex = 2}, parsedBaseEntries.lambo), - Table.merge({inputIndex = 3}, parsedBaseEntries.showtime), + parsedBaseEntries.clem, + parsedBaseEntries.lambo, + parsedBaseEntries.showtime, }, }, }, @@ -208,9 +208,9 @@ describe('Participant Table', function() { { entries = { - Table.merge({inputIndex = 1, seed = 1}, parsedBaseEntries.clem), - Table.merge({inputIndex = 2, seed = 3}, parsedBaseEntries.lambo), - Table.merge({inputIndex = 3, seed = 2}, parsedBaseEntries.showtime), + Table.merge({seed = 1}, parsedBaseEntries.clem), + Table.merge({seed = 3}, parsedBaseEntries.lambo), + Table.merge({seed = 2}, parsedBaseEntries.showtime), }, }, }, @@ -220,15 +220,15 @@ describe('Participant Table', function() { { entries = { - Table.merge({inputIndex = 1}, parsedBaseEntries.clem), - Table.merge({inputIndex = 2}, parsedBaseEntries.lambo), - Table.merge({inputIndex = 3}, parsedBaseEntries.showtime), + parsedBaseEntries.clem, + parsedBaseEntries.lambo, + parsedBaseEntries.showtime, }, }, { entries = { - Table.merge({inputIndex = 2}, parsedBaseEntries.bunny), - Table.merge({inputIndex = 1}, parsedBaseEntries.classic), + parsedBaseEntries.bunny, + parsedBaseEntries.classic, }, }, }, @@ -240,7 +240,6 @@ describe('Participant Table', function() entries = { { dq = false, - inputIndex = 2, isResolved = true, name = 'Classic_(Kim_Doh_Woo) / ShoWTimE', opponent = { @@ -265,7 +264,6 @@ describe('Participant Table', function() }, { dq = false, - inputIndex = 1, isResolved = true, name = 'Clem / Lambo', opponent = { diff --git a/lua/wikis/commons/Features/ParticipantTable/Api/Import.lua b/lua/wikis/commons/Features/ParticipantTable/Api/Import.lua new file mode 100644 index 00000000000..bbbb4b2df08 --- /dev/null +++ b/lua/wikis/commons/Features/ParticipantTable/Api/Import.lua @@ -0,0 +1,29 @@ +--- +-- @Liquipedia +-- page=Module:Features/ParticipantTable/Api/Import +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +local Lua = require('Module:Lua') + +local Logic = Lua.import('Module:Logic') +local TournamentStructure = Lua.import('Module:TournamentStructure') + +local Import = {} + +---@param matchGroupSpec MatchGroupsSpec +---@return match2[] +function Import.fromMatchGroupSpec(matchGroupSpec) + if Logic.isEmpty(matchGroupSpec) then + return {} + end + return mw.ext.LiquipediaDB.lpdb('match2', { + conditions = tostring(TournamentStructure.getMatch2Filter(matchGroupSpec)), + query = 'pagename, match2bracketdata, match2opponents, winner', + order = 'date asc', + limit = 5000, + }) +end + +return Import diff --git a/lua/wikis/commons/Features/ParticipantTable/Components/FactionHeader.lua b/lua/wikis/commons/Features/ParticipantTable/Components/FactionHeader.lua index 1b008c1624a..674d7d5f513 100644 --- a/lua/wikis/commons/Features/ParticipantTable/Components/FactionHeader.lua +++ b/lua/wikis/commons/Features/ParticipantTable/Components/FactionHeader.lua @@ -14,7 +14,7 @@ local Component = Lua.import('Module:Widget/Component') local Html = Lua.import('Module:Widget/Html') local WidgetUtil = Lua.import('Module:Widget/Util') ----@param props {config: StarcraftParticipantTableConfig, factionColumns: string[], +---@param props {config: ParticipantTableConfig, factionColumns: string[], ---factionNumbers: table} ---@return VNode local function ParticipantTableFactionHeader(props) @@ -27,7 +27,7 @@ local function ParticipantTableFactionHeader(props) ' ', Faction.toName(faction), config.isRandomEvent and ' Main' or nil, - config.showCountByFaction and " ''(" .. props.factionNumbers[faction .. 'Display'] .. ")''" or nil + config.count and " ''(" .. props.factionNumbers[faction .. 'Display'] .. ")''" or nil ) return Html.Div{ diff --git a/lua/wikis/commons/Features/ParticipantTable/Components/FactionSection.lua b/lua/wikis/commons/Features/ParticipantTable/Components/FactionSection.lua index 8e84413ffe0..811ac309dab 100644 --- a/lua/wikis/commons/Features/ParticipantTable/Components/FactionSection.lua +++ b/lua/wikis/commons/Features/ParticipantTable/Components/FactionSection.lua @@ -24,7 +24,7 @@ local makeRow = function(children) return Html.Div{classes = {'participantTable-row'}, children = children} end ----@param props {config: StarcraftParticipantTableConfig, section: StarcraftParticipantTableSection, +---@param props {config: ParticipantTableConfig, section: ParticipantTableSection, ---factionColumns: string[]} ---@return VNode[] local function ParticipantTableFactionSection(props) diff --git a/lua/wikis/commons/Features/ParticipantTable/Components/FactionTable.lua b/lua/wikis/commons/Features/ParticipantTable/Components/FactionTable.lua index 9eb4cabe0aa..f7a80f33876 100644 --- a/lua/wikis/commons/Features/ParticipantTable/Components/FactionTable.lua +++ b/lua/wikis/commons/Features/ParticipantTable/Components/FactionTable.lua @@ -16,8 +16,8 @@ local WidgetUtil = Lua.import('Module:Widget/Util') local Header = Lua.import('Module:Features/ParticipantTable/Components/FactionHeader') local Section = Lua.import('Module:Features/ParticipantTable/Components/FactionSection') ----@param props {config: StarcraftParticipantTableConfig, factionColumns: string[], ----factionNumbers: table, sections: StarcraftParticipantTableSection[]} +---@param props {config: ParticipantTableConfig, factionColumns: string[], +---factionNumbers: table, sections: ParticipantTableSection[]} ---@return VNode local function ParticipantTableFactionTable(props) local config = props.config @@ -27,7 +27,7 @@ local function ParticipantTableFactionTable(props) classes = {'participantTable', 'participantTable-faction'}, css = { ['grid-template-columns'] = 'repeat(' .. colSpan .. ', 1fr)', - width = (colSpan * config.soloColumnWidth) .. 'px', + width = (colSpan * config.factionColumnWidth) .. 'px', }, children = WidgetUtil.collect( Header{ diff --git a/lua/wikis/commons/Features/ParticipantTable/Lib/ParseImported.lua b/lua/wikis/commons/Features/ParticipantTable/Lib/ParseImported.lua new file mode 100644 index 00000000000..42f2de208d2 --- /dev/null +++ b/lua/wikis/commons/Features/ParticipantTable/Lib/ParseImported.lua @@ -0,0 +1,72 @@ +--- +-- @Liquipedia +-- page=Module:Features/ParticipantTable/Lib/ParseImported +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +local Lua = require('Module:Lua') + +local Array = Lua.import('Module:Array') +local Logic = Lua.import('Module:Logic') +local Operator = Lua.import('Module:Operator') +local Opponent = Lua.import('Module:Opponent/Custom') +local Set = Lua.import('Module:Set') + +local Parser = {} + +---@param config ParticipantTableConfig +---@param entries ParticipantTableEntry[] +---@param matchRecords match2[] +---@return ParticipantTableEntry[] +function Parser.parseImported(config, entries, matchRecords) + if Logic.isEmpty(matchRecords) then + return {} + end + ---@type Set + local alreadyProcessed = Set(Array.map(entries, Operator.property('name'))) + ---@cast matchRecords -nil + + local newEntries = {} + Array.forEach(matchRecords, function(matchRecord) + Array.forEach(matchRecord.match2opponents, function(opponentRecord, opponentIndex) + if not Parser._shouldInclude(opponentIndex, matchRecord, config.importOnlyQualified) then + return + end + + local entry = Parser._entryFromOpponentRecord(opponentRecord) + if not entry or alreadyProcessed:contains(entry.name) then return end + + alreadyProcessed:add(entry.name) + + table.insert(newEntries, entry) + end) + end) + + return newEntries +end + +---@param opponentIndex integer +---@param matchRecord table +---@param importOnlyQualified boolean? +---@return boolean +function Parser._shouldInclude(opponentIndex, matchRecord, importOnlyQualified) + local bracketData = matchRecord.match2bracketdata + return not importOnlyQualified or Logic.readBool(bracketData.quallose) or + Logic.readBool(bracketData.qualwin) and tonumber(matchRecord.winner) == opponentIndex +end + +---@param opponentRecord table +---@return ParticipantTableEntry? +function Parser._entryFromOpponentRecord(opponentRecord) + local opponent = Opponent.fromMatch2Record(opponentRecord) + if Opponent.isTbd(opponent) then + return + end + return { + opponent = opponent, + name = opponentRecord.name, + } +end + +return Parser diff --git a/lua/wikis/commons/Features/ParticipantTable/Lib/ParseInput.lua b/lua/wikis/commons/Features/ParticipantTable/Lib/ParseInput.lua new file mode 100644 index 00000000000..8dce6b15230 --- /dev/null +++ b/lua/wikis/commons/Features/ParticipantTable/Lib/ParseInput.lua @@ -0,0 +1,182 @@ +--- +-- @Liquipedia +-- page=Module:Features/ParticipantTable/Lib/ParseInput +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +local Lua = require('Module:Lua') + +local Array = Lua.import('Module:Array') +local DateExt = Lua.import('Module:Date/Ext') +local Faction = Lua.import('Module:Faction') +local Info = Lua.import('Module:Info', {loadData = true}) +local Json = Lua.import('Module:Json') +local Logic = Lua.import('Module:Logic') +local Lpdb = Lua.import('Module:Lpdb') +local MathUtil = Lua.import('Module:MathUtil') +local Namespace = Lua.import('Module:Namespace') +local Opponent = Lua.import('Module:Opponent/Custom') +local Set = Lua.import('Module:Set') +local Table = Lua.import('Module:Table') +local TournamentStructure = Lua.import('Module:TournamentStructure') +local Variables = Lua.import('Module:Variables') + +local Parser = {} + +---@param args table +---@param parentConfig ParticipantTableConfig? +---@return ParticipantTableConfig +function Parser.readConfig(args, parentConfig) + parentConfig = parentConfig or {} + + local showTeams = not Logic.readBool(args.disable_teams) + + local config = { + lpdbPrefix = args.lpdbPrefix or parentConfig.lpdbPrefix or Variables.varDefault('lpdbPrefix'), + noStorage = Logic.readBool(args.noStorage or parentConfig.noStorage or + Lpdb.isStorageDisabled() or not Namespace.isMain()), + matchGroupSpec = TournamentStructure.readMatchGroupsSpec(args), + syncPlayers = Logic.nilOr(Logic.readBoolOrNil(args.syncPlayers), parentConfig.syncPlayers, true), + showCountBySection = Logic.readBool(args.showCountBySection or parentConfig.showCountBySection), + count = tonumber(args.count), + colSpan = parentConfig.colSpan or MathUtil.toInteger(args.colspan) or 4, + onlyNotable = Logic.readBool(args.onlyNotable or parentConfig.onlyNotable), + resolveDate = args.date or parentConfig.resolveDate or DateExt.getContextualDate(), + sortPlayers = Logic.nilOr(Logic.readBoolOrNil(args.sortPlayers), parentConfig.sortPlayers, + (Info.config.participants or {}).sortPlayersInTable), + sortOpponents = Logic.nilOr(Logic.readBoolOrNil(args.sortOpponents), parentConfig.sortOpponents, true), + showTeams = showTeams, + title = args.title, + importOnlyQualified = Logic.readBool(args.onlyQualified), + display = not Logic.readBool(args.hidden), + showTitle = not Logic.readBool(args.hideTitle), + -- the following configs only apply to faction table version + soloAsFactionTable = Logic.nilOr(Logic.readBoolOrNil(args.soloAsFactionTable), + (Info.config.participants or {}).soloAsFactionTable), + displayUnknownColumn = Logic.readBoolOrNil(args.unknowncolumn), + displayRandomColumn = Logic.readBoolOrNil(args.randomcolumn), + displayMultipleFactionColumn = Logic.readBoolOrNil(args.multiplecolumn), + isRandomEvent = Logic.nilOr(Logic.readBoolOrNil(args.is_random_event), parentConfig.isRandomEvent), + manualFactionCounts = Table.map(Faction.knownFactions, function(key, faction) + return faction, tonumber(args[Faction.toName(faction):lower()]) + end), + factionColumnWidth = tonumber(args.entrywidth) or showTeams and 212 or 156, + -- todo: remove the fallback after bot runs (after merge) + showCountByFaction = Logic.readBool(args.countByFaction or args.count), + } + + config.width = parentConfig.width + if not config.width then + local columnWidth = parentConfig.columnWidth or tonumber(args.entrywidth) or showTeams and 212 or 156 + config.width = (columnWidth * config.colSpan) .. 'px' + end + config.columnWidth = config.columnWidth or ((100 / config.colSpan) .. '%') + + return config +end + +---@param args table +---@param config ParticipantTableConfig +---@return ParticipantTableSection[] +function Parser.readSections(args, config) + local sectionsArgs = Array.mapIndexes(function (index) + local parsed = Json.parseIfTable(args[index]) + if type(parsed) == 'table' and parsed.type == 'section' then + return parsed + end + end) + --case no sections: use whole table as first section + if Logic.isEmpty(sectionsArgs) then + sectionsArgs = {args} + end + + return Array.map(sectionsArgs, function(sectionArgs) + local sectionConfig = Parser.readConfig(sectionArgs, config) + return { + config = sectionConfig, + entries = Parser._readEntries(sectionArgs, sectionConfig) + } + end) +end + +---@param args table +---@param config ParticipantTableConfig +---@return ParticipantTableEntry[] +function Parser._readEntries(args, config) + ---@type Set + local alreadyUsed = Set{} + + return Table.mapArgumentsByPrefix(args, {'p', 'player'}, function(key, index) + local entry = Parser._readEntry(args, key, index, config) + entry.sortName = Opponent.toName(entry.opponent) + + if entry.opponent and Opponent.isTbd(entry.opponent) then + entry.name = Opponent.toName(entry.opponent) + return entry + end + + entry.opponent = Opponent.resolve(entry.opponent, config.resolveDate, { + syncPlayer = config.syncPlayers, + overwritePageVars = true, + }) + entry.isResolved = true + entry.name = Opponent.toName(entry.opponent) + + if alreadyUsed:contains(entry.name) then + error('Duplicate Input "|' .. key .. '=' .. args[key] .. '"') + end + + alreadyUsed:add(entry.name) + + return entry + end) +end + +---@param sectionArgs table +---@param key string +---@param index integer +---@param config ParticipantTableConfig +---@return ParticipantTableEntry +function Parser._readEntry(sectionArgs, key, index, config) + local prefix = 'p' .. index + local valueFromArgs = function(postfix) + return sectionArgs[key .. postfix] or sectionArgs[prefix .. postfix] + end + + --if not a json assume it is a solo opponent + local opponentArgs = Json.parseIfTable(sectionArgs[key]) or { + type = Opponent.solo, + name = sectionArgs[key], + link = valueFromArgs('link'), + flag = valueFromArgs('flag'), + team = valueFromArgs('team'), + dq = valueFromArgs('dq'), + note = valueFromArgs('note'), + seed = valueFromArgs('seed'), + faction = valueFromArgs('faction') or valueFromArgs('race'), + } + + assert(Opponent.isType(opponentArgs.type), 'Invalid opponent type for "' .. sectionArgs[key] .. '"') + + opponentArgs.seed = tonumber(opponentArgs.seed) + + --unset wiki var for random events to not read players as random if prize pool already sets them as random + if config.isRandomEvent and opponentArgs.type == Opponent.solo then + Variables.varDefine(opponentArgs.name .. '_faction', '') + end + local opponent = Opponent.readOpponentArgs(opponentArgs) + + if config.sortPlayers and opponent.players then + Array.sortInPlaceBy(opponent.players, function(player) return (player.displayName or player.pageName):lower() end) + end + + return { + dq = Logic.readBool(opponentArgs.dq), + note = opponentArgs.note, + opponent = opponent, + seed = opponentArgs.seed, + } +end + +return Parser diff --git a/lua/wikis/commons/Features/ParticipantTable/Lib/Util.lua b/lua/wikis/commons/Features/ParticipantTable/Lib/Util.lua new file mode 100644 index 00000000000..3cc52611c23 --- /dev/null +++ b/lua/wikis/commons/Features/ParticipantTable/Lib/Util.lua @@ -0,0 +1,47 @@ +--- +-- @Liquipedia +-- page=Module:Features/ParticipantTable/Lib/Util +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +local Lua = require('Module:Lua') + +local Array = Lua.import('Module:Array') +local Opponent = Lua.import('Module:Opponent/Custom') + +local Util = {} + +---@param section ParticipantTableSection +function Util.backFillEntries(section) + local config = section.config + Array.forEach(section.entries, function(entry) + entry.sortName = entry.sortName or Opponent.toName(entry.opponent) + entry.opponent = entry.isResolved and entry.opponent or Opponent.resolve(entry.opponent, config.resolveDate, { + syncPlayer = config.syncPlayers, + overwritePageVars = true, + }) + entry.name = entry.name or Opponent.toName(entry.opponent) + entry.isResolved = true + end) +end + +---@param section ParticipantTableSection +function Util.sortOpponents(section) + if not section.config.sortOpponents then return end + Array.sortInPlaceBy(section.entries, function(entry) + return entry.sortName:lower() + end) +end + +---@param sections ParticipantTableSection[] +---@return boolean +function Util.hasSeed(sections) + return Array.any(sections, function(section) + return Array.any(section.entries, function(entry) + return entry.seed ~= nil + end) + end) +end + +return Util diff --git a/lua/wikis/commons/Features/ParticipantTable/Types.lua b/lua/wikis/commons/Features/ParticipantTable/Types.lua index 60e62ac3775..ccdb4de9f79 100644 --- a/lua/wikis/commons/Features/ParticipantTable/Types.lua +++ b/lua/wikis/commons/Features/ParticipantTable/Types.lua @@ -34,6 +34,13 @@ local Types = {} ---@field width string ---@field columnWidth string ---@field showTitle boolean only applies for the title of the whole table +---@field displayUnknownColumn boolean? +---@field displayRandomColumn boolean? +---@field displayMultipleFactionColumn boolean? +---@field isRandomEvent boolean? +---@field manualFactionCounts table +---@field factionColumnWidth number +---@field soloAsFactionTable boolean? ---@class ParticipantTableSection ---@field config ParticipantTableConfig @@ -44,30 +51,8 @@ local Types = {} ---@field name string ---@field note string? ---@field dq boolean ----@field inputIndex integer? ---@field isResolved boolean? ---@field sortName string ---@field seed integer? - - --- todo: resolve those with the refactoring ----@class StarcraftParticipantTableConfig: ParticipantTableConfig ----@field displayUnknownColumn boolean? ----@field displayRandomColumn boolean? ----@field displayMultipleFactionColumn boolean? ----@field showCountByFaction boolean ----@field isRandomEvent boolean ----@field isQualified boolean? ----@field manualFactionCounts table ----@field soloColumnWidth number ----@field soloAsFactionTable boolean - ----@class StarcraftParticipantTableEntry: ParticipantTableEntry ----@field isQualified boolean? ----@field opponent StarcraftStandardOpponent - ----@class StarcraftParticipantTableSection: ParticipantTableSection ----@field entries StarcraftParticipantTableEntry[] - return Types diff --git a/lua/wikis/commons/ParticipantTable/Base.lua b/lua/wikis/commons/ParticipantTable/Base.lua index 10634c66f42..a7c643a4872 100644 --- a/lua/wikis/commons/ParticipantTable/Base.lua +++ b/lua/wikis/commons/ParticipantTable/Base.lua @@ -12,25 +12,22 @@ local Lua = require('Module:Lua') local Arguments = Lua.import('Module:Arguments') local Array = Lua.import('Module:Array') local Class = Lua.import('Module:Class') -local DateExt = Lua.import('Module:Date/Ext') local Json = Lua.import('Module:Json') local Logic = Lua.import('Module:Logic') -local Lpdb = Lua.import('Module:Lpdb') -local Namespace = Lua.import('Module:Namespace') local Opponent = Lua.import('Module:Opponent/Custom') local PageVariableNamespace = Lua.import('Module:PageVariableNamespace') local PlayerExt = Lua.import('Module:Player/Ext/Custom') local Table = Lua.import('Module:Table') -local Template = Lua.import('Module:Template') local Tournament = Lua.import('Module:Tournament') -local TournamentStructure = Lua.import('Module:TournamentStructure') local Variables = Lua.import('Module:Variables') -local Import = Lua.import('Module:ParticipantTable/Import') +local Import = Lua.import('Module:Features/ParticipantTable/Api/Import') +local ImportParser = Lua.import('Module:Features/ParticipantTable/Lib/ParseImported') +local Parser = Lua.import('Module:Features/ParticipantTable/Lib/ParseInput') +local Util = Lua.import('Module:Features/ParticipantTable/Lib/Util') local Display = Lua.import('Module:Features/ParticipantTable/Components/Wrapper') -local pageVars = PageVariableNamespace('ParticipantTable') local prizePoolVars = PageVariableNamespace('PrizePool') ---@class ParticipantTable: BaseClass @@ -52,150 +49,21 @@ end ---@return self function ParticipantTable:read() - self.config = self.readConfig(self.args) - self:readSections() + self.config = Parser.readConfig(self.args) + self.sections = Parser.readSections(self.args, self.config) - return self -end - ----@param args table ----@param parentConfig ParticipantTableConfig? ----@return ParticipantTableConfig -function ParticipantTable.readConfig(args, parentConfig) - parentConfig = parentConfig or {} - - local config = { - lpdbPrefix = args.lpdbPrefix or parentConfig.lpdbPrefix or Variables.varDefault('lpdbPrefix'), - noStorage = Logic.readBool(args.noStorage or parentConfig.noStorage or - Lpdb.isStorageDisabled() or not Namespace.isMain()), - matchGroupSpec = TournamentStructure.readMatchGroupsSpec(args), - syncPlayers = Logic.nilOr(Logic.readBoolOrNil(args.syncPlayers), parentConfig.syncPlayers, true), - showCountBySection = Logic.readBool(args.showCountBySection or parentConfig.showCountBySection), - count = tonumber(args.count), - colSpan = parentConfig.colSpan or tonumber(args.colspan) or 4, - onlyNotable = Logic.readBool(args.onlyNotable or parentConfig.onlyNotable), - resolveDate = args.date or parentConfig.resolveDate or DateExt.getContextualDate(), - sortPlayers = Logic.readBool(args.sortPlayers or parentConfig.sortPlayers), - sortOpponents = Logic.nilOr(Logic.readBoolOrNil(args.sortOpponents), parentConfig.sortOpponents, true), - showTeams = not Logic.readBool(args.disable_teams), - title = args.title, - importOnlyQualified = Logic.readBool(args.onlyQualified), - display = not Logic.readBool(args.hidden), - showTitle = not Logic.readBool(args.hideTitle), - } - - config.width = parentConfig.width - if not config.width then - local columnWidth = parentConfig.columnWidth or tonumber(args.entrywidth) or config.showTeams and 212 or 156 - config.width = (columnWidth * config.colSpan) .. 'px' - end - config.columnWidth = config.columnWidth or ((100 / config.colSpan) .. '%') - - return config -end - -function ParticipantTable:readSections() - self.sections = {} - Array.forEach(self:fetchSectionsArgs(), function(sectionArgs) - self:readSection(sectionArgs) - end) -end - ----@return table[] -function ParticipantTable:fetchSectionsArgs() - local args = self.args - - pageVars:set('stashArgs', '1') - - local sectionsArgs = Array.mapIndexes(function (index) - local parsed = Json.parseIfString(args[index]) - if type(parsed) == 'table' and parsed.type == 'section' then - return parsed - end - end) - - if Logic.isNotEmpty(sectionsArgs) then - return sectionsArgs - end - - -- make sure that all sections stashArgs - for _, potentialSection in pairs(args) do - ParticipantTable._stashArgs(potentialSection) - end - - -- retrieve sectionsArgs - sectionsArgs = Template.retrieveReturnValues('ParticipantTable') - pageVars:delete('stashArgs') - - --case no sections: use whole table as first section - if Logic.isEmpty(sectionsArgs) then - return {args} - end - - return sectionsArgs -end - ----access the args so it stashes ----@param potentialSection string ----@return string -function ParticipantTable._stashArgs(potentialSection) - return potentialSection -end - ----@param args table -function ParticipantTable:readSection(args) - local config = self.readConfig(args, self.config) - local section = {config = config} - - local entriesByName = {} - local tbds = {} - Table.mapArgumentsByPrefix(args, {'p', 'player'}, function(key, index) - local entry = self:readEntry(args, key, index, config) - entry.sortName = Opponent.toName(entry.opponent) - - if entry.opponent and Opponent.isTbd(entry.opponent) then - entry.name = Opponent.toName(entry.opponent) - table.insert(tbds, entry) - --needed so index is increased - return entry - end - - entry.opponent = Opponent.resolve(entry.opponent, config.resolveDate, { - syncPlayer = config.syncPlayers, - overwritePageVars = true, - }) - entry.isResolved = true - entry.name = Opponent.toName(entry.opponent) - - if entriesByName[entry.name] then - error('Duplicate Input "|' .. key .. '=' .. args[key] .. '"') - end - - entriesByName[entry.name] = entry - - --needed so index is increased - return entry - end) - - section.entries = Array.map(Import.importFromMatchGroupSpec(config, entriesByName), function(entry) - entry.sortName = entry.sortName or Opponent.toName(entry.opponent) - entry.opponent = entry.isResolved and entry.opponent or Opponent.resolve(entry.opponent, config.resolveDate, { - syncPlayer = config.syncPlayers, - overwritePageVars = true, - }) - entry.name = entry.name or Opponent.toName(entry.opponent) - entry.isResolved = true - self:setCustomPageVariables(entry, config) - return entry - end) - - Array.sortInPlaceBy(section.entries, function(entry) - return config.sortOpponents and entry.sortName:lower() or entry.inputIndex or -1 + Array.forEach(self.sections, function(section) + local matchRecords = Import.fromMatchGroupSpec(section.config.matchGroupSpec) + Array.extendWith(section.entries, ImportParser.parseImported(section.config, section.entries, matchRecords)) + Util.backFillEntries(section) + Util.sortOpponents(section) + Array.forEach(section.entries, function(entry) + self:setCustomPageVariables(entry, section.config) + end) end) + self.hasSeeds = Util.hasSeed(self.sections) - Array.extendWith(section.entries, tbds) - - table.insert(self.sections, section) + return self end ---@param entry ParticipantTableEntry @@ -203,55 +71,6 @@ end function ParticipantTable:setCustomPageVariables(entry, config) end ----@param sectionArgs table ----@param key string|number ----@param index number ----@param config ParticipantTableConfig ----@return ParticipantTableEntry -function ParticipantTable:readEntry(sectionArgs, key, index, config) - local prefix = 'p' .. index - local valueFromArgs = function(postfix) - return sectionArgs[key .. postfix] or sectionArgs[prefix .. postfix] - end - - --if not a json assume it is a solo opponent - local opponentArgs = Json.parseIfTable(sectionArgs[key]) or { - type = Opponent.solo, - name = sectionArgs[key], - link = valueFromArgs('link'), - flag = valueFromArgs('flag'), - team = valueFromArgs('team'), - dq = valueFromArgs('dq'), - note = valueFromArgs('note'), - seed = valueFromArgs('seed'), - } - - assert(Opponent.isType(opponentArgs.type), 'Invalid opponent type for "' .. sectionArgs[key] .. '"') - - opponentArgs.seed = tonumber(opponentArgs.seed) - if opponentArgs.seed then - self.hasSeeds = true - end - - local opponent = Opponent.readOpponentArgs(opponentArgs) - - if config.sortPlayers and opponent.players then - table.sort(opponent.players, function (player1, player2) - local name1 = (player1.displayName or player1.pageName):lower() - local name2 = (player2.displayName or player2.pageName):lower() - return name1 < name2 - end) - end - - return { - dq = Logic.readBool(opponentArgs.dq), - note = opponentArgs.note, - opponent = opponent, - inputIndex = index, - seed = opponentArgs.seed, - } -end - ---@return self function ParticipantTable:store() if self.config.noStorage then return self end diff --git a/lua/wikis/commons/ParticipantTable/Import.lua b/lua/wikis/commons/ParticipantTable/Import.lua deleted file mode 100644 index 4d9353dc1fb..00000000000 --- a/lua/wikis/commons/ParticipantTable/Import.lua +++ /dev/null @@ -1,91 +0,0 @@ ---- --- @Liquipedia --- page=Module:ParticipantTable/Import --- --- Please see https://github.com/Liquipedia/Lua-Modules to contribute --- - -local Lua = require('Module:Lua') - -local Array = Lua.import('Module:Array') -local Logic = Lua.import('Module:Logic') -local Table = Lua.import('Module:Table') - -local Opponent = Lua.import('Module:Opponent/Custom') - -local TournamentStructure = Lua.import('Module:TournamentStructure') - -local ParticipantTableImport = {} - ----@param config ParticipantTableConfig ----@param entriesByName table ----@return ParticipantTableEntry[] -function ParticipantTableImport.importFromMatchGroupSpec(config, entriesByName) - if Table.isEmpty(config.matchGroupSpec) then - return Array.extractValues(entriesByName) - end - - local matchRecords = ParticipantTableImport._fetchMatchRecords(config.matchGroupSpec) - if Table.isEmpty(matchRecords) then - return Array.extractValues(entriesByName) - end - ---@cast matchRecords -nil - return ParticipantTableImport._entriesFromMatchRecords(matchRecords, config, entriesByName) -end - ----@param matchGroupSpec table ----@return table[] -function ParticipantTableImport._fetchMatchRecords(matchGroupSpec) - return mw.ext.LiquipediaDB.lpdb('match2', { - conditions = tostring(TournamentStructure.getMatch2Filter(matchGroupSpec)), - query = 'pagename, match2bracketdata, match2opponents, winner', - order = 'date asc', - limit = 5000, - }) -end - ----@param matchRecords table[] ----@param config ParticipantTableConfig ----@param entriesByName table ----@return ParticipantTableEntry[] -function ParticipantTableImport._entriesFromMatchRecords(matchRecords, config, entriesByName) - Array.forEach(matchRecords, function(matchRecord) - Array.forEach(matchRecord.match2opponents, function(opponentRecord, opponentIndex) - if not ParticipantTableImport._shouldInclude(opponentIndex, matchRecord, config.importOnlyQualified) then - return - end - - local entry = ParticipantTableImport._entryFromOpponentRecord(opponentRecord) - if not entry then return end - - entriesByName[entry.name] = entriesByName[entry.name] or entry - end) - end) - - return Array.extractValues(entriesByName) -end - ----@param opponentIndex integer ----@param matchRecord table ----@param importOnlyQualified boolean? ----@return boolean -function ParticipantTableImport._shouldInclude(opponentIndex, matchRecord, importOnlyQualified) - local bracketData = matchRecord.match2bracketdata - return not importOnlyQualified or Logic.readBool(bracketData.quallose) or - Logic.readBool(bracketData.qualwin) and tonumber(matchRecord.winner) == opponentIndex -end - ----@param opponentRecord table ----@return ParticipantTableEntry? -function ParticipantTableImport._entryFromOpponentRecord(opponentRecord) - local opponent = Opponent.fromMatch2Record(opponentRecord) --[[@as standardOpponent]] - if Opponent.isTbd(opponent) then - return - end - return { - opponent = opponent, - name = opponentRecord.name, - } -end - -return ParticipantTableImport diff --git a/lua/wikis/commons/ParticipantTable/Starcraft.lua b/lua/wikis/commons/ParticipantTable/Starcraft.lua index 76985e92dd1..093b6414c8a 100644 --- a/lua/wikis/commons/ParticipantTable/Starcraft.lua +++ b/lua/wikis/commons/ParticipantTable/Starcraft.lua @@ -9,9 +9,7 @@ local Lua = require('Module:Lua') local Array = Lua.import('Module:Array') local Class = Lua.import('Module:Class') -local Json = Lua.import('Module:Json') local Faction = Lua.import('Module:Faction') -local Logic = Lua.import('Module:Logic') local Opponent = Lua.import('Module:Opponent/Custom') local ParticipantTable = Lua.import('Module:ParticipantTable/Base') local Variables = Lua.import('Module:Variables') @@ -20,8 +18,8 @@ local FactionTable = Lua.import('Module:Features/ParticipantTable/Components/Fac ---@class StarcraftParticipantTable: ParticipantTable ---@operator call(Frame): StarcraftParticipantTable ----@field config StarcraftParticipantTableConfig ----@field sections StarcraftParticipantTableSection[] +---@field config ParticipantTableConfig +---@field sections ParticipantTableSection[] local StarcraftParticipantTable = Class.new(ParticipantTable) ---@param frame Frame @@ -30,103 +28,15 @@ function StarcraftParticipantTable.run(frame) return StarcraftParticipantTable(frame):read():store():create() end ----@param args table ----@param parentConfig StarcraftParticipantTableConfig? ----@return StarcraftParticipantTableConfig -function StarcraftParticipantTable.readConfig(args, parentConfig) - local config = ParticipantTable.readConfig(args, parentConfig) --[[@as StarcraftParticipantTableConfig]] - parentConfig = parentConfig or {} - - config.displayUnknownColumn = Logic.readBoolOrNil(args.unknowncolumn) - config.displayRandomColumn = Logic.readBoolOrNil(args.randomcolumn) - config.displayMultipleFactionColumn = Logic.readBoolOrNil(args.multiplecolumn) - config.showCountByFaction = Logic.readBool(args.showCountByRace or args.count) - config.isRandomEvent = Logic.nilOr(Logic.readBoolOrNil(args.is_random_event), parentConfig.isRandomEvent) - config.isQualified = Logic.nilOr(Logic.readBoolOrNil(args.isQualified), parentConfig.isQualified) - config.sortPlayers = true - --only relevant for solo case since there we need columnWidth in px since colSpan is calculated dynamically - config.soloColumnWidth = tonumber(args.entrywidth) or config.showTeams and 212 or 156 - - config.manualFactionCounts = {} - Array.forEach(Faction.knownFactions, function(faction) - config.manualFactionCounts[faction] = tonumber(args[Faction.toName(faction):lower()]) - end) - - config.soloAsFactionTable = not Logic.readBool(args.soloNotAsRaceTable) - - return config -end - ----@param sectionArgs table ----@param key string|number ----@param index number ----@param config StarcraftParticipantTableConfig ----@return StarcraftParticipantTableEntry -function StarcraftParticipantTable:readEntry(sectionArgs, key, index, config) - local prefix = 'p' .. index - local valueFromArgs = function(postfix) - return sectionArgs[key .. postfix] or sectionArgs[prefix .. postfix] - end - - --if not a json assume it is a solo opponent - local opponentArgs = Json.parseIfTable(sectionArgs[key]) or { - type = Opponent.solo, - name = sectionArgs[key], - link = valueFromArgs('link'), - flag = valueFromArgs('flag'), - team = valueFromArgs('team'), - dq = valueFromArgs('dq'), - note = valueFromArgs('note'), - seed = valueFromArgs('seed'), - faction = valueFromArgs('race') or valueFromArgs('faction'), - } - - assert(Opponent.isType(opponentArgs.type), 'Invalid opponent type for "' .. sectionArgs[key] .. '"') - - opponentArgs.seed = tonumber(opponentArgs.seed) - if opponentArgs.seed then - self.hasSeeds = true - end - - --unset wiki var for random events to not read players as random if prize pool already sets them as random - if config.isRandomEvent and opponentArgs.type == Opponent.solo then - Variables.varDefine(opponentArgs.name .. '_faction', '') - end - - local opponent = Opponent.readOpponentArgs(opponentArgs) - - if config.sortPlayers and opponent.players then - table.sort(opponent.players, function (player1, player2) - local name1 = (player1.displayName or player1.pageName):lower() - local name2 = (player2.displayName or player2.pageName):lower() - return name1 < name2 - end) - end - - return { - dq = Logic.readBool(opponentArgs.dq), - note = opponentArgs.note, - opponent = opponent, - isQualified = Logic.nilOr(Logic.readBoolOrNil(sectionArgs[key .. 'qualified']), config.isQualified), - inputIndex = index, - seed = opponentArgs.seed, - } -end - ---@param lpdbData table ----@param entry StarcraftParticipantTableEntry ----@param config StarcraftParticipantTableConfig +---@param entry ParticipantTableEntry +---@param config ParticipantTableConfig function StarcraftParticipantTable:adjustLpdbData(lpdbData, entry, config) if config.isRandomEvent then lpdbData.opponentplayers.p1faction = Faction.read('r') end - local isQualified = entry.isQualified or config.isQualified - - lpdbData.extradata.isqualified = tostring(isQualified) lpdbData.extradata.mod = Variables.varDefault('tournament_mod') - - lpdbData.qualified = isQualified and 1 or nil end ---@return boolean @@ -210,8 +120,8 @@ function StarcraftParticipantTable:_getFactionNumbers() return factionNumbers end ----@param entry StarcraftParticipantTableEntry ----@param config StarcraftParticipantTableConfig +---@param entry ParticipantTableEntry +---@param config ParticipantTableConfig function StarcraftParticipantTable:setCustomPageVariables(entry, config) if config.isRandomEvent then Variables.varDefine(entry.opponent.players[1].displayName .. '_faction', Faction.read('r')) diff --git a/lua/wikis/starcraft/Info.lua b/lua/wikis/starcraft/Info.lua index 8e3b9162d9e..74d78e75f2b 100644 --- a/lua/wikis/starcraft/Info.lua +++ b/lua/wikis/starcraft/Info.lua @@ -47,5 +47,9 @@ return { }, }, defaultMaxPlayersPerPlacement = 35, + participants = { + soloAsFactionTable = true, + sortPlayersInTable = true, + }, }, } diff --git a/lua/wikis/starcraft2/Info.lua b/lua/wikis/starcraft2/Info.lua index 13ea979d4ae..7572f3fd137 100644 --- a/lua/wikis/starcraft2/Info.lua +++ b/lua/wikis/starcraft2/Info.lua @@ -89,5 +89,9 @@ return { swiss = {'matchdiff', 'gamediff'}, }, }, + participants = { + soloAsFactionTable = true, + sortPlayersInTable = true, + }, }, } diff --git a/lua/wikis/stormgate/Info.lua b/lua/wikis/stormgate/Info.lua index 76add681236..13a0aab076c 100644 --- a/lua/wikis/stormgate/Info.lua +++ b/lua/wikis/stormgate/Info.lua @@ -47,6 +47,10 @@ return { swiss = {'matchdiff', 'gamediff'}, }, }, + participants = { + soloAsFactionTable = true, + sortPlayersInTable = true, + }, }, defaultRoundPrecision = 0, } diff --git a/lua/wikis/warcraft/Info.lua b/lua/wikis/warcraft/Info.lua index 3b175c2778b..d50a7392197 100644 --- a/lua/wikis/warcraft/Info.lua +++ b/lua/wikis/warcraft/Info.lua @@ -73,5 +73,9 @@ return { }, }, defaultMaxPlayersPerPlacement = 20, + participants = { + soloAsFactionTable = true, + sortPlayersInTable = true, + }, }, }