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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +265 -108
app.py CHANGED
@@ -1,117 +1,274 @@
 
 
 
 
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()
 
 
 
1
+ from datetime import datetime, timedelta
2
+ from collections import defaultdict, Counter
3
+ from llama_index.llms.openai import OpenAI
4
+ from composio_llamaindex import ComposioToolSet, App, Action
5
  import gradio as gr
6
+ import os
 
7
  import json
8
+ from dotenv import load_dotenv
9
 
10
+ # Load environment variables
11
+ load_dotenv()
12
 
13
+ llm = OpenAI(model='gpt-4', api_key=os.getenv('OPENAI_API_KEY'))
 
14
 
15
+ class CalendarService:
16
+ def __init__(self):
17
+ self.toolset = ComposioToolSet(api_key=os.getenv('COMPOSIO_API_KEY'))
18
+ self.connection_request = None
 
 
19
 
20
+ def analyze_calendar_events(self, response_data):
21
+ """
22
+ Analyze calendar events and return statistics about meetings.
23
+ """
24
+ current_year = datetime.now().year
25
+ meetings = []
26
+ participants = []
27
+ meeting_times = []
28
+ total_duration = timedelta()
29
+ monthly_meetings = defaultdict(int)
30
+ daily_meetings = defaultdict(int)
31
+
32
+ events = response_data.get('data', {}).get('event_data', {}).get('event_data', [])
33
+
34
+ for event in events:
35
+ start_data = event.get('start', {})
36
+ end_data = event.get('end', {})
37
+
38
+ try:
39
+ start = datetime.fromisoformat(start_data.get('dateTime').replace('Z', '+00:00'))
40
+ end = datetime.fromisoformat(end_data.get('dateTime').replace('Z', '+00:00'))
41
+
42
+ if start.year == current_year:
43
+ duration = end - start
44
+ total_duration += duration
45
+
46
+ monthly_meetings[start.strftime('%B')] += 1
47
+ daily_meetings[start.strftime('%A')] += 1
48
+ meeting_times.append(start.strftime('%H:%M'))
49
+
50
+ if 'attendees' in event:
51
+ for attendee in event['attendees']:
52
+ if attendee.get('responseStatus') != 'declined':
53
+ participants.append(attendee.get('email'))
54
+
55
+ organizer_email = event.get('organizer', {}).get('email')
56
+ if organizer_email:
57
+ participants.append(organizer_email)
58
+
59
+ meetings.append({
60
+ 'start': start,
61
+ 'duration': duration,
62
+ 'summary': event.get('summary', 'No Title')
63
+ })
64
+ except (ValueError, TypeError, AttributeError) as e:
65
+ print(f"Error processing event: {e}")
66
+ continue
67
+
68
+ total_meetings = len(meetings)
69
+ stats = {
70
+ "total_meetings_this_year": total_meetings
71
+ }
72
+
73
+ if total_meetings > 0:
74
+ stats.update({
75
+ "total_time_spent": str(total_duration),
76
+ "busiest_month": max(monthly_meetings.items(), key=lambda x: x[1])[0] if monthly_meetings else "N/A",
77
+ "busiest_day": max(daily_meetings.items(), key=lambda x: x[1])[0] if daily_meetings else "N/A",
78
+ "most_frequent_participant": Counter(participants).most_common(1)[0][0] if participants else "N/A",
79
+ "average_meeting_duration": str(total_duration / total_meetings),
80
+ "most_common_meeting_time": Counter(meeting_times).most_common(1)[0][0] if meeting_times else "N/A",
81
+ "monthly_breakdown": dict(monthly_meetings),
82
+ "daily_breakdown": dict(daily_meetings)
83
+ })
84
  else:
85
+ stats.update({
86
+ "total_time_spent": "0:00:00",
87
+ "busiest_month": "N/A",
88
+ "busiest_day": "N/A",
89
+ "most_frequent_participant": "N/A",
90
+ "average_meeting_duration": "0:00:00",
91
+ "most_common_meeting_time": "N/A",
92
+ "monthly_breakdown": {},
93
+ "daily_breakdown": {}
94
+ })
95
+
96
+ return stats
97
+
98
+ def initiate_connection(self, entity_id: str, redirect_url: str = "https://calendar-wrapped-eight.vercel.app/") -> dict:
99
+ try:
100
+ self.connection_request = self.toolset.initiate_connection(
101
+ entity_id=entity_id,
102
+ app=App.GOOGLECALENDAR,
103
+ redirect_url=redirect_url
104
+ )
105
+
106
+ return {
107
+ 'success': True,
108
+ 'data': {
109
+ 'redirect_url': self.connection_request.redirectUrl,
110
+ 'message': "Please authenticate using the provided link."
111
+ }
112
+ }
113
+ except Exception as e:
114
+ return {
115
+ 'success': False,
116
+ 'error': str(e)
117
+ }
118
+
119
+ def check_connection_status(self, entity_id: str) -> dict:
120
+ try:
121
+ if not self.connection_request:
122
+ return {
123
+ 'success': False,
124
+ 'error': 'No active connection request found'
125
+ }
126
+
127
+ status = self.connection_request.connectionStatus
128
+ return {
129
+ 'success': True,
130
+ 'data': {
131
+ 'status': status,
132
+ 'message': f"Connection status: {status}"
133
+ }
134
+ }
135
+ except Exception as e:
136
+ return {
137
+ 'success': False,
138
+ 'error': str(e)
139
+ }
140
+
141
+ def generate_wrapped(self, entity_id: str) -> dict:
142
+ try:
143
+ current_year = datetime.now().year
144
+ request_params = {
145
+ "calendar_id": "primary",
146
+ "timeMin": f"{current_year},1,1,0,0,0",
147
+ "timeMax": f"{current_year},12,31,23,59,59",
148
+ "single_events": True,
149
+ "max_results": 2500,
150
+ "order_by": "startTime"
151
+ }
152
+
153
+ events_response = self.toolset.execute_action(
154
+ action=Action.GOOGLECALENDAR_FIND_EVENT,
155
+ params=request_params,
156
+ entity_id=entity_id
157
+ )
158
+
159
+ if events_response["successfull"]:
160
+ stats = self.analyze_calendar_events(events_response)
161
+
162
+ # Generate prompts for LLM analysis
163
+ billionaire_prompt = f"""Based on these calendar stats, which tech billionaire's schedule does this most resemble and why?
164
+ Stats:
165
+ - {stats['total_meetings_this_year']} total meetings
166
+ - {stats['total_time_spent']} total time in meetings
167
+ - Most active on {stats['busiest_day']}s
168
+ - Busiest month is {stats['busiest_month']}
169
+ - Average meeting duration: {stats['average_meeting_duration']}
170
+
171
+ Return as JSON with format: {{"name": "billionaire name", "reason": "explanation"}}
172
+ """
173
+
174
+ stats_prompt = f"""Analyze these calendar stats and write a brief, insightful one-sentence comment for each metric:
175
+ - Total meetings: {stats['total_meetings_this_year']}
176
+ - Total time in meetings: {stats['total_time_spent']}
177
+ - Busiest month: {stats['busiest_month']}
178
+ - Busiest day: {stats['busiest_day']}
179
+ - Average meeting duration: {stats['average_meeting_duration']}
180
+ - Most common meeting time: {stats['most_common_meeting_time']}
181
+ - Most frequent participant: {stats['most_frequent_participant']}
182
+
183
+ Return as JSON with format: {{"total_meetings_comment": "", "time_spent_comment": "", "busiest_times_comment": "", "collaborator_comment": "", "habits_comment": ""}}
184
+ """
185
+
186
+ try:
187
+ billionaire_response = json.loads(llm.complete(billionaire_prompt).text)
188
+ stats_comments = json.loads(llm.complete(stats_prompt).text)
189
+
190
+ stats["schedule_analysis"] = billionaire_response
191
+ stats["metric_insights"] = stats_comments
192
+ except Exception as e:
193
+ print(f"Error processing LLM responses: {e}")
194
+ stats["schedule_analysis"] = {"name": "Unknown", "reason": "Analysis unavailable"}
195
+ stats["metric_insights"] = {
196
+ "total_meetings_comment": "",
197
+ "time_spent_comment": "",
198
+ "busiest_times_comment": "",
199
+ "collaborator_comment": "",
200
+ "habits_comment": ""
201
+ }
202
+
203
+ return {
204
+ 'success': True,
205
+ 'data': stats
206
+ }
207
+ else:
208
+ return {
209
+ 'success': False,
210
+ 'error': events_response.get("error", "Failed to fetch calendar events")
211
+ }
212
+
213
+ except Exception as e:
214
+ return {
215
+ 'success': False,
216
+ 'error': str(e)
217
+ }
218
 
219
+ def create_gradio_interface():
220
+ service = CalendarService()
221
+
222
+ def handle_connection(entity_id: str, redirect_url: str = None) -> str:
223
+ return json.dumps(service.initiate_connection(entity_id, redirect_url))
224
+
225
+ def check_status(entity_id: str) -> str:
226
+ return json.dumps(service.check_connection_status(entity_id))
227
+
228
+ def generate_wrapped(entity_id: str) -> str:
229
+ return json.dumps(service.generate_wrapped(entity_id))
230
+
231
+ # Create Gradio interface
232
+ with gr.Blocks(title="Calendar Wrapped API") as interface:
233
+ gr.Markdown("# Calendar Wrapped API")
234
+
235
+ with gr.Tab("Connect"):
236
+ entity_input = gr.Textbox(label="Entity ID")
237
+ redirect_input = gr.Textbox(
238
+ label="Redirect URL",
239
+ placeholder="https://yourwebsite.com/connection/success",
240
+ value="https://calendar-wrapped-eight.vercel.app/"
241
+ )
242
+ connect_btn = gr.Button("Initialize Connection")
243
+ connect_output = gr.JSON()
244
+ connect_btn.click(
245
+ fn=handle_connection,
246
+ inputs=[entity_input, redirect_input],
247
+ outputs=connect_output
248
+ )
249
+
250
+ with gr.Tab("Check Status"):
251
+ status_input = gr.Textbox(label="Entity ID")
252
+ status_btn = gr.Button("Check Status")
253
+ status_output = gr.JSON()
254
+ status_btn.click(
255
+ fn=check_status,
256
+ inputs=status_input,
257
+ outputs=status_output
258
+ )
259
+
260
+ with gr.Tab("Generate Wrapped"):
261
+ wrapped_input = gr.Textbox(label="Entity ID")
262
+ wrapped_btn = gr.Button("Generate Wrapped")
263
+ wrapped_output = gr.JSON()
264
+ wrapped_btn.click(
265
+ fn=generate_wrapped,
266
+ inputs=wrapped_input,
267
+ outputs=wrapped_output
268
+ )
269
+
270
+ return interface
271
 
272
+ if __name__ == "__main__":
273
+ interface = create_gradio_interface()
274
+ interface.launch(server_name="0.0.0.0", server_port=7860)