I made a thing! It's a calendar for software engineering conferences in the UK, but the data is stored in a CSV file that anyone can submit pull requests to.
Why I made this
I don't actually enjoy going to conferences all that much, but they are a decent way to learn stuff. If I've got a learning & development budget available through my job, then I want to make sure I'm getting the most of out of that.
There are loads of blog posts listing good conferences for developers, but whenever I look at these I find I have just missed the conference I would get the most value out of, or it's ages away. The last couple of places I've worked we've had internal lists of interesting conferences, but even so, I usually fail to look at them at the right time. I thought a better way to keep track would be to combine it with my calendar, so I can view what's coming up and ignore events in the past.
Github as a review mechanism
I want to avoid low quality and out of date information, so I decided to limit my calendar to events that have confirmed dates and you can buy tickets for, rather than assume conferences will keep running every year.
I decided to store the data in a github repository because I think this is a nice simple way of managing open data. It's easy for anyone to contribute, and I can review contributions as they come in.
Why just UK events?
This is a fairly arbitrary restriction - I live in the UK, so these are the events are (usually) most convenient to get to for me, and I thought if I didn't restrict the scope there would be way too many events for the calendar to be useful in practice. If you want the same thing for other areas I recommend forking the repo and creating another calendar. I'd be interested to know if you do this!
Using the calendar
You can import the events into a calendar app by using the iCal export. You can also view the calendar directly.
You can add events by making a pull request against the CSV file.
Some things I learned from this project
I really hate google APIs
I wrote some code to interface with the google calendar API. This was the hardest part of the project because google's API documentation makes me want to cry. The code I needed to configure the client (which I ended up copying from another project) is:
self.calendar_id = os.environ['CALENDAR_ID']
client_secret_dict = ast.literal_eval(os.environ['CLIENT_SECRET_JSON'])
credentials = ServiceAccountCredentials.from_json_keyfile_dict(
client_secret_dict, scopes=self.SCOPES)
http = credentials.authorize(Http())
self.calendar = build('calendar', 'v3', http=http)
The client secret is credentials for a service account which is configured in google cloud. Then I granted that account read/write access over the calendar in google calendar itself. This took me a while to figure out.
NamedTuples work great for matching data
I represent events using a NamedTuple
:
class Event(NamedTuple):
"""
A calendar entry for a conference
"""
start_date: date
end_date: date
website: Optional[str]
description: str
title: str
A named tuple is a kind of value type - meaning that two instances are considered equal if all of its fields are equal.
This is useful when comparing what's in the CSV file to what's in the google calendar. It means I can use events as keys in dictionaries and use set arithmetic on them:
events_to_remove = existing_event_set - desired_events
events_to_add = desired_events - existing_event_set
ids_to_remove = {existing_events[event] for event in events_to_remove}
This allowed me to break up my code into the following steps:
- Read everything from the CSV and everything from the API
- Work out what needs to change
- Actually perform the changes
This turned out to be quite a nice pattern that made it easier to unit test my code.
Github actions is really convenient
I'm using two Github actions workflows on this project:
- one for continuous integration (running the tests whenever I push code)
- one for actually syncing the google calendar
This is the second time I've used Github actions and I think it's fantastic. In the past I probably would have used something like Heroku to actually run the script, but it's nice not to have set up another service, and I can make it only trigger when the code/data changes.
Top comments (0)