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.
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.
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
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.
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 function
weekday ()of
datetime` 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)
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.
credentials.json
.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.
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
dateTimeof
start and ʻend
, respectively, and you're done.
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
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 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.
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.
Building an environment with pyenv and pyenv-virtualenv
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
Recommended Posts