#!/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()