めもめも

このブログに記載の内容は個人の見解であり、必ずしも所属組織の立場、戦略、意見を代表するものではありません。

Google login が必要なアプリのプロトタイプを Notebook で実装する方法

事前準備

アクセス対象アプリの API を有効化

developers.google.com

OAuth 同意画面を設定

developers.google.com

OAuth クライアント ID 認証情報を構成
  • 「ウェブ アプリケーション」クライアントを作成して、「承認済みのリダイレクト URI」に https://www.example.com を設定
  • Client secret の JSON を credentials.json としてダウンロード

developers.google.com

Notebook での実装

Vertex AI Workbench の環境を想定して記述

  • credentials.json をカレントディレクトリにアップロード
  • パッケージをインストールして Kernel を再起動
%pip install --upgrade --user \
    google-api-python-client \
    google-auth-httplib2 \
    google-auth-oauthlib
import IPython
app = IPython.Application.instance()
_ = app.kernel.do_shutdown(True)
  • Google login を擬似的に実行する関数
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
from urllib.parse import urlparse, parse_qs

def google_login(scopes):
    flow = InstalledAppFlow.from_client_secrets_file(
        'credentials.json', scopes, redirect_uri='https://www.example.com',
    )
    auth_url, _ = flow.authorization_url(prompt='consent')
    print('Please open the following URL in your browser and authorize the application:')
    print(auth_url)
    url = input('Enter the URL of the redirected page here: ')
    parsed_url = urlparse(url)
    code = parse_qs(parsed_url.query)['code'][0]
    flow.fetch_token(code=code)
    creds = flow.credentials
    with open('token.json', 'w') as token:
        token.write(creds.to_json())
    print('Access token has been stored in "token.json".')
  • 実行例
SCOPES = ['https://www.googleapis.com/auth/calendar']
google_login(SCOPES)

[出力]

Please open the following URL in your browser and authorize the application:
https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=xxxxx
Enter the URL of the redirected page here:  

表示された URL を開いて認証すると、https://www.example.com にリダイレクトされるので、「リダイレクト先の URL」をコピペで入力すると、アクセストークン token.json がカレントディレクトリに保存される。

  • Google Calendar の API を使用するコード例
import os, datetime

def get_upcoming_events():
    assert(os.path.exists('token.json'))
    creds = Credentials.from_authorized_user_file('token.json', SCOPES)
    if not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
            with open('token.json', 'w') as token:
                token.write(creds.to_json())
    try:
        service = build('calendar', 'v3', credentials=creds)
        now = datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m-%dT%H:%M:%S%z')
        now = now[:-2] + ':' + now[-2:] # Format to include colon in timezone offset
        events_result = (
            service.events()
            .list(
                calendarId='primary',
                timeMin=now,
                maxResults=10,
                singleEvents=True,
                orderBy='startTime',
            )
            .execute()
        )
        events = events_result.get("items", [])
        result = []
        for event in events:
            start = event['start'].get('dateTime', event['start'].get('date'))
            end = event['end'].get('dateTime', event['start'].get('date'))
            if 'attendees' in event.keys():
                attendees = [item.get('email') for item in event['attendees']]
            else:
                attendees = []
            result.append({
                'start': start,
                'end': end,
                'attendees': attendees,
                'event': event['summary']
            })
        return {'upcoming_events': result}

    except HttpError as error:
        return {'error': error}