BroBro87 commited on
Commit
1743e60
·
verified ·
1 Parent(s): b32ce4c

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +303 -0
app.py ADDED
@@ -0,0 +1,303 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from datetime import datetime, timedelta
3
+ from collections import defaultdict, Counter
4
+ from llama_index.llms.openai import OpenAI
5
+ from composio_llamaindex import ComposioToolSet, App, Action
6
+ import os
7
+ import json
8
+ from dotenv import load_dotenv
9
+
10
+ # Load environment variables
11
+ load_dotenv()
12
+
13
+ class CalendarService:
14
+ def __init__(self):
15
+ self.toolset = ComposioToolSet(api_key=os.getenv('COMPOSIO_API_KEY'))
16
+ self.connection_request = None
17
+
18
+ # [Previous CalendarService methods remain the same]
19
+ def analyze_calendar_events(self, response_data):
20
+ """
21
+ Analyze calendar events and return statistics about meetings.
22
+ """
23
+ current_year = datetime.now().year
24
+ meetings = []
25
+ participants = []
26
+ meeting_times = []
27
+ total_duration = timedelta()
28
+ monthly_meetings = defaultdict(int)
29
+ daily_meetings = defaultdict(int)
30
+
31
+ events = response_data.get('data', {}).get('event_data', {}).get('event_data', [])
32
+
33
+ for event in events:
34
+ start_data = event.get('start', {})
35
+ end_data = event.get('end', {})
36
+
37
+ try:
38
+ start = datetime.fromisoformat(start_data.get('dateTime').replace('Z', '+00:00'))
39
+ end = datetime.fromisoformat(end_data.get('dateTime').replace('Z', '+00:00'))
40
+
41
+ if start.year == current_year:
42
+ duration = end - start
43
+ total_duration += duration
44
+
45
+ monthly_meetings[start.strftime('%B')] += 1
46
+ daily_meetings[start.strftime('%A')] += 1
47
+ meeting_times.append(start.strftime('%H:%M'))
48
+
49
+ if 'attendees' in event:
50
+ for attendee in event['attendees']:
51
+ if attendee.get('responseStatus') != 'declined':
52
+ participants.append(attendee.get('email'))
53
+
54
+ organizer_email = event.get('organizer', {}).get('email')
55
+ if organizer_email:
56
+ participants.append(organizer_email)
57
+
58
+ meetings.append({
59
+ 'start': start,
60
+ 'duration': duration,
61
+ 'summary': event.get('summary', 'No Title')
62
+ })
63
+ except (ValueError, TypeError, AttributeError) as e:
64
+ st.error(f"Error processing event: {e}")
65
+ continue
66
+
67
+ total_meetings = len(meetings)
68
+ stats = {
69
+ "total_meetings_this_year": total_meetings
70
+ }
71
+
72
+ if total_meetings > 0:
73
+ stats.update({
74
+ "total_time_spent": str(total_duration),
75
+ "busiest_month": max(monthly_meetings.items(), key=lambda x: x[1])[0] if monthly_meetings else "N/A",
76
+ "busiest_day": max(daily_meetings.items(), key=lambda x: x[1])[0] if daily_meetings else "N/A",
77
+ "most_frequent_participant": Counter(participants).most_common(1)[0][0] if participants else "N/A",
78
+ "average_meeting_duration": str(total_duration / total_meetings),
79
+ "most_common_meeting_time": Counter(meeting_times).most_common(1)[0][0] if meeting_times else "N/A",
80
+ "monthly_breakdown": dict(monthly_meetings),
81
+ "daily_breakdown": dict(daily_meetings)
82
+ })
83
+ else:
84
+ stats.update({
85
+ "total_time_spent": "0:00:00",
86
+ "busiest_month": "N/A",
87
+ "busiest_day": "N/A",
88
+ "most_frequent_participant": "N/A",
89
+ "average_meeting_duration": "0:00:00",
90
+ "most_common_meeting_time": "N/A",
91
+ "monthly_breakdown": {},
92
+ "daily_breakdown": {}
93
+ })
94
+
95
+ return stats
96
+
97
+ def initiate_connection(self, entity_id: str, redirect_url: str = "https://calendar-wrapped-eight.vercel.app/") -> dict:
98
+ try:
99
+ self.connection_request = self.toolset.initiate_connection(
100
+ entity_id=entity_id,
101
+ app=App.GOOGLECALENDAR,
102
+ )
103
+
104
+ return {
105
+ 'success': True,
106
+ 'data': {
107
+ 'redirect_url': self.connection_request.redirectUrl,
108
+ 'message': "Please authenticate using the provided link."
109
+ }
110
+ }
111
+ except Exception as e:
112
+ return {
113
+ 'success': False,
114
+ 'error': str(e)
115
+ }
116
+
117
+ def check_connection_status(self, entity_id: str) -> dict:
118
+ try:
119
+ entity_id = self.toolset.get_entity(id=entity_id)
120
+ connection = entity_id.get_connection(app=App.GOOGLECALENDAR)
121
+ status = connection.status
122
+ return {
123
+ 'success': True,
124
+ 'data': {
125
+ 'status': status,
126
+ 'message': f"Connection status: {status}"
127
+ }
128
+ }
129
+ except Exception as e:
130
+ return {
131
+ 'success': False,
132
+ 'error': str(e)
133
+ }
134
+
135
+ def generate_wrapped(self, entity_id: str) -> dict:
136
+ try:
137
+ current_year = datetime.now().year
138
+ request_params = {
139
+ "calendar_id": "primary",
140
+ "timeMin": f"{current_year},1,1,0,0,0",
141
+ "timeMax": f"{current_year},12,31,23,59,59",
142
+ "single_events": True,
143
+ "max_results": 2500,
144
+ "order_by": "startTime"
145
+ }
146
+
147
+ events_response = self.toolset.execute_action(
148
+ action=Action.GOOGLECALENDAR_FIND_EVENT,
149
+ params=request_params,
150
+ entity_id=entity_id
151
+ )
152
+
153
+ if events_response["successfull"]:
154
+ stats = self.analyze_calendar_events(events_response)
155
+
156
+ llm = OpenAI(model='gpt-4', api_key=os.getenv('OPENAI_API_KEY'))
157
+
158
+ billionaire_prompt = f"""Based on these calendar stats, which tech billionaire's schedule does this most resemble and why?
159
+ Stats:
160
+ - {stats['total_meetings_this_year']} total meetings
161
+ - {stats['total_time_spent']} total time in meetings
162
+ - Most active on {stats['busiest_day']}s
163
+ - Busiest month is {stats['busiest_month']}
164
+ - Average meeting duration: {stats['average_meeting_duration']}
165
+ Suggest a different billionaire each time, dont say elon.
166
+ Return as JSON with format: {{"name": "billionaire name", "reason": "explanation"}}
167
+ """
168
+
169
+ stats_prompt = f"""Analyze these calendar stats and write a brief, insightful one-sentence comment for each metric:
170
+ - Total meetings: {stats['total_meetings_this_year']}
171
+ - Total time in meetings: {stats['total_time_spent']}
172
+ - Busiest month: {stats['busiest_month']}
173
+ - Busiest day: {stats['busiest_day']}
174
+ - Average meeting duration: {stats['average_meeting_duration']}
175
+ - Most common meeting time: {stats['most_common_meeting_time']}
176
+ - Most frequent participant: {stats['most_frequent_participant']}
177
+
178
+ Return as JSON with format: {{"total_meetings_comment": "", "time_spent_comment": "", "busiest_times_comment": "", "collaborator_comment": "", "habits_comment": ""}}
179
+ """
180
+
181
+ try:
182
+ billionaire_response = json.loads(llm.complete(billionaire_prompt).text)
183
+ stats_comments = json.loads(llm.complete(stats_prompt).text)
184
+
185
+ stats["schedule_analysis"] = billionaire_response
186
+ stats["metric_insights"] = stats_comments
187
+ except Exception as e:
188
+ st.error(f"Error processing LLM responses: {e}")
189
+ stats["schedule_analysis"] = {"name": "Unknown", "reason": "Analysis unavailable"}
190
+ stats["metric_insights"] = {
191
+ "total_meetings_comment": "",
192
+ "time_spent_comment": "",
193
+ "busiest_times_comment": "",
194
+ "collaborator_comment": "",
195
+ "habits_comment": ""
196
+ }
197
+
198
+ return {
199
+ 'success': True,
200
+ 'data': stats
201
+ }
202
+ else:
203
+ return {
204
+ 'success': False,
205
+ 'error': events_response.get("error", "Failed to fetch calendar events")
206
+ }
207
+
208
+ except Exception as e:
209
+ return {
210
+ 'success': False,
211
+ 'error': str(e)
212
+ }
213
+
214
+ def main():
215
+ st.set_page_config(page_title="Calendar Wrapped", layout="wide")
216
+
217
+ st.title("Calendar Wrapped")
218
+
219
+ service = CalendarService()
220
+
221
+ tab1, tab2, tab3 = st.tabs(["Connect", "Check Status", "Generate Wrapped"])
222
+
223
+ with tab1:
224
+ st.header("Initialize Connection")
225
+ entity_id = st.text_input("Entity ID")
226
+ redirect_url = st.text_input("Redirect URL", value="https://calendar-wrapped-eight.vercel.app/")
227
+
228
+ if st.button("Initialize Connection"):
229
+ if entity_id:
230
+ result = service.initiate_connection(entity_id, redirect_url)
231
+ if result['success']:
232
+ st.success(result['data']['message'])
233
+ st.markdown(f"[Click here to authenticate]({result['data']['redirect_url']})")
234
+ else:
235
+ st.error(f"Error: {result['error']}")
236
+ else:
237
+ st.warning("Please enter an Entity ID")
238
+
239
+ with tab2:
240
+ st.header("Check Connection Status")
241
+ status_entity_id = st.text_input("Entity ID", key="status_entity_id")
242
+
243
+ if st.button("Check Status"):
244
+ if status_entity_id:
245
+ result = service.check_connection_status(status_entity_id)
246
+ if result['success']:
247
+ st.success(result['data']['message'])
248
+ else:
249
+ st.error(f"Error: {result['error']}")
250
+ else:
251
+ st.warning("Please enter an Entity ID")
252
+
253
+ with tab3:
254
+ st.header("Generate Calendar Wrapped")
255
+ wrapped_entity_id = st.text_input("Entity ID", key="wrapped_entity_id")
256
+
257
+ if st.button("Generate Wrapped"):
258
+ if wrapped_entity_id:
259
+ with st.spinner("Generating your Calendar Wrapped..."):
260
+ result = service.generate_wrapped(wrapped_entity_id)
261
+ if result['success']:
262
+ data = result['data']
263
+
264
+ # Display basic stats
265
+ col1, col2, col3 = st.columns(3)
266
+ with col1:
267
+ st.metric("Total Meetings", data['total_meetings_this_year'])
268
+ with col2:
269
+ st.metric("Total Time in Meetings", data['total_time_spent'])
270
+ with col3:
271
+ st.metric("Average Meeting Duration", data['average_meeting_duration'])
272
+
273
+ # Display schedule analysis
274
+ st.subheader("Schedule Analysis")
275
+ if data.get('schedule_analysis'):
276
+ st.write(f"Your schedule most resembles: **{data['schedule_analysis']['name']}**")
277
+ st.write(f"*{data['schedule_analysis']['reason']}*")
278
+
279
+ # Display insights
280
+ st.subheader("Insights")
281
+ if data.get('metric_insights'):
282
+ insights = data['metric_insights']
283
+ for key, value in insights.items():
284
+ if value: # Only display non-empty insights
285
+ st.write(f"- {value}")
286
+
287
+ # Display breakdowns
288
+ col1, col2 = st.columns(2)
289
+ with col1:
290
+ st.subheader("Monthly Breakdown")
291
+ st.bar_chart(data['monthly_breakdown'])
292
+
293
+ with col2:
294
+ st.subheader("Daily Breakdown")
295
+ st.bar_chart(data['daily_breakdown'])
296
+
297
+ else:
298
+ st.error(f"Error: {result['error']}")
299
+ else:
300
+ st.warning("Please enter an Entity ID")
301
+
302
+ if __name__ == "__main__":
303
+ main()