lugcal/generate_calendar.py

179 lines
5.3 KiB
Python

#!/usr/bin/env python3
"""
Generate calendar.ics and README.md from meetings.yaml
"""
import yaml
from datetime import datetime, timedelta
from zoneinfo import ZoneInfo
import hashlib
def load_meetings(yaml_file='meetings.yaml'):
"""Load meetings from YAML file"""
with open(yaml_file, 'r') as f:
data = yaml.safe_load(f)
return data['meetings']
def filter_meetings(meetings, months=3):
"""Filter meetings to show only those within the last/next N months"""
now = datetime.now()
cutoff_past = now - timedelta(days=30 * months)
cutoff_future = now + timedelta(days=30 * months)
filtered = []
for meeting in meetings:
meeting_date = datetime.strptime(meeting['date'], '%Y-%m-%d')
if cutoff_past <= meeting_date <= cutoff_future:
filtered.append(meeting)
return sorted(filtered, key=lambda m: m['date'])
def generate_ics(meetings, output_file='calendar.ics'):
"""Generate iCalendar file from meetings"""
lines = [
'BEGIN:VCALENDAR',
'VERSION:2.0',
'PRODID:-//Linux Cast User Group//Events//EN',
'CALSCALE:GREGORIAN',
'METHOD:PUBLISH',
'X-WR-CALNAME:Linux Cast User Group Meetings',
'X-WR-TIMEZONE:America/New_York',
'X-WR-CALDESC:Bi-monthly meetings for the Linux Cast User Group',
''
]
for meeting in meetings:
# Parse date and time
date_str = meeting['date']
time_str = meeting['time']
tz_str = meeting['timezone']
dt = datetime.strptime(f"{date_str} {time_str}", '%Y-%m-%d %H:%M')
tz = ZoneInfo(tz_str)
dt_local = dt.replace(tzinfo=tz)
# Convert to UTC for iCalendar
dt_utc = dt_local.astimezone(ZoneInfo('UTC'))
dt_end_utc = dt_utc + timedelta(hours=1)
# Generate unique ID
uid_base = f"{date_str}-{meeting['title']}"
uid = hashlib.md5(uid_base.encode()).hexdigest()
# Format timestamps
dtstamp = datetime.now(ZoneInfo('UTC')).strftime('%Y%m%dT%H%M%SZ')
dtstart = dt_utc.strftime('%Y%m%dT%H%M%SZ')
dtend = dt_end_utc.strftime('%Y%m%dT%H%M%SZ')
lines.extend([
'BEGIN:VEVENT',
f'UID:{uid}@thelinuxcast.org',
f'DTSTAMP:{dtstamp}',
f'DTSTART:{dtstart}',
f'DTEND:{dtend}',
f'SUMMARY:Linux Cast User Group: {meeting["title"]}',
f'DESCRIPTION:{meeting["title"]}\\n\\n{meeting["description"]}',
f'LOCATION:{meeting.get("location", "Online")}',
'STATUS:CONFIRMED',
'END:VEVENT',
''
])
lines.append('END:VCALENDAR')
with open(output_file, 'w') as f:
f.write('\n'.join(lines))
print(f"Generated {output_file}")
def generate_readme(meetings, output_file='README.md'):
"""Generate README.md from meetings"""
now = datetime.now()
# Separate past and upcoming meetings
past_meetings = []
upcoming_meetings = []
for meeting in meetings:
meeting_date = datetime.strptime(meeting['date'], '%Y-%m-%d')
if meeting_date < now:
past_meetings.append(meeting)
else:
upcoming_meetings.append(meeting)
# Sort
past_meetings = sorted(past_meetings, key=lambda m: m['date'], reverse=True)
upcoming_meetings = sorted(upcoming_meetings, key=lambda m: m['date'])
lines = [
'# Linux Cast User Group Meetings',
'',
'**[Subscribe to Calendar](calendar.ics)** | **[Download ICS](calendar.ics)**',
'',
]
# Upcoming events
if upcoming_meetings:
lines.append('## Upcoming Events')
lines.append('')
for meeting in upcoming_meetings:
dt = datetime.strptime(f"{meeting['date']} {meeting['time']}", '%Y-%m-%d %H:%M')
formatted_date = dt.strftime('%B %d, %Y at %I:%M %p')
lines.extend([
f"### {meeting['title']}",
f"- **Date:** {formatted_date} EST",
f"- **Location:** {meeting.get('location', 'Online')}",
f"- **Description:** {meeting['description']}",
''
])
# Recent past events
if past_meetings:
lines.append('## Recent Past Events')
lines.append('')
for meeting in past_meetings:
dt = datetime.strptime(f"{meeting['date']} {meeting['time']}", '%Y-%m-%d %H:%M')
formatted_date = dt.strftime('%B %d, %Y at %I:%M %p')
lines.extend([
f"### {meeting['title']}",
f"- **Date:** {formatted_date} EST",
f"- **Location:** {meeting.get('location', 'Online')}",
f"- **Description:** {meeting['description']}",
''
])
with open(output_file, 'w') as f:
f.write('\n'.join(lines))
print(f"Generated {output_file}")
def main():
"""Main function"""
print("Loading meetings from meetings.yaml...")
meetings = load_meetings()
print(f"Found {len(meetings)} total meetings")
# Filter to 3 months
filtered_meetings = filter_meetings(meetings, months=3)
print(f"Filtered to {len(filtered_meetings)} meetings within 3 months")
# Generate files
generate_ics(filtered_meetings)
generate_readme(filtered_meetings)
print("Done!")
if __name__ == '__main__':
main()