From 7ca26c69f23e70ba0b6e3ab9f7faffc8a460e257 Mon Sep 17 00:00:00 2001 From: Misha Patel Date: Sun, 18 Oct 2020 19:55:58 -0700 Subject: [PATCH 1/2] Adding code solution for week 2 livestream --- week2/stream/lambda_function.py | 269 ++++++++++++++++++++++++++++++++ week2/stream/requirements.txt | 4 + 2 files changed, 273 insertions(+) create mode 100644 week2/stream/lambda_function.py create mode 100644 week2/stream/requirements.txt diff --git a/week2/stream/lambda_function.py b/week2/stream/lambda_function.py new file mode 100644 index 0000000..522cbec --- /dev/null +++ b/week2/stream/lambda_function.py @@ -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() diff --git a/week2/stream/requirements.txt b/week2/stream/requirements.txt new file mode 100644 index 0000000..900d82b --- /dev/null +++ b/week2/stream/requirements.txt @@ -0,0 +1,4 @@ +boto3==1.9.216 +ask-sdk-core==1.11.0 +ask-sdk-s3-persistence-adapter +pytz From 91894594d04353750ffdd5629c8160ccd71bbf44 Mon Sep 17 00:00:00 2001 From: Misha Patel Date: Sun, 18 Oct 2020 20:04:01 -0700 Subject: [PATCH 2/2] Adding readme for week 2 --- week2/README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 week2/README.md diff --git a/week2/README.md b/week2/README.md new file mode 100644 index 0000000..20d224d --- /dev/null +++ b/week2/README.md @@ -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)