Module:TeamsMatchup/versus: Difference between revisions

From Liquipedia Call of Duty Wiki
mNo edit summary
No edit summary
Line 175: Line 175:
:css('height', '100%')
:css('height', '100%')
:css('width', '100%')
:css('width', '100%')
:css('color', 'black')
:css('font-weight', 'bold')
:css('font-weight', 'bold')
:css('display', 'flex')
:css('display', 'flex')

Revision as of 17:40, 30 August 2025

Documentation for this module may be created at Module:TeamsMatchup/versus/doc

local p = {} -- p stands for package

local DateExt = require('Module:Date/Ext')
local getArgs = require('Module:Arguments').getArgs
local args

function p.queryMatchLPDB(frame, args)
	local LPDBoutput = mw.ext.LiquipediaDB.lpdb('match', {
			conditions = '[[stream_' .. args.provider .. '::' .. args.channel .. ']] AND [[finished::0]] AND [[date::!' .. DateExt.defaultDateTime .. ']] AND [[matchid::!-1]] AND [[status::!cancelled]] AND [[status::!postponed]]',
			order = 'date asc',
			limit = 1
			})
	
	return LPDBoutput[1]
end

function p.queryLogoLPDB(frame, args, pagename, team)
	local LPDBoutput = mw.ext.LiquipediaDB.lpdb('placement', {
			conditions = '[[participant::' .. team .. ']] AND [[pagename::' .. pagename .. ']]',
			query = 'image, imagedark',
			limit = 1
			})
	
	if LPDBoutput == {} then
		return {light = 'File:Crossfire default lightmode.png', dark = 'File:Crossfire default darkmode.png'}
	else
		return {light = (LPDBoutput[1] or {}).image or 'File:Crossfire default lightmode.png', dark = (LPDBoutput[1] or {}).imagedark or 'File:Crossfire default darkmode.png'} 
	end
end

function p.queryTournamentLogo(pagename)
	local LPDBoutput = mw.ext.LiquipediaDB.lpdb('tournament', {
			conditions = '[[pagename::' .. pagename .. ']]',
			query = 'banner, extradata',
			limit = 1
			})
	
	local lightmode = (LPDBoutput[1] or {}).banner
	local darkmode = (LPDBoutput[1] or {}).bannerdark
	
	return {lightmode = lightmode, darkmode = darkmode ~= '' and darkmode or lightmode} 
end

function p.printPlayerRow(frame, wrapper, player, flag)
	player = player or ''
	local LPDBoutput = mw.ext.LiquipediaDB.lpdb('player', {
			conditions = '[[pagename::' .. mw.ext.TeamLiquidIntegration.resolve_redirect(player):gsub(' ','_') .. ']]',
			limit = 1
			})
	
	local image
	if (LPDBoutput[1] or {}).image and (LPDBoutput[1] or {}).image ~= '' then
		image = 'File:' .. LPDBoutput[1].image
	elseif (((LPDBoutput[1] or {}).extradata or {}).image) and (((LPDBoutput[1] or {}).extradata or {}).image ~= '') then
		image = 'File:' .. ((LPDBoutput[1] or {}).extradata or {}).image
	else
		image = 'File:Blank Player Image.png'
	end
	
	if player == '' then
		display = '<abbr title="To Be Determined">TBD</abbr>'
	else
		display = frame:expandTemplate{title = 'player', args = {player:gsub(' %((.*)%)', ''), link = player, flag = flag:lower()}}
	end
	
	tr = wrapper:tag('tr')
	td = tr:tag('td')
		:css('padding','0')
		:css('text-align','center')
		:wikitext('[[' .. image ..'|225x150px|link=' .. player .. ']]')
	tr = wrapper:tag('tr')
	th = tr:tag('th')
		:css('position','absolute')
		:css('margin-top','-32px')
		:css('width','225px')
		:css('opacity','95%')
		:wikitext(display)
	
	return wrapper
end

function p.create(frame)
	local args = getArgs(frame)
	
	if args.provider == nil or args.channel == nil then
		return ''
	end
	
	local match = p.queryMatchLPDB(frame, args) or {}
	
	if match.pagename == nil then
		return ''
	end
	
	mw.ext.VariablesLua.vardefine('match_team1', match.opponent1)
	mw.ext.VariablesLua.vardefine('match_team2', match.opponent2)
	mw.ext.VariablesLua.vardefine('match_pagename', match.pagename)
	mw.ext.VariablesLua.vardefine('match_staticid', match.staticid)
	mw.ext.VariablesLua.vardefine('match_matchid', match.matchid)
	
	if match.parent == '' then
		match.parent = nil
	end
	
	local team1_logos = p.queryLogoLPDB(frame, args, (match.parent or match.pagename), match.opponent1)
	local team2_logos = p.queryLogoLPDB(frame, args, (match.parent or match.pagename), match.opponent2)
	
	local team1_wins
	local team2_wins
	local team1_losses
	local team2_losses
	local team1_winrate
	local team2_winrate
	
	local matchDate = args.date or os.date("%Y-%m-%d", (os.time() - 7884000))
	
	local timezoneID
	if ((match.extradata or {}).timezoneID == '') or ((match.extradata or {}).timezoneID == nil) then
		timezoneID = 'UTC'
	else
		timezoneID = (match.extradata or {}).timezoneID
	end
	local formattedDate = p.formatDate(p.adjustDate(match.date,(match.extradata or {}).timezone)) .. ' ' .. frame:expandTemplate{ title='Abbr/' .. (timezoneID or 'UTC')}
	
	local team1_h2h
	local team2_h2h
	local team1_h2hg
	local team2_h2hg

	local wrapper = mw.html.create('div')
		:addClass('row')
		:css('text-align','center')
		:css('border','1px solid #bbbbbb !important')
		:css('margin','5px 0')
		:css('padding', '5px')
		:css('min-height', '170px')

	local colteam1 = wrapper:tag('div')
		:addClass('col-sm-4')
		:addClass('col-xs-6')
  		:addClass('col-sm-pull-4')
		:css('display', 'flex')
		:css('flex-direction', 'column')
	colteam1:tag('div')
			:css('margin', 'auto')
			:addClass('logo lightmode')
			:wikitext('[[' .. team1_logos.light ..'|150x150px|link=' .. match.opponent1 .. ']]')
	colteam1:tag('div')
			:css('margin', 'auto')
			:css('display', 'none')
			:addClass('logo darkmode')
			:wikitext('[[' .. team1_logos.dark ..'|150x150px|link=' .. match.opponent1 .. ']]')
	colteam1:tag('strong')
			:css('font-size','130%')
			:wikitext('[[' .. match.opponent1 .. '|' .. match.opponent1:gsub(' %((.*)%)', '') .. ']]')

	local colvs = wrapper:tag('div')
		:addClass('col-sm-4')
		:addClass('col-xs-12')
  		:addClass('col-sm-push-4')
		:css('min-height', '160px')
	local background = colvs:tag('div')
		:css('position', 'absolute')
		:css('height', '100%')
		:css('width', '100%')
		:css('opacity', '15%')
		:css('display', 'flex')
	local logos = p.queryTournamentLogo(match.parent ~= '' and match.parent or match.pagename)
	background:tag('div')
			  :css('margin', 'auto')
			  :wikitext('<span class="banner lightmode">[[File:' .. (logos.lightmode or 'Filler 600px.png') .. '|300x155px|link=]]</span>')
			  :wikitext('<span class="banner darkmode">[[File:' .. (logos.darkmode or 'Filler 600px.png') .. '|300x155px|link=]]</span>')
	local infodiv = colvs:tag('div')
		:css('position', 'absolute')
		:css('height', '100%')
		:css('width', '100%')
		:css('font-weight', 'bold')
		:css('display', 'flex')
		:css('flex-direction', 'column')
	infodiv:tag('span')
		:css('font-size','125%')
		:wikitext('[[' .. match.pagename .. '|' .. match.tournament .. ']]')
	infodiv:tag('span')
		:css('margin', 'auto')
		:css('font-size','250%')
		:wikitext('VS')
	infodiv:tag('span')
		:wikitext(frame:expandTemplate({title = 'countdown', args = {date = formattedDate, rawdatetime = 'true'}}))

	local colteam2 = wrapper:tag('div')
		:addClass('col-sm-4')
		:addClass('col-xs-6')
  		:addClass('pull-right')
		:css('display', 'flex')
		:css('flex-direction', 'column')
	colteam2:tag('div')
			:css('margin', 'auto')
			:addClass('logo lightmode')
			:wikitext('[[' .. team2_logos.light ..'|150x150px|link=' .. match.opponent2 .. ']]')
	colteam2:tag('div')
			:css('margin', 'auto')
			:css('display', 'none')
			:addClass('logo darkmode')
			:wikitext('[[' .. team2_logos.dark ..'|150x150px|link=' .. match.opponent2 .. ']]')
	colteam2:tag('strong')
			:css('font-size','130%')
			:wikitext('[[' .. match.opponent2 .. '|' .. match.opponent2:gsub(' %((.*)%)', '') .. ']]')


	local wrapper2 = mw.html.create('table')
		:addClass('table')
		:addClass('wikitable')
		:css('float','left')
		:css('margin','0')
		:css('width', '227px')
	tr = wrapper2:tag('tr')
		:addClass('wiki-backgroundcolor-light')
		:css('font-size','150%')
	th = tr:tag('th')
		:css('background-color','#c1dfdf !important')
		:css('padding','5px')
		:wikitext('Players')
	p.printPlayerRow(frame, wrapper2, match.opponent1players.p1, match.opponent1players.p1flag)
	p.printPlayerRow(frame, wrapper2, match.opponent1players.p2, match.opponent1players.p2flag)
	p.printPlayerRow(frame, wrapper2, match.opponent1players.p3, match.opponent1players.p3flag)
	p.printPlayerRow(frame, wrapper2, match.opponent1players.p4, match.opponent1players.p4flag)
	p.printPlayerRow(frame, wrapper2, match.opponent1players.p5, match.opponent1players.p5flag)
	
	local wrapper3 = mw.html.create('table')
		:addClass('table')
		:addClass('wikitable')
		:css('float','right')
		:css('margin','0')
		:css('width', '227px')
	tr = wrapper3:tag('tr')
		:addClass('wiki-backgroundcolor-light')
		:css('font-size','150%')
	th = tr:tag('th')
		:css('background-color','#c1dfdf !important')
		:css('padding','5px')
		:wikitext('Players')
	p.printPlayerRow(frame, wrapper3, match.opponent2players.p1, match.opponent2players.p1flag)
	p.printPlayerRow(frame, wrapper3, match.opponent2players.p2, match.opponent2players.p2flag)
	p.printPlayerRow(frame, wrapper3, match.opponent2players.p3, match.opponent2players.p3flag)
	p.printPlayerRow(frame, wrapper3, match.opponent2players.p4, match.opponent2players.p4flag)
	p.printPlayerRow(frame, wrapper3, match.opponent2players.p5, match.opponent2players.p5flag)
	
	local wrapper4 = mw.html.create('div')
		:css('width','80%')
	local div = wrapper4:tag('div')
		:css('display','inline-block')
		:css('vertical-align','top')
		:css('width','25%')
		:css('margin-right','5px')
	
	local div = wrapper4:tag('div')
		:css('display','inline-block')
		:css('vertical-align','top')
		:css('width','25%')
	local table = div:tag('table')
		:addClass('table stats')
		:css('border','1px solid #bbbbbb !important')
		:css('text-align','center')
		:css('width','100%')
	tr = table:tag('tr')
		:addClass('wiki-backgroundcolor-light')
		:css('font-size','130%')
	th = tr:tag('th')
		:attr('colspan',3)
		:css('padding','5px')
		:wikitext(frame:expandTemplate{title = 'Abbr', args = {'Overview Stats', 'Over the last 3 months'}})
	
	data = mw.ext.LiquipediaDB.lpdb('match', {
			limit = 5000,
			offset = 0,
			conditions = '(([[opponent1::' .. match.opponent1 .. ']] AND [[winner::1]]) OR ([[opponent2::' .. match.opponent1 .. ']] AND [[winner::2]])) AND [[date::>' .. matchDate .. ']] AND [[matchid::!-1]]',
			query = 'count::winner',
		})
	
	team1_wins = tonumber(data[1].count_winner)
	
	data = mw.ext.LiquipediaDB.lpdb('match', {
			limit = 5000,
			offset = 0,
			conditions = '(([[opponent1::' .. match.opponent1 .. ']] AND [[winner::2]]) OR ([[opponent2::' .. match.opponent1 .. ']] AND [[winner::1]])) AND [[date::>' .. matchDate .. ']] AND [[matchid::!-1]]',
			query = 'count::winner',
		})
	
	team1_losses = tonumber(data[1].count_winner)
	
	team1_winrate = (team1_wins / (team1_wins + team1_losses)) * 100
	
	tr = table:tag('tr')
		:addClass('stats-row')
	td = tr:tag('td')
		:css('text-align','center')
		:wikitext(p.round(team1_winrate, 0) .. '%')
	
	data = mw.ext.LiquipediaDB.lpdb('match', {
			limit = 5000,
			offset = 0,
			conditions = '(([[opponent1::' .. match.opponent2 .. ']] AND [[winner::1]]) OR ([[opponent2::' .. match.opponent2 .. ']] AND [[winner::2]])) AND [[date::>' .. matchDate .. ']] AND [[matchid::!-1]]',
			query = 'count::winner',
		})
	
	team2_wins = tonumber(data[1].count_winner)
	
	data = mw.ext.LiquipediaDB.lpdb('match', {
			limit = 5000,
			offset = 0,
			conditions = '(([[opponent1::' .. match.opponent2 .. ']] AND [[winner::2]]) OR ([[opponent2::' .. match.opponent2 .. ']] AND [[winner::1]])) AND [[date::>' .. matchDate .. ']] AND [[matchid::!-1]]',
			query = 'count::winner',
		})
	
	team2_losses = tonumber(data[1].count_winner)
	
	team2_winrate = (team2_wins / (team2_wins + team2_losses)) * 100
	
	td = tr:tag('td')
		:css('text-align','center')
		:css('font-weight','bold')
		:wikitext('Winrate')
	td = tr:tag('td')
		:css('text-allign','center')
		:wikitext(p.round(team2_winrate, 0) .. '%')
	
	data = mw.ext.LiquipediaDB.lpdb('match', {
			limit = 5000,
			offset = 0,
			conditions = '(([[opponent1::' .. match.opponent1 .. ']] AND [[opponent2::' .. match.opponent2 .. ']] AND [[winner::1]]) OR ([[opponent1::' .. match.opponent2 .. ']] AND [[opponent2::' .. match.opponent1.. ']] AND [[winner::2]])) AND [[date::>' .. matchDate .. ']] AND [[matchid::!-1]]',
			query = 'count::winner',
		})
	
	team1_h2h = tonumber(data[1].count_winner)
	
	data = mw.ext.LiquipediaDB.lpdb('game', {
			limit = 5000,
			offset = 0,
			conditions = '(([[opponent1::' .. match.opponent1 .. ']] AND [[opponent2::' .. match.opponent2 .. ']] AND [[winner::1]]) OR ([[opponent1::' .. match.opponent2 .. ']] AND [[opponent2::' .. match.opponent1.. ']] AND [[winner::2]])) AND [[date::>' .. matchDate .. ']] AND [[matchid::!-1]]',
			query = 'count::winner',
		})
	
	team1_h2hg = tonumber(data[1].count_winner)
	
	data = mw.ext.LiquipediaDB.lpdb('match', {
			limit = 5000,
			offset = 0,
			conditions = '(([[opponent1::' .. match.opponent1 .. ']] AND [[opponent2::' .. match.opponent2 .. ']] AND [[winner::2]]) OR ([[opponent1::' .. match.opponent2 .. ']] AND [[opponent2::' .. match.opponent1.. ']] AND [[winner::1]])) AND [[date::>' .. matchDate .. ']] AND [[matchid::!-1]]',
			query = 'count::winner',
		})
	
	team2_h2h = tonumber(data[1].count_winner)
	
	data = mw.ext.LiquipediaDB.lpdb('game', {
			limit = 5000,
			offset = 0,
			conditions = '(([[opponent1::' .. match.opponent1 .. ']] AND [[opponent2::' .. match.opponent2 .. ']] AND [[winner::2]]) OR ([[opponent1::' .. match.opponent2 .. ']] AND [[opponent2::' .. match.opponent1.. ']] AND [[winner::1]])) AND [[date::>' .. matchDate .. ']] AND [[matchid::!-1]]',
			query = 'count::winner',
		})
	
	team2_h2hg = tonumber(data[1].count_winner)
	
	tr = table:tag('tr')
		:addClass('stats-row')
	td = tr:tag('td')
		:css('text-align','center')
		if team1_h2h+team2_h2h == 0 then
			td:wikitext(team1_h2h)
		else
			td:wikitext(team1_h2h .. ' (' .. p.round(((team1_h2h / (team1_h2h + team2_h2h)) * 100), 1) .. '%)')
		end
	
	td = tr:tag('td')
		:css('text-align','center')
		:css('font-weight','bold')
		:wikitext('Head to Head')
	td = tr:tag('td')
		:css('text-align','center')
		if team1_h2h+team2_h2h == 0 then
			td:wikitext(team2_h2h)
		else
			td:wikitext(team2_h2h .. ' (' .. p.round(((team2_h2h / (team1_h2h + team2_h2h)) * 100), 1) .. '%)')
		end
	
	local div = wrapper4:tag('div')
		:css('display','inline-block')
		:css('vertical-align','top')
		:css('width','25%')
		:css('margin-left','5px')
	local table = div:tag('table')
		:addClass('table streams-table streams')
		:css('border','1px solid #bbbbbb !important')
		:css('text-align','center')
		:css('width','100%')
	tr = table:tag('tr')
		:addClass('wiki-backgroundcolor-light')
		:css('font-size','130%')
	th = tr:tag('th')
		:attr('colspan',4)
		:css('padding','5px')
		:wikitext('Streams')
	tr = table:tag('tr')
	if (match.stream.twitch) and (match.stream.twitch ~= '') then
	td = tr:tag('td')
		:wikitext('[[Special:StreamPage/twitch/' .. match.stream.twitch .. '|<i class="lp-icon lp-twitch"></i>]]')
	end
	if (match.stream.youtube) and (match.stream.youtube ~= '') then
	td = tr:tag('td')
		:wikitext('[[Special:StreamPage/youtube/' .. match.stream.youtube .. '|<i class="lp-icon lp-youtube"></i>]]')
	end
	if (match.stream.facebook) and (match.stream.facebook ~= '') then
	td = tr:tag('td')
		:wikitext('[[Special:StreamPage/facebook/' .. match.stream.facebook .. '|<i class="lp-icon lp-facebook"></i>]]')
	end
	if (match.stream.steamtv) and (match.stream.steamtv ~= '') then
	td = tr:tag('td')
		:wikitext('[https://steam.tv/' .. match.stream.steamtv .. ' <i class="lp-icon lp-steam"></i>]')
	end
	
	return tostring(wrapper:done()) .. tostring(wrapper2:done()) .. tostring(wrapper3:done()) .. '<center>' .. tostring(wrapper4:done()) .. '</center>'
	
end

function p.round(num, numDecimalPlaces)
	num = tonumber(num)
	local mult = 10^(numDecimalPlaces or 0)
	return math.floor(num * mult + 0.5) / mult
end

function p.formatDate(date)
    return os.date( "!%B %d, %Y - %H:%M", date)
end

function p.serverTimeOffset()
	local utcDate = os.date("!%Y-%m-%d %H:%M:%S")
	local utcYear,utcMonth,utcDay,utcHour,utcMin,utcSec=utcDate:match('(%d+)-(%d+)-(%d+) (%d+):(%d+):(%d+)')
	local utcUnixDate = os.time({day=utcDay,month=utcMonth,year=utcYear,hour=utcHour,min=utcMin,sec=utcSec})
	local localDate = os.date("%Y-%m-%d %H:%M:%S")
	local localYear,localMonth,localDay,localHour,localMin,localSec=localDate:match('(%d+)-(%d+)-(%d+) (%d+):(%d+):(%d+)')
	local localUnixDate = os.time({day=localDay,month=localMonth,year=localYear,hour=localHour,min=localMin,sec=localSec})
	return localUnixDate - utcUnixDate
end

function p.adjustDate(date, timezone)
  	if type(date) == 'string' then
		local year, month, day, hour, min, sec = date:match('(%d+)-(%d+)-(%d+) (%d+):(%d+):(%d+)')
		date = os.time({day=day, month=month, year=year, hour=hour, min=min, sec=sec})
		date = date + p.serverTimeOffset() -- adjust for the fact that the server might not be set to UTC
	end
  	if not timezone or timezone == '' then
		timezone = '+00:00'
	end
    local timezoneSign, timezoneHour, timezoneMin = string.match(timezone, '^([+-])(%d+):(%d+)') 
  	return date + tonumber(timezoneSign .. '1') * (timezoneHour * 3600 + timezoneMin * 60) 
end

return p