Teamsが仕事の司令塔に！次世代タスク管理術(2)：Power Automateの実装手順

https://qiita.com/EasyCording/items/5c5474c6c1e0af2bc259


# 個人用タスク管理チャットボット: 個人の「ナビゲーター」兼「タイムキーパー」


### Power Automateの実装手順

ユーザーの要望に基づき、前日に未完了のタスクと当日の新しいタスクを1枚のアダプティブカードにまとめて表示し、タイマーループで開始、応答待ちで進行、タイマーで終了するタスク管理ボットの実装手順を以下に詳しく説明します。この手順は、Power AutomateとExcel Online Scriptsを活用し、朝8:00に開始、夜18:00に終了する設計を前提としています。

---

## 実装手順

### 1. **事前準備**
#### Excelの設定
1. **Excelファイルの作成**  
   - Microsoft 365で新しいExcelファイルを作成し、OneDriveやSharePointに保存（例: `TaskManagement.xlsx`）。
2. **シートの準備**  
   - シート名: `Tasks`  
   - テーブル名: `TaskTable`  
   - 列: `TaskId`, `TaskName`, `Status`, `DueDate`, `WorkTime`, `StartTime`  
   - サンプルデータ:  
     ```
     TaskId | TaskName      | Status  | DueDate  | WorkTime | StartTime
     123    | API連携       | 進行中  | 4/3      | 1:23     | 2025-04-01T08:47:00Z
     124    | テストとデバッグ | 未着手 | 4/4      | 0:00     | 
     ```
3. **Excel Scriptの作成**  
   - Excelの「自動化」タブから「新しいスクリプト」を作成。  
   - スクリプト名: `GetTasksForCard`  
   - コード:  
     ```typescript
     function main(workbook: ExcelScript.Workbook) {
       const sheet = workbook.getWorksheet("Tasks");
       const table = sheet.getTable("TaskTable");
       const rows = table.getRangeBetweenHeaderAndTotal().getValues();
       
       const incompleteTasks = [];
       const todayTasks = [];
       
       rows.forEach(row => {
         const task = {
           taskId: row[0],
           taskName: row[1],
           status: row[2],
           dueDate: row[3],
           workTime: row[4]
         };
         if (task.status !== "完了") {
           if (task.workTime && task.workTime !== "0:00") {
             incompleteTasks.push(task); // 未完了で作業済み
           } else {
             todayTasks.push(task); // 新しいタスク
           }
         }
       });
       
       return JSON.stringify({ incompleteTasks, todayTasks });
     }
     ```
   - スクリプトを保存。

#### Teamsの設定
- Microsoft Teamsに「Workflows」アプリがインストールされていることを確認（Power Automateのフローボットが動作するために必要）。

---

### 2. **Power Automateフロー（メイン）の作成**
#### フロー名: `DailyTaskManager`
1. **Power Automateにアクセス**  
   - [Power Automate](https://make.powerautomate.com/)にログイン。
   - 「作成」→「スケジュールされたクラウドフロー」を選択。

2. **トリガーの設定**  
   - **トリガー**: 「定期実行」（Recurrence）  
   - **間隔**: 1日ごと  
   - **開始時刻**: 毎日8:00（ユーザーのタイムゾーンを設定、例: JSTならUTC+9で23:00）  
   - 保存。

3. **アクション1: Excel Scriptの実行**  
   - **アクション**: 「Excel Online (Business)」→「スクリプトの実行」  
   - **場所**: OneDriveまたはSharePoint  
   - **ドキュメントライブラリ**: ファイルの保存先  
   - **ファイル**: `TaskManagement.xlsx`  
   - **スクリプト**: `GetTasksForCard`  
   - 保存。

4. **アクション2: JSONのパース**  
   - **アクション**: 「データの操作」→「JSONのパース」  
   - **コンテンツ**: `Result`（Excel Scriptの出力）  
   - **スキーマ**:  
     ```json
     {
       "type": "object",
       "properties": {
         "incompleteTasks": {
           "type": "array",
           "items": {
             "type": "object",
             "properties": {
               "taskId": { "type": "string" },
               "taskName": { "type": "string" },
               "status": { "type": "string" },
               "dueDate": { "type": "string" },
               "workTime": { "type": "string" }
             }
           }
         },
         "todayTasks": {
           "type": "array",
           "items": {
             "type": "object",
             "properties": {
               "taskId": { "type": "string" },
               "taskName": { "type": "string" },
               "status": { "type": "string" },
               "dueDate": { "type": "string" },
               "workTime": { "type": "string" }
             }
           }
         }
       }
     }
     ```
   - 保存。

5. **アクション3: アダプティブカードの投稿**  
   - **アクション**: 「Microsoft Teams」→「アダプティブカードを投稿して応答を待機する」  
   - **投稿先**: チャット（ユーザー選択、例: 自分のメールアドレス）  
   - **アダプティブカード**: 動的生成用のJSON  
     ```json
     {
       "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
       "version": "1.0",
       "type": "AdaptiveCard",
       "body": [
         {
           "type": "TextBlock",
           "text": "おはようございます、田沢さん！\n今日は@{formatDateTime(utcNow(), 'yyyy年MM月dd日 (ddd)')}です。現在の時刻は@{formatDateTime(utcNow(), 'HH:mm')}です。",
           "wrap": true
         },
         {
           "type": "TextBlock",
           "text": "【昨日の未完了タスク】",
           "weight": "bolder",
           "size": "medium"
         },
         {
           "type": "Container",
           "items": @{body('Parse_JSON')?['incompleteTasks']?['length'] > 0 ? array(body('Parse_JSON')?['incompleteTasks']?['map']('item', format('{"type": "TextBlock", "text": "▢ @{item.taskName}\n作業済: @{item.workTime} | 締切: @{item.dueDate}", "wrap": true}, {"type": "ActionSet", "actions": [{"type": "Action.Submit", "title": "再開", "data": {"action": "resume_task", "taskId": "@{item.taskId}", "taskName": "@{item.taskName}"}}]}'))) : [{"type": "TextBlock", "text": "なし"}]}
         },
         {
           "type": "TextBlock",
           "text": "【今日のタスク】",
           "weight": "bolder",
           "size": "medium"
         },
         {
           "type": "Container",
           "items": @{body('Parse_JSON')?['todayTasks']?['length'] > 0 ? array(body('Parse_JSON')?['todayTasks']?['map']('item', format('{"type": "TextBlock", "text": "▢ @{item.taskName}\n予定時間: 2時間 | 締切: @{item.dueDate}", "wrap": true}, {"type": "ActionSet", "actions": [{"type": "Action.Submit", "title": "開始", "data": {"action": "start_task", "taskId": "@{item.taskId}", "taskName": "@{item.taskName}"}}]}'))) : [{"type": "TextBlock", "text": "なし"}]}
         }
       ],
       "actions": [
         {
           "type": "Action.Submit",
           "title": "全て終了",
           "data": { "action": "endDay" }
         }
       ]
     }
     ```
   - 保存。

6. **アクション4: 条件分岐**  
   - **アクション**: 「制御」→「条件」  
   - **条件**: `Action`（応答データ）が "endDay" と等しい  
   - **Yesの場合**: フローを終了  
   - **Noの場合**: 次のステップへ。

7. **アクション5: Excel Scriptの実行（ロジック処理）**  
   - **アクション**: 「Excel Online (Business)」→「スクリプトの実行」  
   - **スクリプト**: `ProcessTaskAction`  
   - **パラメータ**: `actionData`（応答データのJSON）  
   - コード:  
     ```typescript
     function main(workbook: ExcelScript.Workbook, actionData: string) {
       const data = JSON.parse(actionData);
       const sheet = workbook.getWorksheet("Tasks");
       const table = sheet.getTable("TaskTable");
       const rows = table.getRangeBetweenHeaderAndTotal();
       for (let i = 0; i < rows.getRowCount(); i++) {
         const row = rows.getRow(i);
         if (row.getCell(0).getValue() === data.taskId) {
           if (data.action === "start_task" || data.action === "resume_task") {
             row.getCell(2).setValue("進行中");
             row.getCell(5).setValue(new Date().toISOString());
           }
           break;
         }
       }
       return JSON.stringify({ message: `${data.taskName} を開始しました` });
     }
     ```
   - 保存。

8. **アクション6: カードの更新**  
   - アクション3に戻り、更新されたタスク情報を反映したカードを再投稿（ループ）。

---

### 3. **Power Automateフロー（終了）の作成**
#### フロー名: `EndDaySummary`
1. **トリガーの設定**  
   - **トリガー**: 「定期実行」  
   - **間隔**: 1日ごと  
   - **開始時刻**: 毎日18:00（JSTならUTC+9で9:00）  

2. **アクション1: Excel Scriptの実行**  
   - **スクリプト**: `GenerateSummary`  
   - コード:  
     ```typescript
     function main(workbook: ExcelScript.Workbook) {
       const sheet = workbook.getWorksheet("Tasks");
       const table = sheet.getTable("TaskTable");
       const rows = table.getRangeBetweenHeaderAndTotal().getValues();
       let completed = [];
       let remaining = [];
       rows.forEach(row => {
         if (row[2] === "完了") {
           completed.push({ taskName: row[1], workTime: row[4] });
         } else {
           remaining.push({ taskName: row[1] });
         }
       });
       return JSON.stringify({ completed, remaining });
     }
     ```

3. **アクション2: サマリーカードの投稿**  
   - **アクション**: 「Microsoft Teams」→「アダプティブカードを投稿する」  
   - **アダプティブカード**:  
     ```json
     {
       "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
       "version": "1.0",
       "type": "AdaptiveCard",
       "body": [
         {
           "type": "TextBlock",
           "text": "本日の作業サマリー\nお疲れ様でした！",
           "weight": "bolder"
         },
         {
           "type": "TextBlock",
           "text": "■ 完了したタスク\n@{join(body('Parse_JSON_2')?['completed']?['map']('item', format('✓ @{item.taskName}: @{item.workTime}')), '\n')}",
           "wrap": true
         },
         {
           "type": "TextBlock",
           "text": "■ 残タスク\n@{join(body('Parse_JSON_2')?['remaining']?['map']('item', format('▢ @{item.taskName}')), '\n')}",
           "wrap": true
         }
       ]
     }
     ```

---

### 4. **テストと確認**
1. **フローのテスト**  
   - 「DailyTaskManager」を手動で実行し、Teamsにカードが表示されるか確認。
   - ボタンを押してタスク開始、Excelの更新を確認。
   - 「EndDaySummary」を手動実行し、サマリーが表示されるか確認。

2. **調整**  
   - タイムゾーンやタスクデータのフォーマットに問題があれば修正。

---

### 結論
この手順で、Power Automateが朝8:00にタスクカードを投稿し、ユーザーの応答を待機してタスクを更新、夜18:00にサマリーで終了するボットを実装できます。タイマーループは1日の中で自然に進行し、明確な終了条件を設けることでエンドレスを回避します。試してみてください！