DjangoでGoogle Calendarへアクセス②
こんなことがしたかった
- 定期的にGoogle Calendarに予定を取りに行きDBにデータを格納したかった
厳密にはもう少し複雑ではあるのだが、Djangoで作った予定管理アプリのようなものに予定を追加したときに、Google Calendarにも同期したかったのである。当記事はGoogle Calendar→Djangoの方を書く
GASでイベント時にトリガー追加して、Django側にはAPIを準備してPOSTしてもよかったのだが、今回はDjango側でGoogle Calendarにアクセスしイベントを取りに行くパターンで対応する。
こんな感じ
Djangoのカスタムコマンドを準備し、python manage.py 〜
で実行できるようにし、crontabに登録して定期実行することとした。
カスタムコマンドの準備
どうやら、自分のアプリケーション内にmanagementディレクトリを切り、その中で更にcommandsディレクトリを切り、その中にinit.pyとカスタムコマンド用のスクリプトをおけば良いようだ。
以下のような感じ
Project app management commands __init__.py customcommand.py
customcommand.pyとそれに絡む中身は以下のような感じ。ちなみに細かい要件は以下の通り
- 終日予定は取り込まない
- 一度取り込んだり、Django→Google Calendarで連携した予定は除外
- 実行日時以降の予定を対象
※例によって動かなかったらごめんなさい あと、実際に書いたプログラムを一部改変しているので、余計なimportがあるかもしれない
※settings.pyは先日と全く同じなため省略
- customcommand.py
import datetime import os.path import requests import httplib2 from django.conf import settings from django.core.management.base import BaseCommand from pytz import timezone from app.models import Model from googleapiclient.discovery import build from oauth2client.service_account import ServiceAccountCredentials from google.auth.transport.requests import Request class Command(BaseCommand): help = 'Google Calendar Import Events' # ここが本体 def handle(self, *args, **kwargs): events = self.get_gcal() self.insert_db(events) self.stdout.write(self.style.SUCCESS('Successfully import Google Calendar')) # イベントを取ってくる def get_gcal(self): # 認証情報を作成 credentials = ServiceAccountCredentials.from_json_keyfile_name( settings.GCAL_PRIVATE_KEY_PATH, scopes=settings.GCAL_SCOPES ) http = credentials.authorize(httplib2.Http()) service = build('calendar', 'v3', http=http) # 情報を取得 now = datetime.datetime.utcnow().isoformat() + 'Z' events_result = service.events().list(calendarId=settings.GCAL_CALENDAR_ID, timeMin=now, \ singleEvents=True, orderBy='startTime').execute() events = events_result.get('items', []) return events # DBにデータを入れる def insert_db(self,events): now = datetime.datetime.now() # DB内の情報を取得 obj = Model.objects.filter(enddt__gte=now) for event in events: name = event.get("summary") startdt = event.get("start").get("dateTime") enddt = event.get("end").get("dateTime") gcal_eventid = event.get("id") # すでにDBに登録されている場合は対象外 if obj.filter(gcal_eventid=gcal_eventid).exists(): pass # 終日予定は取り込み対象外 elif startdt is None or enddt is None: pass # 上記以外は取り込み else: Model(name=name,startdt=startdt,enddt=enddt,gcal_eventid=gcal_eventid).save()
- models.py
from django.db import models class Model(models.Model): class Meta: db_table = 'model' app_label = 'model' name = models.CharField( verbose_name = '名前', blank = False, max_length = 64, ) startdt = models.DateTimeField( verbose_name = '開始日', blank = False, ) enddt = models.DateTimeField( verbose_name = '終了日', blank = False, ) gcal_eventid = models.CharField( verbose_name = 'google calendar eventid', blank = True, max_length = 256, null = True, )
これでpython manage.py customcommand
を実行すれば、DBにGoogle Calendarのイベントを取り込める。
ちなみに、実行する際はcrontabで1分ごとに動かしている。
crontabの設定
詳細は省くが、1点のみちょっとだけハマったところが。
当然ではあるが、venv環境だとcrontabで単純にpython manage.py customcommand
を書いても動かない。
上記のほか、cdとactivate、deactivateを使用し、venv上で実行できるように組む必要がある。
実装してみて
今回は単純に取り込む形を書いたが、予定を移動した場合もDB側に反映させる必要があるので、
Update的な処理をバッチ内に入れる必要があるように思う。
また、今回は1分ごとの実行としたが、リアルタイム性を求めるのであれば別の方法を考える必要があるかもしれない。