見出し画像

請求書と業務報告書のPDFを自動生成するようにした

どこの誰が考えたか知らないが、「業務報告書」と「請求書」を定期的に、日付だけ変えて送らないと死ぬ人(or会社)があるらしく、吾輩は追い先短い貴重な時間を配達もサボってそんな間の抜けた非生産的なことのために使い、しかも毎月間違うのであった。間違うとさらにメールのやり取りが発生して大変無駄である。

例えば「7月分」と書かなければならないところを書き換え忘れて「2月分」のまま、作業した時間を一覧で書いて送るのだが、同じ日付を書く場所が3箇所もある。バカなのか。なぜ三回も書く必要があるのだ。大事なことだから三回コピペするのか?まあExcelで参照するようにしたらそこは一発になったのだが。

これを読む人は、このフォーマットを考えた人は、いったい何を考えていたのだ。

最初はExcelで管理していたのだが、微妙に面倒なのが「作業した時間」について書かなくてはならないところで、これはGoogle Calenderを遡って自分のスケジュールを振り返り、「あーこの日話をしたなー」という漠然とした記憶を辿る。この時間が無駄である。

さらに請求書も同時に発行しなければならず、これも毎回、金額同じ作業内容同じ金額なので、日付を変えるだけである。そしてなんとここにも作業した月を書き換えなくてはならない。なんだこの仕事は。

吾輩の灰色の脳細胞はこの非生産的な作業に音を立ててヒビが入っていくのである。

今や世は大AI時代。それなのになんだこの間抜けな仕事は。ChatGPTだってもう少しマシな業務に従事しているはずだぞ。

人間が読んでなんらかの法的な証憑として残したいだけのどうでもいい紙(のようなデータ)を毎月せっせと送らなくては仕事が回らないとは。
昔ならこういうのは秘書がやってくれていたが今の吾輩には秘書を雇うみたいな贅沢は許されない。明日をもしれないUberEats配達員なのである。

那智暴虐の請求書と業務報告書、およびその送付作業。全く馬鹿げている。
あと半年の契約だが、これを自動化せねばなるまい。なぜなら面倒だから。

面倒なことを自動化しようと思うと、急にやる気が出てくるというのがコンピュータ屋の多少厄介なところなのだ。多分手作業で直せば10分で済んだはずだが、もう止まらない吾輩のハッカー魂、つまり結果よりプロセスを重視するという性質によって、Google Calenderを読み込んで先月のクライアントとの応談時間を割り出し、自動的に業務報告書の日付を書き換えてPDF化するところまで作った。

ちなみにこのページを参考にした

Google Apps系は独自の専門用語がビュンビュン飛び交っている野蛮な世界なのでこういうガイドがないと死ぬ。ChatGPTにも活躍してもらった。

PDF化はreportlabというモジュールを使った。便利。

極めて雑なソースコードは以下

import datetime, re
import googleapiclient.discovery
import google.auth


# ①Google APIの準備をする
SCOPES = ['https://www.googleapis.com/auth/calendar']
calendar_id = '自分のGoogleアカウント'
# Googleの認証情報をファイルから読み込む
gapi_creds = google.auth.load_credentials_from_file('自分で作ったクレデンシャル.json', SCOPES)[0]
# APIと対話するためのResourceオブジェクトを構築する
service = googleapiclient.discovery.build('calendar', 'v3', credentials=gapi_creds)


# 今日の日付を取得
today = datetime.date.today()

# 1ヶ月前の日付を得る(今日の日付から1を引いて1ヶ月前の月を取得) ChatGPTが考えた
one_month_ago = today.replace(year=today.year - (today.month == 1), month=today.month - 1 if today.month != 1 else 12)

# 1ヶ月前の1日の日付を得る ChatGPTが考えた
first_day_of_last_month = one_month_ago.replace(day=1).isoformat()+'T00:00:00.00000Z'
first_day_of_this_month = today.replace(day=1)
first_day_of_this_month = first_day_of_this_month.replace(day=1).isoformat()+'T00:00:00.00000Z'


print(first_day_of_last_month)
print(first_day_of_this_month)
print(now)

event_list = service.events().list(
     calendarId=calendar_id, timeMin=first_day_of_last_month,timeMax=first_day_of_this_month,
     maxResults=300, singleEvents=True,
     orderBy='startTime').execute()


events = event_list.get('items', [])
formatted_events = [(event['start'].get('dateTime', event['start'].get('date')), # start time or day
     event['end'].get('dateTime', event['end'].get('date')), # end time or day
     event['summary']) for event in events]

for event in formatted_events:
     if re.match(r'^\d{4}-\d{2}-\d{2}$', event[0]):
         start_date = '{0:%Y-%m-%d}'.format(datetime.datetime.strptime(event[1], '%Y-%m-%d'))
         response += '{0} All Day\n{1}\n\n'.format(start_date, event[2])
     # For all day events
     else:
         start_time = '{0:%Y-%m-%d}'.format(datetime.datetime.strptime(event[0], '%Y-%m-%dT%H:%M:%S+09:00'))
         end_time = '{0:%H:%M}'.format(datetime.datetime.strptime(event[1], '%Y-%m-%dT%H:%M:%S+09:00'))
         response += '{0} ~ {1}\n{2}\n\n'.format(start_time, end_time, event[2])
         if "クライアント名" in event[2]: #ここを自分のクライアントの名前に変える
             print(start_time)
             break

#本当はこんなにimportする必要ないが面倒なのでコピペした
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4, portrait
import os

from reportlab.pdfgen import canvas
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.cidfonts import UnicodeCIDFont
from reportlab.lib.pagesizes import A4, portrait #用紙の向き
from reportlab.platypus import Table, TableStyle
from reportlab.lib.units import mm
from reportlab.lib import colors
from reportlab.platypus.frames import Frame



from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.platypus import SimpleDocTemplate, Paragraph

# Notoフォントで十分だろ
pdfmetrics.registerFont(TTFont("Noto", "Noto_Sans_JP/static/NotoSansJP-Regular.ttf"))
pdfmetrics.registerFont(TTFont("NotoBold", "Noto_Sans_JP/static/NotoSansJP-ExtraBold.ttf"))
page_width, page_height = A4
print(A4)

page  = canvas.Canvas("sxrep.pdf", pagesize=portrait(A4))

# もともと請求書の画像をpngとして保存しておき、適当な座標に文字を埋め込めるようにする
page.drawImage("rep.png", 0, 0, width=page_width, height=page_height)
page.drawString(530,800,str(today))
print(today) 
#先月の数字
page.drawString(255,750,str(one_month_ago.month))
page.drawString(92,670,str(start_time))
page.drawString(430,670,str(start_time).replace("2023",""))
page.drawString(500,670,str(start_time))

# PDFファイルとして保存
page.save()

#自動的にメールするようにしたいが危険も感じるのでとりあえずここまで

なんということでしょう。
アホみたいな仕事をする時間がなんと生産的な時間に
元々Google Calendarに連動するサムシングが作ってみたかった(他にもあるのよ)ので丁度いい肩慣らしになった。

作業時間1時間。ということはあと半年で10分ずつ使うはずの時間を6回分使い切ったのでプラマイゼロに近いが、非生産的なことに10分x6回かけるよりは、新しいスキル(この場合Google Calandarとの連動)を身につけた1時間は遥かに価値が高い。こんなことを考えるのも根っからの貧乏性がなせる技だろう。

ハマったのはGoogleの連携の設定。特に会社用アカウントとの繋ぎこみが面倒だった(個人用なら簡単)。

みんなもくだらないことはPythonにやらせよう。

ってかなんで紙しかないんだよ。