Module:Women in Red event/sandbox

require('strict')

local p = {}

local data = mw.loadJsonData('Wikipedia:WikiProject Women in Red/events')

local currentDate = os.date('*t')

local lang = mw.getContentLanguage()

local frame = mw.getCurrentFrame()

local args = frame.args

local yesno = require('Module:Yesno')

local format_date = function(_date)

local month = _date.month and _date.month-1 or 0

return _date.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 data = data[tonumber(event)]

if data and data[field] then

return data[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 frame:expandTemplate{title = 'Collapsed top', args = {

bg = 'transparent',

width = '80%',

[1] = title

}} .. text .. '

'

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 _start = value(event, 'start') or {

month = value(event, 'month'),

year = value(event, 'year')

}

local _end = value(event, 'end')

if not _end and endDefaultsToStart then

_end = _start

end

return _start, _end

end

local _date = function(event, sentence, abbrev)

local _start, _end = getDates(event)

local ret = ''

if sentence then

ret = _end and 'from ' or 'in '

end

ret = ret .. (_start.month and monthName(_start.month, abbrev) or '')

if not _end or _start.year~=_end.year then

ret = ret .. (_start.month and ' ' or '') .. tostring(_start.year)

end

if _end then

local to

if sentence then

to = ' to '

else

if _start.year==_end.year then

to = '–' -- en dash

else

to = ' – ' -- spaced en dash

end

end

ret = ret .. to .. monthName(_end.month, abbrev) .. ' ' .. tostring(_end.year)

end

local sortkey = os.time{

year = _start.year or 0,

month = _start.month or 1,

day = _start.month and 2 or 1

}

return ret, sortkey

end

p.date = function(frame)

local event = tonumber(args[1])

if data[event] then

local date, _ = _date(

event,

yesno(frame.args.sentence or 'false'),

yesno(frame.args.abbrev or 'false')

)

return date

end

end

local _name = function(meetup)

local series = value(meetup, 'series')

local name = value(meetup, 'name')

local out = series or ''

if series and name then

out = out .. ': '

end

out = out .. (name 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')

.. ']] '

.. _date(meetup, true, false)

end

local link = function(n, name)

local label = name and _name(n) or n

return '' .. label .. ''

end

local buildTable = function(option, show, _sort)

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', 'category'}) do

if option[col] then

cols = cols + 1

local col = mw.html.create('th'):wikitext(lang:ucfirst(col))

header:node(col)

end

end

local sorted_data = {}

for n, _ in pairs(data) do

table.insert(sorted_data, n)

end

if _sort then

table.sort(sorted_data, _sort)

end

local showDate = function(meetup)

local dates, sortkey = _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 .. ''

local cell = mw.html.create('td'):wikitext(icon)

row:node(cell)

end

if option.series then

local series = event.series and '' .. event.series .. '' or

local cell = mw.html.create('td'):wikitext(series)

row:node(cell)

end

local name = option.series and event.name or _name(n)

local link = '' .. name .. ''

local name_cell = mw.html.create('td'):wikitext(link)

row:node(name_cell)

:node(showDate(n))

if option.image then

local pic = event.image and 'File:' .. event.image .. ''

local cell = mw.html.create('td'):wikitext(pic)

row:node(cell)

end

if option.template then

local template = 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 = '{{WIR-' .. n .. '}}'

local cell = mw.html.create('td'):css('text-align', 'center'):wikitext(wrapper)

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 = '' .. 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, value in pairs(args) do

option[key] = yesno(value)

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

if start1==start2 then

earlier = m

end

return earlier

end

return buildTable(

option,

function(n, _)

return n>=first and n<=last

end,

order

)

end

local eventStatus = function(event, _date)

--function to decide whether an event is upcoming, current, ongoing, recently completed or past

local month = currentMonth

if _date then

month = format_date(_date)

end

local _start = format_date(event['start'] or {

year = event.year,

month = event.month or 1

})

local _end = format_date(event['end'] or {

year = event.year,

month = event.month or 12

})

if _end

return 'past'

elseif _end==month-1 then -- event ended on previous month

return 'recent'

elseif _start==month then -- event has started on current month

return 'current'

elseif _start==month+1 then -- event starts after current month

return 'upcoming'

elseif _start

return 'ongoing'

end

end

local searchEvents = function(_date)

local events = {current = {}, recent = {}, ongoing = {}, upcoming = {}, past = {}}

for n, event in pairs(data) do

local status = eventStatus(event, _date)

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 _date = currentDate

if frame.args[1] then

_date = getDates(frame.args[1])

elseif frame.args.year and frame.args.year~='' then

_date = {

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(

'' .. _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(_date)

local ret = mw.html.create('table')

:node(links(frame.args.recent or 'Recently completed', events.recent))

:node(links(frame.args.current or monthName(_date.month) .. ' ' .. _date.year, events.current))

:node(links(frame.args.ongoing or 'Ongoing initiatives', events.ongoing))

:node(links(frame.args.upcoming or 'Upcoming events', events.upcoming, '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 = _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 ipairs(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

local meetupHeader = function(n, test)

local text = 'This category contains talk pages of articles which were created or improved'

.. ' during the [[Wikipedia:WikiProject Women in Red/Meetup/'

.. n .. '|' .. _name(n) .. ' ' .. value(n, 'type', 'edit-a-thon') .. ']]'

.. ' hosted by the Women in Red project '

.. _date(n, true, false) .. '.'

local cats = {makeCat('WikiProject Women in Red articles by meetup', n, test)}

local _start, _end = getDates(n, true)

for year = _start.year, _end.year do

table.insert(cats, makeCat('WikiProject Women in Red ' .. year .. ' articles', n, test))

end

if value(n, 'series') then

table.insert(cats, makeCat('WikiProject Women in Red ' .. value(n, 'series'):gsub('#', '') .. ' articles', n, test))

end

local empty = mw.site.stats.pagesInCategory('WikiProject Women in Red meetup ' .. n .. ' articles')==0

local future = false

if _start.year>currentDate.year then

future = true

elseif _start.year==currentDate.year then

if _start.month and _start.month>=currentDate.month then

future = true

end

end

return {

text = text,

cat = cats,

image_left = value(n, 'icon', 'Women in Red logo.svg'),

image_right = value(n, 'image') and 'File:' .. value(n, 'image') .. '',

empty = empty and future,

nav = true

}

end

local yearHeader = function(year, test)

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

local text = 'This category contains talk pages of articles which were created or improved in '

.. year .. ' as part of the Women in Red project.'

.. collapse(

'Articles improved during ' .. year,

tostring(buildTable(

{category = true},

inYear

))

)

return {

text = text,

cat = {

makeCat('WikiProject Women in Red in ' .. year, 'Articles', test),

makeCat('WikiProject Women in Red articles by year', year, test)

},

nav = true

}

end

local seriesHeader = function(series, test)

local text = 'This category contains talk pages of articles which were created or improved as part of the ' .. series

.. ' series of meetups hosted by the Women in Red project.'

.. collapse(

'Articles improved during ' .. series .. ' events',

tostring(buildTable(

{category = true},

function(_, event) return event.series==series end

))

)

return {

text = text,

cat = {makeCat('WikiProject Women in Red articles by series', series, test)}

}

end

p.category_header = function(frame)

local pargs = frame:getParent().args

local page = mw.title.getCurrentTitle().text

local n = pargs.test and pargs.n or page:match('WikiProject Women in Red meetup (%d+) articles')

local year = pargs.test and pargs.year or page:match('WikiProject Women in Red (%d+) articles')

local series = pargs.test and pargs.series or page:match('WikiProject Women in Red (.+)')

local cfg, out = {}, {}

if n then

cfg = meetupHeader(n, pargs.test)

elseif year then

cfg = yearHeader(tonumber(year), pargs.test)

elseif series then

cfg = seriesHeader(series:gsub('%sarticles', ''):gsub('1day1woman', '#1day1woman'), pargs.test)

end

local image = cfg.image_left or 'Women in Red logo.svg'

table.insert(out, frame:expandTemplate{

title = 'cmbox', args = {

image = '40px',

text = cfg.text or '',

imageright = cfg.image_right

}

})

table.insert(out, not(pargs.test) and cfg.nav and frame:expandTemplate{

title = 'Category series navigation',

args = {min = '1'}

} or '')

table.insert(out, frame:expandTemplate{title = 'CatAutoTOC'})

table.insert(out, cfg.empty and frame:expandTemplate{

title = 'Possibly empty category',

args = {[1] = 'This category should be populated soon.'}

} or '')

table.insert(out, table.concat(cfg.cat or {}) or '')

return table.concat(out)

end

p.addDateCategories = function()

local n = tonumber(args[1])

local test = yesno(args.test)

local cats = {}

local _start, _end = getDates(n, true)

local start_id, end_id = format_date(_start), format_date(_end)

for year = _start.year, _end.year do

table.insert(cats, makeCat('WikiProject Women in Red in ' .. year, n, test))

end

if _start.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 = _start.year, _end.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(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

return p