Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
839a15c
Add initial draft with storage+fetch
mbergen Mar 1, 2026
b14ef39
Remove header css
mbergen Mar 1, 2026
e10fa9f
Rank alignment
mbergen Mar 1, 2026
ebd8133
tabs not spaces
mbergen Mar 1, 2026
072e405
Merge branch 'main' into vrsstandings
mbergen Mar 8, 2026
cbb3be6
Apply suggestion from review
mbergen Mar 11, 2026
1af0f2d
Update VRSStandings.lua
mbergen Mar 11, 2026
6a510cf
Remove "Remove before release"
mbergen Mar 12, 2026
589846d
Merge branch 'main' into vrsstandings
mbergen Mar 20, 2026
e8fe014
updating vrsstandings functionality (#7279)
MischiefCS Mar 20, 2026
9e98299
Update lua/wikis/counterstrike/VRSStandings.lua
MischiefCS Mar 22, 2026
6b0ba46
Update lua/wikis/counterstrike/MainPageLayout/data.lua
MischiefCS Mar 22, 2026
2198172
Apply suggestions from code review
MischiefCS Mar 22, 2026
d00dd11
resolving errors, functionality and adding two more data types
MischiefCS Mar 22, 2026
2bd7c15
Merge branch 'main' into vrsstandings
MischiefCS Mar 23, 2026
5846f99
Merge branch 'main' into vrsstandings
MischiefCS Mar 23, 2026
af674b2
Apply suggestions from code review
MischiefCS Mar 24, 2026
867bee8
fixing trailing commas in code review
MischiefCS Mar 24, 2026
1e6d1cf
resolving review
MischiefCS Mar 24, 2026
26a6471
distributing code over two modules for widget and data
MischiefCS Mar 26, 2026
d55b25b
Merge branch 'main' into vrsstandings
ElectricalBoy Mar 27, 2026
108ab4b
removing mainpage sizing & allowing custom filter name for subregion
MischiefCS Mar 27, 2026
378627c
Merge branch 'main' into vrsstandings
MischiefCS Mar 27, 2026
765d76e
responding to review comments
MischiefCS Mar 30, 2026
33115ff
resolving review comments
MischiefCS Mar 31, 2026
e20aba6
Merge branch 'main' into vrsstandings
MischiefCS Mar 31, 2026
a3be70b
Apply suggestions from code review
mbergen Apr 1, 2026
f944191
Fix comma, quotes
mbergen Apr 1, 2026
6829887
Split up getStandings
mbergen Apr 1, 2026
bba70db
Remove unused value
mbergen Apr 1, 2026
514a261
Merge branch 'main' into vrsstandings
mbergen Apr 1, 2026
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
19 changes: 17 additions & 2 deletions lua/wikis/counterstrike/MainPageLayout/data.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
---
-- @Liquipedia
-- page=Module:MainPageLayout/data
Expand All @@ -18,6 +18,7 @@
local ThisDayWidgets = Lua.import('Module:Widget/MainPage/ThisDay')
local TransfersList = Lua.import('Module:Widget/MainPage/TransfersList')
local WantToHelp = Lua.import('Module:Widget/MainPage/WantToHelp')
local VRSStandings = Lua.import('Module:Widget/VRSStandings')


local CONTENT = {
Expand Down Expand Up @@ -74,6 +75,16 @@
padding = true,
boxid = MainPageLayoutUtil.BoxId.TOURNAMENTS_TICKER,
},
vrsStandings = {
heading = 'Valve Regional Standings',
body = VRSStandings{
shouldFetch = true,
fetchLimit = 5,
mainpage = true,
},
padding = false,
boxid = 1521,
}
}

return {
Expand Down Expand Up @@ -151,13 +162,17 @@
mobileOrder = 1,
content = CONTENT.specialEvents,
},
{
mobileOrder = 3,
content = CONTENT.vrsStandings,
},
{
mobileOrder = 4,
content = CONTENT.thisDay,
content = CONTENT.transfers,
},
{
mobileOrder = 5,
content = CONTENT.transfers,
content = CONTENT.thisDay,
},
{
mobileOrder = 7,
Expand Down
257 changes: 257 additions & 0 deletions lua/wikis/counterstrike/VRSStandingsData.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
---
-- @Liquipedia
-- page=Module:VRSStandingsData
--
-- 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 FnUtil = Lua.import('Module:FnUtil')
local Json = Lua.import('Module:Json')
local Logic = Lua.import('Module:Logic')
local Lpdb = Lua.import('Module:Lpdb')
local Operator = Lua.import('Module:Operator')
local Opponent = Lua.import('Module:Opponent/Custom')
local Table = Lua.import('Module:Table')

local Condition = Lua.import('Module:Condition')
local BooleanOperator = Condition.BooleanOperator
local Comparator = Condition.Comparator

---@class VRSStandingsData
local VRSStandingsData = {}

---@enum VRSStandingsDataType
VRSStandingsData.DataType = {
MAIN = 'vrs_ranking',
LIVE = 'vrs_ranking_live',
LIQUIPEDIA = 'vrs_ranking_liquipedia',
PREDICTION = 'vrs_ranking_prediction',
}

---@class VRSStandingsStanding
---@field place number
---@field points number
---@field localPlace number?
---@field globalPlace number?
---@field opponent standardOpponent

---@class VRSStandingsSettings
---@field title string
---@field shouldFetch boolean
---@field fetchLimit number?
---@field filterRegion string?
---@field filterSubregion string?
---@field filterCountry string[]?
---@field filterDisplayName string?
---@field filterType 'none' | 'region' | 'subregion' | 'country'
---@field mainpage boolean
---@field datapointType VRSStandingsDataType
---@field updated string

---Parses props, fetches or reads inline data, stores if needed, applies
---filters, and returns the final standings list alongside resolved settings.
---@param props table
---@return VRSStandingsStanding[]
---@return VRSStandingsSettings
function VRSStandingsData.getStandings(props)
local settings = VRSStandingsData._parseSettings(props)

---@type VRSStandingsStanding[]
local standings

if settings.shouldFetch then
local fetchedStandings, fetchedDate = VRSStandingsData._fetch(settings.updated, settings.datapointType)
standings = fetchedStandings
settings.updated = string.sub(fetchedDate, 1, 10) or settings.updated
else
standings = VRSStandingsData._read(props)
VRSStandingsData._store(settings.updated, settings.datapointType, standings)
end

Array.sortInPlaceBy(standings, Operator.property('place'))

-- Filtering
standings = Array.filter(standings, function(entry)
local extradata = entry.opponent.extradata or {}

if settings.filterType == 'region' then
return extradata.region == settings.filterRegion
end

if settings.filterType == 'subregion' then
return extradata.subregion == settings.filterSubregion
end

if settings.filterType == 'country' then
local filterSet = {}
for _, flag in ipairs(settings.filterCountry) do
filterSet[flag] = true
end
local matchingPlayers = Array.filter(entry.opponent.players, function(player)
return not Opponent.playerIsTbd(player)
and player.flag ~= nil
and filterSet[player.flag]
end)
return #matchingPlayers >= 3
end

return true
end)

if settings.fetchLimit then
standings = Array.sub(standings, 1, settings.fetchLimit)
end

Array.forEach(standings, function(entry, index)
entry.localPlace = index
if settings.filterType ~= 'none' then
entry.globalPlace = entry.place
end
end)

return standings, settings
end

---@private
---@param props table
---@return VRSStandingsSettings
function VRSStandingsData._parseSettings(props)
local datapointType = VRSStandingsData.DataType[props.datapointType] or VRSStandingsData.DataType.LIVE

local updated
if props.updated == 'latest' then
assert(Logic.readBool(props.shouldFetch), '\'Latest\' can only be used for fetching data')
updated = 'latest'
elseif props.updated then
updated = DateExt.toYmdInUtc(props.updated)
elseif Logic.readBool(props.shouldFetch) then
updated = 'latest'
else
error('A date must be provided when not fetching data')
end

---@type VRSStandingsSettings
local settings = {
title = props.title,
shouldFetch = Logic.readBool(props.shouldFetch),
fetchLimit = tonumber(props.fetchLimit),
filterRegion = props.filterRegion,
filterSubregion = props.filterSubregion,
filterCountry = Array.parseCommaSeparatedString(props.filterCountry),
filterDisplayName = props.filterDisplayName,
mainpage = Logic.readBool(props.mainpage),
datapointType = datapointType,
updated = updated,
filterType = 'none',
}

if settings.filterRegion then
settings.filterType = 'region'
elseif settings.filterSubregion then
settings.filterType = 'subregion'
elseif settings.filterCountry and #settings.filterCountry > 0 then
settings.filterType = 'country'
end

return settings
end

---@private
---@param props table
---@return VRSStandingsStanding[]
function VRSStandingsData._read(props)
local standings = {}
Table.iter.forEachPair(props, function(key, value)
if not string.match(key, '^%d+$') then
return
end

local data = Json.parse(value)

local opponent = Opponent.readOpponentArgs(Table.merge(data, {
type = Opponent.team,
}))

-- unset team template input to not conflict with player args
data[1] = nil
opponent.players = Array.map(Array.range(1, 5), FnUtil.curry(Opponent.readPlayerArgs, data))

opponent.extradata = opponent.extradata or {}
opponent.extradata.region = data.region
opponent.extradata.subregion = data.subregion
opponent.extradata.country = data.country

table.insert(standings, {
place = tonumber(key),
points = tonumber(data.points),
opponent = opponent
})
end)
return standings
end

---@private
---@param updated string
---@param datapointType string
---@param standings VRSStandingsStanding[]
function VRSStandingsData._store(updated, datapointType, standings)
if Lpdb.isStorageDisabled() then
return
end

local dataPoint = Lpdb.DataPoint:new{
objectname = datapointType .. '_' .. updated,
type = datapointType,
name = 'Unofficial VRS (' .. updated .. ')',
date = updated,
extradata = standings
}

dataPoint:save()
end

---@private
---@param updated string
---@param datapointType string
---@return VRSStandingsStanding[]
---@return string
function VRSStandingsData._fetch(updated, datapointType)
local conditions = Condition.Tree(BooleanOperator.all):add{
Condition.Node(Condition.ColumnName('namespace'), Comparator.eq, 0),
}

if updated ~= 'latest' then
conditions:add{
Condition.Node(Condition.ColumnName('date'), Comparator.eq, updated)
}
end

if datapointType == VRSStandingsData.DataType.LIVE then
conditions:add(
Condition.Util.anyOf(
Condition.ColumnName('type'),
{VRSStandingsData.DataType.LIVE, VRSStandingsData.DataType.MAIN}
)
)
else
conditions:add{
Condition.Node(Condition.ColumnName('type'), Comparator.eq, datapointType)
}
end

local data = mw.ext.LiquipediaDB.lpdb('datapoint', {
conditions = tostring(conditions),
query = 'extradata, date',
order = 'date desc',
limit = 1,
})

assert(data[1], 'No VRS data found for type "' .. datapointType .. '" on date "' .. updated .. '"')
return data[1].extradata, data[1].date
end

return VRSStandingsData
Loading
Loading