I made a system that automatically decides whether to run tomorrow with Python and adds it to Google Calendar.

Introduction

I basically run every day on weekdays, but of course I don't feel like running on rainy days or holidays. So, I wrote a program in Python that automates the decision whether to run the next day and adds an appointment to Google Calendar.

environment

Since I am using crontab for regular execution, I am assuming macOS or Linux, but on Windows you may be able to do the same with AWS etc.

Python settings

First, set up Python. Personally, I don't like to pollute the environment, so I make a virtualenv for each project. Please refer to here for details, as it deviates from the main subject.

$ pyenv virtualenv 3.8.1 develop_3.8.1
$ pyenv activate develop_3.8.1
$ pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib

Determine tomorrow's weather

I used OpenWeatherMap to get the weather forecast.

It's very simple to use, get the API key from the tab called "API keys" after creating the account and set it to ʻOPENWEATHER_API_KEY`. By the way, this time I used the zip code to specify the location, but it seems that you can also specify the city name and latitude / longitude (https://qiita.com/nownabe/items/aeac1ce0977be963a740).

import requests

BASE_URL = 'http://api.openweathermap.org/data/2.5/forecast'
OPENWEATHER_API_KEY = 'Your OpenWeather Api key'
ZIP_CODE = 'Zip code of your area'

url = '{}?units=metric&zip={zip},JP&APPID={key}'.format(BASE_URL, zip=ZIP_CODE, key=OPENWEATHER_API_KEY)

res = requests.get(url).json()

Then, such a result will be returned.

{
    "cod": "200",
    "message": 0,
    "cnt": 40,
    "list": [
        {
            "dt": 1582956000,
            "main": {
                "temp": 13.03,
                "feels_like": 9.94,
                "temp_min": 12.61,
                "temp_max": 13.03,
                "pressure": 1016,
                "sea_level": 1016,
                "grnd_level": 1010,
                "humidity": 40,
                "temp_kf": 0.42
            },
            "weather": [
                {
                    "id": 803,
                    "main": "Clouds",
                    "description": "broken clouds",
                    "icon": "04d"
                }
            ],
            "clouds": {
                "all": 60
            },
            "wind": {
                "speed": 1.52,
                "deg": 150
            },
            "sys": {
                "pod": "d"
            },
            "dt_txt": "2020-02-29 06:00:00"
        },
        ...

As for the result, the result is returned every 3 hours until 5 days later. The array of list contains the weather forecast for the date and time of dt_txt.

--temp: Temperature --feels_like: Feels temperature --temp_min: Minimum temperature --temp_max: Maximum temperature --pressure: Atmospheric pressure --sea_level: Atmospheric pressure on the surface of the sea --grnd_level: Atmospheric pressure on the ground --humidity: humidity

And so on. There is a lot of information and it is wonderful. But this time I just want to know if it's raining, so I'd like to focus on the main of the weather.

import datetime

tommorow = datetime.datetime.now() + datetime.timedelta(days=1)
tommorow_str = tommorow.strftime('%Y-%m-%d')

def is_rain(tommorow_str):
    tommorow_morning_dt = [tommorow_str + ' 06:00:00', tommorow_str + ' 09:00:00']
    tommorow_morning_weather = []

    weather_preds = res['list']

    for pred in weather_preds:
        if pred['dt_txt'] in tommorow_morning_dt:
            for weather in pred['weather']:
                tommorow_morning_weather.append(weather['main'])

    return 'Rain' in tommorow_morning_weather

First, use datetime to calculate tomorrow's date. And since I'm running early in the morning, I'll put the dates around 6am and 9am tomorrow in tommorow_morning_dt as strings. And compared to the previous result, if dt_txt is the desired date, it is added to tommorow_morning_weather.

Now you can get the weather for tomorrow's running time. However, I honestly think this implementation is subtle, and it feels nonsense to compare dates by string, so please let me know if there is a good way.

Determine if it's a holiday

Basically, I don't feel like running except on weekdays, so I want to exclude holidays. It's easy to talk about just excluding holidays, but holidays must also be considered, so refer to here and jpholiday I used a package called . You can also use ʻis_rain ()to determine if it's raining, which you just made, to express the conditions for running. By the way, the functionweekday ()ofdatetime` corresponds to 0 ~ 4 from Monday to Friday, and corresponds to 5,6 on Saturday and Sunday.

import jpholiday

if tommorow.weekday() < 5 and not jpholiday.is_holiday(tommorow) and not is_rain(tommorow_str):
    #Add events to Google Calendar
    add_event(tommorow_str)

Add an event to Google Calendar

Finally, add an event to Google Calendar. Please refer to here for easy-to-understand instructions on how to enable the Google Calendar API. I will leave the details to the link destination, but I will briefly explain the procedure to use the API.

  1. Press the "Enable the Google Calendar API" button in Python Quickstart.
  2. After the pop-up is displayed, press the button labeled "DOWNLOAD CLIENT CONFIGURATION" to download credentials.json.
  3. Move credentials.json to the same directory as your project.

For now, you're ready to use the API.

Next, check the ID of the calendar you want to add. The ID verification method is also explained in detail in the previous link, so I will briefly show the flow.

  1. Open Google Calendar.
  2. From the overflow menu of "My Calendar" on the left sidebar, press the "Settings & Share" button of the calendar you want to add.
  3. The "Calendar ID" is displayed in the top item of "Combine calendars", so make a note of it.

By setting the confirmed calendar ID to CALENDAR_ID, you can finally add an event.

import os.path
import pickle

from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request

CALENDAR_ID = 'Your Google Calendar ID'
DIR = os.path.dirname(os.path.abspath(__file__))

def add_event(tommorow):
    creds = None
    # The file token.pickle stores the user's access and refresh tokens, and is
    # created automatically when the authorization flow completes for the first
    # time.
    token_pickle_path = os.path.join(DIR, 'token.pickle')
    credentials_path = os.path.join(DIR, 'credentials.json')

    if os.path.exists(token_pickle_path):
        with open(token_pickle_path, 'rb') as token:
            creds = pickle.load(token)
    # If there are no (valid) credentials available, let the user log in.
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(credentials_path, SCOPES)
            creds = flow.run_local_server(port=0)
        # Save the credentials for the next run
        with open(token_pickle_path, 'wb') as token:
            pickle.dump(creds, token)

    service = build('calendar', 'v3', credentials=creds)

    # Call the Calndar API
    event = {
        'summary': 'running',
        'location': 'Your running location',
        'start': {
            'dateTime': '{}T07:30:00'.format(tommorow),
            'timeZone': 'Japan',
        },
        'end': {
            'dateTime': '{}T09:00:00'.format(tommorow),
            'timeZone': 'Japan',
        },
    }

    service.events().insert(calendarId=CALENDAR_ID, body=event).execute()

The first half of the code is taken from the Official Sample, so you need to make some special changes. It doesn't look like it. After that, enter the title of the event you want to add to summary of ʻevent, and enter the start time and end time in dateTimeofstart and ʻend, respectively, and you're done.

Completion code

add_calendar.py


import datetime
import os.path
import pickle
import requests

import jpholiday
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request

# If modifying these scopes, delete the file token.pickle.
SCOPES = ['https://www.googleapis.com/auth/calendar']

#Set any value.
CALENDAR_ID = 'Your Google Calendar ID'
OPENWEATHER_API_KEY = 'You Openweather API key'
ZIP_CODE = 'Zip code of your area'
DIR = os.path.dirname(os.path.abspath(__file__))


def add_event(tommorow):
    creds = None
    # The file token.pickle stores the user's access and refresh tokens, and is
    # created automatically when the authorization flow completes for the first
    # time.
    token_pickle_path = os.path.join(DIR, 'token.pickle')
    credentials_path = os.path.join(DIR, 'credentials.json')

    if os.path.exists(token_pickle_path):
        with open(token_pickle_path, 'rb') as token:
            creds = pickle.load(token)
    # If there are no (valid) credentials available, let the user log in.
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(credentials_path, SCOPES)
            creds = flow.run_local_server(port=0)
        # Save the credentials for the next run
        with open(token_pickle_path, 'wb') as token:
            pickle.dump(creds, token)

    service = build('calendar', 'v3', credentials=creds)

    event = {
        'summary': 'running',
        'location': 'Your running location',
        'start': {
            'dateTime': '{}T07:30:00'.format(tommorow),
            'timeZone': 'Japan',
        },
        'end': {
            'dateTime': '{}T09:00:00'.format(tommorow),
            'timeZone': 'Japan',
        },
    }

    service.events().insert(calendarId=CALENDAR_ID, body=event).execute()


def is_rain(tommorow):
    BASE_URL = 'http://api.openweathermap.org/data/2.5/forecast'
    url = '{}?units=metric&zip={zip},JP&APPID={key}'.format(BASE_URL, zip=ZIP_CODE, key=OPENWEATHER_API_KEY)

    res = requests.get(url).json()
    weather_preds = res['list']

    tommow_morning_dt = [tommorow + ' 06:00:00', tommorow + ' 09:00:00']
    tommow_morning_weather = []

    for pred in weather_preds:
        if pred['dt_txt'] in tommow_morning_dt:
            for weather in pred['weather']:
                tommow_morning_weather.append(weather['main'])

    return 'Rain' in tommow_morning_weather


def main():
    tommorow = datetime.datetime.now() + datetime.timedelta(days=1)
    tommorow_str = tommorow.strftime('%Y-%m-%d')

    # weekday: 0 ~ 4
    if tommorow.weekday() < 5 and not jpholiday.is_holiday(tommorow) and not is_rain(tommorow_str):
        add_event(tommorow_str)


if __name__ == '__main__':
    main()

In addition, this code

$ python add_calendar.py

When you execute with, the warning screen is displayed for the first time, but there is no problem, so

  1. Select an account
  2. Click the "Details" link
  3. Click the "Go to Quickstart (unsafe page)" link
  4. Click the "Allow" button
  5. Click the "Allow" button

Please proceed according to the procedure of. You do not need to perform this operation from the second time onward.

Now that you've confirmed that tomorrow's weather isn't raining, it's a weekday, and you've completed a script to add your running schedule, I'd like to automate this.

Periodic execution of additional events

Periodic execution is easy with the default crontab on macOS and Linux. For more information, please refer to here. First,

$ crontab -e

Launch crontab with. And write like this:

00 18 * * 0-4 [python path] [Project directory]/add_calendar.py

The python path is

$ which python

You can check with.

00 18 * * 0-4 means run from Sunday to Thursday at 18:00. From left to right, it represents "minute", "hour", "day", "month", and "day of the week". I don't think it is necessary to specify the day of the week because the program determines whether it is a weekday, but I added it to reduce unnecessary processing as much as possible.

Summary

This time I wrote a script for the purpose of adding running, but I think that it is quite frequent that you want to judge the conditions and add an appointment to the calendar, so please make use of it.

reference

Building an environment with pyenv and pyenv-virtualenv

A script that combines datetime and jpholiday to determine whether it is a weekday or a weekend or holiday

Get weather information from OpenWeatherMap using python

Try using OpenWeatherMap, a free weather forecast API

Add appointments to Google Calendar in Python

Get / add Google Calendar appointments using Google Calendar API

How to write crontab

Recommended Posts

I made a system that automatically decides whether to run tomorrow with Python and adds it to Google Calendar.
[Introduction to system trading] I drew a Stochastic Oscillator with python and played with it ♬
I made a server with Python socket and ssl and tried to access it from a browser
I made a library that adds docstring to a Python stub file.
I made a tool to automatically browse multiple sites with Selenium (Python)
A story that I was addicted to when I made SFTP communication with python
I made a LINE BOT with Python and Heroku
I tried to automatically generate a password with Python3
I generated a lot of images like Google Calendar favicon with Python and incorporated it into Vue's project
[Python] I made a script that automatically cuts and pastes files on a local PC to an external SSD.
I want to run a quantum computer with Python
I made a program to convert images into ASCII art with Python and OpenCV
I made a Docker Image that reads RSS and automatically tweets regularly and released it.
I made a web application that maps IT event information with Vue and Flask
I made a calendar that automatically updates the distribution schedule of Vtuber (Google Calendar edition)
I made a package to filter time series with python
[Python] I made a function that decrypts AES encryption just by throwing it with pycrypto.
I made a toolsver that spits out OS, Python, modules and tool versions to Markdown
I made a tool that makes it a little easier to create and install a public key.
I made a program to collect images in tweets that I liked on twitter with Python
A program that failed when trying to create a linebot with reference to "Dialogue system made with python"
When writing to a csv file with python, a story that I made a mistake and did not meet the delivery date
I made a fortune with Python.
I made a daemon with Python
I made a simple circuit with Python (AND, OR, NOR, etc.)
I made a library to easily read config files with Python
I made a package that can compare morphological analyzers with Python
I want to use a wildcard that I want to shell with Python remove
A memorandum when I tried to get it automatically with selenium
I made a Nyanko tweet form with Python, Flask and Heroku
I tried to make a periodical process with Selenium and Python
I made a shuffle that can be reset (reverted) with Python
I made a segment tree with python, so I will introduce it
I made a program that automatically calculates the zodiac with tkinter
I made a chatbot with Tensor2Tensor and this time it worked
[Python] I made a decorator that doesn't seem to have any use.
I tried to make a system to automatically acquire the program guide → register it in the calendar in one day
I made a music bot using discord.py and Google Drive API (tested with Docker → deployed to Heroku)
I made a web application in Python that converts Markdown to HTML
Deploy a Python app on Google App Engine and integrate it with GitHub
I made a Discord bot in Python that translates when it reacts
I tried to discriminate a 6-digit number with a number discrimination application made with python
I made a Hex map with Python
[Outlook] I tried to automatically create a daily report email with Python
I made a tool that makes decompression a little easier with CLI (Python3)
I made a roguelike game with Python
I made a simple blackjack with Python
I tried updating Google Calendar with CSV appointments using Python and Google APIs
I made a configuration file with Python
I made a neuron simulator with Python
I made a module PyNanaco that can charge nanaco credit with python
[Python] I made a web scraping code that automatically acquires the news title and URL of Nikkei Inc.
I tried it with SymPy Live, Wolfram Alpha and google with reference to "Algorithm learned with Python 4th: Prime numbers".
I tried to make a periodical process with CentOS7, Selenium, Python and Chrome
Make a Python program a daemon and run it automatically when the OS starts
I made a Line Bot that uses Python to retrieve unread Gmail emails!
I made a tool to notify Slack of Connpass events and made it Terraform
[ES Lab] I tried to develop a WEB application with Python and Flask ②
I want to write an element to a file with numpy and check it.
[Python] I made a LINE Bot that detects faces and performs mosaic processing.
I made a familiar function that can be used in statistics with Python