はじめに
業務をやっていると週報を書く必要が出てきますよね。
週報だけならともかく、日報も書く必要がある時ってすごいめんどくさいですよね…
その場合の週報のサボり方を今回紹介します。完全無料でできます。
この記事はWindowsの環境を想定しています。
Windows10の環境では動作することを確認しています。
実現方法
Gemini、Python、Excel、Teams、タスクスケジューラ
これらを使ってサボります。
Excel:日報管理に使います。
Gemini:日報をまとめて週報を作ってもらいます。(無料枠があるので)
Python:Excel、Gemini、Teamsの制御に使用します。
タスクスケジューラ:pythonの定期実行に使用します。
Teams:週報の出力先として使用します。
以下が仕様です。
・今日の日付から過去5日の週報をまとめる。
例えば、週報を書くのが水曜日の場合、
土日を除く、水、火、月、先週の金、先週の木を対象とする。(祝日も除く)
・先週の日付が月を跨ぐ場合、それを考慮して日付を取得する。
先月のシートがない場合は、無視する。今月分だけで作成する。
・年を跨ぐ場合は無視する(1月の最初の週のみなので)
・5日中1日しか日報が取得できなかった場合は、作成しない。その旨をTeamsで伝える。
手順
各ツールのインストール&設定をしてください。
Pythonの準備
今更なのでここでは説明しませんが、以下のサイト等を見てインストールしてください。
PythonからGeminiを使うために以下のコマンドを打ってください。
!pip install -q -U google-generativeai
その他以下のライブラリが必要になりますので、インストールしてください。
pip install openpyxl requests pandas jpholiday
Geminiの準備
以下のサイトを見てGemini APIキーを取得してください。
APIキーの取得が完了したら、環境変数にこのAPIキーを登録してください。
環境変数名は、「GOOGLE_API_KEY」で登録してください。
Teamsの準備
TeamsのWorkflowsを使用します。以下の記事からフローを作成してください。
Excelの準備
日報のテンプレートを用意しましたので、このテンプレートを真似してください。
A1セルに”日付”を入力する。
B1セルに”内容”を入力する。内容欄に日々の日報を書いていく感じです。
A2セルには以下の関数を入力してください。
=IF(ROW(A2)-1 > DAY(EOMONTH(DATE(YEAR(TODAY()), MID(CELL("filename", A2), FIND("]", CELL("filename", A2)) + 1, LEN(CELL("filename", A2)) - FIND("]", CELL("filename", A2)) - 1) + 0, 1), 0)), "", DATE(YEAR(TODAY()), MID(CELL("filename", A2), FIND("]", CELL("filename", A2)) + 1, LEN(CELL("filename", A2)) - FIND("]", CELL("filename", A2)) - 1) + 0, ROW(A2)-1))
A3セルに以下の関数を入力し、A32セルまでオートフィルしてください。
=IF(A2<>"", IF(A2+1 <= EOMONTH(A2, 0), A2+1, ""), "")
シート名に該当の月を入力してください。(日報は月ごとの管理になります)
すると、A列に日付が自動的に埋まります。
例) 4月、8月など
あと、Excelファイルの名前ですが、「YYYY_xxx.xlsx」としてください。
”YYYY_”部分でファイルを検索、取得しています。
例)2025_report.xlsx
タスクスケジューラの準備
以下のサイトを見てpythonをタスクに登録してください。
(これはpythonのコードが用意できてからやってください)
これで事前準備は完了です。
ソースコード
import os
import google.generativeai as genai
import glob
import openpyxl
import requests
import datetime
import json
import pandas as pd
import datetime
import jpholiday
## 任意で設定 #########################################################################
# WorkFlowsのHTTPエンドポイント
WORKFLOWS_URL = "各自のURLを入力してください"
# Geminiのバージョン
GEMINI_VERSION = "gemini-1.5-flash"
# Teamsに送信するメッセージのテンプレート
msg = {
"attachments": [
{
"contentType": "application/vnd.microsoft.card.adaptive",
"content": {
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.2",
"body": [
{
"type": "TextBlock",
"text": "",
"size": "Large",
"weight": "Bolder"
},
{
"type": "TextBlock",
"text": "",
"wrap": True,
"markdown": True
}
]
}
}
]
}
## 編集不要の設定 #####################################################################
# Google API Keyの設定
GOOGLE_API_KEY = os.getenv('GOOGLE_API_KEY')
genai.configure(api_key=GOOGLE_API_KEY)
## 関数定義 #########################################################################
def glob_files(year):
# 指定した年のExcelファイルを取得する
files = glob.glob(f"{year}_*.xlsx")
return files
def get_last_5_business_days(file_path, today):
business_days = []
current_date = today
all_filtered_data = []
while len(business_days) < 5:
# 年をまたいだ場合は終了(前年に突入した場合)
if current_date.year < today.year:
print("前年に突入したため、処理を中止します")
break
# 土日・祝日を除外
if current_date.weekday() < 5 and not jpholiday.is_holiday(current_date):
business_days.append(current_date)
# シート名を決定(現在の月)
sheet_name = f"{current_date.month}月"
try:
# Excelの該当シートを読み込む
df = pd.read_excel(file_path, sheet_name=sheet_name)
df['日付'] = pd.to_datetime(df['日付'], errors='coerce').dt.date
# 該当する日付のデータを取得
filtered_df = df[df['日付'] == current_date]
if not filtered_df.empty:
all_filtered_data.append(filtered_df)
except ValueError:
# シートが存在しない場合はスキップ
continue
current_date -= datetime.timedelta(days=1) # 1日戻る
# 全データを統合
if all_filtered_data:
return pd.concat(all_filtered_data)
else:
return pd.DataFrame() # データがなかった場合は空の DataFrame を返す
def send_message(url, msg):
# POSTリクエストを送信
response = requests.post(
url=url,
data=json.dumps(msg),
headers={"Content-Type": "application/json"}
)
# レスポンスを確認
if response.status_code == 200:
print("メッセージを送信しました")
elif response.status_code == 202:
print("メッセージの送信を受け付けました")
else:
print(f"エラーが発生しました: {response.status_code}, {response.text}")
return response.status_code
def read_excel(file_path):
wb = openpyxl.load_workbook(file_path)
data = {}
for sheet in wb.sheetnames:
ws = wb[sheet]
sheet_data = []
for row in ws.iter_rows(values_only=True):
sheet_data.append(row)
data[sheet] = sheet_data
return data
def call_gemini(filtered_df):
# 過去5営業日のデータを文字列に変換し、Geminiに要約を依頼する
prompt = (
"以下のExcelデータを箇条書きで要約してください。\n"
"【注意点】\n"
"・箇条書きは各項目の内容ごとではなく、同じような内容のものをまとめて書く\n"
":\n\n"
)
# DataFrameからプロンプトを作成
for _, row in filtered_df.iterrows():
prompt += f"{row['内容']}\n" # 内容欄のデータを追加
# Geminiに送信
model = genai.GenerativeModel(GEMINI_VERSION)
response = model.generate_content(prompt)
return response.text
if __name__ == "__main__":
# 今日の日付を取得
today = datetime.date.today()
# 読み込むExcelファイルを今日の年から取得
file_path = glob_files(today.year)[0]
# 過去5営業日を取得
filtered_df = get_last_5_business_days(file_path, today)
nan_count = filtered_df.isnull().sum(axis=1).sum()
# 過去5営業日のデータが4つ以上欠損している場合は週報を作成しない
if nan_count >= 4:
summary = "データが不足しているため、週報を作成できませんでした。"
else:
summary = call_gemini(filtered_df)
title = datetime.date.today().strftime('%Y/%m/%d') + "の週報"
msg["attachments"][0]["content"]["body"][0]["text"] = title
msg["attachments"][0]["content"]["body"][1]["text"] = summary
# Teamsにメッセージを送信
status = send_message(WORKFLOWS_URL, msg)
基本的にはこれをコピペして使用すればOKですが、Geminiに対しての指示は各自で修正したほうがいいかなと思います。載せてるのはあくまで参考程度にぐらいです。
call_gemini()関数のprompt変数の内容を修正すればOKです。
終わりに
最後に一言、いい週報レスライフを!!