-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlambda_function.py
164 lines (133 loc) · 6.16 KB
/
lambda_function.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
from datetime import datetime, timedelta
from urllib import request
import json
from libeary.abe_event import ABEEvent
from libeary.avs_intent import AVSIntent
def lambda_handler(req, context):
"""
Entry function (called by AWS)
:param req: a dictionary with the JSON values sent from AVS
:param context: don't know what this is
:return: a dictionary (to be formatted as a JSON string)
"""
# Convert the server request to an AVSIntent object
intent = AVSIntent(req)
# Check if the user just opened the Skill (without specifying any actions)
if intent.is_launch_request:
return prepare_response('Welcome to the ABE, the Olin calendar. How may I help you?')
# Check if the user wants to know what's happening next
elif intent.name == 'WhatsHappeningNext':
return handle_whats_happening_next_request(intent)
# Check if the user wants to know what featured events are happening next
elif intent.name == 'WhatsHappeningNextFeatured':
return handle_whats_happening_next_request(intent, 'featured')
elif intent.name == 'WhatsHappeningOn': # Look up what's happening on/at a specific day/time
return handle_whats_happening_on_request(intent)
# There was a problem interpreting the intent
return prepare_response("I didn't recognize the intent " + intent.name)
def handle_whats_happening_next_request(intent, labels=None):
"""
This function queries ABE for events happening in the next week. It handles the "WhatsHappeningNext" request from AVS.
:param {AVSIntent} intent: the intent from AVS
:param {list} labels: a list of labels to filter events by
:return {list}: the events found in the next week
"""
# Resolve the dates to look between
today = datetime.now()
week_from_today = today + timedelta(weeks=1)
# Get the events
events = get_events(start=today, end=week_from_today, labels=labels)
if events is None: # Make sure there wasn't an error talking to ABE
return prepare_abe_connectivity_problem_response()
# Build the response
text_res = 'I found {} events coming up on the Olin calendar in the next week.'.format(len(events))
for event in events:
text_res += " {}, there's {} {}.".format(event.get_start_speech(), event.title, 'in ' + event.location if event.location else '')
return prepare_response(text_res)
def handle_whats_happening_on_request(intent):
"""
This function queries ABE for events happening on a specific date. It handles the "WhatsHappeningOn" intent from AVS.
:param {AVSIntent} intent: the intent from AVS
:return {list}: the events found on the given date
"""
# Convert intent date to Python date
date = intent.slots['date'].value
date = datetime.strptime(date, '%Y-%m-%d')
tomorrow_morning = date+timedelta(days=1) # The end time for our query
# Get the events
events = get_events(start=date, end=tomorrow_morning)
if events is None: # Make sure there wasn't an error talking to ABE
return prepare_abe_connectivity_problem_response()
# Build the response
date_as_words = date.strftime('%A, %B %d')
count = len(events)
text_res = 'I found {} event{} on {}.'.format('no' if count == 0 else count, '' if count == 1 else 's', date_as_words)
for event in events:
text_res += " {}, there's {} {}.".format(event.get_start_speech(), event.title, 'in ' + event.location if event.location else '')
return prepare_response(text_res)
def prepare_abe_connectivity_problem_response():
"""
Generates a response to send to AVS indicating there was a problem talking to ABE.
:return {dict}: a response to be sent back to AVS
"""
return prepare_response('There was a problem speaking to ABE. Please contact your Library Overlord.')
def get_events(start=None, end=None, labels=None):
"""
Makes any necessary HTTP requests and does any filtering necessary to get events from ABE.
:param {datetime} start: (optional) the first day to fetch events for
:param {datetime} end: (optional) the last day to fetch events for
:param {list} labels: (optional) a list of tags to filter results based on
:return {list}: the events found
"""
print('Getting events between {} and {}'.format(start, end))
# Make an HTTP request to ABE
request_url = 'https://abe-dev.herokuapp.com/events/' # TODO Load this from an environment variable
if start and end: # If we're searching within a specific range, add that to the GET parameters
request_url += '?start={}&end={}'.format(format_date_url(start), format_date_url(end))
try:
with request.urlopen(request.Request(request_url)) as res:
# Parse the server response TODO Error checking
res = res.read().decode()
if res.startswith('[]'): # Why is this necessary????
print('Found 0 events')
return [] # No events found
print('Found some events:', res)
result = json.loads(res)
except Exception as e:
print('Error parsing response from ABE')
print(e)
return None # Some error talking to ABE
# Convert the result into the format we want
events = []
for event in result:
# Convert JSON into an event object
event = ABEEvent(event)
# Filter, if necessary TODO ABE does this
if labels:
if event.has_labels(labels):
events.append(event)
else: # Filtering not necessary
events.append(event)
return events
def format_date_url(date, fmt='%Y-%m-%d'):
"""
Formats a date as a string for making ABE requests.
:param {datetime} date: the date to format
:param {str} fmt: the format to use (defaults to YYYY-MM-dd)
:return: the date in the specified string format
"""
return date.strftime(fmt)
def prepare_response(text):
"""
Generates a response object to be sent to AVS.
:param text: the text for Alexa to speak
:return {dict}: the result to send back to AVS
"""
return {
'version': '1.0',
'response': {
'outputSpeech': {
'type': 'PlainText', 'text': text
}
}
}