2022年7月8日ころからTeams会議の参加者レポートCSVの処理フローでエラー発生

PowerAutomateを活用している方はしばしばMicrosoft 365（Office 365）を利用していて、、するとTeamsもおそらく利用されていて、、、Teams会議の参加者レポートCSVをワークフローを使って処理していることもまたしばしばあると思います。
少なくとも私はそうでした。同類の方はたしかにいらっしゃるようで、[こんなワークフローを考案された猛者](https://tomriha.com/how-to-process-teams-attendance-report-with-power-automate/)も。。

それはそれとして、今回はその参加者レポートCSVに関するトラブルシューティングです。

# Problem
だいたい2022年7月8日ころから、私の運用しているワークフローのうちTeams会議の参加者レポートCSVを処理するワークフローでエラーが発生するようになりました。

原因を調べてみると、参加者レポートCSVフォーマットが変更。
正確な日時はわかっていませんが、CSVファイル（空行で分かたれた複数セクションからなるTSVコンテンツを含む）のレイアウトがガラッと変わりました。

```
1. 要約
出席済み参加者	10
開始時刻	22/7/11 18:29:28
終了時刻	22/7/11 20:03:11
会議の長さ	1h 33m 43s
出席時間の平均	1h 28m 18s

2. 参加者
名前	初めての参加	最終退出	会議の長さ	メール	参加者 ID (UPN)	役割
パワー 太郎	22/7/11 18:29:32	22/7/11 20:03:11	1h 33m 39s	taro.power@example.co.jp	taro.power@example.com	開催者
オート 花子	22/7/11 18:30:36	22/7/11 20:01:16	1h 30m 40s	hanako.auto@example.co.jp	hanako.auto@example.com	発表者

3. 会議中のアクティビティ
名前	参加時刻	退出時刻	期間	メール	役割
オート 花子	22/7/11 18:29:32	22/7/11 20:03:11	1h 33m 39s	hanako.auto@example.co.jp	開催者
パワー 太郎	22/7/11 18:30:36	22/7/11 20:01:16	1h 30m 40s	taro.power@example.co.jp	発表者
```

このため従来のフォーマットを処理していたワークフローやそこから呼び出していたOffice Scriptsスクリプトはエラーになるか、エラーにならないまでも正しく処理をできなくなっている可能性があります。

# Solution

原因が原因なのでワークフローやスクリプトを修正するしかありません。

前述のエラーが発生したワークフローでは、以下のようにExcel Online（Business）の「スクリプトの実行」アクションからCSVファイル内容を処理するスクリプトをキックしていました：

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/4e507a76-988f-90a1-2b37-cff2b76ed4f3.png)

今回のフォーマット変更に伴い、次のようなスクリプトに変更し、必要なデータを抽出するようにしました：

```ts
function main(
  workbook: ExcelScript.Workbook,
  tsvContent: string
) {
  const crlf = new RegExp('\r|\n');
  const treeParts = tsvContent.split(crlf).map(l => l.split('\t'))
    .reduce((accumulate, element) => {
      if (element.length === 0 || element[0].length === 0) {
        accumulate.push([] as string[][]);
      } else {
        accumulate[accumulate.length - 1].push(element);
      }
      return accumulate;
    }, [[]] as string[][][]);
    
  const summaryLines = treeParts[0];
  const attendeesLines = treeParts[1];
  const activitiesLines = treeParts[2];
  const datePrefix = (date: string) => date.startsWith('20') ? date : '20' + date;
  return {
    start: datePrefix(summaryLines.filter(cells => cells[0] === '開始時刻')[0][1]),
    end: datePrefix(summaryLines.filter(cells => cells[0] === '終了時刻')[0][1]),
    attendees: attendeesLines.slice(2).map(cells => {
      return {
        displayName: cells[0],
        join: datePrefix(cells[1]),
        leave: datePrefix(cells[2]),
        mail: cells[4],
        userPrincipalName: cells[5],
        role: cells[6],
      };
    }),
  };
}
```

このスクリプトの結果のスキーマはこんな感じ：

```json
{
    "type": "object",
    "properties": {
        "result": {
            "type": "object",
            "properties": {
                "start": {
                    "type": "string"
                },
                "end": {
                    "type": "string"
                },
                "attendees": {
                    "type": "array",
                    "items": {
                        "type": "object",
                        "properties": {
                            "displayName": {
                                "type": "string"
                            },
                            "join": {
                                "type": "string"
                            },
                            "leave": {
                                "type": "string"
                            },
                            "mail": {
                                "type": "string"
                            },
                            "userPrincipalName": {
                                "type": "string"
                            },
                            "role": {
                                "type": "string"
                            }
                        },
                        "required": [
                            "displayName",
                            "join",
                            "leave",
                            "mail",
                            "userPrincipalName",
                            "role"
                        ]
                    }
                }
            }
        },
        "logs": {
            "type": "array"
        }
    }
}
```

なお、従来はCSVファイル内にTeams会議のタイトル（Subject）も含まれていたのですが、フォーマット変更によりこれがなくなりました。
このため、タイトルが必要な場合は、CSVファイル名に埋め込まれたTeams会議タイトル（ただしファイル名として許容されない一部の文字が `"_"` などに置換されてしまっている）を拾うか、それ以外の方法を考えざるを得ません。。