How to save time and money by building an automatic meal planner

Use the Google Calendar and Google Sheets APIs to select the right recipe on the right day.

Bert Carremans
We’ve moved to freeCodeCamp.org/news
10 min readJan 11, 2019

--

Photo by rawpixel on Unsplash

Do you also get stressed out when you get the question “what’s for dinner tonight?” You’re not alone. I guess it’s the most asked question as the clock strikes 4 p.m. Deciding what to eat can be a tedious chore. Especially when you have small children with various after-school activities.

To avoid going to the supermarket every day, we usually write up a menu with recipes for the coming week. That way we can buy all our groceries in one supermarket visit. This saves us a lot of time. Besides that, it also saves us money. That is because we are less exposed to all the selling tricks supermarkets use.

Finding recipes for a whole week requires some thinking and planning. We have to take into account the eating preferences of all family members. Besides that, we have a limited time available for cooking each day. To make this easier, I built an automatic meal planner with these features:

  • extract the work planning for me and my wife from our shared Google calendars
  • extract our preferred recipes from a Google spreadsheet,
  • repeat some recipes each week on the same day
  • leave one week in between before repeating the other recipes
  • I like cooking more than my wife. So on days that I can’t cook the recipes should be short in time
  • upload the week menu in a Google calendar

Let’s jump right in.

Using the Google calendar API and Google sheets API

First, we’ll need to create a new Google Cloud project. Before we can use the Google calendar and sheets in this project, we need to enable the API’s. This is very well explained on the web pages below:

When that’s done, we continue by importing the necessary Python packages.

Configuration

For privacy and security reasons, I keep some parameters in a separate config.py file. We import the file with the alias cfg. I will discuss these parameters further below with fictitious values. You can include them for your own app with values relevant to your case.

Scopes

With scopes, we define the access levels for the Google calendars and sheets. We will need read and write access to both the calendars and sheet. Thus we use the URLs below.

Google sheet ID and range

We need to specify the ID of the Google sheet with the recipes. Additionally, we specify the sheet range containing the recipes.

You can find the ID of your Google sheets by right-clicking on the sheet in Google Drive. Then select “Get shareable link”. You can find the ID after “https://drive.google.com/open?id=”.

In my Google sheet “recepten”, columns A to G contain information on each recipe. The screenshot below shows some sample content. So RANGE needs to be set to “recepten!A:G”.

Google Calendar IDs

We need to specify the Google Calendar IDs to get the events from. Make sure you have access to all calendars you want to include. You can find the ID by executing this script from the APIs Explorer.

For this project, we will extract the events of only two calendars. But you could adapt the code to loop over more calendars. I’ve also created a separate calendar to upload the recipes.

Event labels

My wife works in shifts and adds them to her Google Calendar by using letter codes. For example: “B” stands for the afternoon shift. This event is one of the BUSY_EVENTS.

When I have a day off, I add “HOLIDAY” to my calendar. This event is one of the FREE_EVENTS.

All the events are full-day events in the Google Calendars. You can use your own event labels scheme.

Traditions

With TRADITIONS, I mean that our family has a few days in the week on which we prepare a certain recipe. As we are from Belgium, this means eating fries once a week (for us on Thursday). And yes, before you’d ask, that is fries with mayonnaise.

You can specify your own traditions in a dictionary, with the name of the day as the key and the recipe as the value.

Number of days to plan ahead

Sometimes we can’t go to the supermarket on the day a new week menu is created. We might need some days to plan ahead. With NB_DAYS_BEFORE we give ourselves some slack. This means that the new week menu will be generated a certain number of days before the previous week menu has finished.

Using a service account

We will use a service account to make use of the APIs in the project. The credentials.json file is the file that you can download when enabling the APIs.

We create the credentials creds with the code below. These credentials enable authentication in the Google Calendars and Google sheet.

Getting the Google Calendar events

We start by creating the service object with the build method.

We are only interested in the events for the coming week. To filter these events, we specify the dates and format them with isoformat(). The parameters timeMin and timeMax need this format.

With the method events().list of the service object, we extract the events. The extracted events are then filtered for the BUSY and FREE events. All other events on the Google Calendars are not relevant in this project. We keep the start and end date and the summary of the events.

Some events spread over more than one day. For example, when you take holidays for more than one day. We unfold these multi-day events in daily events within the range of the coming week.

Finally, we want a Pandas DataFrame with the events of both calendars for the coming week. To get to that result, we convert the events lists to data frames and merge on the date. We also add the weekday to the merged data frame.

To make sure we cover all dates of the coming week, we use a period_range and reindex the merged data frame.

Getting the recipes from the Google sheet

At this point, we have a data frame with all days of the coming week and the events (if any) occurring in the two calendars. Now we can start to extract the recipes from the Google sheet and assign a recipe to each day. As with the Google Calendar API, let’s start by creating the service object for the Google Sheets API.

With the method spreadsheets().values().get we can extract the recipes from the Google Sheet.

Next, we create a data frame with the recipes. I like working with Pandas DataFrames, but you could use other data structures as well of course.

The row_number is a field calculated in the Google Sheet itself. We use the Google Sheet function ROW() for that. It will help to update the field last_date_on_menu in the correct row. We will update that date when a recipe is chosen for the coming week.

We need to make sure that a recipe is only repeated after one week. So we filter recipes_df by last_date_on_menu. This date must be empty or before the previous week.

Generating the week menu

In this step, we will assign an eligible recipe to each day of the coming week.

To take into account the work planning (BUSY and FREE events), we will use the difficulty of each recipe. A random recipe of the preferred difficulty will be added to weekmenu_df. Finally we drop it from the eligible recipes to avoid duplicate recipes in the same week.

The method spreadsheets().values().update updates the Google Sheet.

We iterate over each row of weekmenu_df. If the weekday is one of the TRADITIONS weekdays, we assign the corresponding recipe. For the other weekdays, we apply the following logic:

  • In the weekend, choose a difficult recipe
  • During the week, when I’m at home or my wife has a day off, choose a recipe with medium difficulty
  • During the week, when I or my wife are at work, choose an easy recipe

Adding the week menu to a Google Calendar

Now that we have a menu for the coming week, we can add it as events to a Google Calendar. I’ve created a separate calendar for it. Share this calendar with the client_email in credentials.json. In the settings of your calendar you also need to give it permission to make changes in the events.

Let’s automate

Until now we have taken into account all the requested features for the application. But you would still have to run the code by hand to generate the week menu.

I found this great website PythonAnyWhere where you can schedule Python programs. The free Beginner account allows to schedule one Python program on a daily basis. That’s exactly what we need.

First, we need to stitch all the functions together and put them in a Python file. In this file, I do an extra check to see where we are in the current week menu. I do this by looking at the last date with a recipe in the Google Calendar with get_date_last_event.

That date is stored in DATE_LAST_RECIPE. If the current day is after DATE_LAST_RECIPE minus NB_DAYS_BEFORE we can generate a new week menu.

You can find the complete script on Github.

On PythonAnyWhere I’ve created a subfolder week menu. I’ve uploaded the following files config.py, generate_weekmenu.py and credentials.json.

Project files on PythonAnyWhere.com

I then schedule a daily task that will run the generate_weekmenu.py script in the Tasks section. And voilà, we’re all set.

The result

After the first run of the script, we have a nice menu in our shared Google calendar.

Automated week menu in a shared Google Calendar

Conclusion

This script takes into account your professional schedule on your Google calendars. It selects your preferred recipes from a Google sheet. And by scheduling the script the recipes appear in an automated way in your Google Calendar. This frees you from the annoying chore to decide what to eat.

If you want to take it further, here are some ideas to fine-tune the script:

  • take into account the cooking time of a recipe
  • allow a tradition of having at least one vegetarian meal per week
  • generate a grocery list for the chose recipes

I hope you enjoyed reading this story. If you have questions or suggestions about the script you can write a comment below. And if you liked it, feel free to clap for it.

--

--