目次
Connector

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。カレンダーと期間で絞り込みます。この読み取り元にキーはありません。

型

CalendarActQL備考
id、iCalUID、recurringEventIdstring安定した識別子
start/end の dateTimetimestampオフセット付き。all_day = false
start/end の datedate終日イベント。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 のいずれか
statusstringconfirmed、tentative、cancelled
etagstring更新と削除の適用条件にも使う
created、updatedtimestampUTC

書き込みは同じ規則で逆方向に変換します。終日イベントは、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、LIMITORDER 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