Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -2,15 +2,12 @@ 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
|
6 |
-
from
|
7 |
-
from llama_index.llms.openai import OpenAI
|
8 |
-
from llama_index.llms.gemini import Gemini
|
9 |
-
from dotenv import load_dotenv
|
10 |
import gradio as gr
|
11 |
import os
|
12 |
import json
|
13 |
-
from
|
14 |
|
15 |
# Load environment variables
|
16 |
load_dotenv()
|
@@ -37,8 +34,85 @@ class APIResponse:
|
|
37 |
class CalendarService:
|
38 |
def __init__(self):
|
39 |
self.toolset = ComposioToolSet(api_key=os.getenv('COMPOSIO_API_KEY'))
|
40 |
-
self.llm = OpenAI(model="gpt-4o", api_key=os.getenv('OPENAI_API_KEY'))
|
41 |
self.connections: Dict[str, Dict[str, Any]] = {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
42 |
|
43 |
def initiate_connection(self, entity_id: str, redirect_url: Optional[str] = None) -> APIResponse:
|
44 |
try:
|
@@ -80,8 +154,6 @@ class CalendarService:
|
|
80 |
error="No connection found for this entity ID"
|
81 |
)
|
82 |
|
83 |
-
# In a real implementation, you would check the actual status
|
84 |
-
# For now, we'll simulate the status check
|
85 |
connection = self.connections[entity_id]
|
86 |
|
87 |
return APIResponse(
|
@@ -100,52 +172,37 @@ class CalendarService:
|
|
100 |
|
101 |
def generate_wrapped(self, entity_id: str) -> APIResponse:
|
102 |
try:
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
Generate a creative Google Calendar Wrapped summary including:
|
117 |
-
1. Total meetings this year
|
118 |
-
2. Overall time spent in meetings
|
119 |
-
3. Busiest month
|
120 |
-
4. Busiest day
|
121 |
-
5. Most frequent meeting participant
|
122 |
-
6. Average meeting duration
|
123 |
-
7. Most common meeting time
|
124 |
-
|
125 |
-
Be entertaining and witty with the analysis. Conclude by assigning
|
126 |
-
a Greek god persona based on their meeting patterns.
|
127 |
-
Search data from last 6 months instead of a year
|
128 |
-
"""
|
129 |
-
)
|
130 |
-
],
|
131 |
-
max_function_calls=10,
|
132 |
-
allow_parallel_tool_calls=False,
|
133 |
-
verbose=True
|
134 |
-
).as_agent()
|
135 |
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
"calendar id is primary"
|
141 |
)
|
142 |
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
|
|
|
|
|
|
|
|
|
|
149 |
|
150 |
except Exception as e:
|
151 |
return APIResponse(
|
|
|
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 |
import gradio as gr
|
8 |
import os
|
9 |
import json
|
10 |
+
from dotenv import load_dotenv
|
11 |
|
12 |
# Load environment variables
|
13 |
load_dotenv()
|
|
|
34 |
class CalendarService:
|
35 |
def __init__(self):
|
36 |
self.toolset = ComposioToolSet(api_key=os.getenv('COMPOSIO_API_KEY'))
|
|
|
37 |
self.connections: Dict[str, Dict[str, Any]] = {}
|
38 |
+
|
39 |
+
def analyze_calendar_events(self, response_data):
|
40 |
+
"""
|
41 |
+
Analyze calendar events and return statistics about meetings.
|
42 |
+
"""
|
43 |
+
current_year = datetime.now().year
|
44 |
+
meetings = []
|
45 |
+
participants = []
|
46 |
+
meeting_times = []
|
47 |
+
total_duration = timedelta()
|
48 |
+
monthly_meetings = defaultdict(int)
|
49 |
+
daily_meetings = defaultdict(int)
|
50 |
+
|
51 |
+
events = response_data.get('data', {}).get('event_data', {}).get('event_data', [])
|
52 |
+
|
53 |
+
for event in events:
|
54 |
+
start_data = event.get('start', {})
|
55 |
+
end_data = event.get('end', {})
|
56 |
+
|
57 |
+
try:
|
58 |
+
start = datetime.fromisoformat(start_data.get('dateTime').replace('Z', '+00:00'))
|
59 |
+
end = datetime.fromisoformat(end_data.get('dateTime').replace('Z', '+00:00'))
|
60 |
+
|
61 |
+
if start.year == current_year:
|
62 |
+
duration = end - start
|
63 |
+
total_duration += duration
|
64 |
+
|
65 |
+
monthly_meetings[start.strftime('%B')] += 1
|
66 |
+
daily_meetings[start.strftime('%A')] += 1
|
67 |
+
meeting_times.append(start.strftime('%H:%M'))
|
68 |
+
|
69 |
+
if 'attendees' in event:
|
70 |
+
for attendee in event['attendees']:
|
71 |
+
if attendee.get('responseStatus') != 'declined':
|
72 |
+
participants.append(attendee.get('email'))
|
73 |
+
|
74 |
+
organizer_email = event.get('organizer', {}).get('email')
|
75 |
+
if organizer_email:
|
76 |
+
participants.append(organizer_email)
|
77 |
+
|
78 |
+
meetings.append({
|
79 |
+
'start': start,
|
80 |
+
'duration': duration,
|
81 |
+
'summary': event.get('summary', 'No Title')
|
82 |
+
})
|
83 |
+
except (ValueError, TypeError, AttributeError) as e:
|
84 |
+
print(f"Error processing event: {e}")
|
85 |
+
continue
|
86 |
+
|
87 |
+
total_meetings = len(meetings)
|
88 |
+
stats = {
|
89 |
+
"total_meetings_this_year": total_meetings
|
90 |
+
}
|
91 |
+
|
92 |
+
if total_meetings > 0:
|
93 |
+
stats.update({
|
94 |
+
"total_time_spent": str(total_duration),
|
95 |
+
"busiest_month": max(monthly_meetings.items(), key=lambda x: x[1])[0] if monthly_meetings else "N/A",
|
96 |
+
"busiest_day": max(daily_meetings.items(), key=lambda x: x[1])[0] if daily_meetings else "N/A",
|
97 |
+
"most_frequent_participant": Counter(participants).most_common(1)[0][0] if participants else "N/A",
|
98 |
+
"average_meeting_duration": str(total_duration / total_meetings),
|
99 |
+
"most_common_meeting_time": Counter(meeting_times).most_common(1)[0][0] if meeting_times else "N/A",
|
100 |
+
"monthly_breakdown": dict(monthly_meetings),
|
101 |
+
"daily_breakdown": dict(daily_meetings)
|
102 |
+
})
|
103 |
+
else:
|
104 |
+
stats.update({
|
105 |
+
"total_time_spent": "0:00:00",
|
106 |
+
"busiest_month": "N/A",
|
107 |
+
"busiest_day": "N/A",
|
108 |
+
"most_frequent_participant": "N/A",
|
109 |
+
"average_meeting_duration": "0:00:00",
|
110 |
+
"most_common_meeting_time": "N/A",
|
111 |
+
"monthly_breakdown": {},
|
112 |
+
"daily_breakdown": {}
|
113 |
+
})
|
114 |
+
|
115 |
+
return stats
|
116 |
|
117 |
def initiate_connection(self, entity_id: str, redirect_url: Optional[str] = None) -> APIResponse:
|
118 |
try:
|
|
|
154 |
error="No connection found for this entity ID"
|
155 |
)
|
156 |
|
|
|
|
|
157 |
connection = self.connections[entity_id]
|
158 |
|
159 |
return APIResponse(
|
|
|
172 |
|
173 |
def generate_wrapped(self, entity_id: str) -> APIResponse:
|
174 |
try:
|
175 |
+
# Get current year's start and end dates
|
176 |
+
current_year = datetime.now().year
|
177 |
+
time_min = f"{current_year},1,1,0,0,0"
|
178 |
+
time_max = f"{current_year},12,31,23,59,59"
|
179 |
|
180 |
+
request_params = {
|
181 |
+
"calendar_id": "primary",
|
182 |
+
"timeMin": time_min,
|
183 |
+
"timeMax": time_max,
|
184 |
+
"single_events": True,
|
185 |
+
"max_results": 2500,
|
186 |
+
"order_by": "startTime"
|
187 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
188 |
|
189 |
+
events_response = self.toolset.execute_action(
|
190 |
+
action=Action.GOOGLECALENDAR_FIND_EVENT,
|
191 |
+
params=request_params,
|
192 |
+
entity_id=entity_id
|
|
|
193 |
)
|
194 |
|
195 |
+
if events_response["successfull"]:
|
196 |
+
stats = self.analyze_calendar_events(events_response)
|
197 |
+
return APIResponse(
|
198 |
+
success=True,
|
199 |
+
data=stats
|
200 |
+
)
|
201 |
+
else:
|
202 |
+
return APIResponse(
|
203 |
+
success=False,
|
204 |
+
error=events_response["error"] or "Failed to fetch calendar events"
|
205 |
+
)
|
206 |
|
207 |
except Exception as e:
|
208 |
return APIResponse(
|