A Custom Google Calendar for upcoming codechef contests. Checkout the demo here.
- The Calendar is publically available here. Simply click this link and tap
yeson the popup forAdd to Calendar. - Alternatively, you can also add the calendar by visiting the demo and then using the
+sign at the bottom right of calendar to add to your calendar.
Once added, it will sync all contests periodically. You are done.
If you still can't see the events, or the calendar in your google calendar android app, this is because, new Calendars are sometimes hidden by default in the Calendar App. To make it visible, go to settings in the calendar app. You'll see a show more button under your id which you used to add the calendar. Tap it, select Codechef Contest Calendar and turn the sync on.
Calendar will now show in your main calendar. If you want to hide it at any point of time, open the side menu and simply unset the checkbox in front of the calendar.
- I first scraped events from codechef website
- Then added them on a newly made google calendar
- Then setup a cron job to periodically auto update the calendar by re-scraping codechef contests once a day
- Then setup slack notifications if a cron build fails
- You can simply add the calendar to your google account and it'll show up in your google calendar app.
These steps will guide you in detail on how to create a custom calendar, and making a exact replica of this project.
- Make sure you have python 3.7
- Install pipenv and get familiar
- Clone this repo https://github.com/jatin69/codechef-contest-calendar.git
- Run
pipenv shellin project root, thenpipenv install - This will setup all the required dependencies in a virtual env in python
- Step 1 - Quickstart guide
- Go to Python quickstart guide for google calendar
- The step 1 of the quickstart guide needs a google account, so make sure you are logged in.
- Execute step 1 and download the client configuration and save to
secretswith namecredentials_user.json - This step 1 your just executed creates a project names
quickstartin your google console, enables the google calendar API, and sets up a oauth client id and secret, and downloads it. - If you like, you can follow all the steps in the quickstart guide to get familiar with everything. However, it is not necessary.
- step 2 - setting up calendar
- You need a calendar to write the events to
- Go to Create calendar with the same google account you used in step 1
- Rename
sample_calendar_secrets.jsontocalendar_secrets.json - Then go to calendar settings, find the calendar id, and paste it in
calendar_secrets.jsonfile.
- Step 3 - Setting up authentication
- go to
srcdirectory, Runpython authenticate_user.py - Authenticate via the link that comes in the shell.
- This will authenticate the first time, then saves a
token.jsontosrcto be reused for consecutive authentications.
- go to
- step 4 - configuration
- find the
config.pyfile insrcdirectory - make sure the
script modeisuser - events you can choose either
dummyor actualcodechef events
- find the
- step 5 - getting started with project
- Until now, we have setup a calendar, and obtained API keys to work with it, and authenticated. We also have 2 files ready -
credentials_user.jsonandcalendar_secrets.jsonin thesecretsfolder - Inside
src, Runpython main.pyto fetch events and save to your google calendar.
- Until now, we have setup a calendar, and obtained API keys to work with it, and authenticated. We also have 2 files ready -
- Step 6 - checking the calendar
- check the calendar in web UI or android app, refresh, it should show the newly added event
- Step 7 - Understanding accounts and apps
- We first downloaded credentials aka created an app. This is the dev account and basically the app.
- When we create a calendar, we may or may not use the same dev account, but because our purpose is calendar sharing, we create a calendar in same dev account as the app itself.
- Now when we execute the script, the authentication has to be provided to the account where the calendar was created. Basically, we allow a dev app to access our calendar and we authenticate to give that app access to our calendar.
- Because the edits from the script is also done by the dev, we use dev account for everything.
- Conclusion - To keep it all simple, use the same google account to create the app, calendar, and the authentication.
- Step 8 - Run the script once a day manually
- This is up and running.
- If you manually run this command once a day, this will fetch new contests, and add them to calendar.
- This is still cumbersome, because we still have to manually run the script everyday. We would like to setup this as a cron job, so it runs once a day and automatically updates our calendar.
We need to setup a cron job. For this we'll need a service account from google to interact with our API aka server to server interaction.
- Step 1 - create a project manually
- Every API in google console is accessed on per project basis.
- First create a project by going to Google developer's console. Create a new project with any name. We'll use this project to obtain google calendar API credentials.
- we'll name the project
codechef-contest-calendarfor now
- Step 2 - Enable the google calendar API
- Enable the google calendar API in this project.
- This API provides 1 million queries per day for free, so we don't need to worry about billing for now.
- You can manage this newly enabled google calendar API by going here
- Step 3 - Obtain credentials
- You might be prompted to create credentials on this screen
- Go to API and Services section
- create credentials, choose
create service account key - create a new service account for this project, choose key type JSON and create key
- service accounts are used to programmatically access APIs and user data
- download and save this file in
secretsdirectory with filenamecredentials_service_account.json
- Step 4 - obtain calendar secrets
- This is same as done in basic app setup
- rename
sample_calendar_secrets.jsontocalendar_secrets.json - fill calendar id (can be found in your calendar settings. go to all calendars and use three dots beside calendar name to go in settings)
- Step 5 - giving calendar permission to service account
- Go to your calendar settings, go to
share it with specific peopleand share it with service account by entering the service account email id - You can find the service account email here. Use the same service account we created in basic app setup above
- in google calendar, add id of service account
- Go to your calendar settings, go to
- Step 6 - change config
- find the
config.pyfile insrcdirectory - make sure the
script modeisservice_account - events you can choose either
dummyor actualcodechef events
- find the
- Step 7 - Testing
- everything is setup now
- going in
srcfolder and runningpython main.pyshould work - If it throws a error, debug it first
- If everything worked correctly, events should be reflected in google calendar
- Step 8 - Setting up cron job
- we'll use github actions for this, they are simpler to use here instead of gcp cron jobs.
- But we dont want our keys to be exposed, so we'll encrypt them and upload to github and add our decryption key to the project secrets in github repo settings. Read more here
- First set a key as environment variable in your local machine, use
export LARGE_SECRET_PASSPHRASE=YOUR_SECRET_KEY - Go to github repo settings, add secrets a with key as
LARGE_SECRET_PASSPHRASEand value asYOUR_SECRET_KEY - Then go to
secretsdirectory on your local machine, runchmod +x encrypt_secrets.sh, then./encrypt_secrets.sh - This will create
*.gpgfiles, we can safely commit them to github Your secret keyshould preferably be long, probabaly 30-40 chars- After this, we need to create a workflow. Luckily it is already created in this repo, that will work as it is. Find it in
.github/workflows/cron-job.yml - We've configured it to run once a day at 00:00
To share the calendar, simply share the public calendar link (can be found in your calendar settings)
Websites usually changes their structure often. Even minor changes like adding newlines can break scrapers. And our scraper runs unmonitored once a day. We would like to setup a slack notification for when the cron run fails, so we know something has gone wrong. In most case, runs will fail when there has been changes to codechef website contest page.
I'll use this handy github action to create slack notifications. You might want to skim through its readme.
- step 1 : setting up the slack app
- To use this GitHub Action, you'll need a Slack bot token. A bot token must be associated with a Slack app, so we will need to create a slack app.
- Login to slack Login to slack with a email id. Create a slack workspace if you don't already have one. Create a channel in it with name
codechef-contest-calendar. - Create an app. Go to Slack's developer site then click "Create an app". Name the app anything and make sure your just desoired Slack workspace is selected under "Development Slack Workspace".
- Add a Bot user. Browse to the "Bot users" page listed in the sidebar. Name your bot "codechef-calendar-bot" (you can change this later) and leave the other default settings as-is.
- Set an icon for your bot. Browse to the "Basic information" page listed in the sidebar. Scroll down to the section titled "Display information" to set an icon.
- Install your app to your workspace. At the top of the "Basic information" page, you can find a section titled "Install your app to your workspace". Click on it, then use the button to complete the installation.
- step 2 : adding the bot token to github secrets
- Obtain the bot token Browse to "Install apps" in your app setting and you'll find the Bot User OAuth Access Token. Bot user token strings begin with
xoxb-. - Add this token to the github project secrets. Same as we did earlier with decryption key of our secrets. name the key as
SLACK_BOT_TOKENand value is the string starting fromxoxb-
- Obtain the bot token Browse to "Install apps" in your app setting and you'll find the Bot User OAuth Access Token. Bot user token strings begin with
- step 3 : Invite your bot to your slack channel
- Go to channel details for the channel we just create in step 1
- Scroll down, go to add people, type the name of your bot (
codechef-calendar-botin our case ) and add it to channel. We've now added the bot to our channel.
- step 4 : That's all
- This token will be used in the already written workflow yml file.
- Now you'll immediately receive notification if your cron job run fails anyday.