跳轉到

HTTP Transport 與 Batch API

回到 資料流索引

Google Calendar 和 Google Tasks 共用一套 Batch API Transport 層,將多個請求打包為單一 HTTP POST(multipart/mixed),減少網路往返次數。

層級結構

檔案 主要類別/函數
Request Model models_request.py (Calendar) ListEventsRequest, InsertEventRequest, ListCalendarListRequest
Request Model models_request.py (Tasks) ListTasksRequest, InsertTaskRequest, ListTaskListsRequest
API Client api_client_async.py (Calendar) list_events_async(), list_calendar_list_async(), _execute_single_async()
API Client api_client_async.py (Tasks) list_tasks_async(), list_tasklists_async(), _execute_single_async()
Dispatcher google_api_dispatcher.py batch_execute_async()
HTTP Tool http_batch_tool.py build_generic_batch_body(), parse_generic_batch_response(), send_batch_request_payload()

Request Model 設計

每個 Request Model 是 Pydantic BaseModel,必須實作兩個方法:

class ListEventsRequest(GoogleCalendarRequest):
    def to_http(self) -> dict:
        """將參數轉為 HTTP 請求元件 {method, url, params, json}"""
        return {"method": "GET", "url": "/calendar/v3/calendars/.../events", "params": {...}}

    def parse_response(self, data: dict) -> list:
        """將 API 回傳的 JSON dict 轉為 Read Layer Model"""
        return [GoogleEventRead.from_raw(item) for item in data.get("items", [])]

執行流程

Request Model        →  to_http()         →  {method, url, params, json}
                         ↓
API Client            →  _execute_single_async()  (len=1 Batch)
                         ↓
Dispatcher            →  batch_execute_async()
                         ↓
HTTP Batch Tool       →  build_generic_batch_body()   →  multipart/mixed 字串
                         ↓
                         send_batch_request_payload()  →  POST to Google Batch Endpoint
                         ↓
                         parse_generic_batch_response() →  List[{ok, status, data}]
                         ↓
Dispatcher (raw=True) →  直接回傳 {ok, data: JSON dict}
Dispatcher (raw=False)→  req.parse_response(data) → Read Model

batch_execute_async() 函數詳解

  • 做了什麼
  • 從 credentials 提取 token
  • 呼叫每個 request 的 to_http() 取得 HTTP 元件
  • build_generic_batch_body() 打包為 multipart/mixed 格式
  • send_batch_request_payload() 發送至 Google Batch Endpoint
  • parse_generic_batch_response() 解析回應
  • raw=True,直接回傳 {ok, status, data} dict
  • raw=False,呼叫 req.parse_response(data) 轉換為 Read Model

Batch Endpoint

整合 Endpoint
Google Calendar https://www.googleapis.com/batch/calendar/v3
Google Tasks https://www.googleapis.com/batch/tasks/v1

API 原始 JSON 範例

Google Calendar Event(API 回傳的單一事件)

Timed Event (含具體時間)

{
  "kind": "calendar#event",
  "id": "abc123def456",
  "summary": "週會",
  "start": {
    "dateTime": "2026-01-28T10:00:00+08:00",
    "timeZone": "Asia/Taipei"
  },
  "end": {
    "dateTime": "2026-01-28T11:00:00+08:00",
    "timeZone": "Asia/Taipei"
  },
  "recurrence": ["RRULE:FREQ=WEEKLY;BYDAY=WE"],
  "reminders": {"useDefault": false, "overrides": [{"method": "popup", "minutes": 10}]}
}

All-Day Event (全天事件)

{
  "kind": "calendar#event",
  "id": "def456ghi789",
  "summary": "清明節連假",
  "start": {
    "date": "2026-04-04" 
  },
  "end": {
    "date": "2026-04-06"
  }
}
時間欄位觀察: - Google Calendar API 的時間是以物件形式({dateTime: ...}{date: ...})出現。 - 若為全天事件,只會有 date 欄位(start 為當天,end 由於非包含性質,會是結束日的隔天)。 - 這些巢狀結構使得轉換層(GoogleEventRead.from_dict)必須解構提取,不可直接視為字串。

Google Tasks Task(API 回傳的單一任務)

{
  "kind": "tasks#task",
  "id": "MTIzNDU2Nzg5MA",
  "title": "完成期末報告",
  "updated": "2026-01-27T08:30:00.000Z",
  "parent": "OTg3NjU0MzIx",
  "notes": "需要包含數據分析章節",
  "status": "needsAction",
  "due": "2026-02-01T00:00:00.000Z",
  "completed": null
}

時間欄位觀察: - Google Tasks API 的時間是直接的字串欄位(如 "due": "2026-02-01T00:00:00.000Z")。 - due 欄位永遠是 T00:00:00.000Z(UTC 零點),這代表它實際上只有日期概念,缺乏精確到幾點幾分的時間資訊。 - 這也是為何在 GoogleTaskRead.from_dict 中可以放心地將它轉換為 UTC+8 字串 YYYY-MM-DD HH:MM(此時時間必定為 08:00),且把 all_day 寫死為 True

Moodle Calendar Event(AJAX 回傳的單一事件,簡化)

{
  "id": 12345,
  "name": "作業一:Python 基礎練習",
  "description": "<p>請完成以下練習...</p>",
  "component": "mod_assign",
  "modulename": "assign",
  "activityname": "作業一:Python 基礎練習 已於 2025年11月15日 星期六 23:59 到期",
  "timestart": 1731686399,
  "timeduration": 0,
  "overdue": true,
  "course": {
    "id": 67890,
    "fullname": "114學年度 第 1 學期 資訊工程系 EE1001 程式設計 張教授",
    "shortname": "EE1001-114-1"
  },
  "isactionevent": true,
  "action": {
    "name": "繳交作業",
    "url": "https://moodle.ntust.edu.tw/mod/assign/view.php?id=...",
    "itemcount": 1,
    "actionable": true
  }
}