こんにちは、あんこ先生です。
プロジェクト管理といえばガントチャートは切り離せませんよね。
なぜならプランごとのタスクを視覚的に網羅でき進捗管理が容易だからです。
しかし、Plannerにはそのような機能はありません。
厳密にはPlannerのタスクをガントチャートに展開するMicrosoftProjectというサービスが用意されています。
ただ、M365のライセンスとは別料金($10/月~)なので利用するには経営陣に稟議を通す必要があります。
そこで、ないものは作ればいいの精神で、今回はPowerAppsでガントチャートを作成します。
以前紹介したPlannerと連動したタスク管理アプリに加えるとさらに便利なアプリになりそうですね。
より高性能なアプリを手軽に導入されたい方へ。
完成版の販売も行っています。
Contents
Plannerと連動したガントチャートを作成する方法
ガントチャートとは
ガントチャートとは、プロジェクト管理や生産管理などの作業計画を視覚的に表現するために用いられる棒グラフの一種です。
縦にタスクが網羅され、それぞれ横棒によって進捗状況が可視化されます。
やるべきことが一括管理できる便利なツールなのですが、Plannerにはその機能がありません。
そこで、Plannerからタスク情報を読み取り、PowerAppsでガントチャートに展開することにします。
Plannerとの連携
プロジェクト管理やタスク管理はPlannerの領分です。
Webアプリとしてある程度の機能は備わっていますが、いまいち使い勝手がよくありません。
そこで、必要な情報をPowerAppsに渡し、融通の利くアプリ内で運用していきたいと思います。
今回作成するアプリの機能としては、タスクの表示とブラウザ経由で詳細画面を開くことができます。
また、Plannerのタスク情報に対して、完了と期限の延長、新規追加が行えます。
進捗確認を目的としたアプリのため、タスクの削除、再割り当ては行えません。
これらの機能追加自体は可能です。
グラフ描画の仕組み
単体のギャラリーで表示位置が変動する横棒グラフの表現は難しいです。
そこでギャラリーの入れ子を使って対応します。
まず親となる縦型ギャラリーに、タスク情報を割り当てます。
これで、タスクの数だけレコードが作成されます。
次に子となる横型ギャラリーに、1か月分のカレンダー情報を割り当てます。
その際、親のタスク情報から活動期間などを判定し格納していきます。
あとは子のテンプレートにタスク開始日から期限日まで図形を表示させることで横棒グラフを描画します。
1タスクごとに1か月分の子ギャラリーを入れ子にするため、計算回数が多く処理は重めとなります。
Plannerの情報をコレクションに格納する
都度、Plannerからデータを読み込むと操作性が低下します。
今回は進捗確認を目的(参照用)としているため、タスク情報などを一度コレクションに格納していきます。
Planner側のデータを更新する際は、併せてコレクションも更新することで処理時間や再読み込み回数を抑制します。
利便性や保守性を高めるため、上図のようなコレクションに格納するためのメニューも作成した方が便利です。
グループ情報│Office365グループコネクタ
ListGroupMembersを使って、グループ単位のユーザー情報をコレクションに格納します。
この情報に基づいてタスク作成時の割当者を選択します。
今回は機能として取り入れていませんが、担当者別のフィルターにも利用できます。
プラン情報│Plannerコネクタ
ListMyPlansV2を使って、アプリ使用者が権限をもつプラン情報一覧を取得します。
これはコレクションに格納せず、バケットやタスクの一覧を取得するためのプランID取得に利用します。
バケット情報│Plannerコネクタ
ListBucketsV3を使って、プランに含まれるバケット情報をコレクションに格納します。
この情報はバケット名表示に利用します。
任意で表示順を変更したい場合は、SortByColumns関数で並び替えておきます。
今回は機能として取り入れていませんが、バケット別のフィルターにも利用できます。
タスク情報│Plannerコネクタ
ListTasksV3を使って、プランに含まれるタスク情報をコレクションに格納します。
なお、抽出できるタスクの量は、最新400件までという制限があります。
例えば選択したプランに1000件のタスクがあっても、コレクションに格納できる量は最新400件までなので注意が必要です。
つまり、完了していないタスクより後に400件以上のタスクが登録された場合、それはコレクションに格納されません。
並びはタスクを作った順のため、コレクション格納時にSort関数で期限昇順にしておきます。
下記コードは、ギャラリーで各情報を選択する場合の一例です。
個別指定する場合は、GalleryMy….Selected.idの部分に直接idを記述してください。
//Plannerからコレクションへ
Clear(_AllMembers);Clear(_AllTasks);Clear(_AllBuckets);
//選択したグループに含まれるメンバー名一覧
Collect(_AllMembers,Office365グループ.ListGroupMembers(GalleryMyGroup.Selected.id).value);
//選択したプラン名
Set(_title,GalleryMyPlan.Selected.title);
//選択したプランに含まれるバケット名一覧
Collect(_AllBuckets,Planner.ListBucketsV3(GalleryMyPlan.Selected.id,"").value);
//選択したプランに含まれるタスク名一覧
Collect(_AllTasks,Sort(Planner.ListTasksV3(GalleryMyPlan.Selected.id,"").value,dueDateTime,Ascending));
Navigate(ScreenGC, ScreenTransition.Fade)
ガントチャートアプリを作成する│上段
カレンダー部分
横方向にスクロールさせると頻繁に読み込みを行うため、月単位での固定表示にします。
おおまかな作り方は、別記事で紹介しています。
日は選択する必要がないので、Today関数で本日分をピックアップ表示させておきます。
年、または月を選択すると変数_selDaysにその年月の1日が代入されます。
その値を基に、1か月分のカレンダーを作成しています。
なお、初期値はスクリーンのOnVisibleで設定します。
//カレンダー部ギャラリー初期値割当
Select(Gallery年,3);Select(Gallery月,Month(Today()));
//当月月初の代入
Set(_selDays,Date(Gallery年.Selected.Value,Gallery月.Selected.Value,1));
//タスク作成コンテナ表示用
Set(_new,false)
タイトル部分①
ここはプラン名、タスクの処理状況、フィルター条件(期日到来完了分を含むか)があります。
プラン名は数式で求めるか、あらかじめ変数に格納しておくとかんたんです。
処理状況は完了件数/総件数です。
Plannerコネクタの制限により、分母は400件までとなります。
なお、分子の完了タスク数はcompletedDateTimeではなく、percentCompleteを使って集計しています。
進捗率が0以外はカウントされる点に注意してください。
フィルター条件はチェックボックスをひとつ設置するだけです。
判定はギャラリー側で行うため、コードは不要です。
//プラン名、変数に落とし込んでいます。
_title
//処理状況・分子
CountRows(Filter(Gallery親.AllItems,percentComplete))
//処理状況・分母
CountRows(_AllTasks)
//タスク作成、フォームの初期化とコンテナの表示。
Reset(Dropdown新バケット);Reset(TextInput新タスク名);
Reset(DatePicker新開始);Reset(DatePicker新期限);Reset(Gallery作成);
Set(_new,!_new)
タイトル部分②
上段右側に、更新用のアイコンを設置しています。
押下することで、最新のタスク情報に更新します。
アプリ内での変更はコレクションも同時に行っているため、あまり押す機会はありません。
ただ、ブラウザや他アプリ経由での変更は反映されないため当機能を盛り込んでいます。
ガントチャートアプリを作成する│下段
ガントチャート部分の作成です。
データソースはタスク情報のコレクションを指定します。
期限が本日以降または完了していないタスクを表示します。
後述する全データ表示判定用のチェックボックスで、データソースを切り替えます。
また、期限修正時に並びが変わると読み込みがはじまるため、ソートはいれていません。
あらかじめコレクション作成時に並び替えておきましょう。
If(Checkbox表示.Value,
_AllTasks,
Filter(_AllTasks,DateValue(dueDateTime)>=Today()),!percentComplete=!Checkbox表示.Value)
)
タスク表示部分
各コントロールにデータソースの中身を設定していきます。
開始や期限は時間まで持っているので、DateValue関数で日付に変換しておきます。
//バケット名
LookUp(_AllBuckets,id=ThisItem.bucketId,name)
//開始
DateValue(ThisItem.startDateTime)
//期限
DateValue(ThisItem.dueDateTime)
//タスク名
ThisItem.title
代表割当者は参照する_assignmentsがテーブルであるため、入れ子のギャラリーを使います。
複数名表示させるスペースはないので、First関数を用いて最初の1名のみとしています。
//データソース
First(ThisItem._assignments)
//画像
If(!IsBlank(ThisItem.userId) && Office365ユーザー.UserPhotoMetadata(ThisItem.userId).HasPhoto,Office365ユーザー.UserPhotoV2(ThisItem.userId))
//名前
LookUp(_AllMembers,id=ThisItem.userId,surname)
タスク処理部分
タスク詳細リンクと完了用チェックボックスを設置しています。
//詳細、サイト部分は修正してください。
Launch("https://tasks.office.com/サイトURL/ja-JP/Home/Task/" & ThisItem.id)
//完了、Plannerと同時にコレクションも更新
Planner.UpdateTaskV2(ThisItem.id,{percentComplete:"Completed"});
Patch(_AllTasks,ThisItem,{percentComplete:Self.Value})
//解除
Planner.UpdateTaskV2(ThisItem.id,{percentComplete:"Not Started"});
Patch(_AllTasks,ThisItem,{percentComplete:0});
ガントチャート部分
入れ子のギャラリーです。
項目の判定条件や仕組みは、冒頭のグラフ描画で説明したとおりです。
データソースは次を記述します。
//1か月分のカレンダー作成
AddColumns(
ForAll(Sequence(DateDiff(_selDays,DateAdd(_selDays,1,TimeUnit.Months)),0,1),
DateAdd(_selDays,ThisRecord.Value,TimeUnit.Days)
) As Date,
//日付とタスク情報から判定
"開始",Date.Value=If(IsBlank(DateValue(ThisItem.startDateTime)),DateValue(ThisItem.dueDateTime),DateValue(ThisItem.startDateTime)),
"期限",Date.Value=DateValue(ThisItem.dueDateTime),
"完了",Date.Value=DateValue(ThisItem.completedDateTime),
"期間",Date.Value<=DateValue(ThisItem.dueDateTime) && Date.Value>=If(IsBlank(DateValue(ThisItem.startDateTime)),DateValue(ThisItem.dueDateTime),DateValue(ThisItem.startDateTime))
)
その後、データソースの値に応じて、該当日に各種図形を表示させます。
表示方法は図形設置後、Visibleプロパティにブール型のThisItem.期限などと記述するだけです。
- 開始 左上に小さい四角を表示します。1か所のみです。
- 期限 右下に小さい四角を表示します。1か所のみです。
- 期間 開始=<期限の間、四角を表示し続けます。
- 完了 サンプル入れていませんが、赤い四角を表示します。1か所のみです。
- 本日線 紫の四角を表示します。1か所のみです。
判定時の注意点として、タスク情報をそのまま指定すると時間情報まで持ってきてしまいます。
たとえば、期限日以下かどうか判定させる場合、時間の分だけ大きいと評価され意図した結果が得られません。
そこで、DateValue関数で囲み、日付データに変換します。
これにより、下図の通り意図した結果が得られます。
期限の延長
選択したタスクのみ、期限を延長できるようにします。
期限日に時計アイコンを表示させ、その上に透明な日付の選択を重ねます。
アイコンを押したように見せかけて、実際は日付の選択が押されているわけです。
あとはOnChangeで選択した日付に期限を修正するだけです。
一応、開始日以前に変更することを考慮し、開始>期限の場合は開始日も置き換えます。
If(Gallery親.Selected.startDateTime>Self.SelectedDate,
Planner.UpdateTaskV2(Gallery親.Selected.id,{startDateTime:Self.SelectedDate,dueDateTime:Self.SelectedDate});
Patch(_AllTasks,Gallery親.Selected,{startDateTime:Self.SelectedDate,dueDateTime:Self.SelectedDate}),
Planner.UpdateTaskV2(Gallery親.Selected.id,{dueDateTime:Self.SelectedDate});
Patch(_AllTasks,Gallery親.Selected,{dueDateTime:Self.SelectedDate})
)
タスクの作成
タスクの作成機能です。
+アイコンを押すことでコンテナを表示させます。
前回入力情報を保持するので、+アイコン押下時にリセットさせています。
コレクションに担当者を書き込む方法が煩雑だったので、タスク作成時のみコレクションそのものを再作成させます。
すでにコレクション更新用アイコンを設置しているため、Select関数で遠隔操作しています。
タスク作成時のコードは次の通りです。
Planner.CreateTaskV4("",First(_AllTasks).planId,TextInput新タスク名.Text,
{
bucketId:Dropdown新バケット.Selected.id,
startDateTime:DatePicker新開始.SelectedDate,
dueDateTime:DatePicker新期限.SelectedDate,
assignments:Gallery作成.Selected.id
}
);
Notify("登録しました。");
Select(Icon更新);
Set(_new,!_new)
細かい表示部分
使い勝手をよくするために、選択または完了したら枠を太くしたり背景色を変えるようにします。
今回は入れ子やらアイコンやら載せまくっているのでレイヤーが複雑です。
そのため、背景色と枠は別に設置します。
まず、背景の設定は親ギャラリーのTemplateFillで条件判定させます。
If(ThisItem.IsSelected,RGBA(253, 222, 207, 0.5),ThisItem.percentComplete,RGBA(192,192,192, 0.25),RGBA(0, 0, 0, 0))
次に枠を太くするのですが最前面に配置するため、上下2本分けて作らないと他のコントロールが押せなくなります。
ガントチャートアプリのまとめ
今回はPlannerと連動したガントチャートを作成する方法を紹介しました。
一応公式な有料サービスも用意されていますが、機能を縮小すればPowerAppsでもなんとかなります。
ガントチャートはプロジェクト管理はもちろん、チーム運営にも欠かせないツールなので、活用いただければ幸いです。
記事を取得できませんでした。記事IDをご確認ください。
コメントを残す