<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Life Hacking - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Life Hacking - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Thu, 18 Jun 2026 23:24:21 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/life-hacking/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to save time and money by building an automatic meal planner ]]>
                </title>
                <description>
                    <![CDATA[ By Bert Carremans Use the Google Calendar and Google Sheets APIs to select the right recipe on the right day. 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 a... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-save-time-and-money-by-building-an-automatic-meal-planner-7c7a9351d124/</link>
                <guid isPermaLink="false">66d45ddd55db48792eed3f3f</guid>
                
                    <category>
                        <![CDATA[ api ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Life Hacking ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Productivity ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technology ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Fri, 11 Jan 2019 16:17:17 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/0*s7pV_mfdT_FlIhCW" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Bert Carremans</p>
<h4 id="heading-use-the-google-calendar-and-google-sheets-apis-to-select-the-right-recipe-on-the-right-day">Use the Google Calendar and Google Sheets APIs to select the right recipe on the right day.</h4>
<p>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.</p>
<p>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 <a target="_blank" href="https://www.rd.com/health/healthy-eating/supermarket-tricks/">tricks supermarkets use</a>.</p>
<p>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:</p>
<ul>
<li>extract the work planning for me and my wife from our shared Google calendars</li>
<li>extract our preferred recipes from a Google spreadsheet,</li>
<li>repeat some recipes each week on the same day</li>
<li>leave one week in between before repeating the other recipes</li>
<li>I like cooking more than my wife. So on days that I can’t cook the recipes should be short in time</li>
<li>upload the week menu in a Google calendar</li>
</ul>
<p>Let’s jump right in.</p>
<h3 id="heading-using-the-google-calendar-api-and-google-sheets-api">Using the Google calendar API and Google sheets API</h3>
<p>First, we’ll need to <a target="_blank" href="https://cloud.google.com/resource-manager/docs/creating-managing-projects">create a new Google Cloud project</a>. 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:</p>
<ul>
<li><a target="_blank" href="https://developers.google.com/calendar/quickstart/python">Enabling the Google Calendar API</a></li>
<li><a target="_blank" href="https://developers.google.com/sheets/api/quickstart/python">Enabling the Google Sheets API</a></li>
</ul>
<p>When that’s done, we continue by importing the necessary Python packages.</p>
<pre><code><span class="hljs-keyword">import</span> config <span class="hljs-keyword">as</span> cfg
<span class="hljs-keyword">import</span> pandas <span class="hljs-keyword">as</span> pd
<span class="hljs-keyword">import</span> numpy <span class="hljs-keyword">as</span> np
<span class="hljs-keyword">from</span> pathlib <span class="hljs-keyword">import</span> Path
<span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> datetime
<span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> timedelta
<span class="hljs-keyword">from</span> googleapiclient.discovery <span class="hljs-keyword">import</span> build
<span class="hljs-keyword">from</span> google.oauth2 <span class="hljs-keyword">import</span> service_account
</code></pre><h3 id="heading-configuration">Configuration</h3>
<p>For privacy and security reasons, I keep some parameters in a separate config.py file. We import the file with the alias <code>cfg</code>. I will discuss these parameters further below with fictitious values. You can include them for your own app with values relevant to your case.</p>
<h4 id="heading-scopes">Scopes</h4>
<p>With scopes, we define the access levels for the Google calendars and sheets. We will need read and write access to both the <a target="_blank" href="https://developers.google.com/calendar/auth">calendars</a> and <a target="_blank" href="https://developers.google.com/sheets/api/guides/authorizing">sheet</a>. Thus we use the URLs below.</p>
<pre><code class="lang-python">SCOPES = [<span class="hljs-string">'https://www.googleapis.com/auth/calendar'</span>
          , <span class="hljs-string">'https://www.googleapis.com/auth/spreadsheets'</span>]
</code></pre>
<h4 id="heading-google-sheet-id-and-range">Google sheet ID and range</h4>
<pre><code class="lang-python">SPREADSHEET_ID = &lt;Your Google sheet ID&gt;
RANGE = <span class="hljs-string">'recepten!A:G'</span>
</code></pre>
<p>We need to specify the ID of the Google sheet with the recipes. Additionally, we specify the sheet range containing the recipes.</p>
<p>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=”.</p>
<p>In my Google sheet “recepten”, columns A to G contain information on each recipe. The screenshot below shows some sample content. So <code>RANGE</code> needs to be set to “recepten!A:G”<em>.</em></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/NsQwKBmPmvZY-OYzX30WqMo9GC8j3NKV4XR1" alt="Image" width="800" height="148" loading="lazy"></p>
<h4 id="heading-google-calendar-ids">Google Calendar IDs</h4>
<pre><code class="lang-python">CALENDARID_1 = &lt;Your Google Calendar ID&gt;
CALENDARID_2 = &lt;Your partne<span class="hljs-string">r's Google Calendar ID&gt;
CALENDARID_WEEKMENU = &lt;Google Calendar ID for the week menu&gt;</span>
</code></pre>
<p>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 <a target="_blank" href="https://developers.google.com/apis-explorer/#p/calendar/v3/calendar.calendarList.list">script from the APIs Explorer</a>.</p>
<p>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.</p>
<h4 id="heading-event-labels">Event labels</h4>
<pre><code class="lang-python">BUSY_EVENTS = [&lt;Labels of busy calendar events&gt;]
FREE_EVENTS = [&lt;Labels of free calendar events&gt;]
ALL_EVENTS = BUSY_EVENTS + FREE_EVENTS
</code></pre>
<p>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 <code>BUSY_EVENTS</code>.</p>
<p>When I have a day off, I add “HOLIDAY” to my calendar. This event is one of the <code>FREE_EVENTS.</code></p>
<p>All the events are full-day events in the Google Calendars. You can use your own event labels scheme.</p>
<h4 id="heading-traditions">Traditions</h4>
<pre><code class="lang-python">TRADITIONS = {   <span class="hljs-string">'Thursday'</span> : <span class="hljs-string">'fries'</span>}
</code></pre>
<p>With <code>TRADITIONS</code>, 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.</p>
<p>You can specify your own traditions in a dictionary, with the name of the day as the key and the recipe as the value.</p>
<h4 id="heading-number-of-days-to-plan-ahead">Number of days to plan ahead</h4>
<p>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 <code>NB_DAYS_BEFORE</code> 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.</p>
<pre><code>NB_DAYS_BEFORE = <span class="hljs-number">3</span>
</code></pre><h3 id="heading-using-a-service-account">Using a service account</h3>
<p>We will use a <a target="_blank" href="https://developers.google.com/api-client-library/python/auth/service-accounts">service account</a> to make use of the APIs in the project. The credentials.json file is the file that you can download when enabling the APIs.</p>
<p>We create the credentials <code>creds</code> with the code below. These credentials enable authentication in the Google Calendars and Google sheet.</p>
<pre><code class="lang-python">creds = service_account.Credentials.from_service_account_file(<span class="hljs-string">"credentials.json"</span>, scopes=cfg.SCOPES)
</code></pre>
<h3 id="heading-getting-the-google-calendar-events">Getting the Google Calendar events</h3>
<p>We start by creating the service object with the <code>build</code> method.</p>
<pre><code>service_cal = build(<span class="hljs-string">'calendar'</span>, <span class="hljs-string">'v3'</span>, credentials=creds)
</code></pre><p>We are only interested in the events for the coming week. To filter these events, we specify the dates and format them with <code>isoformat()</code>. The parameters <code>timeMin</code> and <code>timeMax</code> need this format.</p>
<pre><code>def format_date(date):
    date_time = datetime.combine(date, datetime.min.time())
    date_time_utc = date_time.isoformat() + <span class="hljs-string">'Z'</span>
    <span class="hljs-keyword">return</span> date_time_utc
</code></pre><p>With the method <a target="_blank" href="https://developers.google.com/calendar/v3/reference/events/list">events().list</a> 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.</p>
<pre><code>def get_event_date(event, timepoint):
    <span class="hljs-keyword">return</span> event[timepoint].get(<span class="hljs-string">'dateTime'</span>, event[timepoint].get(<span class="hljs-string">'date'</span>))

def get_events_by_calendarId(service, calendarId, timeMin, timeMax, allEvents):
    events_result = service.events().list(calendarId=calendarId
                                        , timeMin=timeMin
                                        , timeMax=timeMax
                                        , singleEvents=True
                                        , orderBy=<span class="hljs-string">'startTime'</span>).execute()
    events = events_result.get(<span class="hljs-string">'items'</span>, [])    
    events_list = [(get_event_date(e, <span class="hljs-string">'start'</span>), get_event_date(e, <span class="hljs-string">'end'</span>), e[<span class="hljs-string">'summary'</span>].upper()) 
                   <span class="hljs-keyword">for</span> e <span class="hljs-keyword">in</span> events 
                   <span class="hljs-keyword">if</span> e[<span class="hljs-string">'summary'</span>].upper() <span class="hljs-keyword">in</span> allEvents]
    <span class="hljs-keyword">return</span> unfold_events_list(events_list)
</code></pre><p>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.</p>
<pre><code>def unfold_events_list(events_list):
    new_events_list = []
    <span class="hljs-keyword">for</span> e <span class="hljs-keyword">in</span> events_list:
        start = datetime.strptime(e[<span class="hljs-number">0</span>], <span class="hljs-string">'%Y-%m-%d'</span>).date()
        end = datetime.strptime(e[<span class="hljs-number">1</span>], <span class="hljs-string">'%Y-%m-%d'</span>).date()
        delta_days = (end - start).days

        <span class="hljs-keyword">if</span> delta_days &gt; <span class="hljs-number">1</span>:
            <span class="hljs-keyword">for</span> d <span class="hljs-keyword">in</span> range(delta_days):
                unfolded_day = start + timedelta(days=d)
                <span class="hljs-keyword">if</span> unfolded_day &gt;= datetime.now().date() and unfolded_day &lt;= datetime.now().date() + timedelta(days=<span class="hljs-number">6</span>):
                    new_events_list.append((unfolded_day, e[<span class="hljs-number">2</span>]))
        <span class="hljs-attr">else</span>:
            new_events_list.append((start, e[<span class="hljs-number">2</span>]))
    <span class="hljs-keyword">return</span> new_events_list
</code></pre><p>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.</p>
<pre><code>def create_events_df(events_list_1, events_list_2):
    events_df_1 = pd.DataFrame.from_records(events_list_1, columns=[<span class="hljs-string">'date'</span>, <span class="hljs-string">'events_cal_1'</span>])
    events_df_2 = pd.DataFrame.from_records(events_list_2, columns=[<span class="hljs-string">'date'</span>, <span class="hljs-string">'events_cal_2'</span>])
    events_df = events_df_1.merge(events_df_2, on=<span class="hljs-string">'date'</span>, how=<span class="hljs-string">'outer'</span>)
    events_df.date = pd.to_datetime(events_df.date)
    events_df.set_index(<span class="hljs-string">'date'</span>, inplace=True)
    events_df.sort_index(inplace=True)

    dates = list(pd.period_range(START_DAY, NEXT_WEEK, freq=<span class="hljs-string">'D'</span>).values)
    new_idx = []
    <span class="hljs-keyword">for</span> d <span class="hljs-keyword">in</span> dates:
        new_idx.append(np.datetime64(d))

    events_df = events_df.reindex(new_idx)
    events_df.reset_index(inplace=True)
    events_df[<span class="hljs-string">'weekday'</span>] = events_df.date.apply(lambda x: x.strftime(<span class="hljs-string">'%A'</span>))
    events_df.set_index(<span class="hljs-string">'date'</span>, inplace=True)
    <span class="hljs-keyword">return</span> events_df
</code></pre><p>To make sure we cover all dates of the coming week, we use a <code>period_range</code> and <code>reindex</code> the merged data frame.</p>
<h3 id="heading-getting-the-recipes-from-the-google-sheet">Getting the recipes from the Google sheet</h3>
<p>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.</p>
<pre><code>service_sheet = build(<span class="hljs-string">'sheets'</span>, <span class="hljs-string">'v4'</span>, credentials=creds)
</code></pre><p>With the method <a target="_blank" href="https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values/get">spreadsheets().values().get</a> we can extract the recipes from the Google Sheet.</p>
<pre><code>def get_recipes(service, spreadsheetId, range):
    recipes_result = service.spreadsheets().values().get(spreadsheetId=spreadsheetId, range=range).execute()
    recipes = recipes_result.get(<span class="hljs-string">'values'</span>, [])
    recipes_df = pd.DataFrame.from_records(recipes[<span class="hljs-number">1</span>:], columns=recipes[<span class="hljs-number">0</span>])
    recipes_df.last_date_on_menu = pd.to_datetime(recipes_df.last_date_on_menu, dayfirst=True)
    recipes_df.set_index(<span class="hljs-string">'row_number'</span>, inplace=True)
    eligible_recipes = recipes_df[ (recipes_df.last_date_on_menu &lt; PREV_WEEK) | (np.isnat(recipes_df.last_date_on_menu)) ]
    <span class="hljs-keyword">return</span> recipes_df, eligible_recipes
</code></pre><p>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.</p>
<p>The <code>row_number</code> is a field calculated in the Google Sheet itself. We use the Google Sheet function <code>ROW()</code> for that. It will help to update the field <code>last_date_on_menu</code> in the correct row. We will update that date when a recipe is chosen for the coming week.</p>
<p>We need to make sure that a recipe is only repeated after one week. So we filter <code>recipes_df</code> by <code>last_date_on_menu</code>. This date must be empty or before the previous week.</p>
<h3 id="heading-generating-the-week-menu">Generating the week menu</h3>
<p>In this step, we will assign an eligible recipe to each day of the coming week.</p>
<pre><code>def generate_weekmenu(service, events_df, traditions, free_events):
    weekmenu_df = events_df.copy()

    <span class="hljs-keyword">for</span> i, r <span class="hljs-keyword">in</span> events_df.iterrows():
        <span class="hljs-keyword">if</span> r.weekday <span class="hljs-keyword">in</span> traditions.keys():
            weekmenu_df.loc[i, <span class="hljs-string">'recipe'</span>] = traditions[r.weekday]
            weekmenu_df.loc[i, <span class="hljs-string">'description'</span>] = <span class="hljs-string">''</span>
        <span class="hljs-attr">else</span>:
            <span class="hljs-keyword">if</span> r.weekday <span class="hljs-keyword">in</span> [<span class="hljs-string">'Saturday'</span>, <span class="hljs-string">'Sunday'</span>]:
                row_number = choose_recipe(<span class="hljs-string">'difficult'</span>, i, weekmenu_df, eligible_recipes)
                update_sheet(service, row_number, i.strftime(<span class="hljs-string">'%d-%m-%Y'</span>), cfg.SPREADSHEET_ID)
            elif r.events_cal_1 <span class="hljs-keyword">in</span> free_events or r.events_cal_2 <span class="hljs-keyword">in</span> free_events \
            or pd.isnull(r.events_cal_1) or pd.isnull(r.events_cal_2):
                row_number = choose_recipe(<span class="hljs-string">'medium'</span>, i, weekmenu_df, eligible_recipes)
                update_sheet(service, row_number, i.strftime(<span class="hljs-string">'%d-%m-%Y'</span>), cfg.SPREADSHEET_ID)
            <span class="hljs-attr">else</span>:
                row_number = choose_recipe(<span class="hljs-string">'easy'</span>, i, weekmenu_df, eligible_recipes)
                update_sheet(service, row_number, i.strftime(<span class="hljs-string">'%d-%m-%Y'</span>), cfg.SPREADSHEET_ID)
    <span class="hljs-keyword">return</span> weekmenu_df
</code></pre><p>To take into account the work planning (BUSY and FREE events), we will use the <code>difficulty</code> of each recipe. A random recipe of the preferred difficulty will be added to <code>weekmenu_df.</code> Finally we drop it from the eligible recipes to avoid duplicate recipes in the same week.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">choose_recipe</span>(<span class="hljs-params">difficulty, idx, weekmenu_df, eligible_recipes</span>):</span>
    choice_idx = np.random.choice(eligible_recipes.query(<span class="hljs-string">"difficulty == '"</span> + difficulty + <span class="hljs-string">"'"</span> ).index.values)
    weekmenu_df.loc[idx, <span class="hljs-string">'recipe'</span>] = eligible_recipes.loc[choice_idx, <span class="hljs-string">'recipe'</span>]
    weekmenu_df.loc[idx, <span class="hljs-string">'description'</span>] = eligible_recipes.loc[choice_idx, <span class="hljs-string">'description'</span>]
    eligible_recipes.drop(choice_idx, inplace=<span class="hljs-literal">True</span>)
    <span class="hljs-keyword">return</span> choice_idx
</code></pre>
<p>The method <a target="_blank" href="https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values/update">spreadsheets().values().update</a> updates the Google Sheet.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update_sheet</span>(<span class="hljs-params">service, row_number, date, spreadsheetId</span>):</span>
    range = <span class="hljs-string">"recepten!F"</span>  + str(row_number)
    values = [[date]]
    body = {<span class="hljs-string">'values'</span> : values}
    result = service.spreadsheets().values().update(spreadsheetId=spreadsheetId
                                                    , range=range
                                                    , valueInputOption=<span class="hljs-string">'USER_ENTERED'</span>
                                                    , body=body).execute()
</code></pre>
<p>We iterate over each row of <code>weekmenu_df</code>. If the weekday is one of the TRADITIONS weekdays, we assign the corresponding recipe. For the other weekdays, we apply the following logic:</p>
<ul>
<li>In the weekend, choose a difficult recipe</li>
<li>During the week, when I’m at home or my wife has a day off, choose a recipe with medium difficulty</li>
<li>During the week, when I or my wife are at work, choose an easy recipe</li>
</ul>
<h3 id="heading-adding-the-week-menu-to-a-google-calendar">Adding the week menu to a Google Calendar</h3>
<p>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 <code>client_email</code> in credentials.json. In the settings of your calendar you also need to give it permission to make changes in the events.</p>
<pre><code>def add_weekmenu_to_calendar(service, weekmenu_df, calendarId):
    <span class="hljs-keyword">for</span> i, r <span class="hljs-keyword">in</span> weekmenu_df.iterrows():
        event = {
        <span class="hljs-string">'summary'</span>: r.recipe,
        <span class="hljs-string">'description'</span>: r.description,
        <span class="hljs-string">'start'</span>: {
            <span class="hljs-string">'date'</span>: i.date().isoformat(),
            <span class="hljs-string">'timeZone'</span>: <span class="hljs-string">'Europe/Brussels'</span>
        },
        <span class="hljs-string">'end'</span>: {
            <span class="hljs-string">'date'</span>: i.date().isoformat(),
            <span class="hljs-string">'timeZone'</span>: <span class="hljs-string">'Europe/Brussels'</span>
        }
        }
        event = service.events().insert(calendarId=calendarId, body=event).execute()
</code></pre><h3 id="heading-lets-automate">Let’s automate</h3>
<p>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.</p>
<p>I found this great website <a target="_blank" href="https://www.pythonanywhere.com/">PythonAnyWhere</a> 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.</p>
<p>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 <code>get_date_last_event.</code></p>
<pre><code>def get_date_last_event(service, calendarId):
    events_result = service.events().list(calendarId=calendarId
                                        , singleEvents=True
                                        , orderBy=<span class="hljs-string">'startTime'</span>).execute()
    date_last_event = events_result.get(<span class="hljs-string">'items'</span>, [])[<span class="hljs-number">-1</span>][<span class="hljs-string">'start'</span>][<span class="hljs-string">'date'</span>][:<span class="hljs-number">10</span>]
    date_last_event = datetime.strptime(date_last_event, <span class="hljs-string">'%Y-%m-%d'</span>).date()
    <span class="hljs-keyword">return</span> date_last_event
</code></pre><p>That date is stored in <code>DATE_LAST_RECIPE.</code> If the current day is after <code>DATE_LAST_RECIPE</code> minus <code>NB_DAYS_BEFORE</code> we can generate a new week menu.</p>
<p>You can find the complete script on <a target="_blank" href="https://github.com/bertcarremans/weekmenu/blob/master/generate_weekmenu.py">Github</a>.</p>
<pre><code><span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:
    # Getting credentials <span class="hljs-keyword">from</span> credentials.json
    CREDS_PATH = Path.cwd() / <span class="hljs-string">"weekmenu"</span> / <span class="hljs-string">"credentials.json"</span>
    creds = service_account.Credentials.from_service_account_file(CREDS_PATH, scopes=cfg.SCOPES)

    # Creating service objects
    service_cal = build(<span class="hljs-string">'calendar'</span>, <span class="hljs-string">'v3'</span>, credentials=creds)
    service_sheet = build(<span class="hljs-string">'sheets'</span>, <span class="hljs-string">'v4'</span>, credentials=creds)

    # Defining dates
    DATE_LAST_RECIPE = get_date_last_event(service_cal, cfg.CALENDARID_WEEKMENU) 
    START_DAY = DATE_LAST_RECIPE + timedelta(days=<span class="hljs-number">1</span>)
    NEXT_WEEK = START_DAY + timedelta(days=<span class="hljs-number">6</span>)
    PREV_WEEK = START_DAY + timedelta(days=<span class="hljs-number">-7</span>)
    START_DAY = format_date(START_DAY)
    NEXT_WEEK = format_date(NEXT_WEEK)
    PREV_WEEK = format_date(PREV_WEEK)

    # Getting the recipes <span class="hljs-keyword">from</span> the Google Sheet
    recipes_df, eligible_recipes = get_recipes(service_sheet, cfg.SPREADSHEET_ID, cfg.RANGE)

    # Check <span class="hljs-keyword">if</span> the last weekmenu is still active
    <span class="hljs-keyword">if</span> DATE_LAST_RECIPE - timedelta(days=cfg.NB_DAYS_BEFORE) &lt; datetime.now().date():
        # Getting the events <span class="hljs-keyword">from</span> the Google Calendars
        events_list_1 = get_events_by_calendarId(service_cal, cfg.CALENDARID_1, START_DAY, NEXT_WEEK, cfg.ALL_EVENTS)
        events_list_2 = get_events_by_calendarId(service_cal, cfg.CALENDARID_2, START_DAY, NEXT_WEEK, cfg.ALL_EVENTS)

        # Merge the two events lists
        events_df = create_events_df(events_list_1, events_list_2)

        # Generating the weekmenu
        weekmenu_df = generate_weekmenu(service_sheet, events_df, cfg.TRADITIONS, cfg.FREE_EVENTS)

        # Adding the weekmenu to a Google Calendar
        add_weekmenu_to_calendar(service_cal, weekmenu_df, cfg.CALENDARID_WEEKMENU)
        print(<span class="hljs-string">'Week menu is added to Google Calendar'</span>)
    <span class="hljs-attr">else</span>:
        print(<span class="hljs-string">'Program stopped. Last week menu is not finished yet.'</span>)
</code></pre><p>On PythonAnyWhere I’ve created a subfolder week menu. I’ve uploaded the following files config.py, generate_weekmenu.py and credentials.json.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/RhtFm84JZutzhpZgJopF8gwsWlZJxqdgalRr" alt="Image" width="800" height="323" loading="lazy">
<em>Project files on PythonAnyWhere.com</em></p>
<p>I then schedule a daily task that will run the generate_weekmenu.py script in the Tasks section. And voilà, we’re all set.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/ouqyRFUVYlqgjHektAW6MFS4yTd3YriPIDE9" alt="Image" width="800" height="160" loading="lazy"></p>
<h3 id="heading-the-result">The result</h3>
<p>After the first run of the script, we have a nice menu in our shared Google calendar.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/sVaq0YRZX3xTTN4EHwF-jThLXdOmCi31TPC2" alt="Image" width="800" height="169" loading="lazy">
<em>Automated week menu in a shared Google Calendar</em></p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>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.</p>
<p>If you want to take it further, here are some ideas to fine-tune the script:</p>
<ul>
<li>take into account the cooking time of a recipe</li>
<li>allow a tradition of having at least one vegetarian meal per week</li>
<li>generate a grocery list for the chose recipes</li>
</ul>
<p>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.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ The best life hack that isn’t on any life hack list ]]>
                </title>
                <description>
                    <![CDATA[ By Sam Harris How touch typing is a universal life hack that will benefit you the rest of your life. These days we have all heard about the benefits of meditating, eating healthy and going to the gym. There are lots of tricks and hacks people publish... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/the-best-life-hack-for-2018-that-isnt-on-any-life-hack-list-40fe3548e656/</link>
                <guid isPermaLink="false">66d460c9a326133d12440a5f</guid>
                
                    <category>
                        <![CDATA[ learning ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Life Hacking ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Productivity ]]>
                    </category>
                
                    <category>
                        <![CDATA[ startup ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 28 Aug 2018 14:49:14 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/0*gtN8lOemEF_7I5bb.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Sam Harris</p>
<h4 id="heading-how-touch-typing-is-a-universal-life-hack-that-will-benefit-you-the-rest-of-your-life">How touch typing is a universal life hack that will benefit you the rest of your life.</h4>
<p>These days we have all heard about the benefits of meditating, eating healthy and going to the gym. There are lots of tricks and hacks people publish to help you get more out of life but I have never come across anything about Touch Typing.</p>
<p>Touch Typing has been a completely overlooked skill that is quite possibly one of the best life hacks anyone can learn without making any radical changes to your life or redefining who you are or anything.</p>
<p>Learning to meditate or going to the gym every day and getting better sleep is for sure great for you, but I’m not sure if it’s even a life hack so I don’t know why Life-Hack lists are always full of them. And for any of those things to be effective you have to learn to actually enjoy doing them, otherwise, it’s just a short-term thing that takes effort and doesn’t change your life in any way at all.</p>
<p>So although learning to Touch Type may sound like perhaps the least sexy life-hack anyone has ever mentioned, I believe it is a truly universal life hack that will pay you back in benefits for the rest of your life, regardless of any personal preferences for pizza or salad, FIFA or yoga etc. Hopefully, this has convinced you enough to go ahead and read the rest of this post to understand why I believe it is so awesome.</p>
<h3 id="heading-definition">Definition</h3>
<p>Touch Typing is the ability to type into a computer relying on touch alone. You never need to look down at the keyboard. You can type using purely muscle memory and no longer use ‘active’ thinking to work out how to type letters and words.</p>
<p>This means that, in the long run, you can type much faster without the constraints of looking down at the keys and flicking back up to the screen to see what you typed. This can let you progress onto skills such as being able to transcribe audio as it happens and to make notes super fast, or just be able to write emails in the dark or whilst talking to someone else you can carry on working.</p>
<h3 id="heading-my-background">My Background</h3>
<p>I have been typing regularly for the past 20 years. I even did a bit of touch typing at school to help learn the keyboard. I type relatively fast and never really thought of it as something I needed to improve. I have one mate who types crazy fast and when he types he looks like a wizard. But, to be honest, the speed that words appear on his computer never really filled me with envy and I assumed my life was just fine.</p>
<p>My typing style involves using most of my fingers so half way to touch typing. I guess I do glance down at the keyboard a lot just to check where keys are. I have definitely reached a plateau at typing speed but thought that was fine.</p>
<p>After a conversation with Alex Denne on my podcast (<a target="_blank" href="https://www.growthmindsetpodcast.com/blog/2018/1/28/12the-basics-of-entrepreneurship-how-to-master-anything">Growth Mindset Podcast — How to Master Anything</a>), he sent me a link to a touch typing tutorial. It had a great UI, was free and the in-app gamification was cool. Trying just ten minutes a day seemed worth a try.</p>
<h3 id="heading-benefits">Benefits</h3>
<p>You really don’t know what you don’t know, and I hadn’t realized in the slightest how much my typing style was slowing me down in so many other ways (besides just my words per minute rate).</p>
<h4 id="heading-fewer-errors">Fewer Errors</h4>
<p>Touch typing means you can be watching the screen the whole time for errors, as such I make about 80% fewer errors in my typing straight away. This is amazing in itself and makes my writing and emailing much better, There is less time needed for checking and less stupid errors getting through which make me appear a complete sloppy dumbass</p>
<h4 id="heading-free-thought">Free Thought</h4>
<p>This is something I really hadn’t expected. But typing with the help of your eyes to check where keys are actual takes a portion of conscious effort dedicated to just the action of typing. By moving the act of typing into the unconscious side of the brain, this frees up your mind to think more openly about other things. This has two great benefits.</p>
<p>When you are no longer worried about typing the immediate words right now you start being able to think about the words ahead. I would say it’s like learning to drive.</p>
<p><strong>Car Driving Analogy</strong></p>
<p>When you first try to drive you only look at the road immediately in front of you and your brain is thinking, “crap I need to move the wheel and change gear and change how much I’m pressing the pedals and Oh God ? there’s someone coming and I need to go round a corner”. And you generally mess it all up and can never plan ahead and you feel like a noob.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/w5JY2Gf7EEJm2f2VoWmwSKIodei2aZRJFieC" alt="Image" width="800" height="600" loading="lazy"></p>
<p>Once you learn to master all these different things and automate them in your sub-conscience you start to look much further ahead and see how the road turns. You can already plan ahead what gear you should be in when arriving and your body starts doing things naturally to fit all the adjustments into your plan as you go along rather than reacting to it. You are able to stay aware of potential hazards and enjoy a pleasant conversation or daydreaming or singing along as you drive along just fine. You are not stuck in an endless cycle of focusing only on the immediate road just ahead of you and scrambling to try and do the right thing right now.</p>
<p>It’s the same when you know how to touch type. You can stop thinking about how to put your finger on the right letter at the right time. You don’t need to focus on where a particular letter is on the keyboard and get each word out one by one. You can think about the entire body of thoughts that are in your head and how you are going to distill it into a sensible text</p>
<h4 id="heading-creativity">Creativity</h4>
<p>When you are no longer writing with a slight disability, you can dedicate your full brain space to the act of thinking about the sentence you want to write. You can also think more long term as you go along about the story you want to tell and genuinely become a better and more creative writer by removing this limitation.</p>
<p>If you have a great idea, you can write it down immediately instead of focusing on how to type the words to express your idea.</p>
<h4 id="heading-social">Social</h4>
<p>This sounds silly, but people often try to talk to you whilst your typing and it can end up in a total mess in two ways, either:</p>
<ul>
<li><p>You stop what you are doing and dedicate your full mental energy to the person. This is very polite but then you might forget what you were doing and have to start again.</p>
</li>
<li><p>You try to carry on finishing your train of thought whilst keeping the other person on hold in a weird limbo where you give them enough info to keep them there but don’t really tell them anything useful. You finish off your email or idea but it takes longer because you are also talking to the other person and its all a bit rude and both the writing and conversation suck.</p>
</li>
</ul>
<p>With touch typing, you have more brain space available to finish off your typing whilst also listening to the person and answering them in full “real English” sentences, and you can even look at them whilst you do it. Amazing!</p>
<h3 id="heading-future-thoughts">Future Thoughts</h3>
<p>Increasing accuracy of speech to text and news about projects like Nueralink (brain-machine interface to connect humans and computers) grabbing headlines might make you think there is no point in learning this skill if it will become a relic in the future.</p>
<p>Back to the “Driving analogy”.</p>
<p>People may not consider learning to drive in 2018 on the presumption that driverless cars are coming out in the near future and all cars will be driverless. Learning to drive is expensive, time-consuming, dangerous and quite frankly a little embarrassing. So why bother unless you live somewhere rural and really need to transport yourself right now.</p>
<p>But for touch typing, I do not agree with this argument of wasted time due to future tech making the skill pointless.</p>
<p><strong>Firstly,</strong> the investment to learn is much smaller. You can learn touch typing for free! And it doesn’t take half as many hours. And you can’t kill yourself or anyone else whilst doing it. ?</p>
<p><strong>Secondly,</strong> there will still be plenty of occasions when the technology isn’t ready or appropriate and traditional “old-school” typing will still apply. Even when once speech-to-text is 100% accurate and people think, hey I can just speak all day and never type, what will actually happen next? We will have to wait 20 years for every office ever to be completely re-designed to let you sit in your own bunker of silence so that you can work with just your computer, instead of working with other people. Except that’s a crap idea and if I’m in an office I want to be able to talk to other people and I really don’t want to hear them talking to their computers all day every day. Every email or document should not be read out loud to the whole office by all my colleagues as they try to decide what to write!</p>
<p>If you’ve ever used Alexa, or Siri or Google Voice etc… then I’m sure you know that feeling when someone starts talking to it and everyone else goes quiet whilst this takes place. It is unnatural and totally not a viable option for real work.</p>
<p>The fact is you will be typing a lot of the time, even if you won’t need to type as much of the time.</p>
<p>And brain-computer interface Neuroprosthetics such as Nueralink despite being super mega awesome fantastic would still take a long time to become practically usable.</p>
<p>You should just embrace the old school version of depositing thoughts from your mind and uploading these thoughts to others via written down text. By learning to touch type and getting familiar with your keyboard, you can make this flow of consciousness as seamless as possible and experience the liberation it provides!</p>
<h3 id="heading-summary">Summary</h3>
<p>Touch typing releases brain space by moving conscious thought into the subconscious so you can think more freely about more stuff, and then get that stuff down into writing faster. So you become a more creative and more productive person at the same time.</p>
<p>It doesn’t take long to learn and can be acquired over a month with just 5–10 minutes of active work on it a day. And this time you will quickly get back as you can type and think much faster during your working day.</p>
<p>Not learning to touch type is basically hindering your entire ability to work efficiently if you are a human being who works at all with computers.</p>
<p>So whether you are a writer, CEO, secretary or a student. My number one life hack for 2018 is learning to touch Type.</p>
<h4 id="heading-links">Links</h4>
<p><strong>Touch Typing</strong><br>I really enjoyed <a target="_blank" href="http://www.typingclub.com">www.typingclub.com</a> as a great site to learn touch typing for free. A good introduction to the keyboard and improving your skills and fun gamification. There are plenty of other websites and apps available.</p>
<p><strong>My Podcast</strong></p>
<p>You can listen to the audio version of this blog here on <a target="_blank" href="http://www.growthmindsetpodcast.com">www.growthmindsetpodcast.com</a></p>
<p>Or subscribe in your preferred app</p>
<ul>
<li><p><a target="_blank" href="https://itunes.apple.com/gb/podcast/growth-mindset-podcast/id1273606075">iTunes</a></p>
</li>
<li><p><a target="_blank" href="https://open.spotify.com/show/2rQ6Aug8EBudJMSBYTi8nM">Spotify</a></p>
</li>
<li><p><a target="_blank" href="https://www.stitcher.com/podcast/samuel-harris/the-growth-mindset-podcast/e/51292507">Stitcher</a></p>
</li>
<li><p><a target="_blank" href="https://anchor.fm/growth-mindset-podcast/episodes/28---Stealing-Bitcoins-and-Hacking-World-Banks---Anonymous-e1te3o">All other players…</a></p>
</li>
</ul>
<p>If you liked this article you’ll definitely enjoy these:</p>
<ul>
<li><p><a target="_blank" href="https://www.growthmindsetpodcast.com/blog/2018/8/9/28-anonymous-hacker">Growth Mindset Podcast — Hacking the world Banks</a><br>  Interview with an anonymous Hacker who explains how he hacked into several world banks last year and stole over 45 bitcoins!</p>
</li>
<li><p><a target="_blank" href="https://www.growthmindsetpodcast.com/blog/2018/8/29-hitman-life">Growth Mindset Podcast — Conversation with a Hitman</a><br>  I talk with a real-life hitman. The kind that kills people for a living. It is a mental piece of audio.</p>
</li>
<li><p><a target="_blank" href="https://www.growthmindsetpodcast.com/blog/learned-helplessness">Growth Mindset Podcast — Learnt Helplessness and Learned Optimism</a><br>  How to overcome anything that holds you back. Specifically all the things you didn’t even realise were holding you back because they are so ingrained in your mind that they are just an accepted part of life.</p>
</li>
</ul>
<h4 id="heading-if-you-enjoyed-this-story-please-click-the-button-and-share-to-help-others-find-it-feel-free-to-leave-a-comment-below">If you enjoyed this story, please click the ? button and share to help others find it! Feel free to leave a comment below.</h4>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How a Bubble Plot Reveals the Best Cities to Live in the US ]]>
                </title>
                <description>
                    <![CDATA[ By Zhen Liu In this article, I’ll show you some exciting facts about American cities, the value of bubble plots in deciding which city to live in, and how to create those plots. Are you thinking about investing in real estate in 2018? Moving to a new... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-a-bubble-plot-can-reveal-the-best-places-to-live-in-the-us-e2054c844062/</link>
                <guid isPermaLink="false">66c34ca10fa3812cdd5ea9dd</guid>
                
                    <category>
                        <![CDATA[ Life Hacking ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Data Science ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Real Estate ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 02 Apr 2018 11:03:26 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*HffpBV9kNhHCBT0D77zm3w.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Zhen Liu</p>
<p>In this article, I’ll show you some exciting facts about American cities, the value of bubble plots in deciding which city to live in, and how to create those plots.</p>
<p>Are you thinking about investing in real estate in 2018? Moving to a new city? When considering these decisions, you need to weigh in different factors like unemployment rate, housing price, the size of the city, safety and so on. Even with all that data and four corresponding bar charts, you’ll still be clueless staring at that table. You’ll try to find the best candidates, but those factors are telling different stories… Sounds like a complex problem.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*YJKL-SBnPHPO5Eb69w-eGQ.png" alt="Image" width="469" height="222" loading="lazy"></p>
<p>So, is there a way we can visualize all these factors in 1 chart and compare them ALL? Yes, we can use a bubble plot!</p>
<h4 id="heading-whats-a-bubble-plot"><em>What’s a bubble plot?</em></h4>
<p>A bubble plot is a type of chart that displays more than two dimensions of data (compared to traditional scatter plots). In addition to plotting a dot on an X-Y plane, it uses the size, color, or shape of the point to display more dimensions.</p>
<p>We use <strong>unemployment rate as the X-axis, median home price as the Y-axis</strong>, and the <strong>population of the cities as the size of the dots.</strong> This makes a good third dimension. Color is randomly assigned to each city.</p>
<h3 id="heading-the-best-city-in-the-us-to-live-in-iswait-for-it">The best city in the US to live in is…(wait for it)</h3>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*wyVxxjaM_oQF0utG6__cog.png" alt="Image" width="800" height="455" loading="lazy"></p>
<p>Winner<em>:</em> <strong>Nashville!</strong></p>
<p>Other recommendations: <strong>Austin, Omaha, Milwaukee, Dallas, Minneapolis, Denver and Aurora.</strong></p>
<p>They have low unemployment (and therefore there’s higher chance of finding a job), and low home price, because they are on the lower left side of the plot. What does that mean?</p>
<p><strong>It means you can make your choices based on this plot.</strong></p>
<p>For example, if you consider unemployment rate to be more important and don’t mind the higher home prices, then Honolulu, Oakland, Boston, and San Diego are strong candidates</p>
<h3 id="heading-what-about-adding-safety-as-another-factor">What about adding safety as another factor?</h3>
<p>Sure. Let’s add safety as a fourth factor (the other three factors are still home price, unemployment rate, and population). Instead of randomly assigning a color for a city, we use the <strong>color scale for crime</strong> (crime rate per 100,000 people). <strong>Red</strong> means more crime and <strong>blue</strong> means less.</p>
<h4 id="heading-does-the-result-change">Does the result change?</h4>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*hjgKmyxuTeXeNTyxE9shiA.png" alt="Image" width="800" height="510" loading="lazy"></p>
<p>It did! If safety is very important for you, then Milwaukee might not be such a great choice among the previous recommendations (even though it’s at the lower left side of the graph).</p>
<p><strong>Now you see the power of a bubble plot:</strong> the ability to demonstrate multiple factors in one 2-D plot. If you only have bar charts for those factors, it’s hard for you to identify the cities with an ideal combination of factors. The bubble plot basically created a “visual objective function” for you to optimize a multi-variable decision-making problem.</p>
<h3 id="heading-how-do-unemployment-rate-and-home-price-change-over-time"><strong>How do unemployment rate and home price change over time?</strong></h3>
<p>We can create an interactive motion chart to add time as a dimension (2013 to 2017) to see how the factors change for these cities over time.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*yYFcThB3pQ8wxPODrP4ZgQ.gif" alt="Image" width="600" height="506" loading="lazy"></p>
<p>To avoid too much visual information, I didn’t use crime data and used the different colors to represent a few selected cities.</p>
<p>The good news is that the unemployment rate for almost all cities decreased significantly (moving from right to left). But the bad news is that the housing prices are going up pretty fast (especially for San Francisco, San Jose, Los Angles, New York, and Seattle).</p>
<p>Want to create the charts yourself? Here is my code for the bubble plots and the motion chart in R. Have fun playing with the plots :)</p>
<pre><code>################ Bubble Plot ################library(data.table)library(ggplot2)library(ggrepel)
</code></pre><pre><code>bubble_data &lt;-fread(<span class="hljs-string">"https://raw.githubusercontent.com/zhendata/Medium_Posts/c007346db1575aca391a6623c87bb5a31a60b365/bubble_plot_merged_city_data.csv"</span>,sep=<span class="hljs-string">","</span>)
</code></pre><pre><code>bubble_plot &lt;- ggplot(bubble_data,                aes(x = Unemployment_Rate, y = Home_Price/<span class="hljs-number">1000</span>)) +
</code></pre><pre><code>geom_point(aes(size = Population, fill = Total_Crime),shape=<span class="hljs-number">21</span>) +# Create <span class="hljs-string">'Bubble'</span> by assigning size a variable #
</code></pre><pre><code>scale_fill_continuous(low = <span class="hljs-string">"#33FFFF"</span>, high =<span class="hljs-string">"#FF6699"</span> ) +scale_size_area(max_size = <span class="hljs-number">20</span>)+# Select bubble color scale and bubble maximum size #
</code></pre><pre><code>geom_text_repel(          aes(label = City),nudge_x = <span class="hljs-number">0</span>,nudge_y = <span class="hljs-number">0.75</span>,size = <span class="hljs-number">6</span>) +# Use geom_text_repel to repel the labels away <span class="hljs-keyword">from</span> each other #
</code></pre><pre><code>theme_bw()+# Use white background instead <span class="hljs-keyword">of</span> the <span class="hljs-keyword">default</span> grey one #
</code></pre><pre><code>ggtitle(<span class="hljs-string">"Best Cities in US to Live in"</span>) +labs(x = <span class="hljs-string">"Unemployment Rate%"</span>, y = <span class="hljs-string">"Home Price"</span>,       size = <span class="hljs-string">"Population"</span>,fill=<span class="hljs-string">"Crime"</span>) +theme(plot.title = element_text(size=<span class="hljs-number">25</span>, hjust = <span class="hljs-number">0.5</span>),        axis.title=element_text(size=<span class="hljs-number">20</span>, face = <span class="hljs-string">"bold"</span>),        axis.text=element_text(size=<span class="hljs-number">15</span>)) +# Style title and axis #
</code></pre><pre><code>scale_y_continuous(name=<span class="hljs-string">"Home Price"</span>, breaks = seq(<span class="hljs-number">0</span>, <span class="hljs-number">1500</span>, by=<span class="hljs-number">250</span>),                       labels=c(<span class="hljs-string">"0"</span>, <span class="hljs-string">"250K"</span>, <span class="hljs-string">"500K"</span>, <span class="hljs-string">"750K"</span>, <span class="hljs-string">"1000k"</span>,    <span class="hljs-string">"1250k"</span>, <span class="hljs-string">"1500K"</span>))# Make y-axis more readable by replacing scientific number by <span class="hljs-string">"K"</span> #
</code></pre><pre><code>print(bubble_plot)
</code></pre><pre><code>################# Motion Chart #################library(data.table)library(googleVis)
</code></pre><pre><code>motion_data &lt;-fread(<span class="hljs-string">"https://raw.githubusercontent.com/zhendata/Medium_Posts/c007346db1575aca391a6623c87bb5a31a60b365/motion_chart_merged_city_data.csv"</span>,sep=<span class="hljs-string">","</span>)
</code></pre><pre><code>motion_chart &lt;- gvisMotionChart(motion_data, idvar = <span class="hljs-string">"City"</span>, timevar = <span class="hljs-string">"Year"</span>,xvar = <span class="hljs-string">"Unemployment Rate"</span>,yvar= <span class="hljs-string">"Home Price"</span>,sizevar=<span class="hljs-string">"Population"</span>)
</code></pre><pre><code>plot(motion_chart)# R automatically opens a tab <span class="hljs-keyword">in</span> the browser <span class="hljs-keyword">for</span> you# The flash player needs to be enabled <span class="hljs-keyword">in</span> browser
</code></pre><p><img src="https://cdn-media-1.freecodecamp.org/images/1*J9UiAP39_gGy14vmn_Qtrw.png" alt="Image" width="441" height="306" loading="lazy">
<em>Click the “⌽” icon to enable Flash</em></p>
<pre><code>######### Data #########<span class="hljs-string">""</span><span class="hljs-string">"The datasets I used are from Zillow (medium housing), FBI’s UCR program, census.gov (population), Bureau of Labor (unemployment). I did some data cleaning and joining for the format I needed in this article, and you can click the links below to download."</span><span class="hljs-string">""</span>bubble_plot_merged_city_data.csv, motion_chart_merged_city_data.csv
</code></pre><p>Follow me and give me a few claps if you found this helpful!</p>
<p>You can also read my previous articles on data science, real estate, and decision making:</p>
<p><a target="_blank" href="https://medium.freecodecamp.org/how-to-analyze-seasonality-and-trends-to-save-money-on-your-apartment-lease-714d1d82771a"><strong>How to analyze seasonality and trends to save money on your apartment lease.</strong></a><br><a target="_blank" href="https://medium.freecodecamp.org/how-to-analyze-seasonality-and-trends-to-save-money-on-your-apartment-lease-714d1d82771a">_When I was looking for a new apartment to rent, I started to wonder: is there a data-driven decision making strategy…_medium.freecodecamp.org</a><a target="_blank" href="https://medium.freecodecamp.org/https-medium-freecodecamp-org-how-to-predict-rent-and-select-the-best-lease-duration-to-save-money-5cf35145d398"><strong>How to Use Data to Predict Rent and Optimize Your Lease Duration So You Can Save Money</strong></a><br><a target="_blank" href="https://medium.freecodecamp.org/https-medium-freecodecamp-org-how-to-predict-rent-and-select-the-best-lease-duration-to-save-money-5cf35145d398">_In my last post, we talked about how to pick the best month to sign the lease based on seasonality. Now, how long…_medium.freecodecamp.org</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to scrape websites with Python and BeautifulSoup ]]>
                </title>
                <description>
                    <![CDATA[ By Justin Yek There is more information on the Internet than any human can absorb in a lifetime. What you need is not access to that information, but a scalable way to collect, organize, and analyze it. You need web scraping. Web scraping automatical... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-scrape-websites-with-python-and-beautifulsoup-5946935d93fe/</link>
                <guid isPermaLink="false">66c354539fd49a6729cdd9d6</guid>
                
                    <category>
                        <![CDATA[ Life Hacking ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Sat, 10 Jun 2017 14:16:51 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*BrUAg3-OqIHkoTz_CRIzTA.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Justin Yek</p>
<p>There is more information on the Internet than any human can absorb in a lifetime. What you need is not access to that information, but a scalable way to collect, organize, and analyze it.</p>
<p>You need web scraping.</p>
<p>Web scraping automatically extracts data and presents it in a format you can easily make sense of. In this tutorial, we’ll focus on its applications in the financial market, but web scraping can be used in a wide variety of situations.</p>
<p>If you’re an avid investor, getting closing prices every day can be a pain, especially when the information you need is found across several webpages. We’ll make data extraction easier by building a web scraper to retrieve stock indices automatically from the Internet.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/trTuoesIzruCn5cstkdhl9poG5I8IjL9K5kP" alt="Image" width="800" height="424" loading="lazy"></p>
<h3 id="heading-getting-started">Getting Started</h3>
<p>We are going to use Python as our scraping language, together with a simple and powerful library, BeautifulSoup.</p>
<ul>
<li>For Mac users, Python is pre-installed in OS X. Open up Terminal and type <code>python --version</code>. You should see your python version is 2.7.x.</li>
<li>For Windows users, please install Python through the <a target="_blank" href="https://www.python.org/downloads/">official website</a>.</li>
</ul>
<p>Next we need to get the BeautifulSoup library using <code>pip</code>, a package management tool for Python.</p>
<p>In the terminal, type:</p>
<pre><code class="lang-bash">easy_install pip
pip install BeautifulSoup4
</code></pre>
<p><strong>Note</strong>: If you fail to execute the above command line, try adding <code>sudo</code> in front of each line.</p>
<h3 id="heading-the-basics">The Basics</h3>
<p>Before we start jumping into the code, let’s understand the basics of HTML and some rules of scraping.</p>
<p><strong>HTML tags</strong><br>If you already understand HTML tags, feel free to skip this part.</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>  
<span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>  
    <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span> First Scraping <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span> Hello World <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>This is the basic syntax of an HTML webpage. Every <code>&lt;tag&gt;</code> serves a block inside the webpage:  </p>
<ol>
<li><code>&lt;!DOCTYPE html&gt;</code>: HTML documents must start with a type declaration.  </li>
<li>The HTML document is contained between <code>&lt;html&gt;</code> and <code>&lt;/html&gt;</code>.  </li>
<li>The meta and script declaration of the HTML document is between <code>&lt;head&gt;</code>and <code>&lt;/head&gt;</code>.  </li>
<li>The visible part of the HTML document is between <code>&lt;body&gt;</code> and <code>&lt;/body&gt;</code>tags.  </li>
<li>Title headings are defined with the <code>&lt;h1&gt;</code> through <code>&lt;h6&gt;</code> tags.  </li>
<li>Paragraphs are defined with the <code>&lt;p&gt;</code> tag.</li>
</ol>
<p>Other useful tags include <code>&lt;a&gt;</code> for hyperlinks, <code>&lt;table&gt;</code> for tables, <code>&lt;tr&gt;</code> for table rows, and <code>&lt;td&gt;</code> for table columns.</p>
<p>Also, HTML tags sometimes come with <code>id</code> or <code>class</code> attributes. The <code>id</code> attribute specifies a unique id for an HTML tag and the value must be unique within the HTML document. The <code>class</code> attribute is used to define equal styles for HTML tags with the same class. We can make use of these ids and classes to help us locate the data we want.</p>
<p>For more information on HTML <a target="_blank" href="http://www.w3schools.com/html/">tags</a>, <a target="_blank" href="http://www.w3schools.com/tags/att_global_id.asp">id</a> and <a target="_blank" href="http://www.w3schools.com/html/html_classes.asp">class</a>, please refer to W3Schools <a target="_blank" href="http://www.w3schools.com/">Tutorials</a>.</p>
<p><strong>Scraping Rules</strong></p>
<ol>
<li>You should check a website’s Terms and Conditions before you scrape it. Be careful to read the statements about legal use of data. Usually, the data you scrape should not be used for commercial purposes.</li>
<li>Do not request data from the website too aggressively with your program (also known as spamming), as this may break the website. Make sure your program behaves in a reasonable manner (i.e. acts like a human). One request for one webpage per second is good practice.</li>
<li>The layout of a website may change from time to time, so make sure to revisit the site and rewrite your code as needed</li>
</ol>
<h3 id="heading-inspecting-the-page">Inspecting the Page</h3>
<p>Let’s take one page from the <a target="_blank" href="http://www.bloomberg.com/quote/SPX:IND">Bloomberg Quote</a> website as an example.</p>
<p>As someone following the stock market, we would like to get the index name (S&amp;P 500) and its price from this page. First, right-click and open your browser’s inspector to inspect the webpage.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/i3mWoIwj3FWfSPQPnyfBEQn3oWNp5czTCg0U" alt="Image" width="369" height="206" loading="lazy"></p>
<p>Try hovering your cursor on the price and you should be able to see a blue box surrounding it. If you click it, the related HTML will be selected in the browser console.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/2Tnvx-rIrmKbElQGgSnPaNK1xiG6LJI5xwR8" alt="Image" width="800" height="284" loading="lazy"></p>
<p>From the result, we can see that the price is inside a few levels of HTML tags, which is <code>&lt;div class="basic-quote"&gt;</code> → <code>&lt;div class="price-container up"&gt;</code>→ <code>&lt;div class="price"&gt;</code>.</p>
<p>Similarly, if you hover and click the name “S&amp;P 500 Index”, it is inside <code>&lt;div class="basic-quote"&gt;</code> and <code>&lt;h1 class="name"&gt;</code>.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/o0p8x2WILUOE7lXyS1beHcztnAH63knKOyqd" alt="Image" width="306" height="111" loading="lazy"></p>
<p>Now we know the unique location of our data with the help of <code>class</code> tags.</p>
<h3 id="heading-jump-into-the-code">Jump into the Code</h3>
<p>Now that we know where our data is, we can start coding our web scraper. Open your text editor now!</p>
<p>First, we need to import all the libraries that we are going to use.</p>
<pre><code class="lang-py"><span class="hljs-comment"># import libraries</span>
<span class="hljs-keyword">import</span> urllib2
<span class="hljs-keyword">from</span> bs4 <span class="hljs-keyword">import</span> BeautifulSoup
</code></pre>
<p>Next, declare a variable for the url of the page.</p>
<pre><code class="lang-py"><span class="hljs-comment"># specify the url</span>
quote_page = ‘http://www.bloomberg.com/quote/SPX:IND<span class="hljs-string">'</span>
</code></pre>
<p>Then, make use of the Python urllib2 to get the HTML page of the url declared.</p>
<pre><code class="lang-py"><span class="hljs-comment"># query the website and return the html to the variable ‘page’</span>
page = urllib2.urlopen(quote_page)
</code></pre>
<p>Finally, parse the page into BeautifulSoup format so we can use BeautifulSoup to work on it.</p>
<pre><code class="lang-py"><span class="hljs-comment"># parse the html using beautiful soup and store in variable `soup`</span>
soup = BeautifulSoup(page, ‘html.parser’)
</code></pre>
<p>Now we have a variable, <code>soup</code>, containing the HTML of the page. Here’s where we can start coding the part that extracts the data.</p>
<p>Remember the unique layers of our data? BeautifulSoup can help us get into these layers and extract the content with <code>find()</code>. In this case, since the HTML class <code>name</code> is unique on this page, we can simply query <code>&lt;div class="name"&gt;</code>.</p>
<pre><code class="lang-py"><span class="hljs-comment"># Take out the &lt;div&gt; of name and get its value</span>
name_box = soup.find(‘h1’, attrs={‘<span class="hljs-class"><span class="hljs-keyword">class</span>’:</span> ‘name’})
</code></pre>
<p>After we have the tag, we can get the data by getting its <code>text</code>.</p>
<pre><code class="lang-py">name = name_box.text.strip() <span class="hljs-comment"># strip() is used to remove starting and trailing</span>
<span class="hljs-keyword">print</span> name
</code></pre>
<p>Similarly, we can get the price too.</p>
<pre><code class="lang-py"><span class="hljs-comment"># get the index price</span>
price_box = soup.find(‘div’, attrs={‘<span class="hljs-class"><span class="hljs-keyword">class</span>’:</span>’price’})
price = price_box.text
<span class="hljs-keyword">print</span> price
</code></pre>
<p>When you run the program, you should be able to see that it prints out the current price of the S&amp;P 500 Index.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/R7W6VXymQPtfUACuiHkwldc1CuMEs2NYdcW1" alt="Image" width="118" height="45" loading="lazy"></p>
<h3 id="heading-export-to-excel-csv">Export to Excel CSV</h3>
<p>Now that we have the data, it is time to save it. The Excel Comma Separated Format is a nice choice. It can be opened in Excel so you can see the data and process it easily.</p>
<p>But first, we have to import the Python csv module and the datetime module to get the record date. Insert these lines to your code in the import section.</p>
<pre><code class="lang-py"><span class="hljs-keyword">import</span> csv
<span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> datetime
</code></pre>
<p>At the bottom of your code, add the code for writing data to a csv file.</p>
<pre><code class="lang-py"><span class="hljs-comment"># open a csv file with append, so old data will not be erased</span>
<span class="hljs-keyword">with</span> open(‘index.csv’, ‘a’) <span class="hljs-keyword">as</span> csv_file:
 writer = csv.writer(csv_file)
 writer.writerow([name, price, datetime.now()])
</code></pre>
<p>Now if you run your program, you should able to export an <code>index.csv</code> file, which you can then open with Excel, where you should see a line of data.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/7O7PaHRqlgGALct2ZdjI2K773eGgXMAaJvMH" alt="Image" width="237" height="31" loading="lazy"></p>
<p>So if you run this program everyday, you will be able to easily get the S&amp;P 500 Index price without rummaging through the website!</p>
<h3 id="heading-going-further-advanced-uses">Going Further (Advanced uses)</h3>
<p><strong>Multiple Indices</strong><br>So scraping one index is not enough for you, right? We can try to extract multiple indices at the same time.</p>
<p>First, modify the <code>quote_page</code> into an array of URLs.</p>
<pre><code class="lang-py">quote_page = [‘http://www.bloomberg.com/quote/SPX:IND<span class="hljs-string">', ‘http://www.bloomberg.com/quote/CCMP:IND'</span>]
</code></pre>
<p>Then we change the data extraction code into a <code>for</code> loop, which will process the URLs one by one and store all the data into a variable <code>data</code> in tuples.</p>
<pre><code class="lang-py"><span class="hljs-comment"># for loop</span>
data = []
<span class="hljs-keyword">for</span> pg <span class="hljs-keyword">in</span> quote_page:
 <span class="hljs-comment"># query the website and return the html to the variable ‘page’</span>
 page = urllib2.urlopen(pg)

<span class="hljs-comment"># parse the html using beautiful soap and store in variable `soup`</span>
 soup = BeautifulSoup(page, ‘html.parser’)

<span class="hljs-comment"># Take out the &lt;div&gt; of name and get its value</span>
 name_box = soup.find(‘h1’, attrs={‘<span class="hljs-class"><span class="hljs-keyword">class</span>’:</span> ‘name’})
 name = name_box.text.strip() <span class="hljs-comment"># strip() is used to remove starting and trailing</span>

<span class="hljs-comment"># get the index price</span>
 price_box = soup.find(‘div’, attrs={‘<span class="hljs-class"><span class="hljs-keyword">class</span>’:</span>’price’})
 price = price_box.text

<span class="hljs-comment"># save the data in tuple</span>
 data.append((name, price))
</code></pre>
<p>Also, modify the saving section to save data row by row.</p>
<pre><code class="lang-py"><span class="hljs-comment"># open a csv file with append, so old data will not be erased</span>
<span class="hljs-keyword">with</span> open(‘index.csv’, ‘a’) <span class="hljs-keyword">as</span> csv_file:
 writer = csv.writer(csv_file)
 <span class="hljs-comment"># The for loop</span>
 <span class="hljs-keyword">for</span> name, price <span class="hljs-keyword">in</span> data:
 writer.writerow([name, price, datetime.now()])
</code></pre>
<p>Rerun the program and you should be able to extract two indices at the same time!</p>
<h3 id="heading-advanced-scraping-techniques">Advanced Scraping Techniques</h3>
<p>BeautifulSoup is simple and great for small-scale web scraping. But if you are interested in scraping data at a larger scale, you should consider using these other alternatives:</p>
<ol>
<li><a target="_blank" href="http://scrapy.org/">Scrapy</a>, a powerful python scraping framework</li>
<li>Try to integrate your code with some public APIs. The efficiency of data retrieval is much higher than scraping webpages. For example, take a look at <a target="_blank" href="https://developers.facebook.com/docs/graph-api">Facebook Graph API</a>, which can help you get hidden data which is not shown on Facebook webpages.</li>
<li>Consider using a database backend like <a target="_blank" href="https://www.mysql.com/">MySQL</a> to store your data when it gets too large.</li>
</ol>
<h3 id="heading-adopt-the-dry-method">Adopt the DRY Method</h3>
<p><img src="https://cdn-media-1.freecodecamp.org/images/e71XWhRmRZLario-leNUQuWlNwrMzZXv-e8n" alt="Image" width="800" height="533" loading="lazy"></p>
<p>DRY stands for “Don’t Repeat Yourself”, try to automate your everyday tasks like <a target="_blank" href="http://www.businessinsider.com/programmer-automates-his-job-2015-11">this person</a>. Some other fun projects to consider might be keeping track of your Facebook friends’ active time (with their consent of course), or grabbing a list of topics in a forum and trying out natural language processing (which is a hot topic for Artificial Intelligence right now)!</p>
<p>If you have any questions, please feel free to leave a comment below.</p>
<p><strong>References</strong><br><a target="_blank" href="http://www.gregreda.com/2013/03/03/web-scraping-101-with-python/">http://www.gregreda.com/2013/03/03/web-scraping-101-with-python/</a><br><a target="_blank" href="http://www.analyticsvidhya.com/blog/2015/10/beginner-guide-web-scraping-beautiful-soup-python/">http://www.analyticsvidhya.com/blog/2015/10/beginner-guide-web-scraping-beautiful-soup-python/</a></p>
<p><em>This article was originally published on Altitude Labs’ <a target="_blank" href="http://altitudelabs.com/blog/">blog</a> and was written by our software engineer, <a target="_blank" href="https://medium.com/@leonardmok">Leonard Mok</a>. <a target="_blank" href="http://altitudelabs.com">Altitude Labs</a> is a software agency that specializes in personalized, mobile-first React apps.</em></p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
