Jump to content
Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

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

-- Module:TourIndex

local p = {}

local cargo = mw.ext.cargo

-- Manual items inserted immediately BEFORE the specified show.
-- Add only genuinely editorial/special separators here.
local before = {
	["Live:20000815"] = {
		{ type = "break" },
	},

	["Live:20010928"] = {
		{ type = "break" },
	},

	["Live:20170725"] = {
		{ type = "ripchester" },
	},
}


local function escape(value)
	return tostring(value or "")
end


local function getDateParts(date)
	if not date or date == "" then
		return nil, nil, nil
	end

	local year, month, day =
		tostring(date):match("^(%d%d%d%d)%-(%d%d)%-(%d%d)")

	if year then
		return tonumber(year), tonumber(month), tonumber(day)
	end

	-- Fallback in case Cargo returns YYYYMMDD.
	year, month, day =
		tostring(date):match("^(%d%d%d%d)(%d%d)(%d%d)$")

	if year then
		return tonumber(year), tonumber(month), tonumber(day)
	end

	return nil, nil, nil
end


local function getDisplayDate(row)
	local page = tostring(row.Link or "")
	local raw = page:gsub("^Live:", "")

	local yearSet =
		row.Year ~= nil and tostring(row.Year) ~= ""

	local monthSet =
		row.Month ~= nil and tostring(row.Month) ~= ""

	local daySet =
		row.Day ~= nil and tostring(row.Day) ~= ""

	if yearSet and monthSet and daySet then
		local year, month, day = getDateParts(row.Date)

		if year and month and day then
			return string.format(
				"%04d.%02d.%02d",
				year,
				month,
				day
			)
		end
	end

	-- An incomplete source date is displayed from the page name.
	if #raw >= 8 then
		return raw:sub(1, 4) .. "." ..
			raw:sub(5, 6) .. "." ..
			raw:sub(7, 8)
	end

	return raw
end


local function renderYear(year)
	return table.concat({
		'|-\n',
		'| colspan="5" class="subheader" |\n',
		'==', tostring(year), '==\n',
	})
end


local function renderMonth()
	return table.concat({
		'|-\n',
		'| colspan="5" |  \n',
	})
end


local function renderHeader(text)
	return table.concat({
		'|-\n',
		'| colspan="5" class="subheader" |\n',
		'===[[', tostring(text), ']]===\n',
	})
end


local function renderBreak()
	return table.concat({
		'|-\n',
		'|<hr>||<hr>||<hr>||<hr>\n',
	})
end


local function renderRIPChester()
	return table.concat({
		'|-\n',
		'| colspan="5" style="text-align:center;" | ',
		"'''''On July 20th, the world was shocked and heartbroken ",
		"to learn of the passing of Chester Bennington.'''''\n",
	})
end


local function renderMarker(marker)
	if marker.type == "break" then
		return renderBreak()
	elseif marker.type == "ripchester" then
		return renderRIPChester()
	elseif marker.type == "header" then
		return renderHeader(marker.text)
	elseif marker.type == "month" then
		return renderMonth()
	elseif marker.type == "year" then
		return renderYear(marker.text)
	end

	return ""
end


local function hasStructuralMarker(markers)
	if not markers then
		return false
	end

	for _, marker in ipairs(markers) do
		if marker.type == "break"
			or marker.type == "header"
			or marker.type == "ripchester"
		then
			return true
		end
	end

	return false
end


local function getStyle(row)
	if row.ShowType == "cancelled" then
		return 'style="color: #D24501;"'
	end

	if row.Artist == "Linkin Park" then
		local today = os.date("!%Y-%m-%d")

		if row.Date == today then
			return 'style="color: #00cc00;"'
		elseif row.Date and row.Date > today then
			return 'style="color: #2dbaba;"'
		end
	end

	return ""
end


local function renderRow(row, frame)
	local link = escape(row.Link)
	local city = escape(row.City)
	local province = escape(row.Province)
	local state = escape(row.State)
	local venue = escape(row.Venue)
	local country = escape(row.Country)
	local event = escape(row.Event)
	local month = escape(row.Month)
	local tour = escape(row.Tour)
	local tourLeg = escape(row.TourLeg)

	local style = getStyle(row)
	local displayDate = getDisplayDate(row)

	local function noDisplay(value)
		if value == "" then
			return ""
		end

		return frame:expandTemplate {
			title = "NoDisplay",
			args = { value }
		}
	end

	local flag = ""
	if country ~= "" then
		flag = frame:expandTemplate {
			title = "flag",
			args = { country }
		}
	end

	local function styledCell(value)
		if style ~= "" then
			return style .. " | " .. value
		end

		return value
	end

	------------------------------------------------------------
	-- HIDDEN SEARCHFILTER VALUES
	------------------------------------------------------------

	-- Equivalent to:
	-- {{NoDisplay|Month}}
	local dateHidden =
		noDisplay(month)

	-- Equivalent to:
	-- {{NoDisplay|Province}}{{NoDisplay|State}}City
	local cityHidden =
		noDisplay(province) ..
		noDisplay(state)

	-- Equivalent to:
	-- {{NoDisplay|Country}} + visible flag
	local countryHidden =
		noDisplay(country)

	-- The Lua table displays tours as headers rather than as a
	-- dedicated column, so retain the tour information invisibly
	-- on every applicable show row for SearchFilter.
	local tourHidden =
		noDisplay(tour) ..
		noDisplay(tourLeg)

	local output = {}

	output[#output + 1] = "|-\n"

	-- DATE
	output[#output + 1] =
		"|" ..
		dateHidden ..
		tourHidden ..
		"[[" .. link .. "|" .. displayDate .. "]]"

	-- CITY
	output[#output + 1] =
		"||" ..
		styledCell(cityHidden .. city)

	-- VENUE
	output[#output + 1] =
		"||" ..
		styledCell(venue)

	-- COUNTRY
	output[#output + 1] =
		"||" ..
		countryHidden ..
		flag

	-- EVENT
	output[#output + 1] =
		"||" ..
		styledCell(event)

	output[#output + 1] = "\n"

	return table.concat(output)
end


local preLinkinParkArtists = {
	["The Pricks"] = true,
	["Relative Degree"] = true,
	["Xero"] = true,
	["Xero 818"] = true,
	["Hybrid Theory"] = true,
}


function p.main(frame)
	local fields = table.concat({
		"ShowPage=Link",
		"City",
		"Province",
		"State",
		"Venue",
		"Country",
		"Event",
		"Date",
		"Year",
		"Month",
		"Day",
		"Tour",
		"TourLeg",
		"ShowType",
		"Artist",
	}, ",")

	local args = {
		where = table.concat({
			'Artist IN ("Linkin Park","Hybrid Theory","Xero","Xero 818","The Pricks","Relative Degree")',
			'ShowType IN ("upcoming","current","concert","private","TV","festival","radio","benefit","album","awards","sports","label","LPU","summit","fan rehearsal","studio","acoustic","cancelled")',
		}, " AND "),
		orderBy = "Date ASC, ShowPage ASC",
		limit = 5000,
	}

	local rows = cargo.query(
		"Shows",
		fields,
		args
	)

	local output = {}

	local previousYear = nil
	local previousMonth = nil
	local previousTour = nil
	local previousTourLeg = nil
	local previousArtist = nil

	-- The historical section starts with one special heading rather
	-- than individual calendar-year headings.
	local preLinkinPark = true
	local preLinkinParkHeadingWritten = false

	for _, row in ipairs(rows) do
		local link = tostring(row.Link or "")
		local yearNumber = tonumber(row.Year)

		-- Use the normalized Cargo Date for the numeric month.
		-- The original Month field may contain names such as "November".
		local _, monthNumber = getDateParts(row.Date)

		local year = yearNumber and tostring(yearNumber) or ""
		local month = monthNumber and tostring(monthNumber) or ""

		local artist = tostring(row.Artist or "")
		local tour = tostring(row.Tour or "")

		local markers = before[link]

		------------------------------------------------------------
		-- PRE-LINKIN PARK SECTION
		------------------------------------------------------------

		if preLinkinPark then
			if not preLinkinParkHeadingWritten then
				output[#output + 1] =
					renderYear("pre-Linkin Park")

				preLinkinParkHeadingWritten = true
			end

			-- The original index starts the ordinary yearly
			-- structure with Live:20000525.
			if link == "Live:20000525" then
				preLinkinPark = false

				output[#output + 1] =
					renderYear("2000")

				previousYear = "2000"
				previousMonth = month
				previousTour = nil
				previousTourLeg = nil
				previousArtist = nil
			end
		end

		------------------------------------------------------------
		-- NORMAL YEAR CHANGES
		------------------------------------------------------------

		if not preLinkinPark
			and link ~= "Live:20000525"
			and year ~= ""
			and year ~= previousYear
		then
			output[#output + 1] =
				renderYear(year)

			previousYear = year
			previousMonth = month
			previousTour = nil
			previousTourLeg = nil
			previousArtist = nil
		end

		------------------------------------------------------------
		-- ARTIST HEADERS FOR EARLY BANDS
		------------------------------------------------------------

		if preLinkinPark
			and preLinkinParkArtists[artist]
			and artist ~= previousArtist
		then
			output[#output + 1] =
				renderHeader(artist)

			previousArtist = artist
		end

		------------------------------------------------------------
		-- TOUR / TOUR LEG / MONTH STATE
		------------------------------------------------------------
		
		local tourLeg = tostring(row.TourLeg or "")
		
		local tourChanged =
			tour ~= ""
			and tour ~= previousTour
		
		local tourLegChanged =
			tourLeg ~= ""
			and tourLeg ~= previousTourLeg
		
		-- A new header is required when either the tour changes
		-- or a new tour leg begins.
		local tourHeaderChanged =
			tourChanged or tourLegChanged
		
		local monthChanged =
			not preLinkinPark
			and month ~= ""
			and previousMonth ~= nil
			and previousMonth ~= ""
			and month ~= previousMonth

		------------------------------------------------------------
		-- MONTH BREAK
		--
		-- Always separate calendar months, whether the show is
		-- inside a tour, outside a tour, or resumes an earlier tour.
		--
		-- The only automatic exception is the first show of a
		-- genuinely new tour, because the tour header itself
		-- provides the separator.
		------------------------------------------------------------

		if monthChanged
			and not tourHeaderChanged
			and not hasStructuralMarker(markers)
		then
			output[#output + 1] =
				renderMonth()
		end

		------------------------------------------------------------
		-- MANUAL BREAKS / SPECIAL CONTENT
		------------------------------------------------------------

		if markers then
			for _, marker in ipairs(markers) do
				output[#output + 1] =
					renderMarker(marker)
			end
		end

		------------------------------------------------------------
		-- TOUR HEADER
		------------------------------------------------------------
		
		if not preLinkinPark and tourHeaderChanged then
			local tourLeg = tostring(row.TourLeg or "")
		
			local headerTarget = tour
		
			if tourLeg ~= "" then
				headerTarget = tour .. ": " .. tourLeg
			end
		
			output[#output + 1] =
				renderHeader(headerTarget)
		end

		------------------------------------------------------------
		-- SHOW
		------------------------------------------------------------

		output[#output + 1] =
			renderRow(row, frame)

		------------------------------------------------------------
		-- REMEMBER CURRENT VALUES
		------------------------------------------------------------

		if year ~= "" then
			previousYear = year
		end

		if month ~= "" then
			previousMonth = month
		end

		if tour ~= "" then
			previousTour = tour
		end
		
		if tourLeg ~= "" then
			previousTourLeg = tourLeg
		end

		if artist ~= "" then
			previousArtist = artist
		end
	end

	return table.concat(output)
end


return p