Jump to content

Module:Women in Red event

From Calamity Wiki

Module:Women in Red event has various functions that use the list of WikiProject Women in Red events stored at Wikipedia:WikiProject Women in Red/events. These are used in

and elsewhere.

Usage

main function

{{#invoke:Women in Red event|main|event|field}}

Given an event number, reads one of the fields (event, year, month, type, image, series). For example:

  • {{#invoke:Women in Red event|main|294|name}} gives "Lua error: bad argument #1 to 'mw.loadJsonData' ('Wikipedia:WikiProject Women in Red/events' is not a valid JSON page)."
  • {{#invoke:Women in Red event|main|7|year}} gives "Lua error: bad argument #1 to 'mw.loadJsonData' ('Wikipedia:WikiProject Women in Red/events' is not a valid JSON page)."
  • {{#invoke:Women in Red event|main|293|series}} gives "Lua error: bad argument #1 to 'mw.loadJsonData' ('Wikipedia:WikiProject Women in Red/events' is not a valid JSON page)."

A third optional parameter, allows you to specify a default value if none is defined. For example:

  • {{#invoke:Women in Red event|main|291|type|edit-a-thon}} gives "Lua error: bad argument #1 to 'mw.loadJsonData' ('Wikipedia:WikiProject Women in Red/events' is not a valid JSON page)." because there is no type value defined for 291.

A |hash=no option allows you to remove the # character from the output (this is sometimes useful because this character can upset the formatting of a page and it is not possible to have this character in a page name). For example:

  • {{#invoke:Women in Red event|main|293|series|hash=no}} gives "Lua error: bad argument #1 to 'mw.loadJsonData' ('Wikipedia:WikiProject Women in Red/events' is not a valid JSON page)."

name function

{{#invoke:Women in Red event|name|event}}

Given an event number, returns the name or the series of the event. If the both the name and the series are defined, then it will prepend the series with a colon. For example:

  • {{#invoke:Women in Red event|name|294}} gives "Lua error: bad argument #1 to 'mw.loadJsonData' ('Wikipedia:WikiProject Women in Red/events' is not a valid JSON page)."
  • {{#invoke:Women in Red event|name|7}} gives "Lua error: bad argument #1 to 'mw.loadJsonData' ('Wikipedia:WikiProject Women in Red/events' is not a valid JSON page)."
  • {{#invoke:Women in Red event|name|236}} gives "Lua error: bad argument #1 to 'mw.loadJsonData' ('Wikipedia:WikiProject Women in Red/events' is not a valid JSON page)."

text function

{{#invoke:Women in Red event|text|event}}

Produces a piped link to the event page followed by the date, for use in the project banner. For example:

  • {{#invoke:Women in Red event|text|291}} gives "Lua error: bad argument #1 to 'mw.loadJsonData' ('Wikipedia:WikiProject Women in Red/events' is not a valid JSON page)."

date function

{{#invoke:Women in Red event|date|event|sentence|abbrev}}

Produces a formatted date (including month and year) of the event. For example:

  • {{#invoke:Women in Red event|date|210}} gives "Lua error: bad argument #1 to 'mw.loadJsonData' ('Wikipedia:WikiProject Women in Red/events' is not a valid JSON page)."

This function takes two optional parameters:

  • |sentence= - if set to "yes" then the date will be expressed as a sentence using the words "from" and "to", e.g. "Lua error: bad argument #1 to 'mw.loadJsonData' ('Wikipedia:WikiProject Women in Red/events' is not a valid JSON page)."
  • |abbrev= - if set to "yes" then the names of the months will be abbreviated, e.g. "Lua error: bad argument #1 to 'mw.loadJsonData' ('Wikipedia:WikiProject Women in Red/events' is not a valid JSON page)."

Both of these options may be used simultaneously, but the output is slightly awkward, e.g. "Lua error: bad argument #1 to 'mw.loadJsonData' ('Wikipedia:WikiProject Women in Red/events' is not a valid JSON page)."

findLatest function

{{#invoke:Women in Red event|findLatest|add}}

Shows the latest event number to be defined (currently Lua error: bad argument #1 to 'mw.loadJsonData' ('Wikipedia:WikiProject Women in Red/events' is not a valid JSON page).), ignoring the quasi event numbers "2017" and "2018". For example

  • {{#invoke:Women in Red event|findLatest}} gives "Lua error: bad argument #1 to 'mw.loadJsonData' ('Wikipedia:WikiProject Women in Red/events' is not a valid JSON page)."

An optional parameter |add= can be used to add or subtract numbers to the latest. For example

  • {{#invoke:Women in Red event|findLatest|add=1}} gives "Lua error: bad argument #1 to 'mw.loadJsonData' ('Wikipedia:WikiProject Women in Red/events' is not a valid JSON page)."
  • {{#invoke:Women in Red event|findLatest|add=-5}} gives "Lua error: bad argument #1 to 'mw.loadJsonData' ('Wikipedia:WikiProject Women in Red/events' is not a valid JSON page)."

list function

{{#invoke:Women in Red event|list|first|last|latest|meetup|logo|series|name|date|template|wrapper|category}}

Produce a table of events, for various purposes. The following parameters are recognised.

  • |first=, |last= - specify the first and/or last event to show in the list.

For example {{#invoke:Women in Red event|list|first=52|last=55}} produces Lua error: bad argument #1 to 'mw.loadJsonData' ('Wikipedia:WikiProject Women in Red/events' is not a valid JSON page).

  • |latest= - to show a specified number of the latest events. In this case |first= and |last= will be ignored.

For example, {{#invoke:Women in Red event|list|latest=3}} produces Lua error: bad argument #1 to 'mw.loadJsonData' ('Wikipedia:WikiProject Women in Red/events' is not a valid JSON page).

Columns

The columns of the table can be customised by setting various parameters to yes or no.

  • |meetup= - shows or hides a column with the meetup number. By default this column is shown. If a background colour is defined then it will be used in this cell.
  • |logo= - enables a column to show the logo associated with each event.
  • |series= - enables a column to show the series (if applicable) of each event.
  • |name= - enables a column to show the name of each event, and a link to the meetup page. This will be shown by default.
  • |date= - shows or hides a column which displays the date of each event. Shown by default.
  • |image= - shows or hides a column which displays an image chosen to represent the event.
  • |template= - column to show the syntax for the template using the shortcut {{WIR}}
  • |wrapper= (deprecated) - column to show the wrapper template for each event. These are no longer in general use.
  • |category= - enables a column which links to the category for articles created by each event, along with the population of that category.

If any other columns are required, please ask one of the module's maintainers.

  • |contrast= - enables a column which shows the contrast ratio according to the Web Content Accessibility Guidelines. This is for the benefit of event planners who are choosing the colour theme for an event.

Examples

The table currently shown on Wikipedia talk:WikiProject Women in Red/events is produced by the syntax {{#invoke:Women in Red event|list|logo=yes|series=yes|wrapper=yes|category=yes}}.

The table currently shown on Template:WikiProject Women in Red/doc is produced by the syntax {{#invoke:Women in Red event|list|latest=10|template=yes}} which produces the following: Lua error: bad argument #1 to 'mw.loadJsonData' ('Wikipedia:WikiProject Women in Red/events' is not a valid JSON page).

event_list_by_year function

{{#invoke:Women in Red event|event_list_by_year|year}}

Produces a list of all past events in a particular year, for use in Template:Women in Red navigation.

For example, {{#invoke:Women in Red event|event_list_by_year|2023}} produces:

  • Lua error: bad argument #1 to 'mw.loadJsonData' ('Wikipedia:WikiProject Women in Red/events' is not a valid JSON page).

current_events function

{{#invoke:Women in Red event|current_events|year|month}} or {{#invoke:Women in Red event|current_events|event number}}

Produces a list of current, upcoming, recently completed and ongoing events, for navigational purposes at the top of meetup pages. The display on a certain month and year can be specified by using the parameters:

  • year - specify the 4-digit year, e.g. |year=2022
  • month - specify the month as a number between 1-12. If the year is specified then the month will default to 1, i.e. January.

Alternatively use the event number as an unnamed parameter and the start date of that event will used. For example {{#invoke:Women in Red event|current_events|212}} will display the list of events relative to event 212 which was November 2021.

The labels in front of the boxes can be customised with the following parameters:

  • recent
  • current
  • ongoing
  • upcoming

Examples

  • {{#invoke:Women in Red event|current_events}} produces:

Lua error: bad argument #1 to 'mw.loadJsonData' ('Wikipedia:WikiProject Women in Red/events' is not a valid JSON page).

  • {{#invoke:Women in Red event|current_events|year=2023|month=2|ongoing=Still happening|upcoming=Coming up}} produces:

Lua error: bad argument #1 to 'mw.loadJsonData' ('Wikipedia:WikiProject Women in Red/events' is not a valid JSON page).

events function

{{#invoke:Women in Red event|events}}

Produces a list of current, upcoming and recently completed events, for use on Wikipedia:WikiProject Women in Red/Events. It has a number of optional parameters:

  • monthly - include a section for edit-a-thons in the current month. This parameter defines the heading, e.g. |monthly=Happening now
  • ongoing - include a section for ongoing initiatives or contests. This parameter defines the heading, e.g. |ongoing=Ongoing initiatives
  • upcoming - include a section for upcoming events. This parameter defines the heading, e.g. |upcoming=Coming up
  • recent - include a section for recently completed events (i.e. events in the previous month). This parameter defines the heading, e.g. |recent=Recently completed
  • past - include a section for all past events (except recently completed). This parameter defines the heading, e.g. |past=Past events

category_header function

{{#invoke:Women in Red event|category_header}}

Used by Template:WIR category to produce a header for various categories. Please see that template's documentation.

addDateCategories function

{{#invoke:Women in Red event|addDateCategories|event}}

Used by Template:WIR meetup header to add appropriate categories to meetup pages. This function takes the event number as the first positional parameter.

Example

{{#invoke:Women in Red event|addDateCategories|300}} produces the categories Category:WikiProject Women in Red in 2024 and Category:Wikipedia meetups in March 2024.

pinterest function

{{#invoke:Women in Red event|pinterest|event}}

Generates a link to Pinterest collection based on the date of the event.

The link is of the form https://www.pinterest.com/wikiwomeninred/MONTH-YEAR-editathons where MONTH and YEAR are replaced with the relevant month and year. For annual events, the month of January is used.

Example

  • {{#invoke:Women in Red event|pinterest|Lua error: bad argument #1 to 'mw.loadJsonData' ('Wikipedia:WikiProject Women in Red/events' is not a valid JSON page).}} produces Lua error: bad argument #1 to 'mw.loadJsonData' ('Wikipedia:WikiProject Women in Red/events' is not a valid JSON page).

invitation function

{{#invoke:Women in Red event|invitation|event}} Generates a link to a subpage of Wikipedia:WikiProject Women in Red/Outreach which contains for the invitation to the relevant event.

Example

  • {{#invoke:Women in Red event|invitation|Lua error: bad argument #1 to 'mw.loadJsonData' ('Wikipedia:WikiProject Women in Red/events' is not a valid JSON page).}} produces Lua error: bad argument #1 to 'mw.loadJsonData' ('Wikipedia:WikiProject Women in Red/events' is not a valid JSON page).

require('strict')
local p = {}
local data = mw.loadJsonData('Wikipedia:WikiProject Women in Red/events')
local currentDate = os.date('*t')
local lang = mw.getContentLanguage()
local current_frame = mw.getCurrentFrame()
local args = current_frame.args
local yesno = require('Module:Yesno')

local format_date = function(date2)
	local month = date2.month and date2.month-1 or 0
	return date2.year*12+month
end
local currentMonth = format_date(currentDate)

local monthName = function(month, abbrev)
	local monthcode = month<10 and ('0'..tostring(month)) or tostring(month)
	local timestamp = '2024' .. monthcode .. '01000000'
	local _format = abbrev and 'M' or 'F'
	return lang:formatDate(_format, timestamp)
end

local _findLatest = function()
	local _max = 1
	for n, _ in pairs(data) do
		local val = tonumber(n) or 0
		if val<1000 and val>_max then
			_max = val
		end
	end
	return _max
end
p.findLatest = function()
	return _findLatest() + (tonumber(args.add) or 0)
end

local value = function(event, field, default)
	local data2 = data[tonumber(event)]
	if data2 and data2[field] then
		return data2[field]
	else
		return default
	end
end

local makeCat = function(cat, key, test)
	local key2 = key and not test and ('|' .. key) or ''
	return '[[' .. (test and ':' or '') .. 'Category:' .. cat .. key2 .. ']]'
end

local collapse = function(title, text)
	return current_frame:expandTemplate{title = 'Collapsed top', args = {
			bg = 'transparent',
			width = '80%',
			[1] = title
		}} ..  text .. '</table></div>'
end

p.main = function()
	local ret = value(args[1], args[2], args[3] or '')
	if args.hash=='no' and type(ret)=='string' then
		ret = ret:gsub('#', '')
	end
	if ret and type(ret)=='string' and ret:sub(1,1)=='#' then -- prevent # character from starting a list
		ret = mw.text.nowiki(ret)
	end
	return ret
end

local getDates = function(event, endDefaultsToStart)
	local start2 = value(event, 'start') or {
		month = value(event, 'month'),
		year = value(event, 'year')
	}
	local end2 = value(event, 'end')
	if not end2 and endDefaultsToStart then
		end2 = start2
	end
	return start2, end2
end

local render_date = function(event, sentence, abbrev)
	local start2, end2 = getDates(event)
	local ret = ''
	if sentence then
		ret = end2 and 'from ' or 'in '
	end
	ret = ret .. (start2.month and monthName(start2.month, abbrev) or '')
	if not end2 or start2.year~=end2.year then
		ret = ret .. (start2.month and ' ' or '') .. tostring(start2.year)
	end
	if end2 then
		local to
		if sentence then
			to = ' to '
		else
			if start2.year==end2.year then
				to = '–' -- en dash
			else
				to = ' – ' -- spaced en dash
			end
		end
		ret = ret .. to .. monthName(end2.month, abbrev) .. ' ' .. tostring(end2.year)
	end
	local sortkey = os.time{
		year = start2.year or 0,
		month = start2.month or 1,
		day = start2.month and 2 or 1
	}
	return ret, sortkey
end
p.date = function()
	local event = tonumber(args[1])
	if data[event] then
		local date, _ = render_date(
			event,
			yesno(current_frame.args.sentence or 'false'),
			yesno(current_frame.args.abbrev or 'false')
		)
		return date
	end
end

p.invitation = function()
	local event = tonumber(args[1])
	if not event then
		local page = mw.title.getCurrentTitle().text
		event = tonumber(page:match('WikiProject Women in Red/Meetup/(%d+)'))
	end
	if data[event] then
		local start, _ = getDates(event)
		local anchor = start.month and ('#' .. monthName(start.month) .. ' ' .. start.year) or ''
		return '[[Wikipedia:WikiProject Women in Red/Outreach/' .. start.year 
			.. anchor
			.. '|WikiProject Women in Red/Outreach/' .. start.year
			.. ']]'
	end
end

local name = function(meetup)
	local series = value(meetup, 'series')
	local name2 = value(meetup, 'name')
	local out = series or ''
	if series and name2 then
		out = out .. ': '
	end
	out = out .. (name2 or '')
	return out
end
p.name = function()
	return mw.text.nowiki(name(args[1]))
end

p.nested = function()
-- Function to produce year for nested display in project banner
	local year = value(args[1], 'year')
	if not year and value(args[1], 'start') and value(args[1], 'start').year==value(args[1], 'end').year then
		year = value(args[1], 'start').year
	end
	return name(args[1]) .. (year and (' (' .. year .. ')') or '')
end

p.text = function()
	local meetup = args[1]
	return '[[Wikipedia:WikiProject Women in Red/Meetup/' .. meetup
		.. '|' .. name(meetup) 
		.. ' ' .. value(meetup, 'type', 'edit-a-thon')
		.. ']] '
		.. render_date(meetup, true, false)
end

local link = function(n, name2)
	local label = name2 and name(n) or n
	return '[[Wikipedia:WikiProject Women in Red/Meetup/' .. n .. '|' .. label .. ']]'
end

local buildTable = function(option, show, sort2)
	local meetupCell = function(n)
		local background = value(n, 'background') and ('#' .. value(n, 'background')) or nil
		return mw.html.create('th')
			:css('background-color', background)
			:css('text-align', 'center')
			:wikitext(n)
	end
	local total = 0
	option.meetup = true
	option.name = true
	option.date = true
	local header = mw.html.create('tr')
	local cols = 0
	for _, col in ipairs({'meetup', 'icon', 'series', 'name', 'date', 'image', 'template', 'wrapper', 'contrast', 'category'}) do
		if option[col] then
			cols = cols + 1
			local col2 = mw.html.create('th'):wikitext(lang:ucfirst(col))
			header:node(col2)
		end
	end
	local sorted_data = {}
	for n, _ in pairs(data) do
		table.insert(sorted_data, n)
	end
	if sort2 then
		table.sort(sorted_data, sort2)
	end
	local showDate = function(meetup)
		local dates, sortkey = render_date(meetup, false, true)
		return mw.html.create('td'):attr('data-sort-value', sortkey):wikitext(dates):done()
	end
	local rows = {}
	for _, n in ipairs(sorted_data) do
		local event = data[n]
		if not show or show(n, event) then
			local row = mw.html.create('tr'):node(meetupCell(n))
			if option.icon then
				local icon = event.icon and '[[File:' .. event.icon .. '|center|50px]]'
				local cell = mw.html.create('td'):wikitext(icon)
				row:node(cell)
			end
			if option.series then
				local series = event.series and '[[:Category:WikiProject Women in Red ' .. event.series:gsub('#', '') .. ' articles|' .. event.series .. ']]' or ''
				local cell = mw.html.create('td'):wikitext(series)
				row:node(cell)
			end
			local name2 = option.series and event.name or name(n)
			local link2 = '[[Wikipedia:WikiProject Women in Red/Meetup/' .. n .. '|' .. name2 .. ']]'
			local name_cell = mw.html.create('td'):wikitext(link2)
			row:node(name_cell)
				:node(showDate(n))
			if option.image then
				local pic = event.image and '[[File:' .. event.image .. '|center|x50px|' .. (event.caption or '') .. ']]'
				local cell = mw.html.create('td'):wikitext(pic)
				row:node(cell)
			end
			if option.template then
				local template = current_frame:extensionTag('code', '{{WIR|' .. n .. '}}')
				local cell = mw.html.create('td'):css('text-align', 'center'):wikitext(template)
				row:node(cell)
			end
			if option.wrapper then
				local wrapper = '{{[[:Template:WIR-' .. n .. '|WIR-' .. n .. ']]}}'
				local cell = mw.html.create('td'):css('text-align', 'center'):wikitext(wrapper)
				row:node(cell)
			end
			if option.contrast then
				local background = value(n, 'background')
				local colour, contrast
				if background then
					contrast = current_frame:expandTemplate{
						title = 'Color contrast ratio',
						args = {[1] = '#000000', [2] = background}
					}
					contrast = math.floor(contrast*10)/10
					colour = contrast<4.5 and 'FFCCCB' or contrast<=7 and 'FFFF8F' or 'D1FFBD'
				end
				local cell = mw.html.create('td')
					:css('text-align', 'center')
					:css('background-color', colour and ('#'..colour))
					:wikitext(contrast)
				row:node(cell)
			end
			if option.category then
				local cat = 'WikiProject Women in Red meetup ' .. n .. ' articles'
				local url = mw.uri.fullUrl(':Category:' .. cat, {
					action = 'edit',
					preload = 'Template:WIR category/preload',
					summary = 'Create meetup category for Women in Red'
				})
				local pagesInCat = mw.site.stats.pagesInCategory(cat)
				total = total + pagesInCat
				local catText = '[[' .. ':Category:' .. cat .. '|' .. pagesInCat .. ']]'
				if pagesInCat==0 and not mw.title.new(':Category:' .. cat).exists then
					catText = catText .. ' ([' .. tostring(url) .. ' create])'
				end
				local cell = mw.html.create('td'):css('text-align', 'center'):wikitext(catText)
				row:node(cell)
			end
		table.insert(rows, tostring(row))
		end
	end
	local footer
	if option.category then
		footer = mw.html.create('tr')
			:tag('th'):attr('colspan', cols-1):wikitext('Total'):done()
			:tag('th'):css('text-align', 'center'):wikitext(tostring(total)):done()
		end
	return mw.html.create('table')
		:addClass('wikitable'):addClass('sortable')
		:node(header)
		:node(table.concat(rows))
		:node(footer)
end
p.list = function()
	local first = tonumber(args.first) or 1
	local last = tonumber(args.last) or 9999
	if args.latest then
		last = _findLatest()
		first = last - tonumber(args.latest) + 1
	end
	local option = {}
	for key, val in pairs(args) do
		option[key] = yesno(val)
	end
	local order = function(m, n)
		local start1, _ = getDates(m)
		start1 = format_date(start1)
		local start2, _ = getDates(n)
		start2 = format_date(start2) 
		local earlier = start1<start2
		if start1==start2 then
			earlier = m<n
		end
		return earlier
	end
	return buildTable(
		option,
		function(n, _)
			return n>=first and n<=last
		end,
		order
	)
end

local eventStatus = function(event, date2)
--function to decide whether an event is upcoming, current, ongoing, recently completed or past
	local month = currentMonth
	if date2 then
		month = format_date(date2)
	end
	local start2 = format_date(event['start'] or {
		year = event.year,
		month = event.month or 1
	})
	local end2 = format_date(event['end'] or {
		year = event.year,
		month = event.month or 12
	})
	if end2<month-1 then -- event ended before previous month
		return 'past'
	elseif end2==month-1 then -- event ended on previous month
		return 'recent'
	elseif start2==month then -- event has started on current month
		return 'current'
	elseif start2==month+1 then -- event starts after current month
		return 'upcoming'
	elseif start2<month then -- event starts before current month
		return 'ongoing'
	end
end

local searchEvents = function(date2)
	local events = {current = {}, recent = {}, ongoing = {}, upcoming = {}, past = {}}
	for n, event in pairs(data) do
		local status = eventStatus(event, date2)
		if status then
			table.insert(events[status], n)
		end
	end
	table.sort(events.past, function(a, b) return a > b end)
	return events
end

p.current_events = function(frame)
	local date2 = currentDate
	if frame.args[1] then
		date2 = getDates(frame.args[1])
	elseif frame.args.year and frame.args.year~='' then
		date2 = {
			year = tonumber(frame.args.year),
			month = tonumber(frame.args.month) or 1
		}
	end
	local mClickableButton = require('Module:Clickable button').main
	local button = function(text, colour)
		return mClickableButton{
			[1] = text,
			color = 'white',
			style = colour and ('background-color: #' .. colour)
		}
	end
	local links = function(text, events, extra)
		local list = {}
		if events[1] or extra then
			for _, n in ipairs(events) do
				table.insert(list, button(
					'[[Wikipedia:WikiProject Women in Red/Meetup/' .. n .. '|' .. name(n) .. ']]',
					value(n, 'background')
				) .. ' ')
			end
			if extra then
				table.insert(list, button(extra))
			end
			return mw.html.create('tr')
				:tag('td'):wikitext(text .. ': '):done()
				:tag('td'):wikitext(table.concat(list)):done()
		end
	end
	local events = searchEvents(date2)
	local ret = mw.html.create('table')
		:node(links(frame.args.recent or 'Recently completed', events.recent))
		:node(links(frame.args.current or monthName(date2.month) .. ' ' .. date2.year, events.current))
		:node(links(frame.args.ongoing or 'Ongoing initiatives', events.ongoing))
		:node(links(frame.args.upcoming or 'Upcoming events', events.upcoming, '[[Wikipedia:WikiProject Women in Red/Ideas|Ideas]]'))
	return tostring(ret)
end

p.events = function()
-- function to produce automated list of events on Wikipedia:WikiProject Women in Red/Events
	local ret = mw.html.create('div')
	local ec = searchEvents()
	local links = function(class)
		if not ec[class] or not args[class] then
			return nil
		end
		ret:tag('h3'):wikitext(args[class]):done()
		if ec[class][1] then
			ret:tag('ul')
			for _, n in ipairs(ec[class]) do
				local date2 = render_date(n, false, false)
				if date2==tostring(tonumber(date2)) then
					date2 = date2 .. ' year-long ' .. value(n, 'type', 'editathon')
				end
				ret:tag('li'):wikitext(date2 .. ': ' .. link(n, true)):done()
			end
		else
			ret:tag('p'):css('font-style', 'italic'):wikitext('None to display.'):done()
		end
	end
	links('ongoing')
	links('current')
	links('recent')
	links('upcoming')
	links('past')
	return tostring(ret)
end

p.event_list = function()
-- function to produce bulleted list of events for Template:Women in Red navigation
	local ec = searchEvents()
	local class = args[1]
	if not ec[class] then
		return nil
	end
	local ret = mw.html.create('ul')
	for _, n in ipairs(ec[class]) do
		ret:tag('li'):wikitext(link(n, true)):done()
	end
	return tostring(ret)
end

p.event_list_by_year = function()
-- function to produce bulleted list of past events for Template:Women in Red navigation
	local ret = mw.html.create('ul')
	local year = args[1] and tonumber(args[1]) or 2023
	local events = {}
	for n, event in pairs(data) do
		if (event.year==year or (event.start and event.start.year==year)) and eventStatus(event)=='past' then
			local type = event.type or 'edit-a-thon'
			if not events[type] then
				events[type] = {}
			end
			local month = value(n, 'month', 0)
			if not events[type][month] then
				events[type][month] = {}
			end
			table.insert(events[type][month], n)
		end
	end
	for type, eventsOfType in pairs(events) do
		ret:wikitext(lang:uc(type)..'S: ')
		for _, n in ipairs(eventsOfType[0] or {}) do
			ret:tag('li'):wikitext(link(n, true)):done()
		end
		for month = 1, 12 do
			if eventsOfType[month] then
				local sublist = mw.html.create('ul')
				for _, n in ipairs(eventsOfType[month]) do
					sublist:tag('li'):wikitext(link(n, true)):done()
				end
				ret:tag('li'):wikitext(monthName(month, true) .. ': '):node(sublist):done()
			end
		end
	end
	return tostring(ret)
end

p.category_header = function(frame)
	local pargs = frame:getParent().args
	local test = pargs.test
	local page = mw.title.getCurrentTitle().text
	local n = test and pargs.n or page:match('WikiProject Women in Red meetup (%d+) articles')
	local year = test and pargs.year or page:match('WikiProject Women in Red (%d+) articles')
	local year2 = test and pargs.year2 or page:match('WikiProject Women in Red in (%d+)')
	local series = test and pargs.series or page:match('WikiProject Women in Red (.+)')
	local cats, text, image_right, empty, nav
	local image_left = 'Women in Red logo.svg'
	local makeCatL = function(cat, key)
		return makeCat(cat, key, test)
	end
	if n then
		text = 'This category contains talk pages of articles which were created or improved'
			.. ' during the <b>[[Wikipedia:WikiProject Women in Red/Meetup/'
			.. n .. '|' .. name(n) .. ' ' .. value(n, 'type', 'edit-a-thon') .. ']]</b>'
			.. ' hosted by the [[Wikipedia:WikiProject Women in Red|Women in Red project]] '
			.. render_date(n, true, false) .. '.'
		cats = {makeCatL('WikiProject Women in Red articles by meetup', n)}
		local start2, end2 = getDates(n, true)
		for y = start2.year, end2.year do
			table.insert(cats, makeCatL('WikiProject Women in Red ' .. y .. ' articles', n))
		end
		if value(n, 'series') then
			table.insert(cats, makeCatL('WikiProject Women in Red ' .. value(n, 'series'):gsub('#', '') .. ' articles', n))
		end
		empty = mw.site.stats.pagesInCategory('WikiProject Women in Red meetup ' .. n .. ' articles')==0
		local future = false
		if start2.year>currentDate.year then
			future = true
		elseif start2.year==currentDate.year then
			if start2.month and start2.month>=currentDate.month then
				future = true
			end
		end
		image_left = value(n, 'icon', 'Women in Red logo.svg')
		image_right = value(n, 'image') and '[[File:' .. value(n, 'image') .. '|80px|' .. (value(n, 'caption') or '') .. ']]'
		empty = empty and future
		nav = true
	elseif year then -- for categories such as Category:WikiProject Women in Red 2025 articles
		year = tonumber(year)
		local inYear = function(_, event)
			if event.start and event['end'] then
				return event.start.year<=year and event['end'].year>=year
			else
				return event.year==year
			end
		end
		text = 'This category contains talk pages of articles which were created or improved in '
			.. year .. ' as part of the [[Wikipedia:WikiProject Women in Red|Women in Red project]].'
			.. collapse(
				'Articles improved during ' .. year,
				tostring(buildTable({category = true}, inYear))
			)
		cats = {
			makeCatL('WikiProject Women in Red in ' .. year, 'Articles'),
			makeCatL('WikiProject Women in Red articles by year', year)
		}
		nav = true
	elseif year2 then -- for categories such as Category:WikiProject Women in Red in 2025
		year2 = tonumber(year2)
		text = 'This category contains all meetups of the [[Wikipedia:WikiProject Women in Red|Women in Red project]] in ' .. year2 .. '.'
		cats = {
			makeCatL('Wikipedia meetups in ' .. year2, 'Women in Red'),
			makeCatL('WikiProject Women in Red by year', year2)
		}
		nav = true
	elseif series then
		series = series:gsub('%sarticles', ''):gsub('1day1woman', '#1day1woman')
		text = 'This category contains talk pages of articles which were created or improved as part of the ' .. series
			.. ' series of meetups hosted by the [[Wikipedia:WikiProject Women in Red|Women in Red project]].'
			.. collapse(
				'Articles improved during ' .. series .. ' events',
				tostring(buildTable(
					{category = true},
					function(_, event) return event.series==series end
				))
			)
		cats = {makeCatL('WikiProject Women in Red articles by series', series)}
	end
	local out = {frame:expandTemplate{title = 'cmbox', args = {
		image = '[[File:' .. image_left .. '|40px]]',
		text = text or '',
		imageright = image_right
	}}}
	table.insert(out, not(pargs.test) and nav and frame:expandTemplate{
		title = 'Category series navigation',
		args = {min = '1'}
	} or '')
	table.insert(out, frame:expandTemplate{title = 'CatAutoTOC'})
	table.insert(out, empty and frame:expandTemplate{
		title = 'Possibly empty category',
		args = {[1] = 'This category should be populated soon.'}
	} or '')
	table.insert(out, table.concat(cats or {}) or '')
	return table.concat(out)
end

p.addDateCategories = function()
	local n = tonumber(args[1])
	local test = yesno(args.test)
	local cats = {}
	local start2, end2 = getDates(n, true)
	local start_id, end_id = format_date(start2), format_date(end2)
	for year = start2.year, end2.year do
		table.insert(cats, makeCat('WikiProject Women in Red in ' .. year, n, test))
	end
	if start2.month and start_id+2>=end_id then -- event spans at most three months
		for monthid = start_id, end_id do -- add monthly categories
			local year = math.floor(monthid/12)
			local month = monthid - year*12 + 1
			table.insert(cats, makeCat('Wikipedia meetups in ' .. monthName(month) .. ' ' .. year, 'Women in Red', test))
		end
	else -- event spans more than three months, just use yearly categories
		for year = start2.year, end2.year do
			table.insert(cats, makeCat('Wikipedia meetups in ' .. year, 'Women in Red', test))
		end
	end
	if value(n, 'type')=='contest' then
		table.insert(cats, makeCat('Wikipedia competitions', test))
	end
	return table.concat(cats)
end

p.pinterest = function()
-- Function to generate the correct link to pinterest based on the month of the start of the event
	local event = tonumber(current_frame.args[1])
	if data[event] then
		local start = getDates(event)
		local month = monthName(start.month or 1)
		local label = month .. '-' .. start.year .. '-editathons'
		return '[https://www.pinterest.com/wikiwomeninred/' .. label .. ' ' .. label .. ']'
	end
end

p.planning = function()
-- Function to track progress when creating new monthly events
	local pargs = current_frame:getParent().args
	local parse = function(s)
		local items = {}
		if s then
			for item in mw.text.gsplit(s, '%s*;%s*') do
				local new_item = {
					all = item:match('^([^<]*)'),
					n = item:match('^%s*(%d%d%d)'),
					stat = item:match('<(%w*)>')
				}
				if new_item.n and data[tonumber(new_item.n)]==nil then
					new_item.n = nil
				end
				table.insert(items, new_item)
			end
		end
		return items
	end
	local okay = function(prog)
		local ready = prog and prog~='' and prog:lower()
		if ready then
			return '[[File:' .. (yesno(ready) and 'Green check' or 'Red x') .. '.svg|13px|link=]]'
		end
	end
	local caption
	local ongoing
	if pargs.month and tonumber(pargs.month) and pargs.year and tonumber(pargs.year) then
		caption = mw.html.create('caption'):wikitext('Planning for ' .. monthName(tonumber(pargs.month)) .. ' ' .. pargs.year)
		local events = searchEvents{month=pargs.month, year=pargs.year}
		ongoing = {}
		for _, event in ipairs(events.ongoing) do
			table.insert(ongoing, {n=event, stat='1'})
		end
	end
	local tab = mw.html.create('table'):addClass('wikitable'):node(caption)
	local create_section = function(section, items)
		for i, item in ipairs(items) do
			local row = mw.html.create('tr')
			if i==1 then
				row:tag('th'):attr('rowspan', tostring(#items)):wikitext(lang:ucfirst(section)):done()
			end
			if item.n then
				row:tag('td'):css('width', '40px'):css('text-align', 'center'):wikitext(item.n):done()
				row:tag('td'):wikitext(link(item.n, true)):done()
			else
				row:tag('td'):attr('colspan', '2'):wikitext(item.all):done()
			end
			row:tag('td'):wikitext(okay(item.stat)):done()
			tab:node(row)
		end
	end
	create_section('Ongoing', ongoing or parse(pargs.ongoing))
	create_section('NEW', parse(pargs.new))
	create_section('Announcements', parse(pargs.announcements))
	create_section('Invitation', parse(pargs.invitation))
	create_section('Tip', parse(pargs.tip))
	return tostring(tab)
end

return p