目標:Googleカレンダー⇔CSVファイル⇔別アプリのようにカレンダーと他アプリを連携すること。
以下の続編。
Pythonを使ってCSVの予定をGoogleカレンダーに載っけてみた
https://qiita.com/Octpascal/items/07e53bd89dfbca93bf3e
CSVのファイルに記述した予定でGoogleカレンダーを更新するプログラムを作りました。
現状、CSV⇒Googleカレンダーの一方向です。
CSVファイルは前回と変わらず、以下の記述
id,cid,summary,location,description,start,end
- id : CSV内で識別用ID(str)
- cid : カレンダーに登録したときにGoogleカレンダー上で決定されるID(str)
- summary : イベント名(str)
- location : 開催場所(str)
- description : 詳細(str)
- start : 開始時間(str 例:2020/08/22 09:00 もしくは 2020/08/22)
- end : 終了時間(str 開始時刻と同じ記述。ただし、揃えなければならない)
Googleカレンダー操作用コード
from __future__ import print_function
import datetime
import pickle
import os.path
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']
CID = 'Calendar ID'
def check_token():
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.
if os.path.exists('token.pickle'):
with open('token.pickle', '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.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
service = build('calendar', 'v3', credentials=creds)
return service
def insert(event):
service = check_token()
# Call the Calendar API
create_event = service.events().insert(calendarId=CID, body=event).execute()
return create_event['id']
def get_lists():
service = check_token()
page_token = None
events = []
while True:
events_list = service.events().list(calendarId=CID, pageToken=page_token).execute()
page_token = events_list.get('nextPageToken')
events.extend(events_list['items'])
if not page_token:
break
return events
def update(eid, event):
service = check_token()
updated_event = service.events().update(calendarId=CID, eventId=eid, body=event).execute()
return (updated_event['updated'])
def delete(eid):
service = check_token()
service.events().delete(calendarId=CID, eventId=eid).execute()
CIDには自身のカレンダーIDを入れてください。
check_token
GoogleカレンダーAPIのサンプルコードから。APIの認証用。
insert
イベント登録。辞書型を受け取り投げる。
get_list
既に登録されているイベントの取得。一度に取得できるリスト数に制限があるが、nextPageTokenを送ることで続きのリストを取得できる。
update, delete
そのままの用途
CSVをAPI用に変換するプログラム
import pandas as pd
import datetime
import gg_calendar
import sys
TIMEZONE = 'Japan'
def TransTime(t):
if len(t) > 11:
strtime = datetime.datetime.strptime(t, '%Y/%m/%d %H:%M').isoformat()
dictime = {
'dateTime': strtime,
'timeZone': TIMEZONE
}
else :
strtime = datetime.datetime.strptime(t, '%Y/%m/%d').isoformat()
dictime = {
'date': strtime[0:10],
'timeZone': TIMEZONE
}
return dictime
def get_list():
lists = gg_calendar.get_lists()
new_lists = []
for event in lists :
new_lists.append(
{
'id': event['id'],
'summary': event['summary'],
'location': event['location'],
'description': event['description'],
'start': event['start'],
'end': event['end']
}
)
return new_lists
def search_id(dics, skey, svalue):
df = pd.DataFrame(dics)
return (df[skey] == svalue).any()
csv = pd.read_csv('csv.csv')
csv_d = csv.to_dict(orient='index')
ex_lists = get_list()
# delete
for ex_list in ex_lists:
if not search_id(csv, 'cid', ex_list['id']):
print("delete from calendar id={}".format(ex_list['id']))
gg_calendar.delete(ex_list['id'])
# update, insert
for h, num in zip(csv_d.values(), csv_d.keys()):
h['start'] = TransTime(h['start'])
h['end'] = TransTime(h['end'])
if (search_id(ex_lists, 'id', h['cid'])):
del h['id']
cid = h['cid']
del h['cid']
print('updata')
gg_calendar.update(cid, h)
else:
del h['id']
del h['cid']
try:
event_id = gg_calendar.insert(h)
except:
print("Error: Can't put id={} on your calendar".format(csv.loc[num, 'id']))
else:
print("Put id={} on your calendar as calendar_id={}".format(csv.loc[num, 'id'], event_id))
csv.loc[num, 'cid'] = event_id
csv.to_csv('csv.csv',index=False)
TransTime
上記CSVの書式をGoogleカレンダーAPIの書式へ変換。詳細は前回記事参照。
get_lsit
リストを取得し必要な情報だけの辞書型にする。
search_id
辞書に値があるかどうか判定する
ある辞書型(dics)の辞書キー(skey)にある値(svalue)があるかどうか判定する。
辞書型をpandas DataFrameに変換する。
df[skey] == svalue
でBooleanのpandas Seriesが返ってくる。(例:True, False, False, False)
any()関数でひとつでもTrueがあればTrueとなることで判定できる。
最初は辞書型のままfor文を回していたが、多分これが一番簡単に記述できます。
delete
CSVから消えたものはカレンダーから消します。
update, insert
既にカレンダーに登録されているものは更新。ないものは登録し、返ってきたイベントIDをCSVに追記します。
現状バグ
- カレンダーにひとつも登録がないとエラーが発生する。
- カレンダー内の登録がget_list関数で作成する辞書型の十分条件。例えば、カレンダーにlocationのないイベントがあるとエラーが発生する。⇒現状、id以外利用してないので取得しないことにすると解決する。
改善すべき点
- 既存の登録されているカレンダーをすべてupdateに投げている。⇒終了時刻が一定以上前のデータは更新しないなど対策する。
- カレンダーの既存イベントを全取得している、⇒ こちらもある程度期限を決めて取得することにする。
さいごに
次回は上記点を改善しアップデートしていきたい。