Coverage for mindsdb / integrations / handlers / google_calendar_handler / google_calendar_handler.py: 0%
137 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-21 00:36 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-21 00:36 +0000
1import pandas as pd
2from googleapiclient.discovery import build
4from mindsdb.api.executor.data_types.response_type import RESPONSE_TYPE
5from mindsdb.integrations.libs.api_handler import APIHandler, FuncParser
6from mindsdb.integrations.libs.response import (
7 HandlerStatusResponse as StatusResponse,
8 HandlerResponse as Response,
9)
10from mindsdb.utilities.config import Config
11from mindsdb.utilities import log
12from mindsdb.integrations.utilities.handlers.auth_utilities.google import GoogleUserOAuth2Manager
13from mindsdb.integrations.utilities.handlers.auth_utilities.exceptions import AuthException
15from .google_calendar_tables import GoogleCalendarEventsTable
17DEFAULT_SCOPES = [
18 "https://www.googleapis.com/auth/calendar",
19 "https://www.googleapis.com/auth/calendar.events",
20 "https://www.googleapis.com/auth/calendar.readonly",
21]
23logger = log.getLogger(__name__)
26class GoogleCalendarHandler(APIHandler):
27 """
28 A class for handling connections and interactions with the Google Calendar API.
29 """
31 name = "google_calendar"
33 def __init__(self, name: str, **kwargs):
34 """constructor
35 Args:
36 name (str): the handler name
37 credentials_file (str): The path to the credentials file.
38 scopes (list): The list of scopes to use for authentication.
39 is_connected (bool): Whether the API client is connected to Google Calendar.
40 events (GoogleCalendarEventsTable): The `GoogleCalendarEventsTable` object for interacting with the events table.
41 """
42 super().__init__(name)
43 self.connection_data = kwargs.get("connection_data", {})
45 self.service = None
46 self.is_connected = False
48 self.handler_storage = kwargs["handler_storage"]
50 self.credentials_url = self.connection_data.get("credentials_url", None)
51 self.credentials_file = self.connection_data.get("credentials_file", None)
52 if self.connection_data.get("credentials"):
53 self.credentials_file = self.connection_data.pop("credentials")
54 if not self.credentials_file and not self.credentials_url:
55 # try to get from config
56 gcalendar_config = Config().get("handlers", {}).get("youtube", {})
57 secret_file = gcalendar_config.get("credentials_file")
58 secret_url = gcalendar_config.get("credentials_url")
59 if secret_file:
60 self.credentials_file = secret_file
61 elif secret_url:
62 self.credentials_url = secret_url
64 self.scopes = self.connection_data.get("scopes", DEFAULT_SCOPES)
66 events = GoogleCalendarEventsTable(self)
67 self.events = events
68 self._register_table("events", events)
70 def connect(self):
71 """
72 Set up any connections required by the handler
73 Should return output of check_connection() method after attempting
74 connection. Should switch self.is_connected.
75 Returns:
76 HandlerStatusResponse
77 """
78 if self.is_connected is True:
79 return self.service
81 google_oauth2_manager = GoogleUserOAuth2Manager(
82 self.handler_storage,
83 self.scopes,
84 self.credentials_file,
85 self.credentials_url,
86 self.connection_data.get("code"),
87 )
88 creds = google_oauth2_manager.get_oauth2_credentials()
90 self.service = build("calendar", "v3", credentials=creds)
91 return self.service
93 def check_connection(self) -> StatusResponse:
94 """
95 Check connection to the handler
96 Returns:
97 HandlerStatusResponse
98 """
100 response = StatusResponse(False)
102 try:
103 self.connect()
104 response.success = True
105 response.copy_storage = True
107 except AuthException as error:
108 response.error_message = str(error)
109 response.redirect_url = error.auth_url
110 return response
112 except Exception as e:
113 logger.error(f"Error connecting to Google Calendar API: {e}!")
114 response.error_message = e
116 self.is_connected = response.success
117 return response
119 def native_query(self, query: str = None) -> Response:
120 """
121 Receive raw query and act upon it somehow.
122 Args:
123 query (Any): query in native format (str for sql databases,
124 api's json etc)
125 Returns:
126 HandlerResponse
127 """
128 method_name, params = FuncParser().from_string(query)
130 df = self.call_application_api(method_name, params)
132 return Response(RESPONSE_TYPE.TABLE, data_frame=df)
134 def get_events(self, params: dict = None) -> pd.DataFrame:
135 """
136 Get events from Google Calendar API
137 Args:
138 params (dict): query parameters
139 Returns:
140 DataFrame
141 """
142 service = self.connect()
143 page_token = None
144 events = pd.DataFrame(columns=self.events.get_columns())
145 while True:
146 events_result = service.events().list(calendarId="primary", pageToken=page_token, **params).execute()
147 events = pd.concat(
148 [events, pd.DataFrame(events_result.get("items", []), columns=self.events.get_columns())],
149 ignore_index=True,
150 )
151 page_token = events_result.get("nextPageToken")
152 if not page_token:
153 break
154 return events
156 def create_event(self, params: dict = None) -> pd.DataFrame:
157 """
158 Create an event in the calendar.
159 Args:
160 params (dict): query parameters
161 Returns:
162 DataFrame
163 """
164 service = self.connect()
165 # Check if 'attendees' is a string and split it into a list
166 if isinstance(params["attendees"], str):
167 params["attendees"] = params["attendees"].split(",")
169 event = {
170 "summary": params["summary"],
171 "location": params["location"],
172 "description": params["description"],
173 "start": {
174 "dateTime": params["start"]["dateTime"],
175 "timeZone": params["start"]["timeZone"],
176 },
177 "end": {
178 "dateTime": params["end"]["dateTime"],
179 "timeZone": params["end"]["timeZone"],
180 },
181 "recurrence": ["RRULE:FREQ=DAILY;COUNT=1"],
182 "attendees": [
183 {"email": attendee["email"]}
184 for attendee in (
185 params["attendees"] if isinstance(params["attendees"], list) else [params["attendees"]]
186 )
187 ],
188 "reminders": {
189 "useDefault": False,
190 "overrides": [
191 {"method": "email", "minutes": 24 * 60},
192 {"method": "popup", "minutes": 10},
193 ],
194 },
195 }
197 event = service.events().insert(calendarId="primary", body=event).execute()
198 return pd.DataFrame([event], columns=self.events.get_columns())
200 def update_event(self, params: dict = None) -> pd.DataFrame:
201 """
202 Update event or events in the calendar.
203 Args:
204 params (dict): query parameters
205 Returns:
206 DataFrame
207 """
208 service = self.connect()
209 df = pd.DataFrame(columns=["eventId", "status"])
210 if params["event_id"]:
211 start_id = int(params["event_id"])
212 end_id = start_id + 1
213 elif not params["start_id"]:
214 start_id = int(params["end_id"]) - 10
215 elif not params["end_id"]:
216 end_id = int(params["start_id"]) + 10
217 else:
218 start_id = int(params["start_id"])
219 end_id = int(params["end_id"])
221 for i in range(start_id, end_id):
222 event = service.events().get(calendarId="primary", eventId=i).execute()
223 if params["summary"]:
224 event["summary"] = params["summary"]
225 if params["location"]:
226 event["location"] = params["location"]
227 if params["description"]:
228 event["description"] = params["description"]
229 if params["start"]:
230 event["start"]["dateTime"] = params["start"]["dateTime"]
231 event["start"]["timeZone"] = params["start"]["timeZone"]
232 if params["end"]:
233 event["end"]["dateTime"] = params["end"]["dateTime"]
234 event["end"]["timeZone"] = params["end"]["timeZone"]
235 if params["attendees"]:
236 event["attendees"] = [{"email": attendee} for attendee in params["attendees"].split(",")]
237 updated_event = service.events().update(calendarId="primary", eventId=event["id"], body=event).execute()
238 df = pd.concat(
239 [df, pd.DataFrame([{"eventId": updated_event["id"], "status": "updated"}])], ignore_index=True
240 )
242 return df
244 def delete_event(self, params):
245 """
246 Delete event or events in the calendar.
247 Args:
248 params (dict): query parameters
249 Returns:
250 DataFrame
251 """
252 service = self.connect()
253 if params["event_id"]:
254 service.events().delete(calendarId="primary", eventId=params["event_id"]).execute()
255 return pd.DataFrame([{"eventId": params["event_id"], "status": "deleted"}])
256 else:
257 df = pd.DataFrame(columns=["eventId", "status"])
258 if not params["start_id"]:
259 start_id = int(params["end_id"]) - 10
260 elif not params["end_id"]:
261 end_id = int(params["start_id"]) + 10
262 else:
263 start_id = int(params["start_id"])
264 end_id = int(params["end_id"])
265 for i in range(start_id, end_id):
266 service.events().delete(calendarId="primary", eventId=str(i)).execute()
267 df = pd.concat([df, pd.DataFrame([{"eventId": str(i), "status": "deleted"}])], ignore_index=True)
268 return df
270 def call_application_api(self, method_name: str = None, params: dict = None) -> pd.DataFrame:
271 """
272 Call Google Calendar API and map the data to pandas DataFrame
273 Args:
274 method_name (str): method name
275 params (dict): query parameters
276 Returns:
277 DataFrame
278 """
279 if method_name == "get_events":
280 return self.get_events(params)
281 elif method_name == "create_event":
282 return self.create_event(params)
283 elif method_name == "update_event":
284 return self.update_event(params)
285 elif method_name == "delete_event":
286 return self.delete_event(params)
287 else:
288 raise NotImplementedError(f"Unknown method {method_name}")