Spaces:
Sleeping
Sleeping
Kabilash10
commited on
Commit
•
4c96918
1
Parent(s):
769a147
Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,232 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import pickle
|
3 |
+
from google.oauth2.credentials import Credentials
|
4 |
+
from google_auth_oauthlib.flow import InstalledAppFlow
|
5 |
+
from google.auth.transport.requests import Request
|
6 |
+
from googleapiclient.discovery import build
|
7 |
+
from datetime import datetime, timedelta
|
8 |
+
import gradio as gr
|
9 |
+
|
10 |
+
# SCOPES for Google Calendar API
|
11 |
+
SCOPES = ['https://www.googleapis.com/auth/calendar.readonly', 'https://www.googleapis.com/auth/calendar']
|
12 |
+
|
13 |
+
def authenticate_google():
|
14 |
+
"""Authenticate and return Google Calendar service."""
|
15 |
+
creds = None
|
16 |
+
try:
|
17 |
+
if os.path.exists('token.pickle'):
|
18 |
+
try:
|
19 |
+
with open('token.pickle', 'rb') as token:
|
20 |
+
creds = pickle.load(token)
|
21 |
+
|
22 |
+
if not creds or not creds.valid:
|
23 |
+
if creds and creds.expired and creds.refresh_token:
|
24 |
+
try:
|
25 |
+
creds.refresh(Request())
|
26 |
+
except Exception:
|
27 |
+
os.remove('token.pickle')
|
28 |
+
creds = None
|
29 |
+
else:
|
30 |
+
os.remove('token.pickle')
|
31 |
+
creds = None
|
32 |
+
except Exception:
|
33 |
+
os.remove('token.pickle')
|
34 |
+
creds = None
|
35 |
+
|
36 |
+
if not creds:
|
37 |
+
if not os.path.exists('credentials.json'):
|
38 |
+
raise FileNotFoundError(
|
39 |
+
"credentials.json file not found. Please download it from Google Cloud Console."
|
40 |
+
)
|
41 |
+
|
42 |
+
flow = InstalledAppFlow.from_client_secrets_file('credentials.json', SCOPES)
|
43 |
+
creds = flow.run_local_server(port=0)
|
44 |
+
|
45 |
+
with open('token.pickle', 'wb') as token:
|
46 |
+
pickle.dump(creds, token)
|
47 |
+
|
48 |
+
return build('calendar', 'v3', credentials=creds)
|
49 |
+
|
50 |
+
except Exception as e:
|
51 |
+
raise Exception(f"Authentication failed: {str(e)}")
|
52 |
+
|
53 |
+
def create_event(service, event_name, start_time, end_time, time_zone='Asia/Kolkata'):
|
54 |
+
"""Create an event in Google Calendar."""
|
55 |
+
try:
|
56 |
+
event = {
|
57 |
+
'summary': event_name,
|
58 |
+
'start': {
|
59 |
+
'dateTime': start_time.isoformat(),
|
60 |
+
'timeZone': time_zone,
|
61 |
+
},
|
62 |
+
'end': {
|
63 |
+
'dateTime': end_time.isoformat(),
|
64 |
+
'timeZone': time_zone,
|
65 |
+
},
|
66 |
+
}
|
67 |
+
|
68 |
+
created_event = service.events().insert(calendarId='primary', body=event).execute()
|
69 |
+
return f'Event "{event_name}" created successfully!'
|
70 |
+
except Exception as e:
|
71 |
+
error_msg = f"Failed to create event: {str(e)}"
|
72 |
+
if "invalid_grant" in str(e):
|
73 |
+
if os.path.exists('token.pickle'):
|
74 |
+
os.remove('token.pickle')
|
75 |
+
error_msg += "\nPlease restart the application to re-authenticate."
|
76 |
+
raise Exception(error_msg)
|
77 |
+
|
78 |
+
def parse_date_time(date_str, time_str):
|
79 |
+
"""Parse date and time strings."""
|
80 |
+
try:
|
81 |
+
date_parts = date_str.split('-')
|
82 |
+
if len(date_parts) != 3:
|
83 |
+
raise ValueError("Date must be in DD-MM-YYYY format")
|
84 |
+
|
85 |
+
day, month, year = map(int, date_parts)
|
86 |
+
|
87 |
+
try:
|
88 |
+
time_obj = datetime.strptime(time_str.strip().upper(), '%I:%M %p')
|
89 |
+
except ValueError:
|
90 |
+
raise ValueError("Time must be in HH:MM AM/PM format")
|
91 |
+
|
92 |
+
start_time = datetime(year, month, day,
|
93 |
+
time_obj.hour, time_obj.minute)
|
94 |
+
|
95 |
+
return start_time
|
96 |
+
except ValueError as e:
|
97 |
+
raise ValueError(str(e))
|
98 |
+
|
99 |
+
def get_conversation_stage(history):
|
100 |
+
"""Determine the current stage of the conversation."""
|
101 |
+
user_messages = [msg for role, msg in history if role == "User"]
|
102 |
+
if not user_messages:
|
103 |
+
return 0
|
104 |
+
return len(user_messages)
|
105 |
+
|
106 |
+
def conversational_flow(history, user_input):
|
107 |
+
"""Handle the conversation flow with improved input processing."""
|
108 |
+
if not user_input:
|
109 |
+
return history
|
110 |
+
|
111 |
+
# Check for restart command at any point
|
112 |
+
if user_input.lower() in ['restart', 'start over', 'reset']:
|
113 |
+
history.clear()
|
114 |
+
history.append(("Bot", "Conversation restarted. Hi! How may I assist you?"))
|
115 |
+
return history
|
116 |
+
|
117 |
+
# Add user input to history
|
118 |
+
history.append(("User", user_input))
|
119 |
+
stage = get_conversation_stage(history)
|
120 |
+
|
121 |
+
# Process based on conversation stage
|
122 |
+
if stage == 1: # After first input (Book an appointment)
|
123 |
+
if "appointment" in user_input.lower() or "book" in user_input.lower():
|
124 |
+
history.append(("Bot", "What is the purpose of the appointment?"))
|
125 |
+
else:
|
126 |
+
history.pop() # Remove invalid input
|
127 |
+
history.append(("Bot", "I can help you book an appointment. Please say 'Book an appointment' to start."))
|
128 |
+
|
129 |
+
elif stage == 2: # After purpose
|
130 |
+
event_name = user_input
|
131 |
+
history.append(("Bot", f"Great! You want to create an event for '{event_name}'. What is the date? (DD-MM-YYYY)"))
|
132 |
+
|
133 |
+
elif stage == 3: # After date
|
134 |
+
date_str = user_input
|
135 |
+
try:
|
136 |
+
# Validate date format
|
137 |
+
date_parts = date_str.split('-')
|
138 |
+
if len(date_parts) != 3 or not all(part.isdigit() for part in date_parts):
|
139 |
+
raise ValueError("Date must be in DD-MM-YYYY format")
|
140 |
+
history.append(("Bot", "What is the start time? (HH:MM AM/PM)"))
|
141 |
+
except ValueError as e:
|
142 |
+
history.pop() # Remove invalid date
|
143 |
+
history.append(("Bot", f"Invalid date format: {str(e)}. Please enter the date in DD-MM-YYYY format."))
|
144 |
+
|
145 |
+
elif stage == 4: # After start time
|
146 |
+
start_time_str = user_input
|
147 |
+
try:
|
148 |
+
datetime.strptime(start_time_str.strip().upper(), '%I:%M %p')
|
149 |
+
history.append(("Bot", "What is the end time? (HH:MM AM/PM)"))
|
150 |
+
except ValueError:
|
151 |
+
history.pop()
|
152 |
+
history.append(("Bot", "Invalid time format. Please enter the time in HH:MM AM/PM format (e.g., 02:30 PM)."))
|
153 |
+
|
154 |
+
elif stage == 5: # After end time
|
155 |
+
end_time_str = user_input
|
156 |
+
try:
|
157 |
+
# Extract event details from history
|
158 |
+
event_name = [msg for role, msg in history if role == "User"][1]
|
159 |
+
date_str = [msg for role, msg in history if role == "User"][2]
|
160 |
+
start_time_str = [msg for role, msg in history if role == "User"][3]
|
161 |
+
|
162 |
+
# Parse start and end times
|
163 |
+
start_time = parse_date_time(date_str, start_time_str)
|
164 |
+
end_time = parse_date_time(date_str, end_time_str)
|
165 |
+
|
166 |
+
# Validate end time is after start time
|
167 |
+
if end_time <= start_time:
|
168 |
+
raise ValueError("End time must be after start time")
|
169 |
+
|
170 |
+
# Create event
|
171 |
+
try:
|
172 |
+
service = authenticate_google()
|
173 |
+
result = create_event(service, event_name, start_time, end_time)
|
174 |
+
history.append(("Bot", result))
|
175 |
+
history.append(("Bot", "Do you need to book any other appointments? (yes/no)"))
|
176 |
+
except Exception as e:
|
177 |
+
history.append(("Bot", f"Error: {str(e)}"))
|
178 |
+
except ValueError as e:
|
179 |
+
history.pop()
|
180 |
+
history.append(("Bot", f"Invalid time: {str(e)}. Please enter a valid end time in HH:MM AM/PM format."))
|
181 |
+
|
182 |
+
elif stage == 6: # After yes/no
|
183 |
+
if user_input.lower() == "yes":
|
184 |
+
history.clear()
|
185 |
+
history.append(("Bot", "Hi! How may I assist you?"))
|
186 |
+
elif user_input.lower() == "no":
|
187 |
+
history.append(("Bot", "Thank you! Have a nice day."))
|
188 |
+
else:
|
189 |
+
history.pop()
|
190 |
+
history.append(("Bot", "Please answer with 'yes' or 'no'."))
|
191 |
+
|
192 |
+
return history
|
193 |
+
|
194 |
+
def display_chat(history):
|
195 |
+
"""Display chat messages."""
|
196 |
+
chat_display = []
|
197 |
+
for role, message in history:
|
198 |
+
if role == "User":
|
199 |
+
chat_display.append([message, None])
|
200 |
+
else:
|
201 |
+
chat_display.append([None, message])
|
202 |
+
return chat_display
|
203 |
+
|
204 |
+
# Create Gradio interface
|
205 |
+
with gr.Blocks() as iface:
|
206 |
+
gr.Markdown("""
|
207 |
+
<h1>Google Calendar Appointment Bot</h1>
|
208 |
+
<p>Type 'restart' at any time to start over.</p>
|
209 |
+
""")
|
210 |
+
chatbot = gr.Chatbot()
|
211 |
+
user_input = gr.Textbox(placeholder="Type your response here...")
|
212 |
+
send_button = gr.Button("Send")
|
213 |
+
restart_button = gr.Button("Restart Conversation")
|
214 |
+
|
215 |
+
def reset_conversation():
|
216 |
+
history = [("Bot", "Hi! How may I assist you?")]
|
217 |
+
return display_chat(history), history
|
218 |
+
|
219 |
+
def handle_input(user_input, history):
|
220 |
+
if not user_input.strip():
|
221 |
+
return display_chat(history), history, ""
|
222 |
+
history = conversational_flow(history, user_input)
|
223 |
+
return display_chat(history), history, ""
|
224 |
+
|
225 |
+
history_state = gr.State([])
|
226 |
+
send_button.click(handle_input, inputs=[user_input, history_state], outputs=[chatbot, history_state, user_input])
|
227 |
+
user_input.submit(handle_input, inputs=[user_input, history_state], outputs=[chatbot, history_state, user_input])
|
228 |
+
restart_button.click(reset_conversation, outputs=[chatbot, history_state])
|
229 |
+
iface.load(reset_conversation, outputs=[chatbot, history_state])
|
230 |
+
|
231 |
+
if __name__ == "__main__":
|
232 |
+
iface.launch()
|