Difference between revisions of "Module:Events calendar"
From DFM Wiki
EricThrift (talk | contribs) (Removed hardcoded Wikimedia values) |
EricThrift (talk | contribs) |
||
Line 210: | Line 210: | ||
local last_timestamp = timestamp + 300000000 | local last_timestamp = timestamp + 300000000 | ||
− | local data = mw.text.jsonDecode(frame:expandTemplate{ title = ' | + | local data = mw.text.jsonDecode(frame:expandTemplate{ title = 'Events.json', args = {} }) |
data = dateFilter(data, timestamp, last_timestamp) | data = dateFilter(data, timestamp, last_timestamp) | ||
data = tagFilter(data, tags) | data = tagFilter(data, tags) | ||
Line 272: | Line 272: | ||
local last_timestamp = getLastTimestamp(month, year, true) | local last_timestamp = getLastTimestamp(month, year, true) | ||
− | local data = mw.text.jsonDecode(frame:expandTemplate{ title = ' | + | local data = mw.text.jsonDecode(frame:expandTemplate{ title = 'Events.json', args = {} }) |
data = dateFilter(data, timestamp, last_timestamp) | data = dateFilter(data, timestamp, last_timestamp) | ||
data = tagFilter(data, tags) | data = tagFilter(data, tags) | ||
Line 302: | Line 302: | ||
local last_timestamp = getLastTimestamp(month, year, false) | local last_timestamp = getLastTimestamp(month, year, false) | ||
− | local data = mw.text.jsonDecode(frame:expandTemplate{ title = ' | + | local data = mw.text.jsonDecode(frame:expandTemplate{ title = 'Events.json', args = {} }) |
data = dateFilter(data, timestamp, last_timestamp) | data = dateFilter(data, timestamp, last_timestamp) | ||
data = tagFilter(data, tags) | data = tagFilter(data, tags) | ||
Line 324: | Line 324: | ||
local today = os.date("*t") | local today = os.date("*t") | ||
− | local data = mw.text.jsonDecode(frame:expandTemplate{ title = ' | + | local data = mw.text.jsonDecode(frame:expandTemplate{ title = 'Events.json', args = {} }) |
data = dateFilter(data, getFirstTimestamp(month, year, false), getLastTimestamp(month, year, false)) | data = dateFilter(data, getFirstTimestamp(month, year, false), getLastTimestamp(month, year, false)) | ||
data = tagFilter(data, tags) | data = tagFilter(data, tags) |
Revision as of 15:31, 27 January 2021
Documentation for this module may be created at Module:Events calendar/doc
local p = {} local lang = nil local function getDaysInMonth(month, year) local days_in_month = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } local d = days_in_month[month] -- check for leap year if month == 2 then if math.mod(year,4) == 0 then if math.mod(year,100) == 0 then if math.mod(year,400) == 0 then d = 29 end else d = 29 end end end return d end local function getFirstTimestamp(month, year, extended) first_timestamp = os.time({ year = year, month = month, day = 1, hour = 0, minut = 0 }) if not extended then return first_timestamp end first_day = (os.date("*t", first_timestamp).wday - 2)%7 return first_timestamp - (first_day * 86400) end local function getLastTimestamp(month, year, extended) last_timestamp = os.time({ year = year, month = month, day = getDaysInMonth(tonumber(month), tonumber(year)), hour = 23, minut = 59 }) if not extended then return last_timestamp end last_day = (os.date("*t", last_timestamp).wday - 2)%7 return last_timestamp + ((6-last_day) * 86400) end local function getFirstDayTimestamp(year, month, day) return os.time({ year = year, month = month, day = day, hour = 0 }) end local function getLastDayTimestamp(year, month, day) return os.time({ year = year, month = month, day = day, hour = 23, min = 59, sec = 59 }) end local function rruleToInterval(rrule) local days = 10000 if rrule.freq == 'weekly' then days = 7 end local interval = rrule.interval or 1 return days * interval * 86400 end local function currentDatesByRrule(dtstart, dtend, rrule, t1) local dtuntil = rrule['until'] or 2000000000 -- ~2033 if dtuntil < t1 then return dtstart, dtend end local interval = rruleToInterval(rrule) local duration = dtend - dtstart while dtstart < t1 and dtstart + interval <= dtuntil do dtstart = dtstart + interval end dtend = dtstart + duration return dtstart, dtend end local function dateFilter(data, t1, t2) filtered_data = {} for _,value in pairs(data) do local dtstart = value.dtstart local dtend = value.dtend if value.rrule then dtstart, dtend = currentDatesByRrule(dtstart, dtend, value.rrule, t1) end if (dtstart >= t1 and dtstart <= t2) or (dtend >= t1 and dtend <= t2) or (dtstart < t1 and dtend > t2) then if dtstart < t1 then dtstart = t1 end value.dtstart = dtstart value.dtend = dtend table.insert(filtered_data, value) end end return filtered_data end local function tagFilter(data, searched_tags) if #searched_tags == 0 then return data end filtered_data = {} for _,value in pairs(data) do if value.tags ~= nil then for _,tag in pairs(value.tags) do local break_out = false for _,searched_tag in pairs(searched_tags) do if tag == searched_tag then table.insert(filtered_data, value) break_out = true break end end if break_out then break end end end end return filtered_data end local function locationFilter(data, searched_locations) if #searched_locations == 0 then return data end filtered_data = {} for _,value in pairs(data) do for _,location in pairs(value.location) do local break_out = false for _,searched_location in pairs(searched_locations) do if location == searched_location then table.insert(filtered_data, value) break_out = true break end end if break_out then break end end end return filtered_data end local function geolocFilter(data) filtered_data = {} for _,value in pairs(data) do if value.geoloc.lat ~= nil and value.geoloc.lng ~= nil then table.insert(filtered_data, value) end end return filtered_data end local function formatEvents(data, short) local content = "" for _,event in pairs(data) do if content ~= "" then content = content .. "<br>" end local title = event.title if event.link ~= nil and event.link ~= "" then if mw.ustring.find(event.link, 'https?://') == 1 then title = "[" .. event.link .. " " .. event.title .. "]" else title = "[[" .. event.link .. "|" .. event.title .. "]]" end end local langs = '' if event.langs ~= nil and #event.langs then for _, lang in ipairs( event.langs ) do langs = langs .. '<span style="display:inline-block; border:1px solid #72777d; border-radius:2px; padding:1px 2px; font-variant:small-caps; line-height:1em">' .. lang .. '</span> ' end end local date = os.date("*t", event.dtstart) content = content .. "'''" .. event.location[#(event.location)] .. " " .. string.format("%02d:%02d", date.hour, date.min) .. "''' " .. langs .. title .. " <span style='display: none;' class='ec-pencil' id='" .. mw.text.split(event.id, '@', true)[1] .. "'>[[File:Blue pencil.svg|12px|link=]]</span>" if not short then content = content .. "<br>" .. event.description end end return content end local function arrayToText(array) if #array == 0 then return "" end local text = array[1] for i = 2, #array do text = array[i] .. "," .. text end return text end function p.ical(frame, year, month, locations, tags) local args = frame:getParent().args local today = os.date("*t") local timestamp = getFirstTimestamp(month, year, false) local last_timestamp = timestamp + 300000000 local data = mw.text.jsonDecode(frame:expandTemplate{ title = 'Events.json', args = {} }) data = dateFilter(data, timestamp, last_timestamp) data = tagFilter(data, tags) data = locationFilter(data, locations) local content = "BEGIN:VCALENDAR" .. "\r\nVERSION:2.0" .. "\r\nPRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN" .. "\r\nX-WR-CALNAME:" .. ( args.title == nil and "DFM events" or args.title ) .. "\r\nREFRESH-INTERVAL;VALUE=DURATION:P1H" .. "\r\nX-PUBLISHED-TTL:PT1H" for _,value in pairs(data) do content = content .. "\r\nBEGIN:VEVENT" .. "\r\nCREATED:" .. os.date("%Y%m%dT%H%M%SZ", value.dtcreated) .. "\r\nLAST-MODIFIED:" .. os.date("%Y%m%dT%H%M%SZ", value.dtmodified) .. "\r\nDTSTAMP:" .. os.date("%Y%m%dT%H%M%SZ", value.dtmodified) .. "\r\nUID:" .. value.id .. "\r\nSUMMARY:" .. value.title if value.description ~= nil then content = content .. "\r\nDESCRIPTION:" .. value.description:gsub("\n", ", ") end content = content .. " " .. mw.text.encode(value.link:gsub(" ", "_")) if value.tags ~= nil then content = content .. "\r\nCATEGORIES:" .. arrayToText(value.tags) end if value.location ~= nil then content = content .. "\r\nLOCATION:" .. arrayToText(value.location) end if value.link ~= nil then if mw.ustring.find(value.link, 'https?://') == 1 then content = content .. "\r\nURL:" .. mw.text.encode(value.link:gsub(" ", "_")) else content = content .. "\r\nURL:https://driedfishmatters.org/kb/" .. mw.text.encode(value.link:gsub(" ", "_")) end end if value.rrule ~= nil then local rrule = {} for key, value in pairs( value.rrule ) do if key == 'until' then value = os.date( "%Y%m%dT%H%M%S", value ) end table.insert( rrule, string.upper( key ) .. '=' .. string.upper( value ) ) end content = content .. "\r\nRRULE:" .. table.concat( rrule, ';' ) end content = content .. "\r\nDTSTART:" .. os.date("%Y%m%dT%H%M%S", value.dtstart) .. "\r\nDTEND:" .. os.date("%Y%m%dT%H%M%S", value.dtend) .. "\r\nEND:VEVENT" end content = content .. "\r\nEND:VCALENDAR" return content end function p.grid(frame, year, month, locations, tags) local args = frame:getParent().args local today = os.date("*t") local timestamp = getFirstTimestamp(month, year, true) local last_timestamp = getLastTimestamp(month, year, true) local data = mw.text.jsonDecode(frame:expandTemplate{ title = 'Events.json', args = {} }) data = dateFilter(data, timestamp, last_timestamp) data = tagFilter(data, tags) data = locationFilter(data, locations) local grid_content = "" while timestamp < last_timestamp do local line_content = "" for i=1,7 do local current_date = os.date("*t", timestamp) local events = formatEvents( dateFilter(data, getFirstDayTimestamp(current_date.year, current_date.month, current_date.day), getLastDayTimestamp(current_date.year, current_date.month, current_date.day)), true ) local is_today = current_date.month == today.month and current_date.day == today.day and "yes" or "" local is_grey = current_date.month ~= tonumber(month) and "yes" or "" line_content = line_content .. frame:expandTemplate{ title = 'Events calendar/Table-cell grid', args = { day = current_date.day, event = events, grey = is_grey, today = is_today } } timestamp = timestamp + 86400 end grid_content = grid_content .. frame:expandTemplate{ title = 'Events calendar/Table-line grid', args = {cells = line_content} } end return frame:expandTemplate{ title = 'Events calendar/Table grid', args = {lines = grid_content, monday=lang:formatDate( "l", "20010101000000", false), tuesday=lang:formatDate( "l", "20010102000000", false), wednesday=lang:formatDate( "l", "20010103000000", false), thursday=lang:formatDate( "l", "20010104000000", false), friday=lang:formatDate( "l", "20010105000000", false), saturday=lang:formatDate( "l", "20010106000000", false), sunday=lang:formatDate( "l", "20010107000000", false)} } end function p.list(frame, year, month, locations, tags) local args = frame:getParent().args local today = os.date("*t") local timestamp = getFirstTimestamp(month, year, false) local last_timestamp = getLastTimestamp(month, year, false) local data = mw.text.jsonDecode(frame:expandTemplate{ title = 'Events.json', args = {} }) data = dateFilter(data, timestamp, last_timestamp) data = tagFilter(data, tags) data = locationFilter(data, locations) local list_content = "" while timestamp < last_timestamp do local current_date = os.date("*t", timestamp) local events = dateFilter(data, getFirstDayTimestamp(current_date.year, current_date.month, current_date.day), getLastDayTimestamp(current_date.year, current_date.month, current_date.day)) if #events ~= 0 then local is_today = current_date.month == today.month and current_date.day == today.day and "yes" or "" list_content = list_content .. frame:expandTemplate{ title = 'Events calendar/Table-line list', args = { day = lang:formatDate( "l d", os.date("%Y%m%d000000", timestamp), false), events = formatEvents(events, false), today = is_today } } end timestamp = timestamp + 86400 end return frame:expandTemplate{ title = 'Events calendar/Table list', args = {lines = list_content} } end function p.map(frame, year, month, locations, tags) local args = frame:getParent().args local today = os.date("*t") local data = mw.text.jsonDecode(frame:expandTemplate{ title = 'Events.json', args = {} }) data = dateFilter(data, getFirstTimestamp(month, year, false), getLastTimestamp(month, year, false)) data = tagFilter(data, tags) data = locationFilter(data, locations) data = geolocFilter(data) if #data == 0 then return frame:extensionTag("mapframe", "", {width = "980", height = "500", align = "center", frameless = "", zoom = 2, latitude = 32 , longitude = 5 }) end local geojson = { type = "FeatureCollection", features = {} } local first_timestamp_of_today = getFirstDayTimestamp(today.year, today.month, today.day) local last_timestamp_of_today = getLastDayTimestamp(today.year, today.month, today.day) for _,value in pairs(data) do local title = value.title if value.link ~= nil and value.link ~= "" then if mw.ustring.find(value.link, 'https?://') == 1 then title = "[[" .. value.link .. "|" .. value.title .. "]]" else title = "[[" .. value.link .. "|" .. value.title .. "]]" end end local properties = { title = title, description = "<small>" .. lang:formatDate( "l d F - H:i", os.date("%Y%m%d%H%M00", value.dtstart), false) .. "</small><br/>" .. value.description, } properties["marker-color"] = "#e23131" if value.dtend < first_timestamp_of_today then properties["marker-color"] = "#555555" elseif value.dtstart > last_timestamp_of_today then properties["marker-color"] = "#6d9ce8" end table.insert(geojson.features, { type = "Feature", properties = properties, geometry = { type = "Point", coordinates = { tonumber(value.geoloc.lng), tonumber(value.geoloc.lat), } } }) end return frame:extensionTag("mapframe", mw.text.jsonEncode(geojson), {width = "980", height = "500", align = "center", frameless = ""}) end function p.calendar(frame) local args = frame:getParent().args local display = args.display local lang_code = args.lang == nil and "en" or args.lang local year = args.year == nil and os.date("%Y") or args.year local month = args.month == nil and os.date("%m") or args.month local locations = (args.locations == nil or args.locations == "") and {} or mw.text.split( args.locations, ",", true ) local tags = (args.tags == nil or args.tags == "") and {} or mw.text.split( args.tags, ",", true ) lang = mw.language.new( lang_code ) local content = "" if display == "ical" then return p.ical(frame, year, month, locations, tags) elseif display == "list" then content = p.list(frame, year, month, locations, tags) elseif display == "map" then content = p.map(frame, year, month, locations, tags) else display = "grid" content = p.grid(frame, year, month, locations, tags) end local container_args = {} container_args["class"] = "events-calendar" container_args["data-display"] = display container_args["data-lang"] = lang_code container_args["data-year"] = year container_args["data-month"] = month container_args["data-locations"] = #locations == 0 and "" or args.locations container_args["data-tags"] = #tags == 0 and "" or args.tags return frame:extensionTag('div', frame:expandTemplate{ title = 'Events calendar/Header', args = {month = lang:formatDate( "F", year .. string.format("%02d", tonumber(month)) .. "01000000", false ), year = year} } .. content .. frame:expandTemplate{ title = 'Events calendar/Button', args = {} }, container_args) end return p