ISID テックブログ

ISIDが運営する技術ブログ

テックブログの執筆タスクをMicrosoft Power Apps でカレンダー化する

皆さんこんにちは!金融ソリューション事業部 市場系ソリューション1部の寺山です。
かなり温かく、というか暑くなってきましたね。私は、今の時期から冷房に頼ると本場の夏の暑さに耐えられないぞ!という戦略で冷房を使うのを我慢しています。

これまではインフラ/クラウド関連の記事を投稿してきた私ですが、今回は打って変わってローコード関連の内容となります。背景を踏まえてローコードツール:Power Appsを用いて実現した内容をご紹介したいと思います!

背景

私は、昨年度の弊社アドベントカレンダーで記事を投稿した以降に、当テックブログの運営チームに参加しました。
テックブログの運営において、誰がいつ投稿する予定なのか?/投稿の途切れてしまう期間があるか?といった観点で、 執筆状況をカレンダー形式で可視化したいよね という意見が生まれました。 結論として、Microsoft Plannerで執筆タスクを管理することにより上記の目的を達成しようとなりました。理由は以下のとおりです。

  1. Plannerにはカレンダー機能がある。
  2. 機能がシンプルであり、厳密なタスク管理ルールを策定しないことにより執筆者・運営側の双方の負荷にならないよう、執筆管理を運用可能と考えた。

1.でカレンダー形式で可視化するという当初の目的は達成しました。加えて、弊社内でテックブログの活動や執筆ガイドを共有するためのSharePointポータルサイトを構築していたため、そこにカレンダー形式として埋め込むことで、 活動状況を執筆メンバー以外にも共有したいよね! という考えに発展しました。

そこで、プレビュー版ではあるもののWebパーツが提供されているPower Appsを用いてカレンダーを作成し、SharePointに埋め込んでみようと思い立ったのが背景となります。

実際に埋め込んだ PowreAppsは以下のように表示されます!

Power Appsを作成

前述のPower Appsアプリをどのように作成したのかを簡単にご紹介します。
作業はすべてPower AppsのWebコンソールにて行いました。
私はPower Appsについて無知な状態からスタートしたため、Power Appsの各機能やコンポーネント(Power Apps用語ではコントロール)の説明は割愛し、全体的な流れの説明に比重を置かせていただきます。

アプリの作成とコネクタの追加

まずはアプリを作成します。Power Appsのサブメニューより、作成 > 空のキャンバスアプリ > 作成と選択し、アプリ名を設定して空のキャンバスアプリを作成します。
私は利用していませんが、PowreAppsではアプリのテンプレートも提供されています。

形式ですが、SharePointポータルサイトは弊社の社員が業務中にPCから参照するケースの方が多いと考え、タブレットを選択しました。

次はコネクタの追加です。Power Apps で表示/加工するデータは、Office365の他のアプリケーションやサードパーティツール(DBMSやDWHなど)をデータソースとしてコネクタで接続して取得します。
今回利用したコネクタは以下のとおりです。

  • Planner:カレンダーに表示するPlannerタスクやバケットの取得に利用
  • O365Group:タスクの担当者名の取得に利用

コネクタの追加は、サブメニューのデータ > データの追加より行います。データソースに対する認証情報に自身のOffice365アカウントを指定します。

初期化

キャンバスアプリのトップエンティティはスクリーンです。空のキャンバスアプリを作成すると執筆時点のデフォルトではスクリーンのみが作成された状態となります。

私の作成した Power Apps ではスクリーンのOnVisibleプロパティに以下の関数を設定することで、Power Appsの描画時に初期化がされるようにしました。

// 共通して使用する変数の宣言
UpdateContext(
    {
        planId:"<PlannerのPlanID>",
        groupId:"<PlannerのGroupID>", 
        officeGroupId:"<PlannerのGroupID>",
        weekDay:["Sun.","Mon.","Tue.","Wed.","Thu.","Fri.","Sat."]
    }
);
// カレンダーとして表示対象の月の1日と月末の日付を設定
UpdateContext(
    {
        startOfMonth:Date(TextInputYear_1.Text,DropdownMonth_1.Selected.Value,1),
        endOfMonth:Date(TextInputYear_1.Text,DropdownMonth_1.Selected.Value + 1,1)-1
    }
);
// カレンダーに表示する開始日と終了日を計算
UpdateContext(
    {
        fromDate:startOfMonth-(Weekday(startOfMonth,1)-1),
        toDate:endOfMonth+(7-Weekday(endOfMonth,1))
    }
);  
// PlannerとOffice365Groupデータソースから、Plannerタスク一覧、Plannerのバケット一覧、Office365Groupのメンバー一覧を取得してテーブルに格納
UpdateContext(
    {
        tasks:Planner.ListTasksV3(planId,groupId).value,
        buckets:Planner.ListBucketsV3(planId,groupId).value,
        members:O365Group.ListGroupMembers(officeGroupId).value
    }
);
// カレンダーに表示する開始日から終了日までの全日付をテーブルに格納
UpdateContext(
    {
        days:ForAll(Sequence(toDate-fromDate+1),DateAdd(fromDate-1,Value,Days))
    }
)

UpdateContextはスクリーンがスコープとなる変数を宣言/更新する関数です。
以下は各UpdateContextブロックに対する補足です。

  • 1つ目のブロックで変数の値に設定しているPlannerのPlanIDPlannerのGroupIDはPlannerのURLから確認可能です。

  • 2つ目のブロックでは、後述するTextInputYear_1というテキスト入力コントロールの初期値と、DropdownMonth_1というドロップダウンコントロールの初期値から取得しています。

  • 5つ目のブロックのForAllは、第一引数のテーブルに対して第二引数の操作を繰り返す関数です。
    • Sequence関数を使用してカレンダーに表示する開始日から終了日までのインデックスのテーブルを作成して、カレンダーの開始日からインデックスを相対距離として日付を計算することで、カレンダーの開始から終了日までに含まれる全日付のテーブルを作成しています。

Power Apps上での作業は以下のようになります。

カレンダーの描画

カレンダー風に描画するために、2つのギャラリーコントロールを作成しています。
ギャラリーとは、指定したデータに対し、他のコントロールをテンプレートとして展開可能なコンテナ型のコントロールです。

1つ目のギャラリーは、カレンダーの曜日を表示するための水平ギャラリーです。
前のセクションで初期化したweekdayテーブルをギャラリーのItemsプロパティに指定し、ギャラリー内にはテキストラベルコントロールを配置しています。

テキストラベルに表示するTextプロパティで以下の関数を指定することで、曜日の表す列のように描画しています。ThisItemにはギャラリーのItemsプロパティに指定したテーブルの要素が格納されます。

ThisItem.Value

2つ目はカレンダーの各日のボックスを表現する水平ギャラリーです。Itemsプロパティには前のセクションで初期化したdaysテーブルを指定しています。
水平ギャラリーには、まず垂直コンテナを配置し、内部に配置する2つのコントロールの位置関係と描画範囲を調整しています。

コンテナ内に配置する1つ目のコントロールテキストラベルで、以下の関数を設定することでカレンダーの「日付」を表示しています。

Text(ThisItem.Value,"dd")

Plannerのタスクを表示する領域は垂直ギャラリーを使用しました。ギャラリーに表示するデータは、Itemsプロパティに以下の関数を設定し、前のセクションで取得したPlannerタスク一覧から抽出しています。

Sort(Filter(tasks,And(dueDateTime>=ThisItem.Value,dueDateTime<ThisItem.Value+1)),title)

これにより、「各日のボックスを表現する水平ギャラリー」のThisItemに格納されている各日がPlannerの期日に一致するタスクのテーブルが、垂直ギャラリーのデータに設定されます。
垂直ギャラリーのテンプレートに以下のコントロールを配置することでタスクを表示しています。

  • テキストラベル:Plannerタスクのタイトル
ThisItem.title
  • テキストラベルと楕円の図形:Plannerタスクのバケット

    本件では、Planner タスクのステータスではなく、タスクが属するバケットをタスクのステータス情報として採用しました。
    LookUpは、ExcelのVLOOKUPと同じイメージで、第一引数のテーブルから第二引数の条件でデータを抽出する関数です。
    bucketsには前のセクションで初期化したPlannerのバケット一覧が格納されています。

LookUp(buckets,id=ThisItem.bucketId).name
  • テキストラベル:Plannerタスクのオーナー

    membersには前のセクションで初期化したOffice365Groupのグループメンバー一覧が格納されています。

LookUp(members,id=First(ThisItem._assignments).userId).displayName

ポイントとしては、「カレンダーの各日のボックスを表現する水平ギャラリー」のWrapCount(折り返しの数)プロパティに7を指定している点です。

水平方向に7アイテムが表示される毎に折り返されるので、カレンダーのように描画されます。

また、描画範囲を調整するため、上記の2つの水平ギャラリーはコンテナコントロールに配置しています。

表示する年/月のセレクタ

前述までのセクションでカレンダーは表示されます。
初期化の年/月以外のカレンダーを表示可能なよう、セレクタを作成しました。

年はテキスト入力コントロールで利用者に入力してもらう仕様としました。入力された値は、<コントロール名>.Textで参照できます。
初期値として、Defaultプロパティに以下の関数を設定しています。「初期化」セクションでstartOfMonthendOfMonth変数の入力の一方が、この値です。

Text(Today(),"yyyy")

月は、ドロップダウンコントロールから選択する仕様です。プルダウンに表示するリストは、Itemsプロパティに01~12のテーブルとして設定しました。選択された値は、<コントロール名>.Selected.Valueで参照可能です。
初期値として、Defaultプロパティに以下の関数を設定しています。「初期化」セクションでstartOfMonthendOfMonth変数の入力の他方が、この値です。

Text(Today(),"mm")

上記2つのコントロールの入力をカレンダーに反映するボタンコントロールを追加します。
OnSelectプロパティに以下の関数を設定することでカレンダーを更新します。内容は初期化セクションとほぼ同じです。

UpdateContext(
    {
        startOfMonth:Date(TextInputYear_1.Text,DropdownMonth_1.Selected.Value,1),
        endOfMonth:Date(TextInputYear_1.Text,DropdownMonth_1.Selected.Value + 1,1)-1
    }
);
UpdateContext(
    {
        fromDate:startOfMonth-(Weekday(startOfMonth,1)-1),
        toDate:endOfMonth+(7-Weekday(endOfMonth,1))
    }
);
UpdateContext(
    {
        days:ForAll(Sequence(toDate-fromDate+1),DateAdd(fromDate-1,Value,Days))
    }
)

おまけで、利用者の入力をDefaultに戻すリセットボタンも配置しました。

あとはカレンダーらしさが出る修飾

見た目の修飾として以下を行っています。

  • 当日のボックスの日付を強調
  • 土日の背景をグレー表示
  • 年/月セレクタで選択された月以外の日付をグレー表示
    • 例えば、2022年04月が選択された場合、2022/04は4/1~4/30ですが、カレンダー上には3/27~3/31も表示されるため、これらの日付文字の色はグレーになるようにしています。
  • タスクのステータス(Plannerのバケット)毎に表示色を変更
  • 公開済みステータス(Plannerのバケット)の場合はクラッカーの画像を表示

これらは、各コントロールのプロパティにIf関数を使用して実現しています。すべての紹介は割愛しますが、代表として当日日付の強調をご紹介します。
当日日付はテキストラベルコントロールで、Colorプロパティに以下の関数を設定することで、当日日付のテキストラベルの背景色を変更しています。

If(Text(Today(),"yyyy/mm/dd")=Text(ThisItem.Value,"yyyy/mm/dd"),RGBA(20,165,255,1),RGBA(255,255,255,1))

ローコードをはじめてちゃんと触ってみた感想

ご参考までに利用したコントロールの一覧は下図のとおりです。

私はこれまで、Power Appsに限らずローコードツール/プラットフォームというものはほとんど触ったことがありませんでした。
本件で確りと触ってみて、専門的な知識がなくても、それっぽいものを比較的簡単に作れるという便利さが印象的でした。
Microsoft製品だけでも、Power Appsの他にPower Automateなども提供されています。また、弊社のようなベンダーだけでなくエンドユーザー様でも利用しやすい点から、業務効率化やDXの一環として活用できる強力なツールであると実感しました。

最後までご覧いただきありがとうございました。弊社にはローコード開発やお客様の業務効率化/DX推進のご支援も行っている組織もございます。この記事の内容が参考になるか、弊社に興味を持つ機会になれば幸いです。また次の記事でお会いしましょう!

参考文献/記事

執筆:寺山 輝 (@terayama.akira)、レビュー:@sato.taichiShodoで執筆されました