Google Calendar
Google Calendar Connectorは、カレンダー、イベント、空き時間の情報を読み、イベントの作成、更新、UPSERT、削除、招待への回答、カレンダー間の移動を行います。プログラムは予定を見て、LLMでフォローアップが必要なものを判断し、カレンダーに入れることができます。
読み取り元
calendar.calendars— アカウントから見えるカレンダー。id、summary、time_zone、access_role。キーはid。calendar.events— イベント。id、ical_uid、calendar_id、summary、description、start、end、all_day、location、status、organizer、attendees(email、response_status、optionalを持つオブジェクトの配列)、recurring_event_id、created、updated、etag。キーは(calendar_id、id)。ical_uidはカレンダーをまたいで一意です。カレンダーと期間で絞り込んでください。繰り返しのイベントは個々の回に展開されます。calendar.free_busy— 期間内のカレンダーの予定あり区間。calendar_id、start、end。カレンダーと期間で絞り込みます。この読み取り元にキーはありません。
型
| Calendar | ActQL | 備考 |
|---|---|---|
id、iCalUID、recurringEventId | string | 安定した識別子 |
start/end の dateTime | timestamp | オフセット付き。all_day = false |
start/end の date | date | 終日イベント。all_day = true。1つの列に2つの形が混ざることはない。start は timestamp の列で、終日の開始はカレンダーのタイムゾーンの0時として読み、all_day で区別する |
attendees[] | array<struct<email string, response_status string, optional bool>> | response_status は needsAction、declined、tentative、accepted のいずれか |
status | string | confirmed、tentative、cancelled |
etag | string | 更新と削除の適用条件にも使う |
created、updated | timestamp | UTC |
書き込みは同じ規則で逆方向に変換します。終日イベントは、all_day = TRUE と date 型の start と end を指定して書きます。
Effectと操作
レコードのEffectはキーで書きます。
- insert —
EFFECT INSERT calendar.events (calendar_id, summary, start, end, …) VALUES (…)。 - update —
EFFECT UPDATE calendar.events ID id SET (…)。 - upsert —
EFFECT UPSERT calendar.events ID ical_uid SET (calendar_id = …, summary = …, start = …, end = …, …)は、イベントを作るか、そのiCalUIDを持つイベントを更新します。イベントを重複させずに、カレンダーを元のシステムと同期させる方法です。 - delete —
EFFECT DELETE calendar.events ID id。
id はカレンダーの中のちょうど1つのイベントを識別し、ical_uid はカレンダーをまたいで識別します。ID id で足りるのは、レコードの calendar_id 列がカレンダーを指定するからです。その列のないレコードは check で拒否されます。追加、更新、削除で send_updates = 'all' を指定すると、参加者に招待または取り消しが送られます。既定では通知なしで変更します。
操作:
- respond —
EFFECT COMMAND calendar.respond TARGET event_id ID key INPUT (response = 'accepted' | 'declined' | 'tentative')は、接続したアカウントの招待への回答を記録します。 - move —
EFFECT COMMAND calendar.move TARGET event_id ID key INPUT (destination = calendar_id)は、イベントを別のカレンダーに移します。
どちらも、Calendarが変更を確認した時点で applied で終わります。send_updates が引き起こす招待メールはCalendar自身の仕事で、追跡はしません。
セットアップ
"calendar": {
"connector": "calendar",
"credentials": {
"service_account": {"env": "GOOGLE_SERVICE_ACCOUNT_JSON"},
"delegate": "ops@acme.com"
}
}各カレンダーをサービスアカウントと共有する(読み取りには予定の表示、書き込みには変更の権限)か、calendar.readonly または calendar.events のスコープでドメイン全体の委任を使い、delegate を指定します。招待への回答は、委任されたアカウントとして行われます。ActQL Cloudでは、ホスト型のOAuthでサービスアカウントなしにアカウントにサインインできます。要求するスコープは同じです。
Capabilityとプッシュダウン
| 演算 | プッシュダウンされる条件 |
|---|---|
| 列の選択 | 常に。attendees は列が必要とするときだけ要求する |
WHERE | 述語が calendar_id、start/end の期間、updated、または summary/description に対する自由テキストの一致のとき。イベントAPIの timeMin、timeMax、updatedMin、q で正確に表せる範囲。attendees、status、location に対する述語はHostで実行し、混在する述語は分割して残りをHostが評価する |
ORDER BY、LIMIT | ORDER BY start(昇順)と LIMIT のとき。ほかの並び順はHostで実行する |
AGGREGATE、JOIN | されない。どちらもHostで実行する |
explain は、読み取りをSite connector:calendar、Engine Calendar API で表示します。
プレビューと突き合わせ
プレビュー。 更新、既存イベントのUPSERT、削除、回答、移動は full_diff です。Connectorはイベントを読み、現在の項目と etag を記録し、プレビューには現在の値と新しい値が出ます。リクエストにはその etag が付きます(If-Match)。その間に変わったイベントは failed(conflict)で終わり、続けるよう求められればRunは新しいプレビューを作ります。追加と、作成になるUPSERTは targeted です。プレビューには、作られるイベントの全体と、入る先のカレンダーが出ます。
突き合わせ。 送信前に、Connectorはリクエストのマーカーをイベントの非公開の拡張プロパティに書きます。作成後に応答が失われた場合は、リクエストの時間枠のイベントを一覧し、そのマーカーを持つものを探して突き合わせます。件名と時刻が同じでもマーカーのないイベントは、証跡になりません。更新、回答、移動は、同じリクエストの中で同じ拡張プロパティにリクエストのマーカーを書き込みます。そのため、イベントを読み返して、承認された値とともにそのマーカーがあることで突き合わせます。etag が動いたことや、値だけでは証跡になりません。削除は、イベントの status = 'cancelled' とマーカーの両方で確認します。どれでも結果が確定しなければ、項目は unknown のままで、再送はしません。
上限と費用
- 期間内のイベントは250件ずつ読みます。繰り返しのイベントは個々の回に展開されるので、繰り返しの予定を長い期間で読むと多くのレコードになり、プログラムの件数の上限に数えられます。
- APIにはユーザーごとおよびプロジェクトごとのクォータ単位があります。Connectorはその範囲内でペースを調整し、プログラムの件数の上限に達したら、途中までの結果ではなくエラーで止まります。
send_updates = 'all'は、変更ごとに参加者1人につき1通のメールを送ります。これを付けて多くのイベントを更新するプログラムはEffectの項目数の上限で抑えられ、プレビューには招待が送られることが出ます。- 空き時間の問い合わせは、1リクエストにつきAPIの期間の上限までです。それより長い期間は分割します。
例
来週の顧客とのミーティングです。
FROM calendar.events
|> WHERE calendar_id = 'sales@acme.com'
AND start >= CURRENT_TIMESTAMP()
AND start < TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL 7 DAY)
AND status != 'cancelled'
|> SELECT start, summary, ARRAY_LENGTH(attendees) AS people
|> ORDER BY start;リスクの高い取引先ごとに、確認のミーティングを1回だけ入れます。
SELECT a.id, a.name, a.owner_email, risk.value.reason AS reason
FROM salesforce.accounts AS a
JOIN snowflake.usage_30d AS u ON a.id = u.account_id
|> INFER `model-task:churn-risk.v1`
INPUT (notes = a.description, active_users = u.active_users, trend = u.trend) AS risk
|> WHERE risk.outcome = 'ok' AND risk.value.level = 'high'
|> EXTEND CONCAT('churn-checkin-', id, '@acme.com') AS ical_uid
|> EFFECT UPSERT calendar.events ID ical_uid
SET (calendar_id = owner_email, summary = CONCAT('Check-in: ', name), description = reason,
start = TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL 2 DAY),
end = TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL 2 DAY) + INTERVAL 30 MINUTE);もう一度実行すると、新しいイベントを作るのではなく同じイベントを更新します。
不在予定と重なるミーティングをすべて辞退します。
FROM calendar.events
|> WHERE calendar_id = 'kim@acme.com'
AND start >= @away_from AND start < @away_until
AND status != 'cancelled'
|> EFFECT COMMAND calendar.respond TARGET id ID id INPUT (response = 'declined');Source: actql/actql/docs/connectors/calendar.md at d904416674e4 · Edit on GitHub