Plannerに定期的にタスクを作りたいというリクエストがあったので、方法を考えてみました。
今回は作ってみる編。
最終的なフロー
作ろうとしている流れは次のような感じです。
実際作ってみて考慮が足りてなかった部分も足したりしています。
- OneDrive上のExcelファイルの内容を一旦クリアする(Officeスクリプト)
- リストからアイテムを読み取る(PowerAutomate)
- リストの内容をExcelファイルに保存する(PowerAutomate)
- 保存したアイテムの中から本日対象のタスク情報を抽出する(Officeスクリプト)
- 3.の処理結果を読み取る(PowerAutomate)
- PowerAutomateからPlannerにタスクを投入する(PowerAutomate)
事前準備
SharePoint上のリスト
まずSharePointo上にリストを作ります。
項目名 | 種類 | その他オプション |
---|---|---|
タスクルール名 | 1行テキスト | |
日 | 選択肢 | 1〜31 と"月末" |
タスク名 | 1行テキスト | |
担当者 | ユーザまたはグループ | 複数選択を許可 |
あと適当にテストデータもいくつか突っ込んでおきます。
TeamsのPlannerでタスク登録先
Teams上でPlannerのプランを一つ作成しておきます。
プランを作成したチーム名とプランの名前は後で使うので覚えておきましょう。
詳細処理用Excelファイル
Officeスクリプトで処理させるためのExcelファイルを用意します。
- OneDrive上に配置する
- ShaerPointのリストを格納するテーブルを作る(作成する列はSharePointのリストに合わせて4列)
- Officeスクリプトで対象を絞り込んだ結果を書き込むテーブルを作る(タスク名、期限、担当者の3列)
Officeスクリプトの準備
Officeスクリプトを2つ作ります。一つは「テーブルをクリア」、もう一つは「本日該当のタスク抽出」です。
まずはテーブルクリアの方から。
function main(workbook: ExcelScript.Workbook) { // テーブル取得 let tb1 = workbook.getTable("テーブル1"); let tb2 = workbook.getTable("テーブル2"); // オートフィルタークリア tb1.getAutoFilter().clearCriteria(); tb2.getAutoFilter().clearCriteria(); // 行削除 if (tb1.getRowCount() > 0) { tb1.deleteRowsAt(0, tb1.getRowCount()) } if (tb2.getRowCount() > 0) { tb2.deleteRowsAt(0, tb2.getRowCount()) } }
テーブルには名前がつけられます。ここではデフォルトの「テーブル1」「テーブル2」にしてます。
このあとの処理でオートフィルターをかけたりしているので、まず最初の段階でオートフィルターをクリアして、存在する行数分だけ削除してます。
もう一つは本日該当タスクの抽出。
このフローを毎日起動して、その日に該当するタスクだけを処理するので、この処理が必要になります。
function main(workbook: ExcelScript.Workbook) { // 日付取得(本日・翌月・翌月末) let nowdate = new Date(); // フィルター値と次月同日の作成(今日が月末なら月末キーワードも処理する) let cond:string[]; let nextdate:Date; if(isEndOfMonth(nowdate)){ //月末の場合 cond = [String(nowdate.getDate()),"月末"]; nextdate = MakeNextMonthEndDate(nowdate); } else{ //月末以外の場合 cond = [String(nowdate.getDate())]; nextdate = MakeNextMonth(nowdate); } // テーブル1/2の取得 let tb1 = workbook.getTable("テーブル1"); let tb2 = workbook.getTable("テーブル2"); // フィルター取得 let af = tb1.getAutoFilter() // フィルター設定 af.apply(tb1.getRange(), 1, { filterOn: ExcelScript.FilterOn.values, values: cond }); // 行の読み取り let datarows = tb1.getRangeBetweenHeaderAndTotal().getVisibleView().getValues(); //console.log(datarows); // テーブル2への転記 if(datarows[0].length != 0){ //テーブルが空っぽでも要素数が1になるようなので。 let taskdata: string[] = []; for (const item of datarows) { taskdata.push(String(item[2])); taskdata.push(String(nextdate.toISOString())); taskdata.push(String(item[3])); tb2.addRow(-1, taskdata); } } } //指定日付の1ヶ月後を返す function MakeNextMonth(nowdate: Date): Date{ //今月の月数を取得 let nowMonth = nowdate.getMonth(); //翌々月の0日(翌々月の1日の前日)=翌月の月末日をセット //let nextdate = new Date(nowdate.getFullYear(),nowdate.getMonth()+2,0); let nextdate = MakeNextMonthEndDate(nowdate); //もし今月の日が来月の最終日の日より小さければ、今月の日を来月にもセット(翌月の最終日のほうが小さいならキープ) if(nowdate.getDate() < nextdate.getDate()){ nextdate.setDate(nowdate.getDate()); } return nextdate; } //翌月の月末日を返す function MakeNextMonthEndDate(nowdate:Date):Date{ //翌々月の0日(1日の前日)=翌月の末日 let rtn = new Date(nowdate.getFullYear(),nowdate.getMonth()+2,0); return rtn; } //月末日かどうかをチェックする function isEndOfMonth(nowdate:Date):boolean{ //翌月の0日(1日の前日)=今月末日 let eom = new Date(nowdate.getFullYear(),nowdate.getMonth()+1,0); //年と月と日が同じだったらnowdateは月末日 if((nowdate.getFullYear()==eom.getFullYear())&& (nowdate.getMonth()==eom.getMonth())&& (nowdate.getDate()==eom.getDate())){ return true; } else{ return false; } }
テーブルを2つ用意して、今日処理対象になるアイテムだけ抜き出すようなことをしています。
PowerAutomateでフローを作成する
では準備ができたので、フローの作成に入ります。
以下気になったポイント。
リストの担当者情報
リストの担当者の項目は「ユーザまたはグループ」という種類で「複数選択を許可」としています。アイテムの内容にもよるんでしょうけど、複数の人の情報がこの中に入ることになります。
PowerAutomateの画面上で普通に「担当者Email」を使おうとすると、PowerAutomateが勝手に「Apply to each」を自動挿入してしまいます。中身が複数のデータを想定してるのでそうするのでしょうねきっと。
幸いPlannerのタスクを作成するときの担当者の項目は、セミコロン区切りのメールアドレスでいいようです。なので、変数を一個用意して、そこにメールアドレスをセミコロンつなぎで足し込んでいく部分を作って、それをExcelに保存する処理にしました。
Officeスクリプトの実行待ち
PowerAutomateのフローからOfficeスクリプトを起動できますが、どうも実行完了待ちはしてくれない模様。Officeスクリプトの完了を待つ方法も調べてみたのですが結局わからず。しょうがないので、無条件で数十秒のウエイトだけ入れました。
ちゃんと完了を待つ方法が見つかったら処理を差し替える予定。
今後やりたいこと
- できれば全部PowerAutomateで実現したい
- 日付指定をもっと柔軟にしたい
- タスク作成先も選べるようにしたい
今回はここまで。