Module:PlayerCurrentTeam

From Leaguepedia | League of Legends Esports Wiki
Jump to: navigation, search

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

local util_args = require('Module:ArgsUtil')
local util_cargo = require("Module:CargoUtil")
local util_esports = require("Module:EsportsUtil")
local util_html = require("Module:HtmlUtil")
local util_map = require("Module:MapUtil")
local util_sort = require("Module:SortUtil")
local util_source = require("Module:SourceUtil")
local util_table = require("Module:TableUtil")
local util_text = require("Module:TextUtil")
local util_title = require("Module:TitleUtil")
local util_vars = require("Module:VarsUtil")
local i18n = require('Module:i18nUtil')

local LCS = require('Module:LuaClassSystem')

local OD = require('Module:OrderedDict')

local PCT = LCS.class()
local QB = LCS.class()

local CONTRACT_MAINTAINED_ON_LEAVE = {
	from_sister = true,
	to_sister = true,
	from_academy = true,
	to_academy = true,
	from_main = true,
	to_main = true,
	gcd_from_academy = true,
	gcd_from_main = true,
	gcd_to_academy = true,
	gcd_to_main = true,
}

local h = {}

local p = {}

-- for testing to be called from MW
function p.test(frame)
	local args = util_args.merge()
	local result = p.main(args[1])
	local output = mw.html.create('table')
		:addClass('wikitable')
	for _, row in ipairs(result) do
		output:tag('tr')
			:tag('td'):wikitext(row.team)
			:tag('td'):wikitext(tostring(row.role))
			:tag('td'):wikitext(row.role:images())
	end
	return output
end

-- intended to be called by Infobox/Player
-- so this does not apply any processing to the output
-- and instead just returns a data table
function p.main(player)
	return PCT:init(player)
end

function PCT:init(subject)
	self.CONTRACT_DATES = {}
	self.PREVIOUS_RESIDENCIES = {}
	self.last_team_change_index = nil
	local listOfChanges = self:queryForChanges(subject)
	self:processChangesRows(listOfChanges)
	if #listOfChanges == 0 then return self:defaultOutput() end
	local netStatuses = self:computeNetStatuses(listOfChanges)
	local listOfSubjects = self:getFinalListOfSubjects(netStatuses)
	return self:makeOutput(listOfSubjects, listOfChanges)
end

function PCT:defaultOutput()
	return { last = {}, contractDates = {} }
end

function PCT:queryForChanges(subject)
	return util_cargo.queryAndCast(self:getQuery(subject))
end

-- start cargo shit
local QBRosters = QB:extends()
local QBContracts = QB:extends()
local QBResidency = QB:extends()

function PCT:getQuery(subject)
	local rosBuilder = QBRosters()
	local contractsBuilder = QBContracts()
	local resBuilder = QBResidency()
	local query = {
		union = true,
		{
			tables = rosBuilder:getTables(),
			join = rosBuilder:getJoin(),
			where = rosBuilder:getWhere(subject),
			fields = rosBuilder:getFields(),
			complexTypes = {
				Role = {
					type = 'RoleList',
					args = {
						'Roles',
						modifier = 'RoleModifier',
					}
				}
			},
		},
		{
			tables = contractsBuilder:getTables(),
			join = contractsBuilder:getJoin(),
			where = contractsBuilder:getWhere(subject),
			fields = contractsBuilder:getFields(),
		},
		{
			tables = resBuilder:getTables(),
			join = resBuilder:getJoin(),
			where = resBuilder:getWhere(subject),
			fields = resBuilder:getFields(),
		},
		sortKey = { 'Date_Sort', 'N_LineInDate', 'N_LineInNews' },
		sortOrder = { true, true, true },
	}
	return query
end

function QB:getWhere(player)
	local where = {
		('PR.OverviewPage="%s"'):format(player),
	}
	return util_cargo.concatWhere(where)
end

function QB:getFields()
	local ret = {
		'News.Date_Display',
		'News.Region',
		'News._pageName',
		'News.Date_Sort',
		'News.N_LineInDate',
		'PR.OverviewPage=PlayerLink',
	}
	return ret
end

function QBRosters:getTables()
	local ret = {
		'NewsItems=News',
		'RosterChanges=RC',
		'PlayerRedirects=PR',
		'TeamRedirects=TRed',
		'SisterTeams=ST',
	}
	return ret
end

function QBRosters:getJoin()
	local ret = {
		'News.NewsId=RC.NewsId',
		'RC.Player=PR.AllName',
		
		-- roster changes need to discover the org so we can pull contracts
		'RC.Team=TRed.AllName',
		'TRed._pageName=ST.Team',
	}
	return ret
end

function QBRosters:getFields()
	local fields = self:super('getFields')
	local tbl = {
		'RC.Player',
		'RC.Team',
		'RC.Roles',
		'RC.RoleModifier',
		'RC.Direction',
		'RC.CurrentTeamPriority=Priority',
		'RC.Preload',
		'RC.Direction',
		'RC.N_LineInNews',
		'COALESCE(ST._pageName, RC.Team)=SisterTeamPage',
	}
	util_table.mergeArrays(fields, tbl)
	return fields
end

function QBContracts:getTables()
	local ret = {
		'NewsItems=News',
		'Contracts',
		'PlayerRedirects=PR',
		'TeamRedirects=TRed',
		'SisterTeams=ST',
	}
	return ret
end

function QBContracts:getJoin()
	local ret = {
		'News.NewsId=Contracts.NewsId',
		'Contracts.Player=PR.AllName',
		
		-- contracts need to respect contracts from the same org
		'Contracts.Team=TRed.AllName',
		'TRed._pageName=ST.Team',
	}
	return ret
end

function QBContracts:getFields()
	local fields = self:super('getFields')
	local tbl = {
		'COALESCE(ST._pageName, Contracts.Team)=SisterTeamPage',
		'Contracts.IsRemoval=IsContractRemoval [boolean]',
		'Contracts.ContractEnd',
	}
	util_table.mergeArrays(fields, tbl)
	return fields
end

function QBResidency:getTables()
	local ret = {
		'NewsItems=News',
		'ResidencyChanges=ResC',
		'PlayerRedirects=PR',
	}
	return ret
end

function QBResidency:getJoin()
	local ret = {
		-- residency changes are just the same news id that's it
		'News.NewsId=ResC.NewsId',
		'ResC.Player=PR.AllName',
	}
	return ret
end

function QBResidency:getFields()
	local fields = self:super('getFields')
	local tbl = {
		'ResC.ResidencyOld',
	}
	util_table.mergeArrays(fields, tbl)
	return fields
end

-- end cargo shit

function PCT:processChangesRows(listOfChanges)
	util_map.selfRowsInPlace(self, listOfChanges, self.processOneChangeRow)
end

function PCT:processOneChangeRow(row)
	row.Subject = row.Team
	row.team = row.Team
	row.role = row.Role
	if h.isContractRemoval(row) then
		self.CONTRACT_DATES[row.SisterTeamPage] = nil
	end
	if row.ContractEnd then
		self.CONTRACT_DATES[row.SisterTeamPage] = row.ContractEnd
	end
	if h.isRosterChange(row) then
		self.last_team_change_index = row.index
		util_vars.log(self.last_team_change_index)
	end
	-- this is plaintext so that we can cast it easily as a list when we're done
	self.PREVIOUS_RESIDENCIES[#self.PREVIOUS_RESIDENCIES+1] = row.ResidencyOld
end

function h.isContractRemoval(row)
	-- In contrast to "Module:PlayerTeamHistoryAbstract" we look at the preload to decide whether
	-- or not we have to remove contracts. PTHA instead does this weird reverse-lookup thingy
	-- working backwards through history in two phases. PTHA's approach is significantly more complex
	-- but maybe not unreasonable because it's doing a ton of other things at the same time. But
	-- this approach is hopefully sufficient for this smaller, less-complex use case here.
	if row.IsContractRemoval then return true end
	if row.Direction == 'Join' then return false end
	if not row.Preload then return false end
	return not CONTRACT_MAINTAINED_ON_LEAVE[row.Preload]
end

function h.isRosterChange(row)
	return row.Direction
end

-- get dictionary keyed by subject
function PCT:computeNetStatuses(listOfChanges)
	local currentStatuses = {}
	for _, row in ipairs(listOfChanges) do
		self:updateEntryForThis(currentStatuses, row)
	end
	return currentStatuses
end

function PCT:updateEntryForThis(currentStatuses, row)
	if currentStatuses[row.Subject] then
		row.Priority = row.Priority or currentStatuses[row.Subject].Priority
	end
	if row.Subject then
		currentStatuses[row.Subject] = row
	end
end

-- get ordered list of subjects
function PCT:getFinalListOfSubjects(netStatuses)
	local subjects = {}
	for _, status in pairs(netStatuses) do
		self:addSubjectToOutputIfNeeded(subjects, status)
	end
	util_sort.tablesByKeys(subjects, 'Priority', true)
	return subjects
end

function PCT:addSubjectToOutputIfNeeded(subjects, status)
	if not self:doWeAddSubjectToOutput(status) then return end
	subjects[#subjects+1] = status
end

function PCT:doWeAddSubjectToOutput(status)
	return status.Direction == 'Join'
end

-- make output
function PCT:makeOutput(listOfSubjects, listOfChanges)
	listOfSubjects.last = self:getLastTeamList(listOfChanges)
	listOfSubjects.contractDates = self.CONTRACT_DATES
	listOfSubjects.resPrevList = self.PREVIOUS_RESIDENCIES
	return listOfSubjects
end

function PCT:getLastTeamList(listOfChanges)
	if self.last_team_change_index == nil then
		return {}
	end
	return h.getLastTeamInfo(listOfChanges[self.last_team_change_index])
end

function h.getLastTeamInfo(row)
	local ret = {
		team = row.Team,
		role = row.Role,
	}
	return ret
end

return p