見出し画像

保育園の献立表をGoogleカレンダーに自動で追加したい 【完結編】

はじめに


先日こちらの記事をアップした。
今回はこの続きである。

やはり献立表.pdfを直接読み込んでGoogleカレンダーに書き出したい。
pdf→word→excel→csvというのはどう考えても手間だ。

そこでpythonでpdfを読み込めるライブラリを探すことにした

camelotとの出会い

最初「pdfを読み込んでGoogleカレンダーに書き出すプログラムを作りたい」とChatGPTくんに言ったところ勧められたのがPyPDF2だ。
しかし献立表が悪いのかPyPDF2が表を苦手とするのかうまく表を取得できなかった。
そこで前回はとりあえずGoogleカレンダーに追加することを目指してcsvに手元で変換してプログラムを作成した。

それが完成して数日、pythonにはせっかくありとあらゆるライブラリがあるんだからpdfの表を読み込めるライブラリがないはずがないと思い、空き時間にそれについて調べていた。
そうしたらこのサイトにたどり着いた。

なるほどpdfの表に特化したライブラリか。使ってみよう。

Camelotを使ってみる

Camelotすごい

camelotをインストールし、pycharmのpython consoleで実行してみる。

献立表.pdfの2ページ目
import os.path
import camelot
kondate_table = camelot.read_pdf('献立表.pdf', pages='2')
kondate_df = kondate_table[0].df

え、たったこれだけ?
kondate_dfを見てみると

camelot神

完璧に入っている。
「材 料 名」が複数列に渡って入っていたり、「熱と力になるもの」などが入れ子になっていたりなどやや複雑な表だがきちんとDataFrameに格納できた。
あとはこのDataFrameを整形すればやりたかったことができるじゃん。

Camelot神か。
これは仕事でも使えそうだ。

DataFrameの整形

ここからはよくある整形作業。
列名がついていないのでまずは列名をつける

kondate_df.columns = ['日付', '献立', '栄養', 'エネルギー', 'タンパク質', 'ビタミン', '10時おやつ', '3時おやつ', '行事食等']
kondate_df

1, 2 行目が不要なのでこれらを削除する

kondate_df = kondate_df.drop([0, 1])
kondate_df

もうほぼ理想的なDataFrameができた。
あとは前回書いた通り時刻の列を追加して、
余計な改行(\n)を削除する

    kondate_df['年月日'] = year_month + kondate_df['日付'].str.split().str[0]
    kondate_df['start_time'] = kondate_df['年月日'] + ' 12:00'
    kondate_df['start_time'] = pd.to_datetime(kondate_df['start_time'], format='%Y%m%d %H:%M')
    kondate_df['end_time'] = kondate_df['年月日'] + ' 13:00'
    kondate_df['end_time'] = pd.to_datetime(kondate_df['end_time'], format='%Y%m%d %H:%M')

    kondate_df = kondate_df.replace('\n', '', regex=True)       #余計な改行を削除
できた

完成

プログラム全体

from __future__ import print_function
import os.path
import pandas as pd
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
import camelot

SCOPES = ['https://www.googleapis.com/auth/calendar']


def main(df: pd.DataFrame):
    creds = None
    # The file token.json 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.json'):
        creds = Credentials.from_authorized_user_file('token.json', SCOPES)
    # 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.json', 'w') as token:
            token.write(creds.to_json())

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

        # 献立表の内容をGoogleカレンダーに入力
        for index, row in df.iterrows():
            # メニューを作成
            menu = '昼食:\n' + row["献立"]
            menu += '\n\n10時おやつ:\n' + row['10時おやつ']
            menu += '\n\n3時おやつ :\n' + row['3時おやつ']
            menu += '\n\n\n**************************\n' \
                    '食材:\n' + row['エネルギー'] +\
                    '\n\n' + row['タンパク質'] +\
                    '\n\n' + row['ビタミン']

            # 開始時刻と終了時刻を設定
            start_time = row['start_time']
            end_time = row['end_time']

            # イベントの情報を作成
            event = {
                'summary': '今日の献立',  # イベントのタイトル
                'description': menu,  # 献立をイベントの説明に設定
                'start': {
                    'dateTime': start_time.isoformat(),
                    'timeZone': 'Japan',
                },
                'end': {
                    'dateTime': end_time.isoformat(),
                    'timeZone': 'Japan',
                },
                'colorId': '1',  # ラベンダーの色のID
            }

            # イベントをカレンダーに追加
            service.events().insert(calendarId='primary', body=event).execute()

    except HttpError as error:
        print('An error occurred: %s' % error)


def menu_data(year_month: str):
    # 献立表の整形
    kondate_table = camelot.read_pdf('献立表.pdf', pages='2')
    kondate_df = kondate_table[0].df
    kondate_df.columns = ['日付', '献立', '栄養', 'エネルギー', 'タンパク質', 'ビタミン', '10時おやつ', '3時おやつ', '行事食等']
    kondate_df = kondate_df.drop([0, 1])  # 不要な1, 2行目を削除

    kondate_df['年月日'] = year_month + kondate_df['日付'].str.split().str[0]
    kondate_df['start_time'] = kondate_df['年月日'] + ' 12:00'
    kondate_df['start_time'] = pd.to_datetime(kondate_df['start_time'], format='%Y%m%d %H:%M')
    kondate_df['end_time'] = kondate_df['年月日'] + ' 13:00'
    kondate_df['end_time'] = pd.to_datetime(kondate_df['end_time'], format='%Y%m%d %H:%M')

    kondate_df = kondate_df.replace('\n', '', regex=True)       #余計な改行を削除

    return kondate_df


if __name__ == '__main__':
    year_month = '202304'
    df = menu_data(year_month)
    main(df)

前回とは異なり、おやつの列や食材リストの列も取得できたので、それらもGoogleカレンダーに入れるようにした。
保育園では給食で出る食材はすべて事前に自宅で試す必要があるので、食材リストが出せるようになったのは地味に嬉しい。
これもCamelotが表を適切にDataFrame化してくれたおかげだなぁ。

実行結果

実行結果はこの通り。
もちろんきれいに反映できた。

献立をより詳細に記載した


きちんとすべて反映されている

おわりに

今回は前回からのアップデートだったのでそこまで苦労することなく2時間ほどで完成させることができた。
pdfから直接Googleカレンダーに書き出せるようになったので5月以降も献立表をもらったらすぐにGoogleカレンダーに書き出せると思う。
フォーマットが少し変わるくらいならいいが、pdfの献立表がもらえないことにはプログラムを動かしようがないので、来月からもpdfがもらえることをお祈りしておこう。


この記事が気に入ったらサポートをしてみませんか?