Skip to content

Instantly share code, notes, and snippets.

@bcye
Last active April 29, 2024 09:18
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bcye/32834767641d4676bede41b9004021c3 to your computer and use it in GitHub Desktop.
Save bcye/32834767641d4676bede41b9004021c3 to your computer and use it in GitHub Desktop.
Make ics events recurring

ETH myStudies ics Script

This script is intended to be used with the ics files outputted by myStudies, to make importing your schedule into your calendar easier.

myStudies does not export recurring events, but instead individual events for every date. This makes it hard if you want to edit them, because you can't edit them as a series.

Usage

Call this script with these arguments:

  1. input ics file
  2. output ics file name
  3. start date of events to be considered (choose exactly one week, all events in the given timeframe will be kept with the added weekly recurrence rule)
  4. end date of events to be considered

the dates should be in the YYYYMMDD format for the script to work. (no separators) important: myStudies only gives events for the future. so choose a start and end week that lies in the future (ideally just next week)

This script requires Python 3 (tested with 3.11) and no other dependencies

import sys
import os
import re
def read_ics_file(file_path):
with open(file_path, 'r', encoding='utf-8') as file:
lines = file.readlines()
return lines
def write_ics_file(file_path, lines):
with open(file_path, 'w', encoding='utf-8') as file:
file.writelines(lines)
def parse_datetime_to_ics(date_str, time_str="000000"):
return f"{date_str}T{time_str}Z"
def process_events(lines, start_datetime_ics, end_datetime_ics):
in_event = False
event_lines = []
processed_events = []
for line in lines:
if line.startswith('BEGIN:VEVENT'):
in_event = True
elif line.startswith('END:VEVENT'):
in_event = False
processed_events.extend(process_event(event_lines, start_datetime_ics, end_datetime_ics))
elif in_event:
event_lines.append(line.strip())
else:
processed_events.append(line)
return processed_events
def process_event(event_lines, start_datetime_ics, end_datetime_ics):
event_info = {}
description = []
for line in event_lines:
if line.startswith(' '): # Continuation line
description.append(line.strip())
else:
if description:
event_info['DESCRIPTION'] = ''.join(description)
description = []
key, value = parse_line(line)
if key:
event_info[key] = value
if 'DTSTART' in event_info and event_info['DTSTART'] >= start_datetime_ics and event_info['DTSTART'] <= end_datetime_ics:
event_info['RRULE'] = 'FREQ=WEEKLY'
return generate_event(event_info)
else:
return []
def parse_line(line):
match = re.match(r'([^:]+):(.+)', line)
if match:
return match.group(1), match.group(2).strip()
return None, None
def generate_event(event_info):
event_lines = ['BEGIN:VEVENT']
for key in event_info:
if key == 'DESCRIPTION':
# Wrap long descriptions
desc = event_info[key]
for i in range(0, len(desc), 75):
part = desc[i:i+75]
if i > 0:
part = ' ' + part
event_lines.append(f'{key}:{part}')
else:
event_lines.append(f'{key}:{event_info[key]}')
event_lines.append('END:VEVENT')
return [line+'\n' for line in event_lines]
if __name__ == '__main__':
if len(sys.argv) != 5:
print("Usage: python make_weekly_recurring.py <input.ics> <output.ics> <start_date> <end_date>")
sys.exit(1)
input_file_path = sys.argv[1]
output_file_path = sys.argv[2]
start_date = sys.argv[3]
end_date = sys.argv[4]
if not os.path.exists(input_file_path):
print(f"The input file {input_file_path} does not exist.")
sys.exit(1)
start_datetime_ics = parse_datetime_to_ics(start_date)
end_datetime_ics = parse_datetime_to_ics(end_date)
ics_lines = read_ics_file(input_file_path)
processed_lines = process_events(ics_lines, start_datetime_ics, end_datetime_ics)
write_ics_file(output_file_path, processed_lines)
print(f"Processed events have been written to {output_file_path}")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment