電気ひつじ牧場

技術メモ

大学から借りた図書の返却期限が迫ってきたらLINEに通知が来るやつ作った

作ったもの

f:id:cha-shu00:20180507135042p:plain




こんなの。画像ではテストの為に返却期限がその日から20日以内の書籍を全て通知していますが、現在は期限が3日後に迫っているものを通知するようにしています。

きっかけ

私は図書館を利用する為に大学に通ってるのですが、たまに借りた本の返却期限を忘れて本当にごめんなさいという気持ちになるので、そうならない為に作成しました。

処理の流れ

1)Pythonのrequestsを使って大学図書館が運営しているLibrary Web Serviceにログインする。
2)現在借りている書籍とその返却期限がテーブルとして表示されるので、beautifulsoupで該当要素を取得する
3)返却期限が迫っている書籍があればタイトルと期限をLINE APIにPOSTする。
4)macのlaunchdを用いて上記1〜3を毎日定刻に実行する。

ソースコード

import datetime
from enum import Enum, auto
import sys

import requests
import requests.exceptions
import keyring as kr
from bs4 import BeautifulSoup


class LoanPeriodState(Enum):
    """借りた本の期限が、まだ先/もうすぐ/過ぎてる のうちどの状態かを表す"""
    AWAY = auto()
    SOON = auto()
    OVERDUE = auto()


def decide_loan_period_state(ret_date, _reminder_days=3):
    ret_date_formatted = datetime.datetime.strptime(ret_date, "%Y.%m.%d").date()
    now = datetime.date.today()
    delta = (ret_date_formatted - now).days
    if delta < 0:
        return LoanPeriodState.OVERDUE
    if 0 <= delta <= _reminder_days:
        return LoanPeriodState.SOON
    return LoanPeriodState.AWAY


def download_my_page(_session):
    elms_id = kr.get_password('elmsid', 'elmsid')
    passwd = kr.get_password('elms', elms_id)
    payload = {'PSTKBN': '2',
                'LOGIN_USERID': elms_id,
                'LOGIN_PASS': passwd, }
    url = 'https://opac.lib.hokudai.ac.jp/opac-service/srv_odr_stat.php'
    response = _session.post(url, data=payload)
    response.raise_for_status()
    return BeautifulSoup(response.text, 'html.parser')


def get_formatted_data(_book_data):
    title = ''.join(_book_data.find_all('td')[6].string.split('/')[:-1])
    ret_date = _book_data.find_all('td')[4].string
    return title, ret_date


def send_notify(message, *args):
    payload = {"message": message.format(*args)}
    auth_url = "https://notify-api.line.me/api/notify"
    headers = {'Content-Type': 'application/x-www-form-urlencoded',
               'Authorization': 'Bearer ' + kr.get_password('line_token', 'books'),
               }
    requests.post(auth_url, headers=headers, data=payload)


def main():
    with requests.Session() as session:
        soup = download_my_page(session)
        try:
            for book_data in soup.find_all('table')[3].find_all('tr')[1:]:
                title, ret_date = get_formatted_data(book_data)

                if len(sys.argv) == 2:
                    state = decide_loan_period_state(ret_date, _reminder_days=int(sys.argv[1]))
                else:
                    state = decide_loan_period_state(ret_date)

                if state == LoanPeriodState.AWAY:
                    return
                elif state == LoanPeriodState.OVERDUE:
                    message = "**書籍の返却期限が過ぎています!**。\n{}\n返却期限: {}"
                elif state == LoanPeriodState.SOON:
                    message = "書籍の返却期限が迫っています。\n{}\n返却期限: {}"

                send_notify(message, title, ret_date)

        except IndexError:
            """何も借りていない時はここを通る"""
            pass

        except requests.exceptions.HTTPError as error:
            message = "データを取得できませんでした。\n{}"
            send_notify(message, error.__traceback__)

if __name__ == '__main__':
    main()

launchdについて

https://qiita.com/rsahara/items/7d37a4cb6c73329d4683#fnref1qiita.com
上の記事の方が詳しく説明してくださっているので、それを元に上記スクリプトを毎日0時0分に定期実行するようなエージェントを作成します。
定刻にMacがスリープ状態ならスリープから復帰した際に直ちに実行されます。電源が切ってあると実行はされず、次回の実行を待ちます。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
        <string>hokudai_library</string>
    <key>Program</key>
        <string>/Path/to/the/exec/script</string>
    <key>StartCalendarInterval</key>
	<dict>
            <key>Hour</key>
	        <integer>0</integer>
	    <key>Minute</key>
	        <integer>0</integer>
	</dict>
    <key>StandardErrorPath</key>
        <string>/Path/to/the/stderr</string>
</dict>
</plist>

おわりに

作りがシンプルなので割とすぐに完成しました。今回初めて使ったkeyring(https://pypi.org/project/keyring/)というライブラリが便利で、Macのキーチェーンからパスワードなどの情報を取得できます。テストコードを初めて書こうとして適当に終わってしまったのがやや残念ですが練習のためこれから追加していくかもしれません。