この記事はSchoo Advent Calendar2025の4日目の記事です
こんにちは。
Schooでエンジニアはエンジニアでも、放送技術的な意味でのエンジニアをやっています。@yktkと申します。
Schooアドベントカレンダーを見返すと2016年から手をつけていなかったらしく、このたび全社的にお声もかかったのをきっかけに一枠をいただきました。
この10年ほどでノーコードツールというものが色々と出てまいりまして、非エンジニアでも細々とした作業をツールを作成して便利に進めることができるようになってまいりました。
今回はGoogleのノーコードツール、AppSheetを活用して、スタジオ予約システムを内製した話をします。
この記事でわかること
- AppSheetを使ったGoogle Calendarの予約、削除の方法
- なんらかの事情で日時を直接入力しなかった場合に必要なDateTimeの値を出すまでの手順
背景
大まかな前提
弊社では2つのスタジオを運営しており、主に平日の20時台、21時台に生放送で授業コンテンツを自社サービスに配信しています。
一本の生放送に対しては放送開始前に演者込みのリハーサルなどを実施する時間として1h程度、案件終了後の撤収時間として30分未満の時間を必要としています。
これに加えて演者なしのテクリハやセットアップの時間も必要です。
一般的なスタジオだと
- スタジオ管理者とディレクターは何時から何時まで、という拘束時間だけ決めておく
- 中の運用はディレクターとスタッフや出演者の相談で決めておき、スタジオ管理者は特に入りと出の時間以外は感知しない
というパターンが多いかと思うんですが、弊社の場合は
- 放送技術的なユニットがスタジオ管理者としてスタジオリソースを管理している
- ディレクターは必ずしも映像や放送のバックグラウンドがあるわけではなく、企画と技術は一定の連携を必要とする
という事情があります。
このような中でディレクターがスタジオを予約した場合、実施内容に対してどの程度のリハーサルが必要であるか、技術的にクリアすべき事柄がどの程度あるか、ディレクターだけで見積もることが難しい場合があります。
そのため、スタジオを管理する側も予約時点でスタジオの予約と同時にある程度の稼働状況や利用実態を把握できるようにして、ディレクターの希望が予約した時間と場所のリソースで実現可能かをイメージできることが望ましいです。
- 放送するスタッフとしては予約に応じて1案件に対してそれぞれどのくらいの時間を使うか、個別で把握したい
- ディレクターとしては予約時にはとにかく放送する時間を抑えることに集中したいし、そもそもリハや撤収時間を個別の予定として予約するなどは現実的でない
という相反する希望が出てきます。
これらを解決するために予約用のツールを用意していたのですが、
運用を続けるうちに挙動が不安定な箇所や、運用上の課題が出てきたので新規に作ることにしました。
やりたいこと
- ディレクターの生配信予約をなるべく楽で確実にやってもらいたい
- カレンダー予約を行う、もしくはそれに近い範囲での使用感を目指したい
- 入力必須な内容はやむを得ないとしても、計算で済むものは計算させたい
- その上で、リハーサルや撤収時間をちゃんと設定されていてほしい
- 収録においては収録コンテンツの実尺やリテイク予定を予約するのはナンセンスなので、スタジオの利用開始と終了だけ確保したい
- これらの予約枠がそれぞれどういう目的(生放送形式/収録形式/その他利用)か、予定からわかる形にしたい
出来上がったもの
入力画面
できること
- カレンダーの予約
- 生放送案件/収録案件/その他スタジオ利用の区分をクリック一つで可能(タイトルなどに反映)
- 開始日時の設定
- 終了日時の設定(開始日時入力後にデフォルト値を開始の1時間後に設定)
- (生放送予定のみ)自動で暫定のリハーサル時間と撤収時間を設定(分数設定)
- (生放送予定のみ)上記時間に合わせて生放送予定の時間に隣接する形で予約を同時に実行
- カレンダーの削除
- ツールから入力した予定であれば削除可能
- 生放送予定の場合は放送予定を削除すれば前後もまとめて削除する
現段階の作業で用意できていない機能
- レギュラー放送などの繰り返し予定(今後実装予定)
- 予定の変更
現段階のAppSheetの仕様などで解決の見通しが立たないと判断していること
- カレンダー表示からクリックイベントで予約を確保すること
- 予定の作成者をアプリのオーナー以外に設定する方法
実際の作り方
- 必要なデータの準備
- 予約画面などUIの作成
- UXの設定
- 予約用ボタンの作成
- カレンダーへの予約機能
- 予定の削除機能
必要なデータの準備
スプレッドシートでの作業
アプリによる予約を管理するために使うシートを作成します。
適当なスプレッドシートに下記の入力項目を1行目にカラムの名前だけ書いてください。
この時設定したシート名やカラム名はこの後AppSheetで設定するときにも使う名前になります。
まずは適当なスプレッドシートを作成し、
- 予約管理
- ユーザーマスタ
- フォームボタン呼び出し用
という感じの名前でシートを3つ作ります。
作成できたら、下記のテーブルにある「項目名」のテキストを1行目に順番に埋めてください。
事後編集もできますが、編集した後にここのカラム名を参照している箇所全てを変更する必要があります。
丁寧に検討しておいてください。
今回作成に必要な、スプレッドシート内の全シートの項目名と初期値や目的を表にしてあります。
現段階の作業としては、項目名の箇所だけを1行目に順番に入れていただければ問題ありません。
実例:
予約管理テーブルの各項目
| 項目名 | 初期値 | 目的(その項目が果たす役割) |
|---|---|---|
| ID | UNIQUEID() |
予約データ一行ごとを識別するための、システム内部用のユニークなID。 |
| 予約種別 | なし | 予約の種類(生放送/収録/その他)を管理する。ボットが処理を分岐するために使用する。 |
| 案件名 | なし | ユーザーが入力する予約のタイトル。カレンダーの予定タイトルにも使用される。 |
| 開始日時 | (自動計算) |
入力用_開始日と入力用_開始時刻から自動計算される、予約本体の正式な開始日時。 |
| 終了日時 | (自動計算) |
入力用_終了日と入力用_終了時刻から自動計算される、予約本体の正式な終了日時。 |
| 会議室 | なし | 予約する部屋(StudioA/StudioB)を管理する。ボットが書き込み先カレンダーを判断するために使用する。 |
| 主催者 | なし |
ユーザーマスタテーブルを参照する、予約の主催者ID。 |
| 担当者 | なし |
ユーザーマスタテーブルを参照する、予約の担当者ID。カレンダーのタイトル生成に使用される。 |
| 準備時間(分) | 60 |
生放送の時に入力する準備時間(分数)。ボットが準備予約の開始時刻を計算するために使用する。 |
| 撤収時間(分) | 15 |
生放送の時に入力する撤収時間(分数)。ボットが撤収予約の終了時刻を計算するために使用する。 |
| カレンダー同期ステータス | なし | (将来的な拡張用)カレンダーへの書き込みが成功したか否かを記録するための項目。 |
| 関連ID | なし | (将来的な拡張用)複数の予約をグループ化する際などに使用できる予備のID項目。 |
| 入力用_開始日 | なし | ユーザーがフォームで入力する「日付」。(開始日時の計算元) |
| 入力用_開始時刻 | なし | ユーザーがフォームで入力する「時刻」。(開始日時の計算元) |
| 入力用_終了日 | DATE( ([入力用_開始日] + [入力用_開始時刻]) + "001:00:00" ) |
ユーザーがフォームで入力する「終了日」。開始日時に基づき、日付またぎを考慮した1時間後の日付が自動入力される。 |
| 入力用_終了時刻 | [入力用_開始時刻] + "001:00:00" |
ユーザーがフォームで入力する「終了時刻」。開始時刻の1時間後の時刻が自動入力される。 |
| ステータス | "予約済み" |
予約の現在の状態を管理する(例:予約済み、削除済みなど)。 |
| 準備開始時刻 | (ボットが設定) | 生放送の時、ボットが計算した準備開始の最終的な日時を記録する。([開始日時] - [準備時間(分)]の計算結果) |
| 撤収完了時刻 | (ボットが設定) | 生放送の時、ボットが計算した撤収完了の最終的な日時を記録する。([終了日時] + [撤収時間(分)]の計算結果) |
ユーザーマスタテーブルの各項目
| 項目名 | 初期値 | 目的(その項目が果たす役割) |
|---|---|---|
| ID | UNIQUEID() |
ユーザーを識別するための、システム内部用のユニークなID。スプレッドシート側に手動追加した場合は任意の値を埋めるなどしてください。 |
| 姓 | なし | ユーザーの姓。担当者列の計算元として使用される。 |
| 名 | なし | ユーザーの名。担当者列の計算元として使用される。 |
| オプション | なし | 同じ姓(部署内に田中が二人いるなど)用に設けてある、識別用に使えるテキスト欄(例:「田中た」、と「田中さ」に分けるなど) |
| メールアドレス | なし | ユーザーのメールアドレス、予約時に招待する先としても使う |
| 担当者 | (自動計算) |
[姓]と[名]を結合した、アプリ内で使用するユーザーのフルネーム(表示名)。 |
| 予約時表示サンプル | (自動計算) | カレンダーに登録されるタイトルがどのように表示されるかを、ユーザーが確認するためのプレビュー項目。 |
| _RowNumber | (AppSheetが自動設定) | AppSheetが内部的に使用する行番号。 |
| Related 予約管理s | (AppSheetが自動設定) |
予約管理テーブルからこのユーザー(主催者)を参照している予約の一覧を保持する仮想列。 |
| Related 予約管理s By 担当者 | (AppSheetが自動設定) |
予約管理テーブルからこのユーザー(担当者)を参照している予約の一覧を保持する仮想列。 |
フォームボタン呼び出し用シートの各項目
この内容だけでは意味が分かりにくいですが、
実際のアプリでの操作で重要な機能を置くための準備になります。
使い方は後述しますので、ひとまず下記の通り作成ください。
| 項目名 | 初期値 |
|---|---|
| key | 空白用 |
| (何かしらのテキスト) |
※このシートだけ、例外的に2行目であるA2セルにだけ何かしらのデータを入れ、B2セルを空白にしてください
AppSheet側でのテーブル追加
スプレッドシートの追加
シートができていたら、
https://www.appsheet.com/home/apps
からアプリを作成し始めます。
「Create」から「App」→「Start with existing data」を選びます。
アプリの名前を入力し、「Property Management」あたりを選択して作成します。
ただし、この後の作業でほぼ1から作るのでここでのカテゴリはそこまでクリティカルな項目ではありません。
Data sourceからGoogle Sheetsを選択して手前で作成したスプレッドシートを選択してください。
※URLなどの直指定でなかなか出てこない場合があるので、ディレクトリなどから特定してもらうと確実に指定できる印象があります。
各テーブルの追加
選択できた場合、適当な先頭のシートが読み込まれた状態でAppの編集画面が立ち上がります。
DataタブからNew Tableを選択してスプレッドシートを選択すると、
さらに任意のシートを読み込んでくることが可能です。
必要なシートは追加して、不必要なシートが読み込まれていた場合は削除するなどしてください
この時、原則「Update,Add,Delete」などはすべて可能な状態で読み込んでおいてください。
事後的に問題のない範囲を再編集するのは構いませんが、これらの設定でRead Onlyなどの設定をかけていた場合、予約や削除など、目的の作業ができなくなる可能性があります。
カレンダーの追加
AppSheetにおいてはカレンダーもDataとして追加します。
- 予約を行う場合は新しい行を追加する。
- 予約を削除する場合は行を指定して削除を行う
という操作で予定の追加や削除を行います。
そんなわけで、DataタブからNew Tableを選択して今度はGoogle Calendarを選択してください。
オーナーアカウントで予定の管理が可能なリソース全てが表示されるはずなので、目的のカレンダーを選んでください。
目当てのリソースカレンダーを拾えない場合、一旦作業を取りやめてGoogleカレンダー側から管理の権限などを確認するようにしてください。
なお、カレンダーにおいてもスプレッドシートと同じく編集権限を与える必要がありますので、追加の際にご注意ください。
追加したデータの権限確認
追加したデータは添付のように権限を確認できますので、Read-Onlyになっていないかは確認するようにお願いします。
予約画面などUIの作成
予約機能の作成
予約管理テーブル/ユーザーマスタテーブルのAppSheet側設定
AppSheetは
- スプレッドシートなどのデータに対してレコードを書き込むように必要な編集を加えつつ、
- 記入した結果を別のシートやカレンダーなどに同期させたりする
というような流れが基本の動かし方になります。
そのため、
- AppSheetからスプレッドシートに書き込むためのデータを入力して送信
- 送信時、AppSheetの内部で入力されていないデータなどは他の項目を計算するなどして用意する
- 例:リハーサル時間を60分で設定した場合、放送開始時刻の60分前を計算してリハーサルの開始時刻、という時間をAppSheetで計算させる
- 1と2の両方をスプレッドシートに1行で書き込む
というような使い方をします。
他にもどうせ生放送は1hくらいが相場だろうと、開始日時を設定した時点で自動的に見込みの終了日時を入れておくこともできます。
実際の操作性に直結する箇所なので、下記の設定にこだわらず必要に応じて編集ください。
予約管理テーブル:実際に設定した結果
| Name | Type | Key | Label | Formula | Show | Editable | Require | InitialValue |
|---|---|---|---|---|---|---|---|---|
| _RowNumber | Number | No | No | No | No | No | ||
| ID | Text | Yes | No | No | Yes | Yes | UNIQUEID() |
|
| 予約種別 | Enum | No | No | Yes | Yes | Yes | ||
| 案件名 | Text | No | Yes | Yes | Yes | Yes | ||
| 開始日時 | DateTime | No | No | DATETIME(TEXT([入力用_開始日]) & " " & TEXT([入力用_開始時刻])) |
No | Yes | No | |
| 終了日時 | DateTime | No | No | DATETIME(TEXT([入力用_終了日]) & " " & TEXT([入力用_終了時刻])) |
No | Yes | No | |
| 会議室 | Enum | No | No | Yes | Yes | Yes | ||
| 主催者 | Ref | No | No | Yes | No | No | ||
| 担当者 | Ref | No | No | Yes | Yes | No | ||
| 準備時間(分) | Number | No | No | [予約種別] = "生放送" |
Yes | [予約種別] = "生放送" |
60 |
|
| 撤収時間(分) | Number | No | No | [予約種別] = "生放送" |
Yes | [予約種別] = "生放送" |
15 |
|
| カレンダー同期ステータス | Text | No | No | No | Yes | No | ||
| 関連ID | Text | No | No | No | Yes | No | ||
| 入力用_開始日 | Date | No | No | Yes | Yes | Yes | ||
| 入力用_開始時刻 | Time | No | No | Yes | Yes | Yes | ||
| 入力用_終了日 | Date | No | No | Yes | Yes | Yes | DATE( ([入力用_開始日] + [入力用_開始時刻]) + "001:00:00" ) |
|
| 入力用_終了時刻 | Time | No | No | Yes | Yes | Yes | [入力用_開始時刻] + "001:00:00" |
|
| ステータス | Text | No | No | No | No | No | "予約済み" |
|
| 準備開始時刻 | DateTime | No | No | Yes | Yes | No | ||
| 撤収完了時刻 | DateTime | No | No | Yes | Yes | No |
ユーザーマスタテーブル:実際に設定した結果
| Name | Type | Key | Label | Formula | Show | Editable | Require | InitialValue |
|---|---|---|---|---|---|---|---|---|
| _RowNumber | Number | No | No | No | No | No | ||
| ID | Text | Yes | No | Yes | Yes | Yes | UNIQUEID() |
|
| 姓 | Text | No | No | Yes | Yes | Yes | ||
| 名 | Text | No | No | Yes | Yes | Yes | ||
| オプション | Text | No | No | Yes | Yes | No | ||
| メールアドレス | No | No | Yes | Yes | Yes | |||
| 担当者 | Text | No | Yes | CONCATENATE([姓], " ", [名]) |
Yes | No | No | |
| 予約時表示サンプル | Text | No | No | "【"&IF(ISBLANK([オプション]), [姓], CONCATENATE([姓], " ", [オプション]))&"】授業タイトル" |
Yes | No | No | |
| Related 予約管理s | List | No | No | REF_ROWS("予約管理", "主催者") |
No | No | No | |
| Related 予約管理s By 担当者 | List | No | No | REF_ROWS("予約管理", "担当者") |
No | No | No |
オプションについてはあまり直感的でない項目なので、Description欄に
姓が被った時用の欄です。名前の最初をひらがな1文字で入れるとわかりやすくなります
のような形で、入力する担当者が困らないよう使い方を記載していたりします。
必要に応じて活用ください。
フォームボタン呼び出し用テーブル:実際に設定した結果
| Name | Type | Key | Label | Formula | Show | Editable | Require | InitialValue |
|---|---|---|---|---|---|---|---|---|
| ID | Text | Yes | No | No | No | Yes | ||
| 空白用 | Text | No | Yes | No | No | No | "" |
注意点1:AppSheetにおける日時計算
https://support.google.com/appsheet/answer/10107326?sjid=6545679991286452064-NC
こちらを参考にしていたらければと思うのですが、
DateTime型の値を計算する場合、
Date型とTime型を直接足し算して直接DateTimeにはできません。
DateTimeに直接足し引きが可能なのは、上のドキュメントを見る限り
- Number(日数)
- Duration(経過時間)
の二択であるようです。
今回は日程と時間を合わせて日時としたいため、
いったん双方をTEXTにして、それを合わせたものをDateTimeに変換するという手順を踏んでいます。
実例:DATETIME(TEXT([入力用_開始日]) & " " & TEXT([入力用_開始時刻]))
また、Numberの計算で対応できるのは日数までで、分数に関しては小数点以下になることからまとめて0となるように見受けられました。そのため、この後リハーサルの開始時刻や撤収時間を定める際、所要時間として入力されたNumberをDurationとして変換してDateTimeと足し引きして数字を作っています。
※NumberからDateTimeまでの変換と計算は後述します。
なお、この日時計算はAppSheet側で直接日時を入力する欄を設けるなどした場合は不要な計算になります。
私は多少操作性が悪いと感じたため上記のような方法を取りましたが、必要に応じてご判断ください。
予約用フォームの動作チェック
一旦UXなどから予約管理_formというものを開き、実際に入力とsaveが可能であるか確認してください。
現段階での目標は、
できること
- 予約管理シートに開始日時/終了日時の計算結果と、フォームから入力した値がレコードとして入ること
- 準備時間や撤収時間は生放送に選択した時だけ入力できるようになっていること
- 開始日程、開始時刻が設定された時、終了日と終了時刻が自動で入力されていること
- 例えば開始時刻が23:30の場合、終了日が日付を跨いで翌日になっていること
これ以降の作業で対応する機能なので今はできないこと
- 生放送の準備開始時刻や撤収完了時刻の自動計算
- カレンダーへの実際の予約
の双方ができている/いない状態になっていることになります。
この辺が問題ないか、チェックください。
UXの設定
予約の種類に応じて変わる項目を除いて、データの入力形式が概ね整いました。
ここからは実際に予約ツール上の操作画面などを作っていきます。
ビューとはアプリの操作画面、カレンダーやボタンを配置する領域などを設定するもので、
ユーザーが見る、操作するものはこのUXで作る必要があります。
閲覧用カレンダーの作成
個別のカレンダー用表示の設定
UXタブから「New View」を選択してビューを作ります。
カレンダー一つにつき一つのViewを設定する必要があるため、
使用するカレンダーごとに一つのViewを登録するようにしてください
- For this data: (カレンダーの追加で追加したはずのGoogleカレンダーテーブル)
- View name: (この後区別がつく名前にしてください「スタジオA予定」など)
- View type: calendar
- Position: ref
今回作ったアプリではスタジオAとBを並べてチェックできる画面を作ろうとしてます。
まとめたダッシュボードがある中でスタジオAの予定だけを見るシーンは想像しにくいため、今回は直接アクセスができないようにRefという設定をかけています。
別途必要になった場合はPositionの設定を変更ください。
複数スタジオ予定をまとめたダッシュボードの作成
改めてUXタブから「New View」を選択してビューを作ります。
今回はView typeをDashboardにしてください。
Dashboardにするとその他の設定項目が大きく変わります。
下記のように設定ください。
- View type: dashboard
- Position: first
- View name: 例:全スタジオ予定(アプリ上でわかりやすい名前にしてください)
-
View entries
- スタジオA予定(個別のカレンダー用表示で作成した各カレンダー)
- ※その他カレンダーなど、表示させたい画面があれば選択
- Display
- Icon
アプリ上で表示されるアイコンの絵文字を変えることができます。 - Display name
原則アプリ上での表示はView nameと同じテキストが表示されますが、アプリ画面上の表示だけ変更する場合はこちらで個別設定が可能です
- Icon
ここまでで設定画面の右上「save」を押すと、アプリ画面の1番左に全スタジオ予定が確認できるカレンダーが並ぶ形で表示されるはずです。
なお、私が作業した限り、一つのカレンダー表示には一つのカレンダーリソースしか登録できません。
例えばですが、
- スタジオ閲覧専用にアカウントを立ててツールからの予約には必ず閲覧アカウントを招待する
- スタジオ閲覧専用にアカウントはスタジオ予定の全てに招待されており、それ以外に招待されないという状態にする
- アプリ上はスタジオ閲覧専用アカウントのカレンダーだけ表示する
というような手順を踏めば一つのカレンダーに全スタジオの予定を入れることはできなくないと思いますが、重複予約の表示や必要なアカウントを用意するほどのメリットがないので見送っています。
予約用ボタンの作成
予約するためのボタンを作りたいんですが、
これは一旦別の機能でボタンだけを作る必要があります。
Behavior→Actionsから「New Action」を選択し、下記のようなものを設定ください。
| 設定項目 | スタジオAを予約 | スタジオBを予約 |
|---|---|---|
|
Action name UXで読みこむ用の名前です |
スタジオAの予約 |
スタジオBの予約 |
| For a record of this table | フォームボタン呼び出し用 |
フォームボタン呼び出し用 |
| Do this | App: Go to another view within this app |
App: Go to another view within this app |
| Target | LINKTOFORM("予約管理_Form", "会議室", "StudioA") |
LINKTOFORM("予約管理_Form", "会議室", "StudioB") |
| Prominence | Display prominently |
Display prominently |
先ほどのUXと同様に、Display→nameやIconで実際のボタンにある表示を設定できます。
必要な場合はよしなに設定ください。
※Targetはこの機能を使った時に初期値に任意の情報を入れて立ち上げることができる機能です。例えばスタジオでの使用時間に大まかなデフォルト値が存在する場合はTargetの設定項目に、
"入力用_開始時刻", "21:00:00"
などを入れておくと、自動で開始時刻の見込みを入れてくれたりします。
予約用ボタンの表示
ボタンが完成したところでボタンをUXに並べる必要があります。
ここでようやく、フォームボタン呼び出し用シートを活用します。
UXから「New View」を追加して、
- View name, 予約ボタン用ビュー
- For this data, フォームボタン呼び出し用
(冒頭で作成した、ほとんど内容のない3つ目のシート) - View type, detail
- Position, ref
個別のカレンダーと同じでメニューバーに表示されず、Dashboard用の部品となります
Menuなどからもボタンを呼び出したい場合は「menu」などを設定してください -
Column order
Manualにしてみると、予約用ボタンにフォームボタン呼び出し用のシート、key列に追加したテキストが出ているはずです。
keyの列を選択し、Deleteするとkeyという列名とテキストが消えます。
Tips
Behaviorで設定したボタンは、何かしらのデータに紐づく形でしかボタンが出ません。
そのため、
- ほぼ内容のないDataをあえて作成
- 何もないデータに対してボタンを置く
という手順でボタンをとにかく見える位置に配置させました。
そのためアプリとしてはほぼ何もないデータ用のボタンとして予約ボタンを表示し、
予約ボタン内の挙動として予約管理用のシートを編集するフォームが立ち上がる、というプロセスになるよう設定しました。
Dashboardへの再度の追加
先ほど設定した全スタジオ予定のDashboardを再度編集し、View entriesから設定した「予約ボタン用ビュー」を追加してください。
これにより、全スタジオ予定の最後に予約用のボタンが表示されるようになったはずです。
カレンダーへの予約機能
現段階ではアプリから予約を実施できたように見えても、カレンダーに対しては何もアクションができていません。そのため、ここまでに作ったDataやViewから、実際に予約が実施できるように機能を追加していきます。
Aスタジオ、かつ生放送であった場合の予約機能を作成する
この予約機能はAutomationからBotsを作成して実現しています。
Automationは
- 特定の挙動があった場合
- このようなフローで操作を実行する
という機能を作るもので、
今回は予約管理テーブルにレコードが追加されたことをきっかけに、記入内容に応じた会議室リソースの予約を実行するように組み立てます。
やや面倒ですが、各スタジオ予約のBotを場合ごとに作成します。
具体的にはこのように場合分けを行い、合計6つのBotを作成します。
| Aスタジオ | Bスタジオ | |
|---|---|---|
| 生放送 | Aスタジオ生放送 | Bスタジオ生放送 |
| 収録 | Aスタジオ収録 | Bスタジオ収録 |
| その他 | Aスタジオその他 | Bスタジオその他 |
これにより、生放送の予約だけ準備時間の設定を反映させる、案件の種類によってタイトルなどに設定する文言を変更する、といったことが可能になります。
今回は最初にAスタジオ、かつ生放送の場合に動くBotを最初に作り、
複製したのちに対象のスタジオや途中の処理を変えて残りのBotを完成させるようにします。
Aスタジオ、かつ生放送であった場合にBotが動くようにする
まず、AutomationからBotsの項目を開き、「New Bot」をクリックします。
Configure Eventをクリックして、何をきっかけにBotが実行されるかを最初に設定します。
今回は「Create New Event」を選択し、下記のように設定してください。
| 設定項目 | 設定内容 | 意味 |
|---|---|---|
| Event source | App |
AppSheetのイベントをトリガーにします。 |
| Event Type |
Adds のみ設定 |
新しいレコードが追加されたときにのみ起動します。 (Deleteなどにチェックがあると、今後削除した場合にも予定が追加される危険があります) |
| Table | 予約管理 |
予約データが保存されるテーブルです。 |
| Condition | AND( [予約種別] = "生放送", [会議室] = "StudioA" ) |
予約種別が「生放送」かつ、会議室が「StudioA」の場合のみBotを実行します。 |
この設定ではDataの予約管理における予約種別や会議室(スタジオ)の名前を利用してフィルタしています。そのため、Botがそもそも動いていないような場合は、予約管理における命名とここのフィルタ条件を見直すようにしてください。
Aスタジオ、かつ生放送用のbotに相応しい動作を設定する
生放送の場合にだけ必要な機能として、準備開始時刻と撤収完了時刻の計算があります。
まずはこれを作成します。
- Run this PROCESSの箇所にプロセスを追加して、「Run a data action」を選択
- 「Set row values」を選択して、「Set these column(s)」に下記を設定
| Column (更新する列) | Value (設定する数式) |
|---|---|
準備開始時刻 |
[開始日時] + ("00:00:00" - TIME(CONCATENATE(FLOOR([準備時間(分)]/60), ":", MOD([準備時間(分)], 60), ":00"))) |
撤収完了時刻 |
[終了日時] + (TIME(CONCATENATE(FLOOR([撤収時間(分)]/60), ":", MOD([撤収時間(分)], 60), ":00")) - "00:00:00") |
注意点2: AppSheetにおける日時計算(DateTimeとNumber(分数)の計算)
AppSheetにおける日時計算で軽く触れましたが、DateTimeの計算はDateTimeに対してDurationを足し引きするか、一日単位でNumberを足し引きする必要があります。
今回は開始時刻に対して「n分前(Number)」でフォーム入力しているため、まずはNumberに応じた経過時間に変換します。
開始時刻を例に説明すると下記のようになります
("00:00:00" -
TIME(CONCATENATE(FLOOR([準備時間(分)]/60), ":", MOD([準備時間(分)], 60), ":00"))
)
準備時間のNumberを60で割った商を時間、あまりを分として並べ、hh:mm:00という文字列として整えます。
これをTIME関数で時間(Time型)に変換します。TIME型は経過時間を足し引きするとDuration(期間)になるため、
開始日時に対してこれらの結果を経過時間として引き算することで、
開始日時のn分前が何日の何時何分か、を計算することができます。
Googleカレンダーに予定を入力するまでの具体的な手順
ここからの設定で、実際にGoogleカレンダーに対して予定が作成されるようになります。
前項のBotsで準備時間や撤収時間が設定されているとして、その下にある「Add a step」をクリックします。
これで新しいPROCESSが追加できるようになるので、まずは生放送に対して開始と終了時刻に対応したカレンダーを予約できるようにします。
基本的なGoogleカレンダーの予約項目
AppSheetからGoogleカレンダーに予約を追加する場合、カレンダーというDataに対して「Add new rows」を選択し、「With these values」で予約に必要な情報を格納することで予約ができるようになります。
具体的には、Title / Start / End などがあります。
特に開始と終了の日時については設定に失敗すると予約ができないため、予約に失敗した場合には日時の計算をExpression AssistantのTest機能で意図した値になっているかを確認することが望ましいです。
新規プロセスを追加できたら、以下の手順で進めます。
- 「Run a data action」
- 「Custom action」
- 右側のSettingから「Add new rows」
- 「スタジオA」(Dataで読み込んだカレンダーにつけた名前)を選択
予約先のカレンダー以外はこれらの設定は共通です。
実際の予約に関する設定
実際の放送時間に関する予約
開始日時と終了日時を使ってカレンダーの予約設定をする場合は下記の通りになります。
| Column (列) | Value (設定する数式) | 備考 |
|---|---|---|
Title |
CONCATENATE("【", [担当者].[担当者], "】", [案件名]) |
[担当者].[担当者]でユーザーマスタの名前を参照 |
Start |
[開始日時] |
|
End |
[終了日時] |
|
Description |
CONCATENATE(TEXT([開始日時], "YYYYMMDDHHMM"), IF([会議室]="StudioA", "-1:", "-2:"), "main") |
削除機能用の共通ID + 識別子。 これを使って削除機能や編集機能を作る場合、案件を特定する鍵として使うことができる |
Attendees |
LOOKUP([担当者], "ユーザーマスタ", "ID", "メールアドレス") |
担当者として予定に招待するアカウントを指定している |
なお、DescriptionについてはAかBしか予約先がないという前提でかなり雑な作り方をしています。
具体的にはこの手前、Automationの冒頭でConditionを使ってフィルタしていることを利用して、StudioAという会議室リソースが追加されていた場合はYYYYMMDDHHMM-1、
そうでない場合、StudioAではない=まとめて全てStudioBでの案件(YYYYMMDDHHMM-2)とみなす、という振る舞いになっています。
そのため、例えば将来的にCスタジオやレンタルスタジオという現在予定しない会議室を運用するときは使えません。スタジオの追加がある場合はこの箇所の設定も見直すようお願いします。
準備用の時間に関する予約
事前の準備時間を設定する場合は下記の通りです。
本番の予約と違う箇所については備考欄を確認ください。
| Column (列) | Value (設定する数式) | 備考 |
|---|---|---|
Title |
CONCATENATE("【", [担当者].[担当者], "】", [案件名]) |
本番用の予約と同じですが、「準備中」など、わかりやすいメモをつけてもいいかもしれません |
Start |
[準備開始時刻] |
本項の最初の方で設定した準備開始日時を設定します |
End |
[開始日時] |
準備の時間は本番開始なので、開始日時を取りに行きます |
Description |
CONCATENATE(TEXT([開始日時], "YYYYMMDDHHMM"), IF([会議室]="StudioA", "-1:", "-2:"), "pre") |
末尾を”pre”とすることで、Googleカレンダー予約をまとめた時に準備用の予約であることがわかるようにしています。 |
Attendees |
LOOKUP([担当者], "ユーザーマスタ", "ID", "メールアドレス") |
本番用の予約と同じです |
撤収用の時間に関する予約
放送後の撤収時間を設定する場合は下記の通りです。
同じく備考欄を確認ください。
| Column (列) | Value (設定する数式) | 備考 |
|---|---|---|
Title |
CONCATENATE("【", [担当者].[担当者], "】", [案件名]) |
本番用の予約と同じですが、「撤収中」など、わかりやすいメモをつけてもいいかもしれません |
Start |
[終了日時] |
本番の終了から撤収開始なので、そちらを設定します |
End |
[撤収完了日時] |
本項の最初で設定した撤収完了日時を設定します |
Description |
CONCATENATE(TEXT([開始日時], "YYYYMMDDHHMM"), IF([会議室]="StudioA", "-1:", "-2:"), "after") |
末尾を”after”とすることで、Googleカレンダー予約をまとめた時に撤収用の予約であることがわかるようにしています。 |
Attendees |
LOOKUP([担当者], "ユーザーマスタ", "ID", "メールアドレス") |
本番用の予約と同じです |
確認
これらが設定できたら「save」で保存してください。
この段階で当該スタジオに生放送ですという設定でフォームに入力→同期して、
予約が完了するかの確認を行なってください。
なお、Description の項目は予約するだけであれば特に設定不要の項目です。
ただし、この後予定の削除機能を作る場合にはどれが一連の予約であるかを判断する何かを記載する必要があります。
今回はこのDescription の項目に予約の概要を埋め込むことで判断するという仕掛けにしています。
詳細を本来的な目的(詳細の記入やメモ)で使いたい場合は、削除機能はカレンダーを直接操作するなど、別の手段を採用するようにしてください。
他の予約についても設定する場合
ここまでで、一つ目のスタジオに対して準備時間、撤収時間の予約を連結した状態でカレンダー予約ができるツールが作れているはずです。
これをコピー→必要な箇所を編集する形で、他の予約についてもBotsを作っていきます。
完成したBotsをコピーして判別がつくような名前に書き換えた後、下記のように設定してください。
スタジオの変更と予約内容の変更がともに必要な場合は、どちらも編集するようにしてください。(例:Bスタジオ収録予定の場合)
スタジオを変更する場合
| 設定項目 | 設定内容 |
|---|---|
| Condition for event |
AND( [予約種別] = "生放送", [会議室] = "StudioB" )※会議室を "StudioB"に設定 |
| PROCESS | Add row to this table で 別のスタジオ(スタジオBなど)を選択 |
予定の内容を変更する場合
収録予定の場合
| 設定項目 | 設定内容 |
|---|---|
| PROCESS | 本番予約として使うもの以外削除 (削除対象:「準備開始時刻」「撤収完了時刻」を設定するStep、準備時間予約用のStep、撤収時間予約用のStep) |
| Title | CONCATENATE("【収録】【", [担当者].[担当者], "】", [案件名]) |
| Description |
CONCATENATE( TEXT([開始日時], "YYYYMMDDHHMM"), IF([会議室] = "StudioA", "-1:", "-2:"), "recording" )※”recording”に変更 |
そのほかの予定の場合
| 設定項目 | 設定内容 |
|---|---|
| PROCESS | 本番予約として使うもの以外削除 (削除対象:「準備開始時刻」「撤収完了時刻」を設定するStep、準備時間予約用のStep、撤収時間予約用のStep) |
| Title | CONCATENATE("【その他】【", [担当者].[担当者], "】", [案件名]) |
| Description |
CONCATENATE( TEXT([開始日時], "YYYYMMDDHHMM"), IF([会議室] = "StudioA", "-1:", "-2:"), "meeting" )※”meeting”に変更 |
予定の削除機能
予定を削除する機能も作っておきます。
大まかな考え方としては下記の通りです。
- 「予約管理」テーブルから行を削除する
- 削除をトリガーに、削除した予約の「開始時刻」からカレンダー予約のDescriptionにあるはずの記載を探す(カレンダー予約詳細の”20251205-1”の部分)
- 見つけたらその記載がある予定を全て消す
削除する条件を設定する
まずは、予定が削除されたら動く、という起点を設定します。
New Botを作り、EVENTから
- App
- Table:予約管理
- Data change type:Deletes
を選択します。これ以外の設定は特に必要ありません。
分岐を設定する
PROCESSで最初に分岐を設定します。
Stepを作成し、「Branch on a condition」を選択します。
これを活用すると、特定の条件で処理を分岐させることができます。
- Branch on a condition
[_THISROW].[会議室] = "StudioA"
これにより、削除されたリソースが"StudioA”であるか否かで処理を変えます。
ここでいう会議室とは予約管理における会議室の名前なので、エラーなどがある場合はそちらを確認ください。
Branch on a conditionの他の活用方法
これ以外の使用例としては、予約したい会議室リソースがAかBかを判定し、それぞれに予約先の会議室だけを変えた処理を入れることで、Botsの数を絞ることが可能になります。
今回の場合会議室リソースが増えた場合は分岐の処理が大変であることから、
分岐を入れずに会議室の数に応じて三パターンの予約を作る、という形でBotsを組んでいます。
Yes(会議室リソースがA)の場合に削除する
Branch on a conditionを設定した場合、下には「Yes」と「No」という二つの場合に合わせた処理がおけるようになります
まずはYesの場合、[会議室] = "StudioA"の場合の処理を追加します。
「Run a data action」を選択した上で、右側メニューから「Run action on row」を選んでおいてください
Referenced Table |
スタジオA |
削除を行うカレンダーリソース |
|---|---|---|
Referenced rows |
FILTER( "スタジオA", CONTAINS([Description], CONCATENATE(TEXT([_THISROW].[開始日時], "YYYYMMDDHHMM"), "-1:") )) |
20251205-1 のような記載を予約のDescriptionから探します。 |
Referenced Action |
Delete |
見つけたものを削除します。 |
生放送予定に関わる予約は三件できるはずですが、
20251205-1 の箇所は準備から撤収まで全て同じですので、まとめて削除することができます。
No(会議室リソースがA以外≒B)の場合に削除する
概ね前項と同じです。
Referenced Table |
スタジオB |
削除を行うカレンダーリソース |
|---|---|---|
Referenced rows |
FILTER( "スタジオB", CONTAINS([Description], CONCATENATE(TEXT([_THISROW].[開始日時], "YYYYMMDDHHMM"), "-2:") )) |
20251205-2 のような記載を予約のDescriptionから探します。 |
Referenced Action |
Delete |
見つけたものを削除します。 |
まとめ
- 予約した履歴を残すスプレッドシートとカレンダーの双方をAppSheetにDataとして登録することで、カレンダーの予約や削除をAppSheetから行うことができるようになる
- カレンダーの予約はDataとして登録されているカレンダーに対して行の追加/削除として行う
- AppSheetではDate型とTime型を直接足すなどの操作ができないので、一旦文字列に置き換えてから連結してDateTimeに変換する
- DateTimeの計算はDateTimeに対してDurationを足し引きするか、一日単位でNumberを足し引きする必要がある
- Numberを分数として足したり引いたりする場合は、Numberを60で割った商や余りでTIMEに変換してから"00:00:00"という経過時間を足すなり引くことで、DurationにしてDateTimeなどから足したり引いたりする
AppSheetはカメラからバーコードの読み取りを行ったり、レシート画像からOCRで領収書のあれこれを起こしたりと様々な使い方ができます。
DX推進という規模になるとどうしても業務フロー全体の見直しなど影響範囲の足並みを揃える必要が出てきますが、限定的なリソースを効率よく動かす程度であれば、便利なツールをクイックに作ってみることができます。
おまけ
Schooでは、DXの基礎から実際のツールの使い方まで、様々な授業コンテンツを提供しています。
下記に例として挙げますので、ご興味があればチェックいただければ幸いです。
企業組織におけるDX・イノベーションのはじめ方については例えばこちら
基本的なGoogleの各ツールをより活用する場合はこちら
Schooでは一緒に働く仲間を募集しています!





