Skip to content

Instantly share code, notes, and snippets.

@bion
Last active February 20, 2026 21:09
Show Gist options
  • Select an option

  • Save bion/066fcc87b40d376a5f7eb39892d00964 to your computer and use it in GitHub Desktop.

Select an option

Save bion/066fcc87b40d376a5f7eb39892d00964 to your computer and use it in GitHub Desktop.
Wonder weeks

dependencies:

  • python3 -m pip install google-api-python-client google-auth-httplib2 google-auth-oauthlib
  • enable Calendar API and oauth in GCP
usage: weeks.py [-h] [--no_dry_run] due_date

Calculate calendar days for infant development development based on https://thewonderweeks.com and the baby's due
date.

positional arguments:
  due_date      Due date string in ISO 8601 e.g. 2026-03-15

options:
  -h, --help    show this help message and exit
  --no_dry_run  Create Google Calendar events. CALENDAR_ID must be set and a credentials.json file must be in the same
                directory.
#! /usr/bin/env python3
import datetime
from datetime import date, timedelta
import os.path
import sys
import argparse
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
parser = argparse.ArgumentParser(description="Calculate calendar days for infant development development based on https://thewonderweeks.com and the baby's due date.")
parser.add_argument("due_date", type=str, help="Due date string in ISO 8601 e.g. 2026-03-15")
parser.add_argument("--no_dry_run", action="store_true", help="Create Google Calendar events. CALENDAR_ID must be set and a credentials.json file must be in the same directory.")
args = parser.parse_args()
DUE_DATE = date.fromisoformat(args.due_date)
with open('./weeks.txt', 'r') as f:
entries = [line.rstrip() for line in f]
class Event():
leap = None
start = None
kind = None
end = None
def __init__(self, leap, start, kind):
self.leap = leap
self.start = start
self.kind = kind
def __str__(self):
if self.end:
return f'Leap {self.leap}\t\t{self.start_date()} - {self.end_date()}'
else:
return f'Leap {self.leap} {self.kind}\t{self.start_date()}'
def name(self):
if self.end:
return f'Leap {self.leap}'
else:
return f'Leap {self.leap} {self.kind_symbol()}'
def kind_symbol(self):
match self.kind:
case "sunny":
return "☀️"
case "fussy":
return "🌩"
def start_date(self):
return DUE_DATE + timedelta(days=self.start)
def end_date(self):
return DUE_DATE + timedelta(days=self.end) if self.end else None
def parse_weeks(weeks):
pieces = weeks.split('.')
days = int(pieces[0]) * 7
if weeks.endswith('.5'):
days += 3
return days
events = []
current_multi_day_event = None
leap = 1
for entry in entries:
weeks, kind = entry.split(' ')
if not kind in ['start', 'end', 'fussy', 'sunny']:
raise ValueError(f'Not a valid event type: {kind}')
days = parse_weeks(weeks)
match kind:
case 'start':
event = Event(leap, days, kind)
events.append(event)
current_multi_day_event = event
case 'end':
current_multi_day_event.end = days
current_multi_day_event = None
case 'fussy':
event = Event(leap, days, kind)
events.append(event)
case 'sunny':
event = Event(leap, days, kind)
events.append(event)
leap += 1
SCOPES = ['https://www.googleapis.com/auth/calendar.events']
CALENDAR_ID = os.getenv('CALENDAR_ID')
def authenticate_google_calendar():
creds = None
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file('credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
with open('token.json', 'w') as token:
token.write(creds.to_json())
return build('calendar', 'v3', credentials=creds)
def create_calendar_event(service, name, start, end=None):
try:
if not end:
# Single day event: end date is exactly 1 day after the start date.
end = start + datetime.timedelta(days=1)
event_body = {
'summary': name,
'start': {
'date': start.isoformat(),
},
'end': {
'date': end.isoformat(),
}
}
return service.events().insert(calendarId=CALENDAR_ID, body=event_body).execute()
except HttpError as error:
print(f"❌ An error occurred interacting with the Calendar API: {error}", file=sys.stderr)
if args.no_dry_run:
service = authenticate_google_calendar()
for event in events:
print(event)
gcal_event = create_calendar_event(service, event.name(), event.start_date(), event.end_date())
print(f'\tCreated {event}\n\t{gcal_event.get('htmlLink')}')
else:
for event in events:
print(event)
4.5 start
5.5 end
5 fussy
6 sunny
8 fussy
7.5 start
9.5 end
10 sunny
12 fussy
11.5 start
12.5 end
13 sunny
17 fussy
14.5 start
19.5 end
21 sunny
26 fussy
22.5 start
26.5 end
28.5 start
30.5 end
31 sunny
36 fussy
33.5 start
37.5 end
39 sunny
44 fussy
41.5 start
46.5 end
49 sunny
53 fussy
50.5 start
54.5 end
58 sunny
61.5 fussy
59 start
64.5 end
66 sunny
72.5 fussy
70.5 start
75.5 end
79 sunny
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment