2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

週報書いている人、今すぐやめて。サボり方教えます。

Last updated at Posted at 2025-03-22

はじめに

業務をやっていると週報を書く必要が出てきますよね。
週報だけならともかく、日報も書く必要がある時ってすごいめんどくさいですよね…

その場合の週報のサボり方を今回紹介します。完全無料でできます。

この記事は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の準備

日報のテンプレートを用意しましたので、このテンプレートを真似してください。

image.png

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月など

こんな感じ
image.png

あと、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です。

終わりに

最後に一言、いい週報レスライフを!!

2
3
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?