Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding code solution and README for week 2 livestream #23

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions week2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Week 2

### Project title:
_Birthday Tracker_

### Project Description:
This week, students will use the Alexa Skills console to add on to the skill they created in week 1. They will use a systems function API to retrieve the user's current time to compare it to the user's birthday.

### Learning Outcomes:
- Understand system function APIs and how to use them
- Incorporate an API into the skill we created in week 1

### List of Needed Technologies:
- ASK (Alexa Skills Kit)
- AWS account (free accounts are fine here)
- AWS Lambda function (this is under the hood, managed indirectly through the Alexa console)


### Links to relevant resources:
- ["Using the Alexa Settings API"](https://github.com/alexa/skill-sample-python-first-skill/tree/master/module-4)
269 changes: 269 additions & 0 deletions week2/stream/lambda_function.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
# -*- coding: utf-8 -*-

# This sample demonstrates handling intents from an Alexa skill using the Alexa Skills Kit SDK for Python.
# Please visit https://alexa.design/cookbook for additional examples on implementing slots, dialog management,
# session persistence, api calls, and more.
# This sample is built using the handler classes approach in skill builder.
import logging
import ask_sdk_core.utils as ask_utils
import os
import requests
import calendar
from datetime import datetime
from pytz import timezone
from ask_sdk_s3.adapter import S3Adapter
s3_adapter = S3Adapter(bucket_name=os.environ["S3_PERSISTENCE_BUCKET"])

from ask_sdk_core.skill_builder import CustomSkillBuilder
from ask_sdk_core.dispatch_components import AbstractRequestHandler
from ask_sdk_core.dispatch_components import AbstractExceptionHandler
from ask_sdk_core.handler_input import HandlerInput

from ask_sdk_model import Response

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)


class LaunchRequestHandler(AbstractRequestHandler):
"""Handler for Skill Launch."""
def can_handle(self, handler_input):
# type: (HandlerInput) -> bool

return ask_utils.is_request_type("LaunchRequest")(handler_input)

def handle(self, handler_input):
# type: (HandlerInput) -> Response
speak_output = "Hello! This is Birthday Tracker. What is your birthday?"
reprompt_text = "I was born Nov. 6th, 2014. When were you born?"

return (
handler_input.response_builder
.speak(speak_output)
.ask(reprompt_text)
.response
)


class HasBirthdayLaunchRequestHandler(AbstractRequestHandler):
"""Handler for launch after they have set their birthday"""

def can_handle(self, handler_input):
# extract persistent attributes and check if they are all present
attr = handler_input.attributes_manager.persistent_attributes
attributes_are_present = ("year" in attr and "month" in attr and "day" in attr)

return attributes_are_present and ask_utils.is_request_type("LaunchRequest")(handler_input)

def handle(self, handler_input):
attr = handler_input.attributes_manager.persistent_attributes
year = int(attr['year'])
month = attr['month'] # month is a string, and we need to convert it to a month index later
day = int(attr['day'])

# get device id
sys_object = handler_input.request_envelope.context.system
device_id = sys_object.device.device_id

# get Alexa Settings API information
api_endpoint = sys_object.api_endpoint
api_access_token = sys_object.api_access_token

# construct systems api timezone url
url = '{api_endpoint}/v2/devices/{device_id}/settings/System.timeZone'.format(api_endpoint=api_endpoint, device_id=device_id)
headers = {'Authorization': 'Bearer ' + api_access_token}

userTimeZone = ""
try:
r = requests.get(url, headers=headers)
res = r.json()
logger.info("Device API result: {}".format(str(res)))
userTimeZone = res
except Exception:
handler_input.response_builder.speak("There was a problem connecting to the service")
return handler_input.response_builder.response

# getting the current date with the time
now_time = datetime.now(timezone(userTimeZone))

# Removing the time from the date because it affects our difference calculation
now_date = datetime(now_time.year, now_time.month, now_time.day)
current_year = now_time.year

# getting the next birthday
month_as_index = list(calendar.month_abbr).index(month[:3].title())
next_birthday = datetime(current_year, month_as_index, day)

# check if we need to adjust bday by one year
if now_date > next_birthday:
next_birthday = datetime(
current_year + 1,
month_as_index,
day
)
current_year += 1
# setting the default speak_output to Happy xth Birthday!!
# Alexa will automatically correct the ordinal for you.
# no need to worry about when to use st, th, rd
speak_output = "Happy {}th birthday!".format(str(current_year - year))
if now_date != next_birthday:
diff_days = abs((now_date - next_birthday).days)
speak_output = "Welcome back. It looks like there are \
{days} days until your {birthday_num}th\
birthday".format(
days=diff_days,
birthday_num=(current_year-year)
)

handler_input.response_builder.speak(speak_output)

return handler_input.response_builder.response


class CaptureBirthdayIntentHandler(AbstractRequestHandler):
"""Handler for Capture Birthday Intent."""
def can_handle(self, handler_input):
# type: (HandlerInput) -> bool
return ask_utils.is_intent_name("CaptureBirthdayIntent")(handler_input)

def handle(self, handler_input):
# type: (HandlerInput) -> Response
slots = handler_input.request_envelope.request.intent.slots
year = slots["year"].value
month = slots["month"].value
day = slots["day"].value

attributes_manager = handler_input.attributes_manager

birthday_attributes = {
"year": year,
"month": month,
"day": day
}

attributes_manager.persistent_attributes = birthday_attributes
attributes_manager.save_persistent_attributes()

speak_output = 'Thanks, I will remember that you were born {month} {day} {year}.'.format(month=month, day=day, year=year)

return (
handler_input.response_builder
.speak(speak_output)
# .ask("add a reprompt if you want to keep the session open for the user to respond")
.response
)


class HelpIntentHandler(AbstractRequestHandler):
"""Handler for Help Intent."""
def can_handle(self, handler_input):
# type: (HandlerInput) -> bool
return ask_utils.is_intent_name("AMAZON.HelpIntent")(handler_input)

def handle(self, handler_input):
# type: (HandlerInput) -> Response
speak_output = "You can say hello to me! How can I help?"

return (
handler_input.response_builder
.speak(speak_output)
.ask(speak_output)
.response
)


class CancelOrStopIntentHandler(AbstractRequestHandler):
"""Single handler for Cancel and Stop Intent."""
def can_handle(self, handler_input):
# type: (HandlerInput) -> bool
return (ask_utils.is_intent_name("AMAZON.CancelIntent")(handler_input) or
ask_utils.is_intent_name("AMAZON.StopIntent")(handler_input))

def handle(self, handler_input):
# type: (HandlerInput) -> Response
speak_output = "Goodbye!"

return (
handler_input.response_builder
.speak(speak_output)
.response
)


class SessionEndedRequestHandler(AbstractRequestHandler):
"""Handler for Session End."""
def can_handle(self, handler_input):
# type: (HandlerInput) -> bool
return ask_utils.is_request_type("SessionEndedRequest")(handler_input)

def handle(self, handler_input):
# type: (HandlerInput) -> Response

# Any cleanup logic goes here.

return handler_input.response_builder.response


class IntentReflectorHandler(AbstractRequestHandler):
"""The intent reflector is used for interaction model testing and debugging.
It will simply repeat the intent the user said. You can create custom handlers
for your intents by defining them above, then also adding them to the request
handler chain below.
"""
def can_handle(self, handler_input):
# type: (HandlerInput) -> bool
return ask_utils.is_request_type("IntentRequest")(handler_input)

def handle(self, handler_input):
# type: (HandlerInput) -> Response
intent_name = ask_utils.get_intent_name(handler_input)
speak_output = "You just triggered " + intent_name + "."

return (
handler_input.response_builder
.speak(speak_output)
# .ask("add a reprompt if you want to keep the session open for the user to respond")
.response
)


class CatchAllExceptionHandler(AbstractExceptionHandler):
"""Generic error handling to capture any syntax or routing errors. If you receive an error
stating the request handler chain is not found, you have not implemented a handler for
the intent being invoked or included it in the skill builder below.
"""
def can_handle(self, handler_input, exception):
# type: (HandlerInput, Exception) -> bool
return True

def handle(self, handler_input, exception):
# type: (HandlerInput, Exception) -> Response
logger.error(exception, exc_info=True)

speak_output = "Sorry, I had trouble doing what you asked. Please try again."

return (
handler_input.response_builder
.speak(speak_output)
.ask(speak_output)
.response
)

# The SkillBuilder object acts as the entry point for your skill, routing all request and response
# payloads to the handlers above. Make sure any new handlers or interceptors you've
# defined are included below. The order matters - they're processed top to bottom.


sb = CustomSkillBuilder(persistence_adapter=s3_adapter)

sb.add_request_handler(HasBirthdayLaunchRequestHandler())
sb.add_request_handler(LaunchRequestHandler())
sb.add_request_handler(CaptureBirthdayIntentHandler())
sb.add_request_handler(HelpIntentHandler())
sb.add_request_handler(CancelOrStopIntentHandler())
sb.add_request_handler(SessionEndedRequestHandler())
sb.add_request_handler(IntentReflectorHandler()) # make sure IntentReflectorHandler is last so it doesn't override your custom intent handlers

sb.add_exception_handler(CatchAllExceptionHandler())

lambda_handler = sb.lambda_handler()
4 changes: 4 additions & 0 deletions week2/stream/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
boto3==1.9.216
ask-sdk-core==1.11.0
ask-sdk-s3-persistence-adapter
pytz