├── .github └── PULL_REQUEST_TEMPLATE.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── bots └── BIBot.json ├── build-bot.sh ├── build-db.sh ├── buildspec.yml ├── delete-s3.sh ├── delete.sh ├── deletespec.yml ├── intents ├── Compare_Intent.json ├── Count_Intent.json ├── Goodbye_Intent.json ├── Hello_Intent.json ├── Refresh_Intent.json ├── Reset_Intent.json ├── Switch_Intent.json └── Top_Intent.json ├── lambda ├── bibot_config.py ├── bibot_helpers.py ├── bibot_userexits.py ├── compare_intent.py ├── count_intent.py ├── goodbye_intent.py ├── hello_intent.py ├── refresh_intent.py ├── reset_intent.py ├── switch_intent.py └── top_intent.py ├── slots ├── cat_desc.json ├── dimensions.json └── event_name.json └── zip.py /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | *Issue #, if available:* 2 | 3 | *Description of changes:* 4 | 5 | 6 | By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. 7 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check [existing open](https://github.com/aws-samples/amazon-lex-ai-jasper-bot/issues), or [recently closed](https://github.com/aws-samples/amazon-lex-ai-jasper-bot/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *master* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/aws-samples/amazon-lex-ai-jasper-bot/labels/help%20wanted) issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](https://github.com/aws-samples/amazon-lex-ai-jasper-bot/blob/master/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | 61 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this 4 | software and associated documentation files (the "Software"), to deal in the Software 5 | without restriction, including without limitation the rights to use, copy, modify, 6 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 7 | permit persons to whom the Software is furnished to do so. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 10 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 11 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 12 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 13 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 14 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Amazon Lex BI Bot 2 | 3 | Building a Conversational Business Intelligence Bot with Amazon Lex 4 | 5 | ## License Summary 6 | 7 | This sample code is made available under a modified MIT license. See the LICENSE file. 8 | -------------------------------------------------------------------------------- /bots/BIBot.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "BIBot", 3 | "description": "BIBot - a SQL query bot for the TICKIT database", 4 | "intents": [ 5 | { 6 | "intentVersion": "$LATEST", 7 | "intentName": "Hello_Intent" 8 | }, 9 | { 10 | "intentVersion": "$LATEST", 11 | "intentName": "Top_Intent" 12 | }, 13 | { 14 | "intentVersion": "$LATEST", 15 | "intentName": "Compare_Intent" 16 | }, 17 | { 18 | "intentVersion": "$LATEST", 19 | "intentName": "Count_Intent" 20 | }, 21 | { 22 | "intentVersion": "$LATEST", 23 | "intentName": "Switch_Intent" 24 | }, 25 | { 26 | "intentVersion": "$LATEST", 27 | "intentName": "Reset_Intent" 28 | }, 29 | { 30 | "intentVersion": "$LATEST", 31 | "intentName": "Refresh_Intent" 32 | }, 33 | { 34 | "intentVersion": "$LATEST", 35 | "intentName": "Goodbye_Intent" 36 | } 37 | ], 38 | "locale": "en-US", 39 | "abortStatement": { 40 | "messages": [ 41 | { 42 | "content": "Sorry, I could not understand. Goodbye.", 43 | "contentType": "PlainText" 44 | } 45 | ] 46 | }, 47 | "childDirected": false, 48 | "clarificationPrompt": { 49 | "maxAttempts": 5, 50 | "messages": [ 51 | { 52 | "content": "Come again?", 53 | "contentType": "PlainText" 54 | }, 55 | { 56 | "content": "Sorry, can you please repeat that?", 57 | "contentType": "PlainText" 58 | } 59 | ] 60 | }, 61 | "idleSessionTTLInSeconds": 300, 62 | "voiceId": "Matthew" 63 | } 64 | -------------------------------------------------------------------------------- /build-bot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # Builds the bot, intents, and custom slot types 5 | # 6 | 7 | # 8 | # Environment variables to be set in the CodeBuild project 9 | # 10 | # $BOT Name of the Lex bot 11 | # $INTENTS List of intent names for the bot 12 | # $SLOTS List of slot type names for the bot 13 | # $LAMBDA Name of the Lambda fulfillment function for the bot 14 | # $LAMBDA_ROLE_ARN ARN for the Lambda execution role 15 | # $ATHENA_DB Name of the Athena database 16 | # $ATHENA_OUTPUT_LOCATION Name of the S3 bucket for Athena output 17 | # 18 | 19 | # Load the Lambda functions for each Intent 20 | echo "Lambda execution role = $LAMBDA_ROLE_ARN" 21 | for i in $INTENTS 22 | do 23 | module_name=`echo $i | tr '[:upper:]' '[:lower:]'` 24 | echo "Creating Lambda handler function: ${LAMBDA}_${i} from ${module_name}.py" 25 | # echo aws lambda create-function \ 26 | # --function-name ${LAMBDA}_${i} \ 27 | # --description "${LAMBDA} ${i} Intent Handler" \ 28 | # --timeout 300 \ 29 | # --zip-file fileb://${i}.zip \ 30 | # --role $LAMBDA_ROLE_ARN \ 31 | # --handler ${module_name}.lambda_handler \ 32 | # --runtime python3.6 \ 33 | # --environment "Variables={ATHENA_DB=$ATHENA_DB,ATHENA_OUTPUT_LOCATION=$ATHENA_OUTPUT_LOCATION}" 34 | 35 | aws lambda create-function \ 36 | --function-name ${LAMBDA}_${i} \ 37 | --description "${LAMBDA} ${i} Intent Handler" \ 38 | --timeout 300 \ 39 | --zip-file fileb://${i}.zip \ 40 | --role $LAMBDA_ROLE_ARN \ 41 | --handler ${module_name}.lambda_handler \ 42 | --runtime python3.6 \ 43 | --environment "Variables={ATHENA_DB=$ATHENA_DB,ATHENA_OUTPUT_LOCATION=$ATHENA_OUTPUT_LOCATION}" \ 44 | >/dev/null 45 | 46 | # echo "Adding permission to invoke Lambda handler function ${LAMBDA}_${i} from Amazon Lex" 47 | # echo aws lambda add-permission \ 48 | # --function-name ${LAMBDA}_${i} \ 49 | # --statement-id chatbot-fulfillment \ 50 | # --action "lambda:InvokeFunction" \ 51 | # --principal "lex.amazonaws.com" 52 | 53 | aws lambda add-permission \ 54 | --function-name ${LAMBDA}_${i} \ 55 | --statement-id chatbot-fulfillment \ 56 | --action "lambda:InvokeFunction" \ 57 | --principal "lex.amazonaws.com" \ 58 | >/dev/null 59 | 60 | echo; echo; echo 61 | done 62 | 63 | for i in $SLOTS 64 | do 65 | echo "Creating slot type: $i" 66 | aws lex-models put-slot-type --name $i --cli-input-json file://slots/$i.json >/dev/null 67 | done 68 | 69 | # build the intents 70 | for i in $INTENTS 71 | do 72 | echo "Creating intent: $i" 73 | # substitute the ARN for the Lambda intent handler function 74 | 75 | LAMBDA_ARN=`aws lambda get-function --function-name ${LAMBDA}_${i} | grep 'FunctionArn' | sed 's/.*FunctionArn": "\(.*\)".*/\1/'` 76 | 77 | sed "s/{{lambda-arn}}/$LAMBDA_ARN/" intents/$i.json >intents/$i-updated.json 78 | # echo "ARN for $i = `grep -i arn intents/$i-updated.json`" 79 | aws lex-models put-intent --name $i --cli-input-json file://intents/$i-updated.json >/dev/null 80 | done 81 | 82 | # build the bot 83 | echo "Creating bot: $BOT" 84 | if aws lex-models put-bot --name $BOT --cli-input-json file://bots/$BOT.json >/dev/null 85 | then echo "Success: $BOT bot build complete."; exit 0 86 | else echo "Error: $BOT bot build failed, check the log for errors"; exit 1 87 | fi 88 | 89 | 90 | -------------------------------------------------------------------------------- /build-db.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # Creates the Athena database 5 | # 6 | 7 | # 8 | # Environment variables to be set in the CodeBuild project 9 | # 10 | # $ATHENA_DB Name of the Athena database 11 | # $ATHENA_BUCKET Name of the S3 bucket where the data is stored 12 | # $ATHENA_BUCKET_REGION Region for the S3 bucket where the data is stored 13 | # $ATHENA_DB_DESCRIPTION Description for the Athena database 14 | # 15 | 16 | echo "Starting build-db.sh" 17 | echo '$ATHENA_DB' "= $ATHENA_DB" 18 | echo '$ATHENA_BUCKET' "= $ATHENA_BUCKET" 19 | echo '$ATHENA_BUCKET_REGION' "= $ATHENA_BUCKET_REGION" 20 | echo '$ATHENA_DB_DESCRIPTION' "= $ATHENA_DB_DESCRIPTION" 21 | echo 22 | 23 | # Create TICKIT database 24 | echo "Creating Athena database $ATHENA_DB" 25 | aws glue create-database --database-input "Name=$ATHENA_DB,Description=$ATHENA_DB_DESCRIPTION" >/dev/null 26 | 27 | # Create TICKIT users table in Athena 28 | echo "Creating users table..." 29 | aws athena start-query-execution \ 30 | --query-string "create external table users (user_id INT, username STRING, firstname STRING, lastname STRING, city STRING, state STRING, email STRING, phone STRING, like_sports BOOLEAN, liketheatre BOOLEAN, likeconcerts BOOLEAN, likejazz BOOLEAN, likeclassical BOOLEAN, likeopera BOOLEAN, likerock BOOLEAN, likevegas BOOLEAN, likebroadway BOOLEAN, likemusicals BOOLEAN) ROW FORMAT DELIMITED FIELDS TERMINATED BY '|' LOCATION '$ATHENA_BUCKET/users';" \ 31 | --query-execution-context "Database=$ATHENA_DB" \ 32 | --result-configuration "OutputLocation=$ATHENA_BUCKET/output/" \ 33 | >/dev/null 34 | 35 | # Create TICKIT venue table in Athena 36 | echo "Creating venue table..." 37 | aws athena start-query-execution \ 38 | --query-string "create external table venue (venue_id INT, venue_name STRING, venue_city STRING, venue_state STRING, venue_seats INT) ROW FORMAT DELIMITED FIELDS TERMINATED BY '|' LOCATION '$ATHENA_BUCKET/venue';" \ 39 | --query-execution-context "Database=$ATHENA_DB" \ 40 | --result-configuration "OutputLocation=$ATHENA_BUCKET/output/" \ 41 | >/dev/null 42 | 43 | # Create TICKIT category table in Athena 44 | echo "Creating category table..." 45 | aws athena start-query-execution \ 46 | --query-string "create external table category (cat_id INT, cat_group STRING, cat_name STRING, cat_desc STRING) ROW FORMAT DELIMITED FIELDS TERMINATED BY '|' LOCATION '$ATHENA_BUCKET/category';" \ 47 | --query-execution-context "Database=$ATHENA_DB" \ 48 | --result-configuration "OutputLocation=$ATHENA_BUCKET/output/" \ 49 | >/dev/null 50 | 51 | # Create TICKIT date table in Athena 52 | echo "Creating date_dim table..." 53 | aws athena start-query-execution \ 54 | --query-string "create external table date_dim (date_id INT, cal_date DATE, day STRING, week STRING, month STRING, quarter STRING, year INT, holiday BOOLEAN) ROW FORMAT DELIMITED FIELDS TERMINATED BY '|' LOCATION '$ATHENA_BUCKET/date';" \ 55 | --query-execution-context "Database=$ATHENA_DB" \ 56 | --result-configuration "OutputLocation=$ATHENA_BUCKET/output/" \ 57 | >/dev/null 58 | 59 | # Create TICKIT event table in Athena 60 | echo "Creating event table..." 61 | aws athena start-query-execution \ 62 | --query-string "create external table event (event_id INT, venue_id INT, cat_id INT, date_id INT, event_name STRING, start_time TIMESTAMP) ROW FORMAT DELIMITED FIELDS TERMINATED BY '|' LOCATION '$ATHENA_BUCKET/event';" \ 63 | --query-execution-context "Database=$ATHENA_DB" \ 64 | --result-configuration "OutputLocation=$ATHENA_BUCKET/output/" \ 65 | >/dev/null 66 | 67 | # Create TICKIT listing table in Athena 68 | echo "Creating listing table..." 69 | aws athena start-query-execution \ 70 | --query-string "create external table listing (list_id INT, seller_id INT, event_id INT, date_id INT, qty INT, price DECIMAL(8,2), total DECIMAL(8,2), listing_time TIMESTAMP) ROW FORMAT DELIMITED FIELDS TERMINATED BY '|' LOCATION '$ATHENA_BUCKET/listing';" \ 71 | --query-execution-context "Database=$ATHENA_DB" \ 72 | --result-configuration "OutputLocation=$ATHENA_BUCKET/output/" \ 73 | >/dev/null 74 | 75 | # Create TICKIT sales table in Athena 76 | echo "Creating sales table..." 77 | aws athena start-query-execution \ 78 | --query-string "create external table sales (sales_id INT, list_id INT, seller_id INT, buyer_id INT, event_id INT, date_id INT, qty INT, amount DECIMAL(8,2), commission DECIMAL(8,2), sale_time TIMESTAMP) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LOCATION '$ATHENA_BUCKET/sales';" \ 79 | --query-execution-context "Database=$ATHENA_DB" \ 80 | --result-configuration "OutputLocation=$ATHENA_BUCKET/output/" \ 81 | >/dev/null 82 | 83 | 84 | -------------------------------------------------------------------------------- /buildspec.yml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | 3 | phases: 4 | install: 5 | commands: 6 | - echo "Nothing to install." 7 | build: 8 | commands: 9 | - echo Build started on `date` 10 | - cd lambda 11 | - python ../zip.py ../Count_Intent.zip count_intent.py bibot_config.py bibot_helpers.py bibot_userexits.py 12 | - python ../zip.py ../Top_Intent.zip top_intent.py bibot_config.py bibot_helpers.py bibot_userexits.py 13 | - python ../zip.py ../Compare_Intent.zip compare_intent.py bibot_config.py bibot_helpers.py bibot_userexits.py 14 | - python ../zip.py ../Refresh_Intent.zip refresh_intent.py bibot_config.py bibot_helpers.py bibot_userexits.py 15 | - python ../zip.py ../Reset_Intent.zip reset_intent.py bibot_config.py bibot_helpers.py bibot_userexits.py 16 | - python ../zip.py ../Hello_Intent.zip hello_intent.py bibot_config.py bibot_helpers.py bibot_userexits.py 17 | - python ../zip.py ../Goodbye_Intent.zip goodbye_intent.py bibot_config.py bibot_helpers.py bibot_userexits.py 18 | - python ../zip.py ../Switch_Intent.zip switch_intent.py bibot_config.py bibot_helpers.py bibot_userexits.py count_intent.py top_intent.py 19 | - cd .. 20 | - ls -l *.zip 21 | - bash delete.sh 22 | - bash build-db.sh 23 | - bash build-bot.sh 24 | post_build: 25 | commands: 26 | - echo Build completed on `date` 27 | 28 | -------------------------------------------------------------------------------- /delete-s3.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # Deletes the bot, intents, and custom slot types 5 | # in reverse order of build: first the bot, then 6 | # the intents, then the slot types. 7 | # 8 | 9 | # 10 | # Environment variables to be set in the CodeBuild project 11 | # 12 | # $ATHENA_BUCKET S3 bucket where data files reside 13 | # $ATHENA_OUTPUT_LOCATION S3 bucket for Athena output 14 | # $ARTIFACT_STORE S3 bucket for CodePipeline / CodeBuild code sharing 15 | # 16 | 17 | echo "Deleting S3 bucket $ATHENA_BUCKET" 18 | aws s3 rm --recursive $ATHENA_BUCKET 19 | aws s3 rb --force $ATHENA_BUCKET 20 | 21 | echo "Deleting S3 bucket $ATHENA_OUTPUT_LOCATION" 22 | aws s3 rm --recursive $ATHENA_OUTPUT_LOCATION 23 | aws s3 rb --force $ATHENA_OUTPUT_LOCATION 24 | 25 | echo "Deleting S3 bucket $ARTIFACT_STORE" 26 | aws s3 rm --recursive $ARTIFACT_STORE 27 | aws s3 rb --force $ARTIFACT_STORE 28 | 29 | -------------------------------------------------------------------------------- /delete.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # Deletes the bot, intents, and custom slot types 5 | # in reverse order of build: first the bot, then 6 | # the intents, then the slot types. 7 | # 8 | 9 | # 10 | # Environment variables to be set in the CodeBuild project 11 | # 12 | # $BOT Name of the Lex bot 13 | # $INTENTS List of intent names for the bot 14 | # $SLOTS List of slot type names for the bot 15 | # $LAMBDA Name of the Lambda fulfillment function for the bot 16 | # 17 | 18 | SLEEP=2 19 | 20 | # Delete TICKIT database if it exists 21 | if aws glue get-database --name $ATHENA_DB >xxx 2>&1 22 | then 23 | echo "Deleting Athena database $ATHENA_DB" 24 | aws glue delete-database --name $ATHENA_DB >/dev/null 25 | fi 26 | 27 | # delete the bot if it exists 28 | echo -n "Checking for existing bot $BOT... " 29 | if aws lex-models get-bot --name $BOT --version-or-alias '$LATEST' >/dev/null 2>&1 30 | then 31 | echo "deleting" 32 | sleep $SLEEP 33 | aws lex-models delete-bot --name $BOT 34 | sleep $SLEEP 35 | else 36 | echo "not found." 37 | fi 38 | 39 | # delete the intents 40 | for i in $INTENTS 41 | do 42 | echo -n "Checking for existing intent $i... " 43 | if aws lex-models get-intent --name $i --intent-version '$LATEST' >/dev/null 2>&1 44 | then 45 | echo "deleting" 46 | sleep $SLEEP 47 | aws lex-models delete-intent --name $i 48 | sleep $SLEEP 49 | else 50 | echo "not found" 51 | fi 52 | done 53 | 54 | # delete the custom slot types 55 | for i in $SLOTS 56 | do 57 | echo -n "Checking for existing slot type $i... " 58 | if aws lex-models get-slot-type --name $i --slot-type-version '$LATEST' >/dev/null 2>&1 59 | then 60 | echo "deleting" 61 | sleep $SLEEP 62 | aws lex-models delete-slot-type --name $i 63 | sleep $SLEEP 64 | else 65 | echo "not found" 66 | fi 67 | done 68 | 69 | # delete the lambda functions 70 | for i in $INTENTS 71 | do 72 | echo -n "Checking for existing Lambda function ${LAMBDA}_${i}... " 73 | if aws lambda get-function --function-name ${LAMBDA}_${i} >/dev/null 2>&1 74 | then 75 | echo "deleting" 76 | sleep $SLEEP 77 | aws lambda delete-function --function-name ${LAMBDA}_${i} 78 | sleep $SLEEP 79 | else 80 | echo "not found" 81 | fi 82 | done 83 | 84 | -------------------------------------------------------------------------------- /deletespec.yml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | 3 | phases: 4 | install: 5 | commands: 6 | - echo "Nothing to install." 7 | build: 8 | commands: 9 | - echo Build started on `date` 10 | - bash delete.sh 11 | - bash delete-s3.sh 12 | post_build: 13 | commands: 14 | - echo Build completed on `date` 15 | -------------------------------------------------------------------------------- /intents/Compare_Intent.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Compare_Intent", 3 | "description": "Recursively query the dataset", 4 | "sampleUtterances": [ 5 | "Compare sales for {one_category} and {another_category} in {venue_city}", 6 | "Compare sales in {one_state} and {another_state} in {event_month}", 7 | "Compare sales in {one_month} and {another_month} in {venue_state}", 8 | "Compare sales at {one_venue} and {another_venue} for {cat_desc}", 9 | "Compare sales for {one_event} and {another_event} at {venue_name}", 10 | "Compare sales for {one_event} and {another_event} in {venue_city}", 11 | 12 | "Compare {one_category} versus {another_category}", 13 | "Compare {one_state} versus {another_state}", 14 | "Compare {one_city} versus {another_city}", 15 | "Compare {one_month} versus {another_month}", 16 | "Compare {one_venue} versus {another_venue}", 17 | "Compare {one_event} versus {another_event}" 18 | ], 19 | "conclusionStatement": { 20 | "messages": [ 21 | { 22 | "content": "via code hook", 23 | "contentType": "PlainText" 24 | } 25 | ] 26 | }, 27 | "fulfillmentActivity": { 28 | "codeHook": { 29 | "messageVersion": "1.0", 30 | "uri": "{{lambda-arn}}" 31 | }, 32 | "type": "CodeHook" 33 | }, 34 | "slots": [ 35 | { 36 | "slotType": "AMAZON.Month", 37 | "name": "one_month", 38 | "slotConstraint": "Optional", 39 | "valueElicitationPrompt": { 40 | "maxAttempts": 2, 41 | "messages": [ 42 | { 43 | "content": "one event month", 44 | "contentType": "PlainText" 45 | } 46 | ] 47 | }, 48 | "priority": 1, 49 | "sampleUtterances": [] 50 | }, 51 | { 52 | "slotType": "AMAZON.Month", 53 | "name": "another_month", 54 | "slotConstraint": "Optional", 55 | "valueElicitationPrompt": { 56 | "maxAttempts": 2, 57 | "messages": [ 58 | { 59 | "content": "another event month", 60 | "contentType": "PlainText" 61 | } 62 | ] 63 | }, 64 | "priority": 2, 65 | "sampleUtterances": [] 66 | }, 67 | { 68 | "slotType": "AMAZON.MusicVenue", 69 | "name": "one_venue", 70 | "slotConstraint": "Optional", 71 | "valueElicitationPrompt": { 72 | "maxAttempts": 2, 73 | "messages": [ 74 | { 75 | "content": "one venue name", 76 | "contentType": "PlainText" 77 | } 78 | ] 79 | }, 80 | "priority": 3, 81 | "sampleUtterances": [] 82 | }, 83 | { 84 | "slotType": "AMAZON.MusicVenue", 85 | "name": "another_venue", 86 | "slotConstraint": "Optional", 87 | "valueElicitationPrompt": { 88 | "maxAttempts": 2, 89 | "messages": [ 90 | { 91 | "content": "another venue name", 92 | "contentType": "PlainText" 93 | } 94 | ] 95 | }, 96 | "priority": 4, 97 | "sampleUtterances": [] 98 | }, 99 | { 100 | "slotType": "AMAZON.US_CITY", 101 | "name": "one_city", 102 | "slotConstraint": "Optional", 103 | "valueElicitationPrompt": { 104 | "maxAttempts": 2, 105 | "messages": [ 106 | { 107 | "content": "one venue city", 108 | "contentType": "PlainText" 109 | } 110 | ] 111 | }, 112 | "priority": 5, 113 | "sampleUtterances": [] 114 | }, 115 | { 116 | "slotType": "AMAZON.US_CITY", 117 | "name": "another_city", 118 | "slotConstraint": "Optional", 119 | "valueElicitationPrompt": { 120 | "maxAttempts": 2, 121 | "messages": [ 122 | { 123 | "content": "another venue city", 124 | "contentType": "PlainText" 125 | } 126 | ] 127 | }, 128 | "priority": 6, 129 | "sampleUtterances": [] 130 | }, 131 | { 132 | "slotType": "AMAZON.US_STATE", 133 | "name": "one_state", 134 | "slotConstraint": "Optional", 135 | "valueElicitationPrompt": { 136 | "maxAttempts": 2, 137 | "messages": [ 138 | { 139 | "content": "one venue state", 140 | "contentType": "PlainText" 141 | } 142 | ] 143 | }, 144 | "priority": 7, 145 | "sampleUtterances": [] 146 | }, 147 | { 148 | "slotType": "AMAZON.US_STATE", 149 | "name": "another_state", 150 | "slotConstraint": "Optional", 151 | "valueElicitationPrompt": { 152 | "maxAttempts": 2, 153 | "messages": [ 154 | { 155 | "content": "another venue state", 156 | "contentType": "PlainText" 157 | } 158 | ] 159 | }, 160 | "priority": 8, 161 | "sampleUtterances": [] 162 | }, 163 | { 164 | "slotType": "event_name", 165 | "name": "one_event", 166 | "slotConstraint": "Optional", 167 | "valueElicitationPrompt": { 168 | "maxAttempts": 2, 169 | "messages": [ 170 | { 171 | "content": "one event name", 172 | "contentType": "PlainText" 173 | } 174 | ] 175 | }, 176 | "priority": 9, 177 | "slotTypeVersion": "$LATEST", 178 | "sampleUtterances": [] 179 | }, 180 | { 181 | "slotType": "event_name", 182 | "name": "another_event", 183 | "slotConstraint": "Optional", 184 | "valueElicitationPrompt": { 185 | "maxAttempts": 2, 186 | "messages": [ 187 | { 188 | "content": "another event name", 189 | "contentType": "PlainText" 190 | } 191 | ] 192 | }, 193 | "priority": 10, 194 | "slotTypeVersion": "$LATEST", 195 | "sampleUtterances": [] 196 | }, 197 | { 198 | "slotType": "AMAZON.Month", 199 | "name": "event_month", 200 | "slotConstraint": "Optional", 201 | "valueElicitationPrompt": { 202 | "maxAttempts": 2, 203 | "messages": [ 204 | { 205 | "content": "event_month", 206 | "contentType": "PlainText" 207 | } 208 | ] 209 | }, 210 | "priority": 11, 211 | "sampleUtterances": [] 212 | }, 213 | { 214 | "slotType": "AMAZON.MusicVenue", 215 | "name": "venue_name", 216 | "slotConstraint": "Optional", 217 | "valueElicitationPrompt": { 218 | "maxAttempts": 2, 219 | "messages": [ 220 | { 221 | "content": "venue name", 222 | "contentType": "PlainText" 223 | } 224 | ] 225 | }, 226 | "priority": 12, 227 | "sampleUtterances": [] 228 | }, 229 | { 230 | "slotType": "AMAZON.US_CITY", 231 | "name": "venue_city", 232 | "slotConstraint": "Optional", 233 | "valueElicitationPrompt": { 234 | "maxAttempts": 2, 235 | "messages": [ 236 | { 237 | "content": "venue_city", 238 | "contentType": "PlainText" 239 | } 240 | ] 241 | }, 242 | "priority": 13, 243 | "sampleUtterances": [] 244 | }, 245 | { 246 | "slotType": "AMAZON.US_STATE", 247 | "name": "venue_state", 248 | "slotConstraint": "Optional", 249 | "valueElicitationPrompt": { 250 | "maxAttempts": 2, 251 | "messages": [ 252 | { 253 | "content": "venue_state", 254 | "contentType": "PlainText" 255 | } 256 | ] 257 | }, 258 | "priority": 14, 259 | "sampleUtterances": [] 260 | }, 261 | { 262 | "slotType": "cat_desc", 263 | "name": "one_category", 264 | "slotConstraint": "Optional", 265 | "valueElicitationPrompt": { 266 | "maxAttempts": 2, 267 | "messages": [ 268 | { 269 | "content": "The type of event", 270 | "contentType": "PlainText" 271 | } 272 | ] 273 | }, 274 | "priority": 15, 275 | "slotTypeVersion": "$LATEST", 276 | "sampleUtterances": [], 277 | "description": "cat_desc" 278 | }, 279 | { 280 | "slotType": "cat_desc", 281 | "name": "another_category", 282 | "slotConstraint": "Optional", 283 | "valueElicitationPrompt": { 284 | "maxAttempts": 2, 285 | "messages": [ 286 | { 287 | "content": "The type of event", 288 | "contentType": "PlainText" 289 | } 290 | ] 291 | }, 292 | "priority": 16, 293 | "slotTypeVersion": "$LATEST", 294 | "sampleUtterances": [], 295 | "description": "cat_desc" 296 | }, 297 | { 298 | "slotType": "cat_desc", 299 | "name": "cat_desc", 300 | "slotConstraint": "Optional", 301 | "valueElicitationPrompt": { 302 | "maxAttempts": 2, 303 | "messages": [ 304 | { 305 | "content": "The type of event", 306 | "contentType": "PlainText" 307 | } 308 | ] 309 | }, 310 | "priority": 17, 311 | "slotTypeVersion": "$LATEST", 312 | "sampleUtterances": [], 313 | "description": "cat_desc" 314 | }, 315 | { 316 | "slotType": "event_name", 317 | "name": "event_name", 318 | "slotConstraint": "Optional", 319 | "valueElicitationPrompt": { 320 | "maxAttempts": 2, 321 | "messages": [ 322 | { 323 | "content": "event name", 324 | "contentType": "PlainText" 325 | } 326 | ] 327 | }, 328 | "priority": 18, 329 | "slotTypeVersion": "$LATEST", 330 | "sampleUtterances": [] 331 | } 332 | ] 333 | } 334 | -------------------------------------------------------------------------------- /intents/Count_Intent.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Count_Intent", 3 | "description": "Recursively query the dataset", 4 | "conclusionStatement": { 5 | "messages": [ 6 | { 7 | "content": "via code hook", 8 | "contentType": "PlainText" 9 | } 10 | ] 11 | }, 12 | "fulfillmentActivity": { 13 | "codeHook": { 14 | "messageVersion": "1.0", 15 | "uri": "{{lambda-arn}}" 16 | }, 17 | "type": "CodeHook" 18 | }, 19 | "sampleUtterances": [ 20 | "How many {event_name} tickets were sold for {cat_desc}", 21 | "How many {event_name} tickets at {venue_name}", 22 | "Count of {event_name} tickets sold in {venue_city} {venue_state}", 23 | "Count of {event_name} tickets in {venue_state}", 24 | "Total {event_name} ticket sales in {venue_city}", 25 | "Total {event_name} tickets sold in {event_month}", 26 | "Total {event_name} ticket sales", 27 | 28 | "How many tickets were sold for {cat_desc}", 29 | "How many tickets were sold at {venue_name} in {event_month}", 30 | "Tickets sold in {venue_city} {venue_state} for {event_name}", 31 | "Tickets sold in {venue_state} in {event_month}", 32 | "Count of tickets sold in {venue_city} for {event_name}", 33 | "Count of tickets sold in {event_month} in {venue_city}", 34 | "Ticket sales for {event_name} in {venue_state}", 35 | 36 | "How many tickets were sold" 37 | ], 38 | "slots": [ 39 | { 40 | "slotType": "event_name", 41 | "name": "event_name", 42 | "slotConstraint": "Optional", 43 | "valueElicitationPrompt": { 44 | "maxAttempts": 2, 45 | "messages": [ 46 | { 47 | "content": "The name of the event", 48 | "contentType": "PlainText" 49 | } 50 | ] 51 | }, 52 | "priority": 1, 53 | "slotTypeVersion": "$LATEST", 54 | "sampleUtterances": [], 55 | "description": "event_name" 56 | }, 57 | { 58 | "slotType": "AMAZON.Month", 59 | "name": "event_month", 60 | "slotConstraint": "Optional", 61 | "valueElicitationPrompt": { 62 | "maxAttempts": 2, 63 | "messages": [ 64 | { 65 | "content": "The month of the event", 66 | "contentType": "PlainText" 67 | } 68 | ] 69 | }, 70 | "priority": 2, 71 | "sampleUtterances": [], 72 | "description": "event_month" 73 | }, 74 | { 75 | "slotType": "AMAZON.US_CITY", 76 | "name": "venue_city", 77 | "slotConstraint": "Optional", 78 | "valueElicitationPrompt": { 79 | "maxAttempts": 2, 80 | "messages": [ 81 | { 82 | "content": "The city where the event takes place", 83 | "contentType": "PlainText" 84 | } 85 | ] 86 | }, 87 | "priority": 3, 88 | "sampleUtterances": [], 89 | "description": "venue_city" 90 | }, 91 | { 92 | "slotType": "AMAZON.US_STATE", 93 | "name": "venue_state", 94 | "slotConstraint": "Optional", 95 | "valueElicitationPrompt": { 96 | "maxAttempts": 2, 97 | "messages": [ 98 | { 99 | "content": "The state where the event takes place", 100 | "contentType": "PlainText" 101 | } 102 | ] 103 | }, 104 | "priority": 4, 105 | "sampleUtterances": [], 106 | "description": "venue_state" 107 | }, 108 | { 109 | "slotType": "AMAZON.MusicVenue", 110 | "name": "venue_name", 111 | "slotConstraint": "Optional", 112 | "valueElicitationPrompt": { 113 | "maxAttempts": 2, 114 | "messages": [ 115 | { 116 | "content": "The venue where the event takes place", 117 | "contentType": "PlainText" 118 | } 119 | ] 120 | }, 121 | "priority": 5, 122 | "sampleUtterances": [], 123 | "description": "venue_name" 124 | }, 125 | { 126 | "slotType": "cat_desc", 127 | "name": "cat_desc", 128 | "slotConstraint": "Optional", 129 | "valueElicitationPrompt": { 130 | "maxAttempts": 2, 131 | "messages": [ 132 | { 133 | "content": "The type of event", 134 | "contentType": "PlainText" 135 | } 136 | ] 137 | }, 138 | "priority": 6, 139 | "slotTypeVersion": "$LATEST", 140 | "sampleUtterances": [], 141 | "description": "cat_desc" 142 | } 143 | ] 144 | } 145 | -------------------------------------------------------------------------------- /intents/Goodbye_Intent.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Goodbye_Intent", 3 | "description": "Let BIBot know you're done", 4 | "conclusionStatement": { 5 | "messages": [ 6 | { 7 | "content": "via code hook", 8 | "contentType": "PlainText" 9 | } 10 | ] 11 | }, 12 | "fulfillmentActivity": { 13 | "codeHook": { 14 | "messageVersion": "1.0", 15 | "uri": "{{lambda-arn}}" 16 | }, 17 | "type": "CodeHook" 18 | }, 19 | "sampleUtterances": [ 20 | "all set", 21 | "all set thanks", 22 | "bye now", 23 | "bye", 24 | "catch you later", 25 | "catch ya later", 26 | "cool thanks", 27 | "good job", 28 | "i'm done", 29 | "i'm finished", 30 | "ok thank you", 31 | "ok thanks", 32 | "thank you", 33 | "thanks", 34 | "thanks bro", 35 | "thanks dude", 36 | "you're the best", 37 | "see ya", 38 | "see you later" 39 | ], 40 | "slots": [ 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /intents/Hello_Intent.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Hello_Intent", 3 | "description": "Say hello to BIBot", 4 | "conclusionStatement": { 5 | "messages": [ 6 | { 7 | "content": "via code hook", 8 | "contentType": "PlainText" 9 | } 10 | ] 11 | }, 12 | "fulfillmentActivity": { 13 | "codeHook": { 14 | "messageVersion": "1.0", 15 | "uri": "{{lambda-arn}}" 16 | }, 17 | "type": "CodeHook" 18 | }, 19 | "sampleUtterances": [ 20 | "hello", 21 | "hello there", 22 | "bro", 23 | "dude", 24 | "hey", 25 | "hey bro", 26 | "hey dude", 27 | "hey man", 28 | "hey there", 29 | "hi", 30 | "hi there", 31 | "BIBot", 32 | "yo", 33 | "you there" 34 | ], 35 | "slots": [ 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /intents/Refresh_Intent.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Refresh_Intent", 3 | "description": "Refresh dimesions from the database", 4 | "conclusionStatement": { 5 | "messages": [ 6 | { 7 | "content": "via code hook", 8 | "contentType": "PlainText" 9 | } 10 | ] 11 | }, 12 | "fulfillmentActivity": { 13 | "codeHook": { 14 | "messageVersion": "1.0", 15 | "uri": "{{lambda-arn}}" 16 | }, 17 | "type": "CodeHook" 18 | }, 19 | "sampleUtterances": [ 20 | "refresh", 21 | "refresh your data", 22 | "refresh the dimensions", 23 | "refresh dimensions" 24 | ], 25 | "slots": [ 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /intents/Reset_Intent.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Reset_Intent", 3 | "description": "Tell BIBot to start over", 4 | "sampleUtterances": [ 5 | "Forget {venue_city} BIBot", 6 | "Forget about {venue_state}", 7 | "Reset {event_month}", 8 | "Clear {venue_name}", 9 | "Clear {event_name}", 10 | "Forget the {dimension}", 11 | "Start over", 12 | "Reset everything", 13 | "Reset" 14 | ], 15 | "slots": [ 16 | { 17 | "slotType": "dimensions", 18 | "name": "dimension", 19 | "slotConstraint": "Optional", 20 | "valueElicitationPrompt": { 21 | "maxAttempts": 2, 22 | "messages": [ 23 | { 24 | "content": "dimension", 25 | "contentType": "PlainText" 26 | } 27 | ] 28 | }, 29 | "priority": 1, 30 | "slotTypeVersion": "$LATEST", 31 | "sampleUtterances": [] 32 | }, 33 | { 34 | "slotType": "AMAZON.Month", 35 | "name": "event_month", 36 | "slotConstraint": "Optional", 37 | "valueElicitationPrompt": { 38 | "maxAttempts": 2, 39 | "messages": [ 40 | { 41 | "content": "event_month", 42 | "contentType": "PlainText" 43 | } 44 | ] 45 | }, 46 | "priority": 2, 47 | "sampleUtterances": [] 48 | }, 49 | { 50 | "slotType": "AMAZON.MusicVenue", 51 | "name": "venue_name", 52 | "slotConstraint": "Optional", 53 | "valueElicitationPrompt": { 54 | "maxAttempts": 2, 55 | "messages": [ 56 | { 57 | "content": "venue_name", 58 | "contentType": "PlainText" 59 | } 60 | ] 61 | }, 62 | "priority": 3, 63 | "sampleUtterances": [] 64 | }, 65 | { 66 | "slotType": "AMAZON.US_CITY", 67 | "name": "venue_city", 68 | "slotConstraint": "Optional", 69 | "valueElicitationPrompt": { 70 | "maxAttempts": 2, 71 | "messages": [ 72 | { 73 | "content": "venue_city", 74 | "contentType": "PlainText" 75 | } 76 | ] 77 | }, 78 | "priority": 4, 79 | "sampleUtterances": [] 80 | }, 81 | { 82 | "slotType": "AMAZON.US_STATE", 83 | "name": "venue_state", 84 | "slotConstraint": "Optional", 85 | "valueElicitationPrompt": { 86 | "maxAttempts": 2, 87 | "messages": [ 88 | { 89 | "content": "venue_state", 90 | "contentType": "PlainText" 91 | } 92 | ] 93 | }, 94 | "priority": 5, 95 | "sampleUtterances": [] 96 | }, 97 | { 98 | "slotType": "event_name", 99 | "name": "event_name", 100 | "slotConstraint": "Optional", 101 | "valueElicitationPrompt": { 102 | "maxAttempts": 2, 103 | "messages": [ 104 | { 105 | "content": "The name of the event", 106 | "contentType": "PlainText" 107 | } 108 | ] 109 | }, 110 | "priority": 6, 111 | "slotTypeVersion": "$LATEST", 112 | "sampleUtterances": [], 113 | "description": "event_name" 114 | } 115 | ], 116 | "fulfillmentActivity": { 117 | "type": "CodeHook", 118 | "codeHook": { 119 | "uri": "{{lambda-arn}}", 120 | "messageVersion": "1.0" 121 | } 122 | }, 123 | "conclusionStatement": { 124 | "messages": [ 125 | { 126 | "content": "via code hook", 127 | "contentType": "PlainText" 128 | } 129 | ] 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /intents/Switch_Intent.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Switch_Intent", 3 | "description": "Change the latest dimension value", 4 | "sampleUtterances": [ 5 | "And for {cat_desc}", 6 | "How about in {venue_city} {venue_state}", 7 | "How about in {venue_city}", 8 | "What about in {venue_state}", 9 | "And in {event_month}", 10 | "How about at {venue_name}", 11 | "What about for {event_name}" 12 | ], 13 | "slots": [ 14 | { 15 | "slotType": "event_name", 16 | "name": "event_name", 17 | "slotConstraint": "Optional", 18 | "valueElicitationPrompt": { 19 | "maxAttempts": 2, 20 | "messages": [ 21 | { 22 | "content": "event name", 23 | "contentType": "PlainText" 24 | } 25 | ] 26 | }, 27 | "priority": 1, 28 | "slotTypeVersion": "$LATEST", 29 | "sampleUtterances": [] 30 | }, 31 | { 32 | "slotType": "AMAZON.Month", 33 | "name": "event_month", 34 | "slotConstraint": "Optional", 35 | "valueElicitationPrompt": { 36 | "maxAttempts": 2, 37 | "messages": [ 38 | { 39 | "content": "event_month", 40 | "contentType": "PlainText" 41 | } 42 | ] 43 | }, 44 | "priority": 2, 45 | "sampleUtterances": [] 46 | }, 47 | { 48 | "slotType": "AMAZON.MusicVenue", 49 | "name": "venue_name", 50 | "slotConstraint": "Optional", 51 | "valueElicitationPrompt": { 52 | "maxAttempts": 2, 53 | "messages": [ 54 | { 55 | "content": "venue_name", 56 | "contentType": "PlainText" 57 | } 58 | ] 59 | }, 60 | "priority": 3, 61 | "sampleUtterances": [] 62 | }, 63 | { 64 | "slotType": "AMAZON.US_CITY", 65 | "name": "venue_city", 66 | "slotConstraint": "Optional", 67 | "valueElicitationPrompt": { 68 | "maxAttempts": 2, 69 | "messages": [ 70 | { 71 | "content": "venue_city", 72 | "contentType": "PlainText" 73 | } 74 | ] 75 | }, 76 | "priority": 4, 77 | "sampleUtterances": [] 78 | }, 79 | { 80 | "slotType": "AMAZON.US_STATE", 81 | "name": "venue_state", 82 | "slotConstraint": "Optional", 83 | "valueElicitationPrompt": { 84 | "maxAttempts": 2, 85 | "messages": [ 86 | { 87 | "content": "venue_state", 88 | "contentType": "PlainText" 89 | } 90 | ] 91 | }, 92 | "priority": 5, 93 | "sampleUtterances": [] 94 | }, 95 | { 96 | "slotType": "cat_desc", 97 | "name": "cat_desc", 98 | "slotConstraint": "Optional", 99 | "valueElicitationPrompt": { 100 | "maxAttempts": 2, 101 | "messages": [ 102 | { 103 | "content": "category", 104 | "contentType": "PlainText" 105 | } 106 | ] 107 | }, 108 | "priority": 6, 109 | "slotTypeVersion": "$LATEST", 110 | "sampleUtterances": [] 111 | } 112 | ], 113 | "fulfillmentActivity": { 114 | "type": "CodeHook", 115 | "codeHook": { 116 | "uri": "{{lambda-arn}}", 117 | "messageVersion": "1.0" 118 | } 119 | }, 120 | "conclusionStatement": { 121 | "messages": [ 122 | { 123 | "content": "via code hook", 124 | "contentType": "PlainText" 125 | } 126 | ] 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /intents/Top_Intent.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Top_Intent", 3 | "description": "Top five sales by dimension", 4 | "sampleUtterances": [ 5 | "What were the top {count} {dimension} for {cat_desc}", 6 | "Tell me the top {count} {dimension} in {venue_city} {venue_state}", 7 | "Top {count} {dimension} for {cat_desc}", 8 | "Top {count} {dimension} in {venue_state}", 9 | "Top {count} {dimension} in {venue_city}", 10 | "Top {count} {dimension} at {venue_name}", 11 | "Top {count} {dimension} in {event_month}", 12 | "Top {count} {dimension} for {event_name}", 13 | "Top {count} {dimension}", 14 | "Top {dimension}", 15 | "Top {count}" 16 | ], 17 | "slots": [ 18 | { 19 | "slotType": "AMAZON.NUMBER", 20 | "name": "count", 21 | "slotConstraint": "Optional", 22 | "valueElicitationPrompt": { 23 | "maxAttempts": 2, 24 | "messages": [ 25 | { 26 | "content": "number", 27 | "contentType": "PlainText" 28 | } 29 | ] 30 | }, 31 | "priority": 1, 32 | "sampleUtterances": [] 33 | }, 34 | { 35 | "slotType": "dimensions", 36 | "name": "dimension", 37 | "slotConstraint": "Optional", 38 | "valueElicitationPrompt": { 39 | "maxAttempts": 2, 40 | "messages": [ 41 | { 42 | "content": "dimension", 43 | "contentType": "PlainText" 44 | } 45 | ] 46 | }, 47 | "priority": 2, 48 | "slotTypeVersion": "$LATEST", 49 | "sampleUtterances": [] 50 | }, 51 | { 52 | "slotType": "event_name", 53 | "name": "event_name", 54 | "slotConstraint": "Optional", 55 | "valueElicitationPrompt": { 56 | "maxAttempts": 2, 57 | "messages": [ 58 | { 59 | "content": "event name", 60 | "contentType": "PlainText" 61 | } 62 | ] 63 | }, 64 | "priority": 3, 65 | "slotTypeVersion": "$LATEST", 66 | "sampleUtterances": [] 67 | }, 68 | { 69 | "slotType": "AMAZON.Month", 70 | "name": "event_month", 71 | "slotConstraint": "Optional", 72 | "valueElicitationPrompt": { 73 | "maxAttempts": 2, 74 | "messages": [ 75 | { 76 | "content": "month", 77 | "contentType": "PlainText" 78 | } 79 | ] 80 | }, 81 | "priority": 4, 82 | "sampleUtterances": [] 83 | }, 84 | { 85 | "slotType": "AMAZON.MusicVenue", 86 | "name": "venue_name", 87 | "slotConstraint": "Optional", 88 | "valueElicitationPrompt": { 89 | "maxAttempts": 2, 90 | "messages": [ 91 | { 92 | "content": "venue", 93 | "contentType": "PlainText" 94 | } 95 | ] 96 | }, 97 | "priority": 5, 98 | "sampleUtterances": [] 99 | }, 100 | { 101 | "slotType": "AMAZON.US_CITY", 102 | "name": "venue_city", 103 | "slotConstraint": "Optional", 104 | "valueElicitationPrompt": { 105 | "maxAttempts": 2, 106 | "messages": [ 107 | { 108 | "content": "city", 109 | "contentType": "PlainText" 110 | } 111 | ] 112 | }, 113 | "priority": 6, 114 | "sampleUtterances": [] 115 | }, 116 | { 117 | "slotType": "AMAZON.US_STATE", 118 | "name": "venue_state", 119 | "slotConstraint": "Optional", 120 | "valueElicitationPrompt": { 121 | "maxAttempts": 2, 122 | "messages": [ 123 | { 124 | "content": "state", 125 | "contentType": "PlainText" 126 | } 127 | ] 128 | }, 129 | "priority": 7, 130 | "sampleUtterances": [] 131 | }, 132 | { 133 | "slotType": "cat_desc", 134 | "name": "cat_desc", 135 | "slotConstraint": "Optional", 136 | "valueElicitationPrompt": { 137 | "maxAttempts": 2, 138 | "messages": [ 139 | { 140 | "content": "category", 141 | "contentType": "PlainText" 142 | } 143 | ] 144 | }, 145 | "priority": 8, 146 | "slotTypeVersion": "$LATEST", 147 | "sampleUtterances": [] 148 | } 149 | ], 150 | "conclusionStatement": { 151 | "messages": [ 152 | { 153 | "content": "via code hook", 154 | "contentType": "PlainText" 155 | } 156 | ] 157 | }, 158 | "fulfillmentActivity": { 159 | "type": "CodeHook", 160 | "codeHook": { 161 | "uri": "{{lambda-arn}}", 162 | "messageVersion": "1.0" 163 | } 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /lambda/bibot_config.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 5 | # software and associated documentation files (the "Software"), to deal in the Software 6 | # without restriction, including without limitation the rights to use, copy, modify, 7 | # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so. 9 | # 10 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 11 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 12 | # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 13 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 14 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | # 17 | 18 | ORIGINAL_VALUE = 0 19 | TOP_RESOLUTION = 1 20 | 21 | SLOT_CONFIG = { 22 | 'event_name': {'type': TOP_RESOLUTION, 'remember': True, 'error': 'I couldn\'t find an event called "{}".'}, 23 | 'event_month': {'type': ORIGINAL_VALUE, 'remember': True}, 24 | 'venue_name': {'type': ORIGINAL_VALUE, 'remember': True}, 25 | 'venue_city': {'type': ORIGINAL_VALUE, 'remember': True}, 26 | 'venue_state': {'type': ORIGINAL_VALUE, 'remember': True}, 27 | 'cat_desc': {'type': TOP_RESOLUTION, 'remember': True, 'error': 'I couldn\'t find a category called "{}".'}, 28 | 'count': {'type': ORIGINAL_VALUE, 'remember': True}, 29 | 'dimension': {'type': ORIGINAL_VALUE, 'remember': True}, 30 | 'one_event': {'type': TOP_RESOLUTION, 'remember': False, 'error': 'I couldn\'t find an event called "{}".'}, 31 | 'another_event': {'type': TOP_RESOLUTION, 'remember': False, 'error': 'I couldn\'t find an event called "{}".'}, 32 | 'one_venue': {'type': ORIGINAL_VALUE, 'remember': False}, 33 | 'another_venue': {'type': ORIGINAL_VALUE, 'remember': False}, 34 | 'one_month': {'type': ORIGINAL_VALUE, 'remember': False}, 35 | 'another_month': {'type': ORIGINAL_VALUE, 'remember': False}, 36 | 'one_city': {'type': ORIGINAL_VALUE, 'remember': False}, 37 | 'another_city': {'type': ORIGINAL_VALUE, 'remember': False}, 38 | 'one_state': {'type': ORIGINAL_VALUE, 'remember': False}, 39 | 'another_state': {'type': ORIGINAL_VALUE, 'remember': False}, 40 | 'one_category': {'type': TOP_RESOLUTION, 'remember': False, 'error': 'I couldn\'t find a category called "{}".'}, 41 | 'another_category': {'type': TOP_RESOLUTION, 'remember': False, 'error': 'I couldn\'t find a category called "{}".'} 42 | } 43 | 44 | DIMENSIONS = { 45 | 'events': {'slot': 'event_name', 'column': 'e.event_name', 'singular': 'event'}, 46 | 'months': {'slot': 'event_month', 'column': 'd.month', 'singular': 'month'}, 47 | 'venues': {'slot': 'venue_name', 'column': 'v.venue_name', 'singular': 'venue'}, 48 | 'cities': {'slot': 'venue_city', 'column': 'v.venue_city', 'singular': 'city'}, 49 | 'states': {'slot': 'venue_state', 'column': 'v.venue_state', 'singular': 'state'}, 50 | 'categories': {'slot': 'cat_desc', 'column': 'c.cat_desc', 'singular': 'category'} 51 | } 52 | 53 | 54 | class SlotError(Exception): 55 | pass 56 | 57 | -------------------------------------------------------------------------------- /lambda/bibot_helpers.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 5 | # software and associated documentation files (the "Software"), to deal in the Software 6 | # without restriction, including without limitation the rights to use, copy, modify, 7 | # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so. 9 | # 10 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 11 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 12 | # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 13 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 14 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | # 17 | 18 | import boto3 19 | import time 20 | import logging 21 | import json 22 | import pprint 23 | import os 24 | import bibot_config as bibot 25 | import bibot_userexits as userexits 26 | 27 | # 28 | # See additional configuration parameters at bottom 29 | # 30 | 31 | logger = logging.getLogger() 32 | logger.setLevel(logging.DEBUG) 33 | 34 | 35 | def get_bibot_config(): 36 | global ATHENA_DB 37 | global ATHENA_OUTPUT_LOCATION 38 | 39 | try: 40 | ATHENA_DB = os.environ['ATHENA_DB'] 41 | ATHENA_OUTPUT_LOCATION = os.environ['ATHENA_OUTPUT_LOCATION'] 42 | except KeyError: 43 | return 'I have a configuration error - please set up the Athena database information.' 44 | 45 | logger.debug('<> athena_db = ' + ATHENA_DB) 46 | logger.debug('<> athena_output_location = ' + ATHENA_OUTPUT_LOCATION) 47 | 48 | 49 | def execute_athena_query(query_string): 50 | start = time.perf_counter() 51 | 52 | athena = boto3.client('athena') 53 | 54 | response = athena.start_query_execution( 55 | QueryString=query_string, 56 | QueryExecutionContext={'Database': ATHENA_DB}, 57 | ResultConfiguration={ 58 | 'OutputLocation': ATHENA_OUTPUT_LOCATION, 59 | } 60 | ) 61 | 62 | query_execution_id = response['QueryExecutionId'] 63 | 64 | status = 'RUNNING' 65 | while (status == 'RUNNING' or status == 'QUEUED'): 66 | response = athena.get_query_execution(QueryExecutionId=query_execution_id) 67 | status = response['QueryExecution']['Status']['State'] 68 | if (status == 'RUNNING' or status == 'QUEUED'): 69 | #logger.debug('<> query status = ' + status + ': sleep 200ms') 70 | time.sleep(0.200) 71 | 72 | duration = time.perf_counter() - start 73 | duration_string = 'query duration = %.0f' % (duration * 1000) + ' ms' 74 | logger.debug('<> query status = ' + status + ', ' + duration_string) 75 | 76 | response = athena.get_query_results(QueryExecutionId=query_execution_id) 77 | logger.debug('<> query response = ' + json.dumps(response)) 78 | 79 | return response 80 | 81 | 82 | def get_slot_values(slot_values, intent_request): 83 | if slot_values is None: 84 | slot_values = {key: None for key in bibot.SLOT_CONFIG} 85 | 86 | slots = intent_request['currentIntent']['slots'] 87 | 88 | for key,config in bibot.SLOT_CONFIG.items(): 89 | slot_values[key] = slots.get(key) 90 | logger.debug('<> retrieving slot value for %s = %s', key, slot_values[key]) 91 | if slot_values[key]: 92 | if config.get('type', bibot.ORIGINAL_VALUE) == bibot.TOP_RESOLUTION: 93 | # get the resolved slot name of what the user said/typed 94 | if len(intent_request['currentIntent']['slotDetails'][key]['resolutions']) > 0: 95 | slot_values[key] = intent_request['currentIntent']['slotDetails'][key]['resolutions'][0]['value'] 96 | else: 97 | errorMsg = bibot.SLOT_CONFIG[key].get('error', 'Sorry, I don\'t understand "{}".') 98 | raise bibot.SlotError(errorMsg.format(slots.get(key))) 99 | 100 | slot_values[key] = userexits.post_process_slot_value(key, slot_values[key]) 101 | 102 | return slot_values 103 | 104 | 105 | def get_remembered_slot_values(slot_values, session_attributes): 106 | logger.debug('<> get_remembered_slot_values() - session_attributes: %s', session_attributes) 107 | 108 | str = session_attributes.get('rememberedSlots') 109 | remembered_slot_values = json.loads(str) if str is not None else {key: None for key in bibot.SLOT_CONFIG} 110 | 111 | if slot_values is None: 112 | slot_values = {key: None for key in bibot.SLOT_CONFIG} 113 | 114 | logger.debug('<> get_remembered_slot_values() - slot_values: %s', slot_values) 115 | logger.debug('<> get_remembered_slot_values() - remembered_slot_values: %s', remembered_slot_values) 116 | for key,config in bibot.SLOT_CONFIG.items(): 117 | if config.get('remember', False): 118 | logger.debug('<> get_remembered_slot_values() - slot_values[%s] = %s', key, slot_values.get(key)) 119 | logger.debug('<> get_remembered_slot_values() - remembered_slot_values[%s] = %s', key, remembered_slot_values.get(key)) 120 | if slot_values.get(key) is None: 121 | slot_values[key] = remembered_slot_values.get(key) 122 | 123 | return slot_values 124 | 125 | 126 | def remember_slot_values(slot_values, session_attributes): 127 | if slot_values is None: 128 | slot_values = {key: None for key,config in bibot.SLOT_CONFIG.items() if config['remember']} 129 | session_attributes['rememberedSlots'] = json.dumps(slot_values) 130 | logger.debug('<> Storing updated slot values: %s', slot_values) 131 | return slot_values 132 | 133 | 134 | def close(session_attributes, fulfillment_state, message): 135 | response = { 136 | 'sessionAttributes': session_attributes, 137 | 'dialogAction': { 138 | 'type': 'Close', 139 | 'fulfillmentState': fulfillment_state, 140 | 'message': message 141 | } 142 | } 143 | 144 | logger.debug('<> "Lambda fulfillment function response = \n' + pprint.pformat(response, indent=4)) 145 | 146 | return response 147 | 148 | 149 | def increment_counter(session_attributes, counter): 150 | counter_value = session_attributes.get(counter, '0') 151 | 152 | if counter_value: count = int(counter_value) + 1 153 | else: count = 1 154 | 155 | session_attributes[counter] = count 156 | 157 | return count 158 | 159 | 160 | -------------------------------------------------------------------------------- /lambda/bibot_userexits.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 5 | # software and associated documentation files (the "Software"), to deal in the Software 6 | # without restriction, including without limitation the rights to use, copy, modify, 7 | # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so. 9 | # 10 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 11 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 12 | # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 13 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 14 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | # 17 | 18 | import time 19 | import logging 20 | 21 | # 22 | # See additional configuration parameters at bottom 23 | # 24 | 25 | logger = logging.getLogger() 26 | logger.setLevel(logging.DEBUG) 27 | 28 | 29 | # adjust dimension values as necessary prior to inserting into where clause 30 | def pre_process_query_value(key, value): 31 | logger.debug('<> pre_process_query_value(%s, %s)', key, value) 32 | value = value.replace("'", "''") # don't allow any 's in WHERE clause 33 | if key == 'event_month': 34 | value = value[0:3] 35 | elif key == 'venue_name': 36 | value = value.lower().replace('theater', 'theatre') 37 | value = value.lower().replace('u. s.', 'us') 38 | value = value.lower().replace('u.s.', 'us') 39 | elif key == 'venue_state': 40 | value = US_STATES.get(value.lower(), value) 41 | 42 | logger.debug('<> pre_process_query_value() - returning key=%s, value=%s', key, value) 43 | 44 | return value 45 | 46 | 47 | # adjust slot values as necessary after reading from intent slots 48 | def post_process_slot_value(key, value): 49 | if key == 'venue_state': 50 | value = US_STATES.get(value.lower(), value) 51 | logger.debug('<> post_process_slot_value() - returning key=%s, value=%s', key, value) 52 | return value 53 | 54 | 55 | def post_process_dimension_output(key, value): 56 | logger.debug('<> post_process_dimension_output(%s, %s)', key, value) 57 | if key == 'states': 58 | value = get_state_name(value) 59 | elif key == 'months': 60 | value = get_month_name(value) 61 | logger.debug('<> post_process_dimension_output() - returning key=%s, value=%s', key, value) 62 | return value 63 | 64 | 65 | # 66 | # user exit functions for pre- and post-processors 67 | # 68 | 69 | def get_state_name(value): 70 | if not isinstance(value, str): return value 71 | state_name = REVERSE_US_STATES.get(value.upper()) 72 | return state_name if state_name else value.title() 73 | 74 | 75 | def get_month_name(value): 76 | if not isinstance(value, str): return value 77 | month_name = MONTH_NAMES.get(value.upper()[0:3]) 78 | return month_name if month_name else value.title() 79 | 80 | 81 | def post_process_venue_name(value): 82 | if not isinstance(value, str): return value 83 | value = value.title().replace('Us ', 'US ') 84 | return value 85 | 86 | DIMENSION_FORMATTERS = { 87 | 'event_name': {'format': 'For {}', 'function': str.title}, 88 | 'event_month': {'format': 'In the month of {}', 'function': get_month_name}, 89 | 'venue_name': {'format': 'At {}', 'function': post_process_venue_name}, 90 | 'venue_city': {'format': 'In the city of {}', 'function': str.title}, 91 | 'venue_state': {'format': 'In the state of {}', 'function': get_state_name}, 92 | 'cat_desc': {'format': 'For {}', 'function': str.title} 93 | } 94 | 95 | MONTH_NAMES = { 96 | "JAN": "January", 97 | "FEB": "February", 98 | "MAR": "March", 99 | "APR": "April", 100 | "MAY": "May", 101 | "JUN": "June", 102 | "JUL": "July", 103 | "AUG": "August", 104 | "SEP": "September", 105 | "OCT": "October", 106 | "NOV": "November", 107 | "DEC": "December" 108 | } 109 | 110 | US_STATES = { 111 | 'alaska': 'AK', 112 | 'alabama': 'AL', 113 | 'arkansas': 'AR', 114 | 'american samoa': 'AS', 115 | 'arizona': 'AZ', 116 | 'california': 'CA', 117 | 'colorado': 'CO', 118 | 'connecticut': 'CT', 119 | 'district of columbia': 'DC', 120 | 'delaware': 'DE', 121 | 'florida': 'FL', 122 | 'georgia': 'GA', 123 | 'guam': 'GU', 124 | 'hawaii': 'HI', 125 | 'iowa': 'IA', 126 | 'idaho': 'ID', 127 | 'illinois': 'IL', 128 | 'indiana': 'IN', 129 | 'kansas': 'KS', 130 | 'kentucky': 'KY', 131 | 'louisiana': 'LA', 132 | 'massachusetts': 'MA', 133 | 'maryland': 'MD', 134 | 'maine': 'ME', 135 | 'michigan': 'MI', 136 | 'minnesota': 'MN', 137 | 'missouri': 'MO', 138 | 'mississippi': 'MS', 139 | 'montana': 'MT', 140 | 'north carolina': 'NC', 141 | 'north dakota': 'ND', 142 | 'nebraska': 'NE', 143 | 'new hampshire': 'NH', 144 | 'new jersey': 'NJ', 145 | 'new mexico': 'NM', 146 | 'nevada': 'NV', 147 | 'new york': 'NY', 148 | 'ohio': 'OH', 149 | 'oklahoma': 'OK', 150 | 'oregon': 'OR', 151 | 'pennsylvania': 'PA', 152 | 'puerto rico': 'PR', 153 | 'rhode island': 'RI', 154 | 'south carolina': 'SC', 155 | 'south dakota': 'SD', 156 | 'tennessee': 'TN', 157 | 'texas': 'TX', 158 | 'utah': 'UT', 159 | 'virginia': 'VA', 160 | 'virgin islands': 'VI', 161 | 'vermont': 'VT', 162 | 'washington': 'WA', 163 | 'wisconsin': 'WI', 164 | 'west virginia': 'WV', 165 | 'wyoming': 'WY' 166 | } 167 | 168 | REVERSE_US_STATES = { 169 | 'AK': 'Alaska', 170 | 'AL': 'Alabama', 171 | 'AR': 'Arkansas', 172 | 'AS': 'American Samoa', 173 | 'AZ': 'Arizona', 174 | 'CA': 'California', 175 | 'CO': 'Colorado', 176 | 'CT': 'Connecticut', 177 | 'DC': 'District of Columbia', 178 | 'DE': 'Delaware', 179 | 'FL': 'Florida', 180 | 'GA': 'Georgia', 181 | 'GU': 'Guam', 182 | 'HI': 'Hawaii', 183 | 'IA': 'Iowa', 184 | 'ID': 'Idaho', 185 | 'IL': 'Illinois', 186 | 'IN': 'Indiana', 187 | 'KS': 'Kansas', 188 | 'KY': 'Kentucky', 189 | 'LA': 'Louisiana', 190 | 'MA': 'Massachusetts', 191 | 'MD': 'Maryland', 192 | 'ME': 'Maine', 193 | 'MI': 'Michigan', 194 | 'MN': 'Minnesota', 195 | 'MO': 'Missouri', 196 | 'MS': 'Mississippi', 197 | 'MT': 'Montana', 198 | 'NC': 'North Carolina', 199 | 'ND': 'North Dakota', 200 | 'NE': 'Nebraska', 201 | 'NH': 'New Hampshire', 202 | 'NJ': 'New Jersey', 203 | 'NM': 'New Mexico', 204 | 'NV': 'Nevada', 205 | 'NY': 'New York', 206 | 'OH': 'Ohio', 207 | 'OK': 'Oklahoma', 208 | 'OR': 'Oregon', 209 | 'PA': 'Pennsylvania', 210 | 'PR': 'Puerto Rico', 211 | 'RI': 'Rhode Island', 212 | 'SC': 'South Carolina', 213 | 'SD': 'South Dakota', 214 | 'TN': 'Tennessee', 215 | 'TX': 'Texas', 216 | 'UT': 'Utah', 217 | 'VA': 'Virginia', 218 | 'VI': 'Virgin Islands', 219 | 'VT': 'Vermont', 220 | 'WA': 'Washington', 221 | 'WI': 'Wisconsin', 222 | 'WV': 'West Virginia', 223 | 'WY': 'Wyoming' 224 | } 225 | 226 | -------------------------------------------------------------------------------- /lambda/compare_intent.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 5 | # software and associated documentation files (the "Software"), to deal in the Software 6 | # without restriction, including without limitation the rights to use, copy, modify, 7 | # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so. 9 | # 10 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 11 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 12 | # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 13 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 14 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | # 17 | 18 | import time 19 | import logging 20 | import json 21 | import bibot_config as bibot 22 | import bibot_helpers as helpers 23 | import bibot_userexits as userexits 24 | 25 | COMPARE_CONFIG = { 26 | 'events': {'1st': 'one_event', '2nd': 'another_event', 'error': 'Sorry, try "Compare sales for Event 1 versus Event 2'}, 27 | 'months': {'1st': 'one_month', '2nd': 'another_month', 'error': 'Sorry, try "Compare sales for Month 1 versus Month 2'}, 28 | 'venues': {'1st': 'one_venue', '2nd': 'another_venue', 'error': 'Sorry, try "Compare sales for Venue 1 versus Venue 2'}, 29 | 'cities': {'1st': 'one_city', '2nd': 'another_city', 'error': 'Sorry, try "Compare sales for City 1 versus City 2'}, 30 | 'states': {'1st': 'one_state', '2nd': 'another_state', 'error': 'Sorry, try "Compare sales for State 1 versus State 2'}, 31 | 'categories': {'1st': 'one_category', '2nd': 'another_category', 'error': 'Sorry, try "Compare sales for Category 1 versus Category 2'} 32 | } 33 | 34 | # SELECT statement for Compare query 35 | COMPARE_SELECT = "SELECT {}, SUM(s.amount) ticket_sales FROM sales s, event e, venue v, category c, date_dim d " 36 | COMPARE_JOIN = " WHERE e.event_id = s.event_id AND v.venue_id = e.venue_id AND c.cat_id = e.cat_id AND d.date_id = e.date_id " 37 | COMPARE_WHERE = " AND LOWER({}) LIKE LOWER('%{}%') " 38 | COMPARE_ORDERBY = " GROUP BY {} ORDER BY ticket_sales DESC " 39 | 40 | logger = logging.getLogger() 41 | logger.setLevel(logging.DEBUG) 42 | 43 | 44 | def lambda_handler(event, context): 45 | logger.debug('<> Lex event info = ' + json.dumps(event)) 46 | 47 | session_attributes = event['sessionAttributes'] 48 | logger.debug('<> lambda_handler: session_attributes = ' + json.dumps(session_attributes)) 49 | 50 | config_error = helpers.get_bibot_config() 51 | if config_error is not None: 52 | return helpers.close(session_attributes, 'Fulfilled', 53 | {'contentType': 'PlainText', 'content': config_error}) 54 | else: 55 | return compare_intent_handler(event, session_attributes) 56 | 57 | 58 | def compare_intent_handler(intent_request, session_attributes): 59 | method_start = time.perf_counter() 60 | 61 | logger.debug('<> compare_intent_handler: session_attributes = ' + json.dumps(session_attributes)) 62 | 63 | session_attributes['greetingCount'] = '1' 64 | session_attributes['resetCount'] = '0' 65 | session_attributes['finishedCount'] = '0' 66 | session_attributes['lastIntent'] = None # "switch" handling done in Compare_Intent 67 | 68 | # Retrieve slot values from the current request 69 | slot_values = session_attributes.get('slot_values') 70 | 71 | try: 72 | slot_values = helpers.get_slot_values(slot_values, intent_request) 73 | except bibot.SlotError as err: 74 | return helpers.close(session_attributes, 'Fulfilled', {'contentType': 'PlainText','content': str(err)}) 75 | 76 | logger.debug('<> "count_intent_handler(): slot_values: %s', slot_values) 77 | 78 | # Retrieve "remembered" slot values from session attributes 79 | slot_values = helpers.get_remembered_slot_values(slot_values, session_attributes) 80 | logger.debug('<> "count_intent_handler(): slot_values afer get_remembered_slot_values: %s', slot_values) 81 | 82 | # Remember updated slot values 83 | helpers.remember_slot_values(slot_values, session_attributes) 84 | 85 | for key,config in COMPARE_CONFIG.items(): 86 | if slot_values.get(config['1st']): 87 | if slot_values.get(config['2nd']) is None: 88 | return helpers.close(session_attributes, 'Fulfilled', {'contentType': 'PlainText', 'content': config['error'] }) 89 | 90 | slot_values['dimension'] = key 91 | slot_values[bibot.DIMENSIONS[key]['slot']] = None 92 | 93 | the_1st_dimension_value = slot_values[config['1st']].lower() 94 | the_2nd_dimension_value = slot_values[config['2nd']].lower() 95 | 96 | break 97 | 98 | # Build and execute query 99 | select_clause = COMPARE_SELECT.format(bibot.DIMENSIONS[slot_values['dimension']]['column']) 100 | where_clause = COMPARE_JOIN 101 | 102 | the_1st_dimension_value = userexits.pre_process_query_value(bibot.DIMENSIONS[key]['slot'], the_1st_dimension_value) 103 | the_2nd_dimension_value = userexits.pre_process_query_value(bibot.DIMENSIONS[key]['slot'], the_2nd_dimension_value) 104 | where_clause += " AND (LOWER(" + bibot.DIMENSIONS[slot_values['dimension']]['column'] + ") LIKE LOWER('%" + the_1st_dimension_value + "%') OR " 105 | where_clause += "LOWER(" + bibot.DIMENSIONS[slot_values['dimension']]['column'] + ") LIKE LOWER('%" + the_2nd_dimension_value + "%')) " 106 | 107 | logger.debug('<> compare_sales_intent_request - building WHERE clause') 108 | for dimension in bibot.DIMENSIONS: 109 | slot_key = bibot.DIMENSIONS.get(dimension).get('slot') 110 | if slot_values[slot_key] is not None: 111 | logger.debug('<> compare_sales_intent_request - calling userexits.pre_process_query_value(%s, %s)', 112 | slot_key, slot_values[slot_key]) 113 | value = userexits.pre_process_query_value(slot_key, slot_values[slot_key]) 114 | where_clause += COMPARE_WHERE.format(bibot.DIMENSIONS.get(dimension).get('column'), value) 115 | 116 | order_by_group_by = COMPARE_ORDERBY.format(bibot.DIMENSIONS[slot_values['dimension']]['column']) 117 | 118 | query_string = select_clause + where_clause + order_by_group_by 119 | 120 | logger.debug('<> Athena Query String = ' + query_string) 121 | 122 | response = helpers.execute_athena_query(query_string) 123 | 124 | # Build response string 125 | response_string = '' 126 | result_count = len(response['ResultSet']['Rows']) - 1 127 | 128 | # add the English versions of the WHERE clauses 129 | counter = 0 130 | for dimension in bibot.DIMENSIONS: 131 | slot_key = bibot.DIMENSIONS[dimension].get('slot') 132 | logger.debug('<> pre compare_sale_formatter[%s] = %s', slot_key, slot_values.get(slot_key)) 133 | if slot_values.get(slot_key) is not None: 134 | # the DIMENSION_FORMATTERS perform a post-process function and then format the output 135 | # Example: {... 'venue_state': {'format': ' in the state of {}', 'function': get_state_name}, ...} 136 | if userexits.DIMENSION_FORMATTERS.get(slot_key) is not None: 137 | output_text = userexits.DIMENSION_FORMATTERS[slot_key]['function'](slot_values.get(slot_key)) 138 | if counter == 0: 139 | response_string += userexits.DIMENSION_FORMATTERS[slot_key]['format'].format(output_text) 140 | else: 141 | response_string += ', ' + userexits.DIMENSION_FORMATTERS[slot_key]['format'].lower().format(output_text) 142 | counter += 1 143 | logger.debug('<> compare_sales_formatter[%s] = %s', slot_key, output_text) 144 | 145 | if (result_count == 0): 146 | if len(response_string) > 0: 147 | response_string += ', ' 148 | response_string += "I didn't find any results for the " + slot_values['dimension'] 149 | response_string += " " + userexits.post_process_dimension_output(key, the_1st_dimension_value) 150 | response_string += " and " + userexits.post_process_dimension_output(key, the_2nd_dimension_value) + "." 151 | 152 | elif (result_count == 1): 153 | if len(response_string) > 0: 154 | response_string += ', there ' 155 | else: 156 | response_string += 'There ' 157 | response_string += 'is only one ' + bibot.DIMENSIONS[slot_values['dimension']]['singular'] + '.' 158 | 159 | elif (result_count == 2): 160 | # put the results into a dict for easier reference by name 161 | result_set = {} 162 | result_set.update( { response['ResultSet']['Rows'][1]['Data'][0]['VarCharValue'].lower() : [ 163 | response['ResultSet']['Rows'][1]['Data'][0]['VarCharValue'], 164 | float(response['ResultSet']['Rows'][1]['Data'][1]['VarCharValue']) ] } ) 165 | result_set.update( { response['ResultSet']['Rows'][2]['Data'][0]['VarCharValue'].lower() : [ 166 | response['ResultSet']['Rows'][2]['Data'][0]['VarCharValue'], 167 | float(response['ResultSet']['Rows'][2]['Data'][1]['VarCharValue']) ] } ) 168 | 169 | logger.debug('<> compare_intent_handler - result_set = %s', result_set) 170 | 171 | the_1st_dimension_string = result_set[the_1st_dimension_value.lower()][0] 172 | the_1st_dimension_string = userexits.post_process_dimension_output(key, the_1st_dimension_string) 173 | the_2nd_dimension_string = result_set[the_2nd_dimension_value.lower()][0] 174 | the_2nd_dimension_string = userexits.post_process_dimension_output(key, the_2nd_dimension_string) 175 | 176 | if len(response_string) == 0: 177 | response_string = 'Sales for ' + the_1st_dimension_string + ' were ' 178 | else: 179 | response_string += ', sales for ' + the_1st_dimension_string + ' were ' 180 | 181 | the_1st_amount = result_set[the_1st_dimension_value.lower()][1] 182 | the_2nd_amount = result_set[the_2nd_dimension_value.lower()][1] 183 | 184 | the_1st_amount_formatted = '{:,.0f}'.format(the_1st_amount) 185 | the_2nd_amount_formatted = '{:,.0f}'.format(the_2nd_amount) 186 | 187 | if (the_1st_amount == the_2nd_amount): 188 | response_string += 'the same as for ' + the_2nd_dimension_string + ', $' + the_2nd_amount_formatted 189 | else: 190 | if (the_1st_amount < the_2nd_amount): 191 | percent_different = (the_1st_amount - the_2nd_amount) / the_2nd_amount * -1 192 | higher_or_lower = 'lower' 193 | else: 194 | percent_different = (the_1st_amount - the_2nd_amount) / the_2nd_amount 195 | higher_or_lower = 'higher' 196 | 197 | response_string += '{:.0%}'.format(percent_different) + ' ' + higher_or_lower + ' than for ' + the_2nd_dimension_string 198 | response_string += ': $' + the_1st_amount_formatted + ' as opposed to $' + the_2nd_amount_formatted + '.' 199 | 200 | else: # >2, should not occur 201 | response_string = 'I seem to have a problem, I got back ' + str(result_count) + ' ' + dimension + '.' 202 | 203 | logger.debug('<> response_string = ' + response_string) 204 | 205 | method_duration = time.perf_counter() - method_start 206 | method_duration_string = 'method time = %.0f' % (method_duration * 1000) + ' ms' 207 | logger.debug('<> "Method duration is: ' + method_duration_string) 208 | 209 | return helpers.close(session_attributes, 'Fulfilled', {'contentType': 'PlainText','content': response_string}) 210 | 211 | -------------------------------------------------------------------------------- /lambda/count_intent.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 5 | # software and associated documentation files (the "Software"), to deal in the Software 6 | # without restriction, including without limitation the rights to use, copy, modify, 7 | # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so. 9 | # 10 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 11 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 12 | # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 13 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 14 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | # 17 | 18 | import time 19 | import logging 20 | import json 21 | import bibot_config as bibot 22 | import bibot_helpers as helpers 23 | import bibot_userexits as userexits 24 | 25 | # SELECT statement for Count query 26 | COUNT_SELECT = "SELECT SUM(s.qty) FROM sales s, event e, venue v, category c, date_dim d " 27 | COUNT_JOIN = " WHERE e.event_id = s.event_id AND v.venue_id = e.venue_id AND c.cat_id = e.cat_id AND d.date_id = e.date_id " 28 | COUNT_WHERE = " AND LOWER({}) LIKE LOWER('%{}%') " 29 | COUNT_PHRASE = 'tickets sold' 30 | 31 | logger = logging.getLogger() 32 | logger.setLevel(logging.DEBUG) 33 | 34 | def lambda_handler(event, context): 35 | logger.debug('<> Lex event info = ' + json.dumps(event)) 36 | 37 | config_error = helpers.get_bibot_config() 38 | 39 | session_attributes = event['sessionAttributes'] 40 | logger.debug('<> lambda_handler: session_attributes = ' + json.dumps(session_attributes)) 41 | 42 | if config_error is not None: 43 | return helpers.close(session_attributes, 'Fulfilled', 44 | {'contentType': 'PlainText', 'content': config_error}) 45 | else: 46 | return count_intent_handler(event, session_attributes) 47 | 48 | 49 | def count_intent_handler(intent_request, session_attributes): 50 | method_start = time.perf_counter() 51 | 52 | logger.debug('<> count_intent_handler: intent_request = ' + json.dumps(intent_request)) 53 | logger.debug('<> count_intent_handler: session_attributes = ' + json.dumps(session_attributes)) 54 | 55 | session_attributes['greetingCount'] = '1' 56 | session_attributes['resetCount'] = '0' 57 | session_attributes['finishedCount'] = '0' 58 | session_attributes['lastIntent'] = 'Count_Intent' 59 | 60 | # Retrieve slot values from the current request 61 | slot_values = session_attributes.get('slot_values') 62 | 63 | try: 64 | slot_values = helpers.get_slot_values(slot_values, intent_request) 65 | except bibot.SlotError as err: 66 | return helpers.close(session_attributes, 'Fulfilled', {'contentType': 'PlainText','content': str(err)}) 67 | 68 | logger.debug('<> "count_intent_handler(): slot_values: %s', slot_values) 69 | 70 | # Retrieve "remembered" slot values from session attributes 71 | slot_values = helpers.get_remembered_slot_values(slot_values, session_attributes) 72 | logger.debug('<> "count_intent_handler(): slot_values afer get_remembered_slot_values: %s', slot_values) 73 | 74 | # Remember updated slot values 75 | helpers.remember_slot_values(slot_values, session_attributes) 76 | 77 | # build and execute query 78 | select_clause = COUNT_SELECT 79 | where_clause = COUNT_JOIN 80 | for dimension in bibot.DIMENSIONS: 81 | slot_key = bibot.DIMENSIONS.get(dimension).get('slot') 82 | if slot_values[slot_key] is not None: 83 | value = userexits.pre_process_query_value(slot_key, slot_values[slot_key]) 84 | where_clause += COUNT_WHERE.format(bibot.DIMENSIONS.get(dimension).get('column'), value) 85 | 86 | query_string = select_clause + where_clause 87 | 88 | response = helpers.execute_athena_query(query_string) 89 | 90 | result = response['ResultSet']['Rows'][1]['Data'][0] 91 | if result: 92 | count = result['VarCharValue'] 93 | else: 94 | count = 0 95 | 96 | logger.debug('<> "Count value is: %s' % count) 97 | 98 | # build response string 99 | if count == 0: 100 | response_string = 'There were no {}'.format(COUNT_PHRASE) 101 | else: 102 | response_string = 'There were {} {}'.format(count, COUNT_PHRASE) 103 | 104 | # add the English versions of the WHERE clauses 105 | for dimension in bibot.DIMENSIONS: 106 | slot_key = bibot.DIMENSIONS[dimension].get('slot') 107 | logger.debug('<> pre top5_formatter[%s] = %s', slot_key, slot_values.get(slot_key)) 108 | if slot_values.get(slot_key) is not None: 109 | # the DIMENSION_FORMATTERS perform a post-process functions and then format the output 110 | # Example: {... 'venue_state': {'format': ' in the state of {}', 'function': get_state_name}, ...} 111 | if userexits.DIMENSION_FORMATTERS.get(slot_key) is not None: 112 | output_text = userexits.DIMENSION_FORMATTERS[slot_key]['function'](slot_values.get(slot_key)) 113 | response_string += ' ' + userexits.DIMENSION_FORMATTERS[slot_key]['format'].lower().format(output_text) 114 | logger.debug('<> dimension_formatter[%s] = %s', slot_key, output_text) 115 | 116 | response_string += '.' 117 | 118 | return helpers.close(session_attributes, 'Fulfilled', {'contentType': 'PlainText','content': response_string}) 119 | 120 | -------------------------------------------------------------------------------- /lambda/goodbye_intent.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 5 | # software and associated documentation files (the "Software"), to deal in the Software 6 | # without restriction, including without limitation the rights to use, copy, modify, 7 | # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so. 9 | # 10 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 11 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 12 | # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 13 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 14 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | # 17 | 18 | import logging 19 | import json 20 | import bibot_config as bibot 21 | import bibot_helpers as helpers 22 | import bibot_userexits as userexits 23 | 24 | logger = logging.getLogger() 25 | logger.setLevel(logging.DEBUG) 26 | 27 | 28 | def lambda_handler(event, context): 29 | logger.debug('<> Lex event info = ' + json.dumps(event)) 30 | 31 | session_attributes = event['sessionAttributes'] 32 | logger.debug('<> lambda_handler: session_attributes = ' + json.dumps(session_attributes)) 33 | 34 | config_error = helpers.get_bibot_config() 35 | if config_error is not None: 36 | return helpers.close(session_attributes, 'Fulfilled', 37 | {'contentType': 'PlainText', 'content': config_error}) 38 | else: 39 | return goodbye_intent_handler(event, session_attributes) 40 | 41 | 42 | def goodbye_intent_handler(intent_request, session_attributes): 43 | session_attributes['greetingCount'] = '0' 44 | session_attributes['resetCount'] = '0' 45 | session_attributes['queryAttributes'] = None 46 | session_attributes['lastIntent'] = None 47 | 48 | askCount = helpers.increment_counter(session_attributes, 'finishedCount') 49 | 50 | # build response string 51 | if askCount == 1: response_string = 'Nice chatting with you. Talk to you later!' 52 | elif askCount == 2: response_string = 'Bye now!' 53 | elif askCount == 3: response_string = 'Hope I was able to help!' 54 | elif askCount == 4: response_string = 'See ya!' 55 | elif askCount == 5: response_string = 'Really?' 56 | else: response_string = 'Ok' 57 | 58 | slot_values = {key: None for key in bibot.SLOT_CONFIG} 59 | helpers.remember_slot_values(slot_values, session_attributes) 60 | 61 | return helpers.close(session_attributes, 'Fulfilled', {'contentType': 'PlainText','content': response_string}) 62 | 63 | -------------------------------------------------------------------------------- /lambda/hello_intent.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 5 | # software and associated documentation files (the "Software"), to deal in the Software 6 | # without restriction, including without limitation the rights to use, copy, modify, 7 | # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so. 9 | # 10 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 11 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 12 | # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 13 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 14 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | # 17 | 18 | import logging 19 | import json 20 | import bibot_helpers as helpers 21 | 22 | logger = logging.getLogger() 23 | logger.setLevel(logging.DEBUG) 24 | 25 | 26 | def lambda_handler(event, context): 27 | logger.debug('<> Lex event info = ' + json.dumps(event)) 28 | 29 | session_attributes = event['sessionAttributes'] 30 | 31 | if session_attributes is None: 32 | session_attributes = {} 33 | 34 | logger.debug('<> lambda_handler: session_attributes = ' + json.dumps(session_attributes)) 35 | 36 | return hello_intent_handler(event, session_attributes) 37 | 38 | 39 | def hello_intent_handler(intent_request, session_attributes): 40 | session_attributes['resetCount'] = '0' 41 | session_attributes['finishedCount'] = '0' 42 | # don't alter session_attributes['lastIntent'], let BIBot remember the last used intent 43 | 44 | askCount = helpers.increment_counter(session_attributes, 'greetingCount') 45 | 46 | # build response string 47 | if askCount == 1: response_string = "Hello! How can I help?" 48 | elif askCount == 2: response_string = "I'm here" 49 | elif askCount == 3: response_string = "I'm listening" 50 | elif askCount == 4: response_string = "Yes?" 51 | elif askCount == 5: response_string = "Really?" 52 | else: response_string = 'Ok' 53 | 54 | return helpers.close(session_attributes, 'Fulfilled', {'contentType': 'PlainText','content': response_string}) 55 | 56 | -------------------------------------------------------------------------------- /lambda/refresh_intent.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 5 | # software and associated documentation files (the "Software"), to deal in the Software 6 | # without restriction, including without limitation the rights to use, copy, modify, 7 | # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so. 9 | # 10 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 11 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 12 | # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 13 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 14 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | # 17 | 18 | import boto3 19 | import time 20 | import logging 21 | import json 22 | import pprint 23 | import bibot_helpers as helpers 24 | import bibot_userexits as userexits 25 | 26 | # 27 | # parameters for Refresh intent 28 | # 29 | REFRESH_QUERY = 'SELECT DISTINCT event_name from event ORDER BY event_name' 30 | REFRESH_SLOT = 'event_name' 31 | REFRESH_INTENT = 'Compare_Intent' 32 | REFRESH_BOT = 'BIBot' 33 | 34 | logger = logging.getLogger() 35 | logger.setLevel(logging.DEBUG) 36 | 37 | 38 | def lambda_handler(event, context): 39 | logger.debug('<> Lex event info = ' + json.dumps(event)) 40 | 41 | session_attributes = event['sessionAttributes'] 42 | logger.debug('<> lambda_handler: session_attributes = ' + json.dumps(session_attributes)) 43 | 44 | config_error = helpers.get_bibot_config() 45 | if config_error is not None: 46 | return helpers.close(session_attributes, 'Fulfilled', 47 | {'contentType': 'PlainText', 'content': config_error}) 48 | else: 49 | return refresh_intent_handler(event, session_attributes) 50 | 51 | 52 | def refresh_intent_handler(intent_request, session_attributes): 53 | athena = boto3.client('athena') 54 | session_attributes['lastIntent'] = None 55 | 56 | # Build and execute query 57 | logger.debug('<> Athena Query String = ' + REFRESH_QUERY) 58 | 59 | st_values = [] 60 | response = helpers.execute_athena_query(REFRESH_QUERY) 61 | logger.debug('<> query response = ' + json.dumps(response)) 62 | 63 | while len(response['ResultSet']['Rows']) > 0: 64 | for item in response['ResultSet']['Rows']: 65 | st_values.append({'value': item['Data'][0]['VarCharValue']}) 66 | logger.debug('<> appending: ' + item['Data'][0]['VarCharValue']) 67 | 68 | try: 69 | next_token = response['NextToken'] 70 | response = athena.get_query_results(QueryExecutionId=query_execution_id, NextToken=next_token, MaxResults=100) 71 | logger.debug('<> additional query response = ' + json.dumps(response)) 72 | except KeyError: 73 | break 74 | 75 | logger.debug('<> "st_values = ' + pprint.pformat(st_values)) 76 | 77 | lex_models = boto3.client('lex-models') 78 | response = lex_models.get_slot_type(name=REFRESH_SLOT, version='$LATEST') 79 | logger.debug('<> "boto3 version = ' + boto3.__version__) 80 | logger.debug('<> "Lex slot event_name = ' + pprint.pformat(response, indent=4)) 81 | logger.debug('<> "Lex slot event_name checksum = ' + response['checksum']) 82 | logger.debug('<> "Lex slot event_name valueSelectionStrategy = ' + response['valueSelectionStrategy']) 83 | 84 | try: 85 | logger.debug('<> "st_values = ' + pprint.pformat(st_values)) 86 | 87 | st_checksum = response['checksum'] 88 | response = lex_models.put_slot_type(name=response['name'], 89 | description=response['description'], 90 | enumerationValues=st_values, 91 | checksum=response['checksum'], 92 | valueSelectionStrategy=response['valueSelectionStrategy'] 93 | ) 94 | except KeyError: 95 | pass 96 | 97 | response = lex_models.get_intent(name=REFRESH_INTENT, version='$LATEST') 98 | logger.debug('<> Lex get-intent = ' + pprint.pformat(response, indent=4)) 99 | logger.debug('<> Lex get-intent keys = ' + pprint.pformat(response.keys())) 100 | 101 | response = lex_models.put_intent(name=response['name'], 102 | description=response['description'], 103 | slots=response['slots'], 104 | sampleUtterances=response['sampleUtterances'], 105 | conclusionStatement=response['conclusionStatement'], 106 | fulfillmentActivity=response['fulfillmentActivity'], 107 | checksum=response['checksum'] 108 | ) 109 | 110 | response = lex_models.get_bot(name=REFRESH_BOT, versionOrAlias='$LATEST') 111 | logger.debug('<> Lex bot = ' + pprint.pformat(response, indent=4)) 112 | 113 | response = lex_models.put_bot(name=REFRESH_BOT, 114 | description=response['description'], 115 | intents=response['intents'], 116 | clarificationPrompt=response['clarificationPrompt'], 117 | abortStatement=response['abortStatement'], 118 | idleSessionTTLInSeconds=response['idleSessionTTLInSeconds'], 119 | voiceId=response['voiceId'], 120 | processBehavior='SAVE', 121 | locale=response['locale'], 122 | checksum=response['checksum'], 123 | childDirected=response['childDirected'] 124 | ) 125 | 126 | logger.debug('<> Lex put bot = ' + pprint.pformat(response, indent=4)) 127 | 128 | response_string = "I've refreshed the events dimension from the database. Please rebuild me." 129 | return helpers.close(session_attributes, 'Fulfilled', {'contentType': 'PlainText','content': response_string}) 130 | 131 | -------------------------------------------------------------------------------- /lambda/reset_intent.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 5 | # software and associated documentation files (the "Software"), to deal in the Software 6 | # without restriction, including without limitation the rights to use, copy, modify, 7 | # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so. 9 | # 10 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 11 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 12 | # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 13 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 14 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | # 17 | 18 | import time 19 | import logging 20 | import json 21 | import bibot_config as bibot 22 | import bibot_helpers as helpers 23 | import bibot_userexits as userexits 24 | 25 | logger = logging.getLogger() 26 | logger.setLevel(logging.DEBUG) 27 | 28 | 29 | def lambda_handler(event, context): 30 | logger.debug('<> Lex event info = ' + json.dumps(event)) 31 | 32 | session_attributes = event['sessionAttributes'] 33 | logger.debug('<> lambda_handler: session_attributes = ' + json.dumps(session_attributes)) 34 | 35 | config_error = helpers.get_bibot_config() 36 | if config_error is not None: 37 | return helpers.close(session_attributes, 'Fulfilled', 38 | {'contentType': 'PlainText', 'content': config_error}) 39 | else: 40 | return reset_intent_handler(event, session_attributes) 41 | 42 | 43 | def reset_intent_handler(intent_request, session_attributes): 44 | session_attributes['greetingCount'] = '1' 45 | session_attributes['finishedCount'] = '0' 46 | # don't alter session_attributes['lastIntent'], let BIBot remember the last used intent 47 | 48 | # Retrieve "remembered" slot values from session attributes 49 | slot_values = helpers.get_remembered_slot_values(None, session_attributes) 50 | 51 | dimensions_reset = '' 52 | 53 | # Retrieve slot values from the current request to see what needs to be reset 54 | slots_to_reset = helpers.get_slot_values(None, intent_request) 55 | 56 | # check to see if any remembered slots need forgetting 57 | for key,config in bibot.SLOT_CONFIG.items(): 58 | if key == 'dimension': # see below 59 | continue 60 | if config.get('remember', False): 61 | if slots_to_reset.get(key): # asking to reset venue_city: los angeles for example 62 | if slot_values.get(key): 63 | value = userexits.post_process_dimension_output(key, slot_values.get(key)) 64 | dimensions_reset += ' {}'.format(value.title()) 65 | logger.debug('<> reset_intent_handler() - forgetting slot %s value %s', key, slot_values[key]) 66 | slot_values[key] = None 67 | else: 68 | # message = "I wasn't remembering {} - {} anyway.".format(key, slots_to_reset.get(key)) 69 | message = "I wasn't remembering {} anyway.".format(slots_to_reset.get(key)) 70 | return helpers.close(session_attributes, 'Fulfilled', {'contentType': 'PlainText', 'content': message}) 71 | 72 | # check for special case, where the ask is to forget the dimension by name 73 | dimension = slots_to_reset.get('dimension') 74 | if dimension and bibot.DIMENSIONS.get(dimension): 75 | slot_key = bibot.DIMENSIONS[dimension].get('slot') 76 | if slot_values.get(slot_key): 77 | logger.debug('<> reset_intent_handler() - forgetting %s (%s)', dimension, slot_values[slot_key]) 78 | value = userexits.post_process_dimension_output(dimension, slot_values[slot_key]) 79 | dimensions_reset += ' {}'.format(value).title() 80 | logger.debug('<> reset_intent_handler() - forgetting dimension %s slot_key %s value %s', dimension, slot_key, slot_values[slot_key]) 81 | slot_values[slot_key] = None 82 | 83 | if dimensions_reset == '': 84 | slot_values = {key: None for key in bibot.SLOT_CONFIG} 85 | dimensions_reset = 'everything' 86 | 87 | helpers.remember_slot_values(slot_values, session_attributes) 88 | 89 | response_string = 'OK, I have reset ' + dimensions_reset + '.' 90 | 91 | return helpers.close(session_attributes, 'Fulfilled', {'contentType': 'PlainText','content': response_string}) 92 | 93 | -------------------------------------------------------------------------------- /lambda/switch_intent.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 5 | # software and associated documentation files (the "Software"), to deal in the Software 6 | # without restriction, including without limitation the rights to use, copy, modify, 7 | # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so. 9 | # 10 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 11 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 12 | # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 13 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 14 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | # 17 | 18 | import time 19 | import logging 20 | import json 21 | import bibot_helpers as helpers 22 | import bibot_userexits as userexits 23 | import count_intent 24 | import top_intent 25 | 26 | # 27 | # See additional configuration parameters at bottom 28 | # 29 | 30 | logger = logging.getLogger() 31 | logger.setLevel(logging.DEBUG) 32 | 33 | 34 | def lambda_handler(event, context): 35 | logger.debug('<> Lex event info = ' + json.dumps(event)) 36 | 37 | session_attributes = event['sessionAttributes'] 38 | logger.debug('<> lambda_handler: session_attributes = ' + json.dumps(session_attributes)) 39 | 40 | config_error = helpers.get_bibot_config() 41 | if config_error is not None: 42 | return helpers.close(session_attributes, 'Fulfilled', 43 | {'contentType': 'PlainText', 'content': config_error}) 44 | else: 45 | return switch_intent_handler(event, session_attributes) 46 | 47 | 48 | def switch_intent_handler(intent_request, session_attributes): 49 | session_attributes['greetingCount'] = '0' 50 | session_attributes['resetCount'] = '0' 51 | session_attributes['finishedCount'] = '0' 52 | # note: do not alter session_attributes['lastIntent'] here 53 | 54 | if session_attributes.get('lastIntent', None) is not None: 55 | intent_name = session_attributes['lastIntent'] 56 | if INTENT_CONFIG.get(intent_name, False): 57 | logger.debug('<> switch_intent_handler: session_attributes = ' + json.dumps(session_attributes)) 58 | logger.debug('<> switch_intent_handler: refirecting to ' + intent_name) 59 | return INTENT_CONFIG[intent_name]['handler'](intent_request, session_attributes) # dispatch to the event handler 60 | else: 61 | return helpers.close(session_attributes, 'Fulfilled', 62 | {'contentType': 'PlainText', 'content': 'Sorry, I don\'t support the intent called "' + intent_name + '".'}) 63 | else: 64 | return helpers.close(session_attributes, 'Fulfilled', 65 | {'contentType': 'PlainText', 'content': 'Sorry, I\'m not sure what you\'re asking me.'}) 66 | 67 | 68 | INTENT_CONFIG = { 69 | 'Top_Intent': {'handler': top_intent.top_intent_handler}, 70 | 'Count_Intent': {'handler': count_intent.count_intent_handler} 71 | } 72 | 73 | -------------------------------------------------------------------------------- /lambda/top_intent.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 5 | # software and associated documentation files (the "Software"), to deal in the Software 6 | # without restriction, including without limitation the rights to use, copy, modify, 7 | # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so. 9 | # 10 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 11 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 12 | # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 13 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 14 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | # 17 | 18 | import time 19 | import logging 20 | import json 21 | import bibot_config as bibot 22 | import bibot_helpers as helpers 23 | import bibot_userexits as userexits 24 | 25 | # SELECT statement for Top query 26 | TOP_SELECT = "SELECT {}, SUM(s.amount) ticket_sales FROM sales s, event e, venue v, category c, date_dim d " 27 | TOP_JOIN = " WHERE e.event_id = s.event_id AND v.venue_id = e.venue_id AND c.cat_id = e.cat_id AND d.date_id = e.date_id " 28 | TOP_WHERE = " AND LOWER({}) LIKE LOWER('%{}%') " 29 | TOP_ORDERBY = " GROUP BY {} ORDER BY ticket_sales desc" 30 | TOP_DEFAULT_COUNT = '5' 31 | 32 | logger = logging.getLogger() 33 | logger.setLevel(logging.DEBUG) 34 | 35 | 36 | def lambda_handler(event, context): 37 | logger.debug('<> Lex event info = ' + json.dumps(event)) 38 | 39 | session_attributes = event['sessionAttributes'] 40 | logger.debug('<> lambda_handler: session_attributes = ' + json.dumps(session_attributes)) 41 | 42 | config_error = helpers.get_bibot_config() 43 | if config_error is not None: 44 | return helpers.close(session_attributes, 'Fulfilled', 45 | {'contentType': 'PlainText', 'content': config_error}) 46 | else: 47 | return top_intent_handler(event, session_attributes) 48 | 49 | 50 | def top_intent_handler(intent_request, session_attributes): 51 | method_start = time.perf_counter() 52 | 53 | logger.debug('<> top_intent_handler: session_attributes = ' + json.dumps(session_attributes)) 54 | 55 | session_attributes['greetingCount'] = '1' 56 | session_attributes['resetCount'] = '0' 57 | session_attributes['finishedCount'] = '0' 58 | session_attributes['lastIntent'] = 'Top_Intent' 59 | 60 | # Retrieve slot values from the current request 61 | slot_values = session_attributes.get('slot_values') 62 | 63 | try: 64 | slot_values = helpers.get_slot_values(slot_values, intent_request) 65 | except bibot.SlotError as err: 66 | return helpers.close(session_attributes, 'Fulfilled', {'contentType': 'PlainText','content': str(err)}) 67 | 68 | logger.debug('<> "top_intent_handler(): slot_values: %s', slot_values) 69 | 70 | # Retrieve "remembered" slot values from session attributes 71 | slot_values = helpers.get_remembered_slot_values(slot_values, session_attributes) 72 | logger.debug('<> "top_intent_handler(): slot_values afer get_remembered_slot_values: %s', slot_values) 73 | 74 | if slot_values.get('count') is None: 75 | slot_values['count'] = TOP_DEFAULT_COUNT 76 | 77 | if slot_values.get('dimension') is None: 78 | if len(bibot.DIMENSIONS.keys()) > 0: 79 | response_string = 'Please tell me a dimension, for example, "top five ' 80 | for counter, item in enumerate(bibot.DIMENSIONS.keys()): 81 | if counter == 0: 82 | response_string += item + '".' 83 | elif counter == 1: 84 | response_string += ' I can also report on ' + item 85 | if len(bibot.DIMENSIONS.keys()) == 2: 86 | response_string += '.' 87 | elif counter < (len(bibot.DIMENSIONS.keys()) - 1): 88 | response_string += ', ' + item 89 | else: 90 | if len(bibot.DIMENSIONS.keys()) == 3: 91 | response_string += ' and ' + item + '.' 92 | else: 93 | response_string += ', and ' + item + '.' 94 | else: 95 | response_string = 'Please tell me a dimension, for example, "top five months".' 96 | 97 | return helpers.close(session_attributes, 'Fulfilled', {'contentType': 'PlainText','content': response_string}) 98 | 99 | # If switching dimension, forget the prior remembered value for that dimension 100 | dimension_slot = bibot.DIMENSIONS.get(slot_values.get('dimension')).get('slot') 101 | if dimension_slot is not None: 102 | slot_values[dimension_slot] = None 103 | logger.debug('<> "top_intent_handler(): cleared dimension slot: %s', dimension_slot) 104 | 105 | # store updated slot values 106 | logger.debug('<> "top_intent_handler(): calling remember_slot_values_NEW: %s', slot_values) 107 | helpers.remember_slot_values(slot_values, session_attributes) 108 | 109 | # Check for minimum required slot values 110 | if slot_values.get('dimension') is None: 111 | return helpers.close(session_attributes, 'Fulfilled', 112 | {'contentType': 'PlainText', 'content': "Sorry, I didn't understand that. Try \"Top 5 venues for all rock and pop\"."}) 113 | 114 | # Build and execute query 115 | try: 116 | # the SELECT clause is for a particular dimension e.g., top 5 {states}... 117 | # Example: "SELECT {}, SUM(s.amount) ticket_sales FROM sales s, event e, venue v, category c, date_dim ed " 118 | select_clause = TOP_SELECT.format(bibot.DIMENSIONS.get(slot_values.get('dimension')).get('column')) 119 | except KeyError: 120 | return helpers.close(session_attributes, 'Fulfilled', 121 | {'contentType': 'PlainText', 'content': "Sorry, I don't know what you mean by " + slot_values['dimension']}) 122 | 123 | # add JOIN clauses 124 | where_clause = TOP_JOIN 125 | 126 | # add WHERE clause for each non empty slot 127 | for dimension in bibot.DIMENSIONS: 128 | slot_key = bibot.DIMENSIONS.get(dimension).get('slot') 129 | if slot_values[slot_key] is not None: 130 | value = userexits.pre_process_query_value(slot_key, slot_values[slot_key]) 131 | where_clause += TOP_WHERE.format(bibot.DIMENSIONS.get(dimension).get('column'), value) 132 | 133 | try: 134 | # the GROUP BY is by dimension, and the ORDER by is the aggregated fact 135 | # Example: " GROUP BY {} ORDER BY ticket_sales desc" 136 | order_by_group_by = TOP_ORDERBY.format(bibot.DIMENSIONS.get(slot_values.get('dimension')).get('column')) 137 | order_by_group_by += " LIMIT {}".format(slot_values.get('count')) 138 | except KeyError: 139 | return helpers.close( 140 | session_attributes, 141 | 'Fulfilled', 142 | { 143 | 'contentType': 'PlainText', 144 | 'content': "Sorry, I don't know what you mean by " + dimension 145 | } 146 | ) 147 | 148 | query_string = select_clause + where_clause + order_by_group_by 149 | logger.debug('<> Athena Query String = ' + query_string) 150 | 151 | # execute Athena query 152 | response = helpers.execute_athena_query(query_string) 153 | 154 | # Build response text for Lex 155 | response_string = '' 156 | result_count = len(response['ResultSet']['Rows']) - 1 157 | 158 | if result_count < int(slot_values.get('count', 0)): 159 | if result_count == 0: 160 | response_string += "There weren't any " + slot_values.get('dimension') + " " 161 | elif result_count == 1: 162 | response_string += "There was only 1. " 163 | else: 164 | response_string += "There were only " + str(result_count) + ". " 165 | 166 | if result_count == 0: 167 | pass 168 | elif result_count == 1: 169 | try: 170 | response_string += 'The top ' + bibot.DIMENSIONS.get(slot_values.get('dimension')).get('singular') 171 | except KeyError: 172 | response_string += 'The top ' + slot_values.get('dimension') 173 | else: 174 | response_string += 'The top ' + str(result_count) + ' ' + slot_values.get('dimension') 175 | 176 | # add the English versions of the WHERE clauses 177 | for dimension in bibot.DIMENSIONS: 178 | slot_key = bibot.DIMENSIONS[dimension].get('slot') 179 | logger.debug('<> pre top5_formatter[%s] = %s', slot_key, slot_values.get(slot_key)) 180 | if slot_values.get(slot_key) is not None: 181 | # the DIMENSION_FORMATTERS perform a post-process functions and then format the output 182 | # Example: {... 'venue_state': {'format': ' in the state of {}', 'function': get_state_name}, ...} 183 | if userexits.DIMENSION_FORMATTERS.get(slot_key) is not None: 184 | output_text = userexits.DIMENSION_FORMATTERS[slot_key]['function'](slot_values.get(slot_key)) 185 | output_text = userexits.DIMENSION_FORMATTERS[slot_key]['format'].lower().format(output_text) 186 | response_string += ' ' + output_text 187 | logger.debug('<> top5_formatter[%s] = %s', slot_key, output_text) 188 | 189 | if result_count == 0: 190 | pass 191 | elif result_count == 1: 192 | response_string += ' was ' 193 | else: 194 | response_string += ' were ' 195 | 196 | # add the list of top X dimension values to the response text 197 | if result_count > 0: 198 | remembered_value = None 199 | for counter, item in enumerate(response['ResultSet']['Rows']): 200 | if counter > 0: 201 | if counter > 1: 202 | response_string += '; and ' if counter == result_count else '; ' 203 | if result_count > 1: 204 | response_string += str(counter) + ', ' 205 | 206 | value = userexits.post_process_dimension_output(slot_values.get('dimension'), item['Data'][0]['VarCharValue']) 207 | response_string += value 208 | 209 | remembered_value = item['Data'][0]['VarCharValue'] 210 | 211 | response_string += '.' 212 | 213 | logger.debug('<> response_string = ' + response_string) 214 | 215 | # If result count = 1, remember the value for future questions 216 | if result_count == 1: 217 | slot_name = bibot.DIMENSIONS.get(slot_values.get('dimension')).get('slot') 218 | slot_values[slot_name] = remembered_value 219 | 220 | # store updated query attributes 221 | helpers.remember_slot_values(slot_values, session_attributes) 222 | 223 | method_duration = time.perf_counter() - method_start 224 | method_duration_string = 'method time = %.0f' % (method_duration * 1000) + ' ms' 225 | logger.debug('<> "Method duration is: ' + method_duration_string) 226 | 227 | logger.debug('<> top_intent_handler() - sessions_attributes = %s, response = %s', session_attributes, {'contentType': 'PlainText','content': response_string}) 228 | 229 | return helpers.close(session_attributes, 'Fulfilled', {'contentType': 'PlainText','content': response_string}) 230 | 231 | -------------------------------------------------------------------------------- /slots/cat_desc.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cat_desc", 3 | "description": "Categories of events in the TICKIT database", 4 | "valueSelectionStrategy": "ORIGINAL_VALUE", 5 | "enumerationValues": [ 6 | { "value": "Musical theatre" }, 7 | { "value": "All non-musical theatre" }, 8 | { "value": "All opera and light opera" }, 9 | { "value": "All rock and pop music concerts" } 10 | ] 11 | } 12 | 13 | -------------------------------------------------------------------------------- /slots/dimensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dimensions", 3 | "description": "List of dimensions in ticket database", 4 | "valueSelectionStrategy": "TOP_RESOLUTION", 5 | "enumerationValues": [ 6 | { 7 | "synonyms": [ 8 | "month" 9 | ], 10 | "value": "months" 11 | }, 12 | { 13 | "synonyms": [ 14 | "city" 15 | ], 16 | "value": "cities" 17 | }, 18 | { 19 | "synonyms": [ 20 | "stadiums", 21 | "locations", 22 | "venue" 23 | ], 24 | "value": "venues" 25 | }, 26 | { 27 | "synonyms": [ 28 | "category" 29 | ], 30 | "value": "categories" 31 | }, 32 | { 33 | "synonyms": [ 34 | "shows", 35 | "concerts", 36 | "event" 37 | ], 38 | "value": "events" 39 | }, 40 | { 41 | "synonyms": [ 42 | "state" 43 | ], 44 | "value": "states" 45 | } 46 | ] 47 | } 48 | 49 | -------------------------------------------------------------------------------- /slots/event_name.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "event_name", 3 | "description": "Names of events in TICKIT database", 4 | "valueSelectionStrategy": "TOP_RESOLUTION", 5 | "enumerationValues": [ 6 | { "value": "Sample Event 1" }, 7 | { "value": "Another Sample Event" } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /zip.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import zipfile 3 | 4 | z = zipfile.ZipFile(sys.argv[1], 'w') 5 | 6 | for file in sys.argv[2:]: 7 | print("Adding {} to {}").format(file, sys.argv[1]) 8 | z.write(file) 9 | 10 | --------------------------------------------------------------------------------