BroBro87 commited on
Commit
3552a63
·
verified ·
1 Parent(s): b8d3f18

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +105 -304
app.py CHANGED
@@ -1,316 +1,117 @@
1
- from dataclasses import dataclass
2
- from enum import Enum
3
- from typing import Optional, Dict, Any
4
- from composio_llamaindex import ComposioToolSet, App, Action
5
- from datetime import datetime, timedelta
6
- from collections import defaultdict, Counter
7
- from llama_index.llms.openai import OpenAI
8
  import gradio as gr
9
- import os
 
10
  import json
11
- from dotenv import load_dotenv
12
 
13
- # Load environment variables
14
- load_dotenv()
15
 
16
- llm = OpenAI(model='gpt-4o', api_key=os.getenv('OPENAI_API_KEY'))
 
17
 
18
- class ConnectionStatus(Enum):
19
- PENDING = "pending"
20
- ACTIVE = "active"
21
- FAILED = "failed"
22
- NOT_FOUND = "not_found"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
- @dataclass
25
- class APIResponse:
26
- success: bool
27
- data: Optional[Dict[str, Any]] = None
28
- error: Optional[str] = None
29
-
30
- def to_json(self) -> str:
31
- return json.dumps({
32
- "success": self.success,
33
- "data": self.data,
34
- "error": self.error
35
- })
36
 
37
- class CalendarService:
38
- def __init__(self):
39
- self.toolset = ComposioToolSet(api_key=os.getenv('COMPOSIO_API_KEY'))
40
- self.connections: Dict[str, Dict[str, Any]] = {}
41
- self.connectionRequest = None
42
- def analyze_calendar_events(self, response_data):
43
- """
44
- Analyze calendar events and return statistics about meetings.
45
- """
46
- current_year = datetime.now().year
47
- meetings = []
48
- participants = []
49
- meeting_times = []
50
- total_duration = timedelta()
51
- monthly_meetings = defaultdict(int)
52
- daily_meetings = defaultdict(int)
53
-
54
- events = response_data.get('data', {}).get('event_data', {}).get('event_data', [])
55
-
56
- for event in events:
57
- start_data = event.get('start', {})
58
- end_data = event.get('end', {})
59
-
60
- try:
61
- start = datetime.fromisoformat(start_data.get('dateTime').replace('Z', '+00:00'))
62
- end = datetime.fromisoformat(end_data.get('dateTime').replace('Z', '+00:00'))
63
-
64
- if start.year == current_year:
65
- duration = end - start
66
- total_duration += duration
67
-
68
- monthly_meetings[start.strftime('%B')] += 1
69
- daily_meetings[start.strftime('%A')] += 1
70
- meeting_times.append(start.strftime('%H:%M'))
71
-
72
- if 'attendees' in event:
73
- for attendee in event['attendees']:
74
- if attendee.get('responseStatus') != 'declined':
75
- participants.append(attendee.get('email'))
76
-
77
- organizer_email = event.get('organizer', {}).get('email')
78
- if organizer_email:
79
- participants.append(organizer_email)
80
-
81
- meetings.append({
82
- 'start': start,
83
- 'duration': duration,
84
- 'summary': event.get('summary', 'No Title')
85
- })
86
- except (ValueError, TypeError, AttributeError) as e:
87
- print(f"Error processing event: {e}")
88
- continue
89
-
90
- total_meetings = len(meetings)
91
- stats = {
92
- "total_meetings_this_year": total_meetings
93
- }
94
-
95
- if total_meetings > 0:
96
- stats.update({
97
- "total_time_spent": str(total_duration),
98
- "busiest_month": max(monthly_meetings.items(), key=lambda x: x[1])[0] if monthly_meetings else "N/A",
99
- "busiest_day": max(daily_meetings.items(), key=lambda x: x[1])[0] if daily_meetings else "N/A",
100
- "most_frequent_participant": Counter(participants).most_common(1)[0][0] if participants else "N/A",
101
- "average_meeting_duration": str(total_duration / total_meetings),
102
- "most_common_meeting_time": Counter(meeting_times).most_common(1)[0][0] if meeting_times else "N/A",
103
- "monthly_breakdown": dict(monthly_meetings),
104
- "daily_breakdown": dict(daily_meetings)
105
- })
106
  else:
107
- stats.update({
108
- "total_time_spent": "0:00:00",
109
- "busiest_month": "N/A",
110
- "busiest_day": "N/A",
111
- "most_frequent_participant": "N/A",
112
- "average_meeting_duration": "0:00:00",
113
- "most_common_meeting_time": "N/A",
114
- "monthly_breakdown": {},
115
- "daily_breakdown": {}
116
- })
117
-
118
- return stats
119
-
120
- def initiate_connection(self, entity_id: str, redirect_url: Optional[str] = None) -> APIResponse:
121
- try:
122
- if not redirect_url:
123
- redirect_url = "https://calendar-wrapped-eight.vercel.app/"
124
-
125
- connection_request = self.toolset.initiate_connection(
126
- entity_id=entity_id,
127
- app=App.GOOGLECALENDAR,
128
- redirect_url=redirect_url
129
- )
130
-
131
- self.connections[entity_id] = {
132
- 'status': ConnectionStatus.PENDING.value,
133
- 'redirect_url': connection_request.redirectUrl,
134
- 'created_at': datetime.now().isoformat()
135
- }
136
 
137
- self.connectionRequest = connection_request
138
-
139
- return APIResponse(
140
- success=True,
141
- data={
142
- 'status': ConnectionStatus.PENDING.value,
143
- 'redirect_url': connection_request.redirectUrl,
144
- 'wait_time': 60,
145
- 'message': "Please authenticate using the provided link."
146
- }
147
- )
148
-
149
- except Exception as e:
150
- return APIResponse(
151
- success=False,
152
- error=f"Failed to initiate connection: {str(e)}"
153
- )
154
-
155
- def check_status(self, entity_id: str) -> APIResponse:
156
- try:
157
- status = self.connectionRequest.connectionStatus
158
 
159
- if status == 'ACTIVE':
160
- status='active'
161
- connection = self.connections[entity_id]
162
-
163
- return APIResponse(
164
- success=True,
165
- data={
166
- 'status': status,
167
- 'message': f"Connection status: {connection['status']}"
168
- }
169
- )
170
-
171
- except Exception as e:
172
- return APIResponse(
173
- success=False,
174
- error=f"Failed to check status: {str(e)}"
175
- )
176
-
177
- def generate_wrapped(self, entity_id: str) -> APIResponse:
178
- try:
179
- # Get current year's start and end dates
180
- current_year = datetime.now().year
181
- time_min = f"{current_year},1,1,0,0,0"
182
- time_max = f"{current_year},12,31,23,59,59"
183
-
184
- request_params = {
185
- "calendar_id": "primary",
186
- "timeMin": time_min,
187
- "timeMax": time_max,
188
- "single_events": True,
189
- "max_results": 2500,
190
- "order_by": "startTime"
191
- }
192
-
193
- events_response = self.toolset.execute_action(
194
- action=Action.GOOGLECALENDAR_FIND_EVENT,
195
- params=request_params,
196
- entity_id=entity_id
197
- )
198
-
199
- if events_response["successfull"]:
200
- stats = self.analyze_calendar_events(events_response)
201
-
202
- # Get tech billionaire comparison
203
- billionaire_prompt = f"""Based on these calendar stats, which tech billionaire's schedule does this most resemble and why?
204
- Stats:
205
- - {stats['total_meetings_this_year']} total meetings
206
- - {stats['total_time_spent']} total time in meetings
207
- - Most active on {stats['busiest_day']}s
208
- - Busiest month is {stats['busiest_month']}
209
- - Average meeting duration: {stats['average_meeting_duration']}
210
-
211
- Return as JSON with format: {{"name": "billionaire name", "reason": "explanation"}}
212
- """
213
-
214
- # Get comments for each stat
215
- stats_prompt = f"""Analyze these calendar stats and write a brief, insightful one-sentence comment for each metric:
216
- - Total meetings: {stats['total_meetings_this_year']}
217
- - Total time in meetings: {stats['total_time_spent']}
218
- - Busiest month: {stats['busiest_month']}
219
- - Busiest day: {stats['busiest_day']}
220
- - Average meeting duration: {stats['average_meeting_duration']}
221
- - Most common meeting time: {stats['most_common_meeting_time']}
222
- - Most frequent participant: {stats['most_frequent_participant']}
223
-
224
- Return as JSON with format: {{"total_meetings_comment": "", "time_spent_comment": "", "busiest_times_comment": "", "collaborator_comment": "", "habits_comment": ""}}
225
- """
226
-
227
- # Make LLM calls
228
- try:
229
- billionaire_response = json.loads(llm.complete(billionaire_prompt).text)
230
- stats_comments = json.loads(llm.complete(stats_prompt).text)
231
-
232
- # Add new fields to stats
233
- stats["schedule_analysis"] = billionaire_response
234
- stats["metric_insights"] = stats_comments
235
- except (json.JSONDecodeError, Exception) as e:
236
- print(f"Error processing LLM responses: {e}")
237
- # Add empty defaults if LLM processing fails
238
- stats["schedule_analysis"] = {"name": "Unknown", "reason": "Analysis unavailable"}
239
- stats["metric_insights"] = {"total_meetings_comment": "", "time_spent_comment": "",
240
- "busiest_times_comment": "", "collaborator_comment": "",
241
- "habits_comment": ""}
242
-
243
- return APIResponse(
244
- success=True,
245
- data=stats
246
- )
247
- else:
248
- return APIResponse(
249
- success=False,
250
- error=events_response["error"] or "Failed to fetch calendar events"
251
- )
252
-
253
- except Exception as e:
254
- return APIResponse(
255
- success=False,
256
- error=f"Failed to generate wrapped: {str(e)}"
257
- )
258
 
259
- def create_gradio_api():
260
- service = CalendarService()
261
-
262
- def handle_connection(entity_id: str, redirect_url: Optional[str] = None) -> str:
263
- response = service.initiate_connection(entity_id, redirect_url)
264
- return response.to_json()
265
-
266
- def check_status(entity_id: str) -> str:
267
- response = service.check_status(entity_id)
268
- return response.to_json()
269
-
270
- def generate_wrapped(entity_id: str) -> str:
271
- response = service.generate_wrapped(entity_id)
272
- return response.to_json()
273
-
274
- # Create API endpoints
275
- connection_api = gr.Interface(
276
- fn=handle_connection,
277
- inputs=[
278
- gr.Textbox(label="Entity ID"),
279
- gr.Textbox(label="Redirect URL", placeholder="https://yourwebsite.com/connection/success")
280
- ],
281
- outputs=gr.JSON(),
282
- title="Initialize Calendar Connection",
283
- description="Start a new calendar connection for an entity",
284
- examples=[["user123", "https://example.com/callback"]]
285
- )
286
-
287
- status_api = gr.Interface(
288
- fn=check_status,
289
- inputs=gr.Textbox(label="Entity ID"),
290
- outputs=gr.JSON(),
291
- title="Check Connection Status",
292
- description="Check the status of an existing connection",
293
- examples=[["user123"]]
294
- )
295
-
296
- wrapped_api = gr.Interface(
297
- fn=generate_wrapped,
298
- inputs=gr.Textbox(label="Entity ID"),
299
- outputs=gr.JSON(),
300
- title="Generate Calendar Wrapped",
301
- description="Generate a calendar wrapped summary for an entity",
302
- examples=[["user123"]]
303
- )
304
-
305
- # Combine all interfaces
306
- api = gr.TabbedInterface(
307
- [connection_api, status_api, wrapped_api],
308
- ["Connect", "Check Status", "Generate Wrapped"],
309
- title="Calendar Wrapped API",
310
- )
311
-
312
- return api
313
 
314
- if __name__ == "__main__":
315
- api = create_gradio_api()
316
- api.launch(server_name="0.0.0.0", server_port=7860)
 
 
 
 
 
 
 
 
1
  import gradio as gr
2
+ from composio import ComposioToolSet, App, Action
3
+ from datetime import datetime
4
  import json
 
5
 
6
+ # Initialize the Composio toolset
7
+ toolset = ComposioToolSet()
8
 
9
+ # In-memory store for user connections
10
+ user_connections = {}
11
 
12
+ # Endpoint 1: Connect the user
13
+ def connect_user(entity_id):
14
+ connection_request = toolset.initiate_connection(
15
+ entity_id=entity_id,
16
+ app=App.GMAIL,
17
+ )
18
+
19
+ user_connections[entity_id] = {
20
+ "connection_status": connection_request.connectionStatus
21
+ }
22
+
23
+ if connection_request.connectionStatus == "INITIATED":
24
+ user_connections[entity_id]["redirect_url"] = connection_request.redirectUrl
25
+ return f"Connection initiated. Redirect user to: {connection_request.redirectUrl}"
26
+
27
+ elif connection_request.connectionStatus == "ACTIVE":
28
+ return "Connection is active."
29
+
30
+ else:
31
+ return "Connection failed. Please try again."
32
+
33
+ # Endpoint 2: Check connection status
34
+ def check_connection_status(entity_id):
35
+ if entity_id not in user_connections:
36
+ return "No connection found for this user."
37
 
38
+ return user_connections[entity_id]["connection_status"]
 
 
 
 
 
 
 
 
 
 
 
39
 
40
+ # Endpoint 3: Generate wrapped analysis
41
+ def generate_wrapped(entity_id):
42
+ if entity_id not in user_connections or user_connections[entity_id]["connection_status"] != "ACTIVE":
43
+ return "User is not connected or connection is not active."
44
+
45
+ current_year = datetime.now().year
46
+ time_min = f"{current_year}-01-01T00:00:00Z"
47
+ time_max = f"{current_year}-12-31T23:59:59Z"
48
+
49
+ request_params = {
50
+ "calendar_id": "primary",
51
+ "timeMin": time_min,
52
+ "timeMax": time_max,
53
+ "single_events": True,
54
+ "max_results": 2500,
55
+ "order_by": "startTime",
56
+ }
57
+
58
+ try:
59
+ events_response = toolset.execute_action(
60
+ action=Action.GOOGLECALENDAR_FIND_EVENT,
61
+ params=request_params,
62
+ entity_id=entity_id
63
+ )
64
+
65
+ if events_response["successfull"]:
66
+ stats = analyze_calendar_events(events_response)
67
+ return json.dumps(stats, indent=2)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  else:
69
+ return "Failed to fetch calendar events."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
 
71
+ except Exception as e:
72
+ return f"Error: {e}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
 
74
+ # Helper function to analyze events (mocked for simplicity)
75
+ def analyze_calendar_events(events_response):
76
+ # Mocking the analysis part for simplicity
77
+ return {
78
+ "total_meetings_this_year": 120,
79
+ "total_time_spent": "150 hours",
80
+ "busiest_day": "Tuesday",
81
+ "busiest_month": "September",
82
+ "average_meeting_duration": "1 hour",
83
+ "most_common_meeting_time": "10:00 AM",
84
+ "most_frequent_participant": "John Doe",
85
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
 
87
+ # Gradio interface
88
+ def connect(entity_id):
89
+ return connect_user(entity_id)
90
+
91
+ def status(entity_id):
92
+ return check_connection_status(entity_id)
93
+
94
+ def wrapped(entity_id):
95
+ return generate_wrapped(entity_id)
96
+
97
+ with gr.Blocks() as demo:
98
+ gr.Markdown("# Calendar Wrapped API")
99
+
100
+ with gr.Row():
101
+ entity_id_input = gr.Textbox(label="Entity ID")
102
+
103
+ with gr.Row():
104
+ connect_btn = gr.Button("Connect User")
105
+ status_btn = gr.Button("Check Status")
106
+ wrapped_btn = gr.Button("Generate Wrapped")
107
+
108
+ with gr.Row():
109
+ connect_output = gr.Textbox(label="Connect Output")
110
+ status_output = gr.Textbox(label="Status Output")
111
+ wrapped_output = gr.Textbox(label="Wrapped Output")
112
+
113
+ connect_btn.click(connect, inputs=entity_id_input, outputs=connect_output)
114
+ status_btn.click(status, inputs=entity_id_input, outputs=status_output)
115
+ wrapped_btn.click(wrapped, inputs=entity_id_input, outputs=wrapped_output)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
 
117
+ demo.launch()