Skip to content
This repository was archived by the owner on Dec 1, 2023. It is now read-only.

Commit 4a3dbff

Browse files
authored
Merge pull request #54 from alexa/feature/catalog-explorer/selection-by-name
Feature/catalog explorer/selection by name
2 parents ef2e68f + 62b8698 commit 4a3dbff

20 files changed

+624
-161
lines changed

catalog-explorer/examples/BookRecommendation/lambda/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ exports.handler = SkillBuilders.custom()
2020
// are required to be defined by skills using catalog explorer
2121
...CatalogExplorer.createHandlers(
2222
`${skillDomain}.getPage`,
23+
`${skillDomain}.indexOfItemByNameApi`,
2324
`${skillDomain}.selectItemApi`,
2425
`${skillDomain}.search_`,
2526
`${skillDomain}.getProperty_`,

catalog-explorer/examples/BookRecommendation/skill-package/conversations/book_recommendation.acdl

+23-14
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ import prompts.*
1010
import displays.*
1111
import slotTypes.*
1212

13+
/************************
14+
* CREATE CATALOG ITEM
15+
************************/
1316
multiModalWelcome = MultiModalResponse {
1417
apl = WelcomeAPL,
1518
apla = AlexaConversationsWelcome
@@ -29,10 +32,6 @@ import slotTypes.*
2932
}
3033
)
3134

32-
/************************
33-
* CREATE CATALOG ITEM
34-
************************/
35-
3635
type BookItem {
3736
String title
3837
String genre
@@ -161,9 +160,17 @@ selectByIndexEvent = utterances<IndexSlotWrapper>([
161160
"{index}"
162161
])
163162

164-
action RecommendationResult<SearchConditions, BookItem> getPage(SearchConditions searchConditions, Optional<String> pageToken, optional CatalogReference catalogRef)
165-
action RecommendationResult<SearchConditions, BookItem> selectItemApi(SearchConditions searchConditions, Page<BookItem> page, Number index, optional CatalogReference catalogRef)
163+
selectByNameEvent = utterances<ItemNameSlotWrapper<TITLE>>([
164+
"{name}",
165+
"{name} would be good",
166+
"I like {name}",
167+
"{name} sounds interesting",
168+
"show me {name}"
169+
])
166170

171+
action RecommendationResult<SearchConditions, BookItem> getPage(SearchConditions searchConditions, Optional<String> pageToken, optional CatalogReference catalogRef)
172+
action RecommendationResult<SearchConditions, BookItem> selectItemApi(Page<BookItem> page, Number index, optional SearchConditions searchConditions, optional CatalogReference catalogRef)
173+
action NUMBER indexOfItemByNameApi(Page<BookItem> page, TITLE name)
167174

168175
/************************
169176
* OFFERS
@@ -241,25 +248,25 @@ action CatalogActionResult performAction_SendToPhone(List<BookItem> items, optio
241248
* Anonymous Dialogs
242249
************************/
243250

244-
dialog RecommendationResult<SearchConditions, BookItem> allSearchPathsAdaptor(PropertyConfig<SearchConditions, BookItem> config, optional CatalogReference catalogRef) {
251+
dialog RecommendationResult<SearchConditions, BookItem> allSearchPathsAdaptor(PropertyConfig<SearchConditions, BookItem, TITLE> config, optional CatalogReference catalogRef) {
245252
sample {
246253
allSearchPaths_2(config)
247254
}
248255
}
249256

250-
dialog RecommendationResult<SearchConditions, BookItem> baseSearchPathsAdaptor(PropertyConfig<SearchConditions, BookItem> config, optional CatalogReference catalogRef) {
257+
dialog RecommendationResult<SearchConditions, BookItem> baseSearchPathsAdaptor(PropertyConfig<SearchConditions, BookItem, TITLE> config, optional CatalogReference catalogRef) {
251258
sample {
252259
baseSearchPaths_2(config)
253260
}
254261
}
255262

256-
dialog RecommendationResult<SearchConditions, BookItem> allFollowUpPathsAdaptor(PropertyConfig<SearchConditions, BookItem> config, RecommendationResult<SearchConditions, BookItem> priorResult, optional CatalogReference catalogRef) {
263+
dialog RecommendationResult<SearchConditions, BookItem> allFollowUpPathsAdaptor(PropertyConfig<SearchConditions, BookItem, TITLE> config, RecommendationResult<SearchConditions, BookItem> priorResult, optional CatalogReference catalogRef) {
257264
sample {
258265
allFollowUpPaths_5(config, priorResult, catalogRef)
259266
}
260267
}
261268

262-
dialog CatalogActionResult allCatalogActionPathsAdaptor(PropertyConfig<SearchConditions, BookItem> config, List<BookItem> items, optional CatalogReference catalogRef) {
269+
dialog CatalogActionResult allCatalogActionPathsAdaptor(PropertyConfig<SearchConditions, BookItem, TITLE> config, List<BookItem> items, optional CatalogReference catalogRef) {
263270
sample {
264271
allCatalogActionPaths_3(config, items, catalogRef)
265272
}
@@ -305,14 +312,16 @@ dialog MainDialog{
305312
]
306313

307314
//Buidling the navigation configuration
308-
navigationConfig = buildNavigationConfig<SearchConditions, BookItem>(
315+
navigationConfig = buildNavigationConfig<SearchConditions, BookItem, TITLE>(
309316
nextEvent = nextEvent,
310317
prevEvent = prevEvent,
311318
getPageApi = getPage,
312319
selectByOrdinalEvent = selectByOrdinalEvent,
313320
selectByRelativePositionEvent = selectByRelativePositionEvent,
321+
selectByNameEvent = selectByNameEvent,
314322
selectByIndexEvent = selectByIndexEvent,
315-
selectItemApi = selectItemApi
323+
indexOfItemByNameApi = indexOfItemByNameApi,
324+
selectItemApi = selectItemApi,
316325
)
317326

318327
//Buidling the Offers configuration
@@ -375,7 +384,7 @@ dialog MainDialog{
375384
]
376385

377386
//Building the main catalog explorer configuration object
378-
config = buildCatalogConfig<SearchConditions, BookItem>(
387+
config = buildCatalogConfig<SearchConditions, BookItem, TITLE>(
379388
searchPatterns,
380389
navigationConfig,
381390
catalogProperties,
@@ -388,7 +397,7 @@ dialog MainDialog{
388397
)
389398

390399
//calling the the catalog explorer component with the configuration object
391-
result = exploreCatalog<SearchConditions, BookItem>(config)
400+
result = exploreCatalog<SearchConditions, BookItem, TITLE>(config)
392401

393402
//Below part is just for a workaround that the main deployable dialog cannont
394403
//return CatalogExplorationResult which is the return type of the component

catalog-explorer/examples/BookRecommendation/skill-package/interactionModels/custom/en-US.json

+90
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,96 @@
158158
}
159159
}
160160
]
161+
},
162+
{
163+
"name": "TITLE",
164+
"values": [
165+
{
166+
"name": {
167+
"value": "The Shining"
168+
}
169+
},
170+
{
171+
"name": {
172+
"value": "It"
173+
}
174+
},
175+
{
176+
"name": {
177+
"value": "Pet Sematary"
178+
}
179+
},
180+
{
181+
"name": {
182+
"value": "The Green Mile"
183+
}
184+
},
185+
{
186+
"name": {
187+
"value": "Under the Dome"
188+
}
189+
},
190+
{
191+
"name": {
192+
"value": "Doctor Sleep"
193+
}
194+
},
195+
{
196+
"name": {
197+
"value": "Four Past Midnight"
198+
}
199+
},
200+
{
201+
"name": {
202+
"value": "Do androids dream of robotic sheep"
203+
}
204+
},
205+
{
206+
"name": {
207+
"value": "Dune"
208+
}
209+
},
210+
{
211+
"name": {
212+
"value": "A Game of Thrones"
213+
}
214+
},
215+
{
216+
"name": {
217+
"value": "Pride and Prejudice"
218+
}
219+
},
220+
{
221+
"name": {
222+
"value": "Hyperion"
223+
}
224+
},
225+
{
226+
"name": {
227+
"value": "Ready Player One"
228+
}
229+
},
230+
{
231+
"name": {
232+
"value": "Harry Potter: Deathly Hallows"
233+
}
234+
},
235+
{
236+
"name": {
237+
"value": "The Fault in Our Stars"
238+
}
239+
},
240+
{
241+
"name": {
242+
"value": "The Notebook"
243+
}
244+
},
245+
{
246+
"name": {
247+
"value": "The Hobbit"
248+
}
249+
}
250+
]
161251
}
162252
]
163253
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: LicenseRef-.amazon.com.-AmznSL-1.0
3+
// Licensed under the Amazon Software License http://aws.amazon.com/asl/
4+
5+
import { HandlerInput } from 'ask-sdk-core';
6+
import { Response } from "ask-sdk-model";
7+
8+
import { apiNamespace } from '../config';
9+
import { CatalogExplorer } from '../interface';
10+
import { Page } from "../catalog-provider";
11+
import { CatalogExplorerSessionState } from '../state';
12+
import * as util from '../util';
13+
import { BaseApiHandler } from './base-api-handler';
14+
15+
interface Arguments{
16+
// the page to retrieve an item from
17+
page: Page<any>;
18+
19+
// the name of the item
20+
name: string;
21+
}
22+
23+
export type ItemNameMatcher = (items: any[], name: string) => number | undefined;
24+
25+
// called when a user tries to select a item on the current page by name,
26+
// utilizes a matcher callback to determine how to match an item to name;
27+
// assuming each item simply has a "name" field by default
28+
export class IndexOfItemByNameHandler extends BaseApiHandler {
29+
static defaultApiName = `${apiNamespace}.indexOfItemByNameApi`;
30+
31+
// default matcher just assumes each item has a 'name' field that can be matched against
32+
static defaultItemNameMatcher: ItemNameMatcher = (items, name) => {
33+
return items.findIndex((item) => {
34+
return item?.name?.toLowerCase() == name;
35+
});
36+
}
37+
38+
private itemNameMatcher: ItemNameMatcher;
39+
40+
constructor(
41+
apiName: string = IndexOfItemByNameHandler.defaultApiName,
42+
itemNameMatcher: ItemNameMatcher = IndexOfItemByNameHandler.defaultItemNameMatcher,
43+
) {
44+
super(apiName);
45+
this.itemNameMatcher = itemNameMatcher;
46+
}
47+
48+
handle(handlerInput : HandlerInput): Response {
49+
const args = util.getApiArguments(handlerInput) as Arguments;
50+
51+
let currentRecommendationsPage: Page<any>
52+
53+
if(CatalogExplorer.useSessionArgs){
54+
currentRecommendationsPage = this.getRecommendationPageFromSession(handlerInput);
55+
}
56+
else{
57+
currentRecommendationsPage = args.page;
58+
}
59+
60+
const index = this.itemNameMatcher(currentRecommendationsPage.items, args.name);
61+
if (index == undefined || index < 0) {
62+
throw new Error("indexOfItemByName: API called with name matching no displayed element");
63+
} else if (index >= currentRecommendationsPage.items.length) {
64+
throw new Error("indexOfItemByName: item matcher returned index out of bounds of current page");
65+
}else{
66+
console.log("indexOfItemByName: ",index);
67+
}
68+
69+
return handlerInput.responseBuilder
70+
.withApiResponse(index+1) // index in ACDL currently starts at 1 to match ordinal
71+
.withShouldEndSession(false)
72+
.getResponse();
73+
}
74+
75+
getRecommendationPageFromSession(handlerInput: HandlerInput): Page<any>{
76+
const sessionState = CatalogExplorerSessionState.load(handlerInput);
77+
78+
const recommendationResultFromSession = sessionState.argsState?.recommendationResult;
79+
if (recommendationResultFromSession === undefined){
80+
throw new Error("Recommendation Result from session state in undefined");
81+
}
82+
83+
return recommendationResultFromSession.recommendations;
84+
}
85+
}

catalog-explorer/lambda/handlers/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export * from "./base-api-handler";
22
export * from "./convert-ordinal-to-index";
33
export * from "./convert-relative-position-to-index";
4+
export * from "./index-of-by-name";
45
export * from "./accept-offer-handler";
56
export * from "./get-page-handler";
67
export * from "./get-property-handler";

catalog-explorer/lambda/interface.ts

+3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
ConvertOrdinalToIndexHandler,
1414
ConvertRelativePositionToIndexHandler,
1515
GetPageHandler,
16+
IndexOfItemByNameHandler,
1617
GetPropertyHandler,
1718
PerformActionHandler,
1819
SearchHandler,
@@ -146,6 +147,7 @@ export class CatalogExplorer {
146147
// Returns: array of constructed handlers
147148
static createHandlers(
148149
getPageApiName: string,
150+
indexOfItemByNameApiName: string,
149151
selectItemApiName: string,
150152
searchApiName: string,
151153
getPropertyApiName: string,
@@ -164,6 +166,7 @@ export class CatalogExplorer {
164166

165167
// APIs that have to be defined by skill dev (as they use generics)
166168
new SearchHandler(searchApiName),
169+
new IndexOfItemByNameHandler(indexOfItemByNameApiName),
167170
new GetPageHandler(getPageApiName),
168171
new SelectItemHandler(selectItemApiName),
169172
new AcceptOfferHandler(acceptOfferApiName),

catalog-explorer/lambda/providers/ddb-provider.ts

+1-8
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export class DDBListProvider<SearchConditions, Item> implements CatalogProvider
5454
}
5555
}
5656
count = seenListEntries.size;
57-
console.log("Count", count);
57+
console.log("Seen item Count", count);
5858
console.log("Items in table:", results.Table?.ItemCount)
5959
if(count === results.Table?.ItemCount)
6060
{
@@ -222,8 +222,6 @@ export class DDBListProvider<SearchConditions, Item> implements CatalogProvider
222222
excludeList.push(temp[y]);
223223
}
224224
}
225-
console.log("Exclude List:", excludeList);
226-
console.log("DDB key:", key);
227225
const filterExpression= "NOT #pk IN (" + excludeList.map((_: any, index: string) => ":pk" + index).join(", ") + ")";
228226
const expressionAttributeNames = { "#pk": key };
229227
let expressionAttributeValue : any = {};
@@ -232,12 +230,8 @@ export class DDBListProvider<SearchConditions, Item> implements CatalogProvider
232230
});
233231
let unseenList: any[] = [];
234232
let scanResults: any ;
235-
console.log("filterExpression:",filterExpression);
236-
console.log("expressionAttributeNames:",expressionAttributeNames);
237-
console.log("ExpressionAttributeValues:",expressionAttributeValue);
238233
do
239234
{
240-
console.log("INSIDE LOOP");
241235
let scanCommand: ScanCommandInput = {
242236
Limit : pageSize,
243237
TableName : this.tableName,
@@ -322,7 +316,6 @@ export class DDBListProvider<SearchConditions, Item> implements CatalogProvider
322316
try {
323317
const results = await this.client.send(new QueryCommand(params));
324318
this.lek = results.LastEvaluatedKey;
325-
console.log("LEKKKK:",this.lek);
326319
(results.Items || []).forEach(function (element: any, index: any, array: any) {
327320
matchingData.push(unmarshall(element));
328321
});

catalog-explorer/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@alexa-skill-components/catalog-explorer",
3-
"version": "0.0.2",
3+
"version": "0.0.3",
44
"publisher": "Amazon",
55
"description": "Alexa skill component for searching, refining and navigating through a catalog",
66
"license": "AmznSL-1.0",

catalog-explorer/src/actionAdapters.acdl

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import com.amazon.alexa.schema.*
1010
import com.amazon.alexa.skill.components.catalog_explorer.types.*
1111
import com.amazon.alexa.skill.components.catalog_explorer.action.*
1212

13-
dialog CatalogActionResult allCatalogActionPaths_1<SearchConditions, Item> (
14-
PropertyConfig<SearchConditions, Item> config,
13+
dialog CatalogActionResult allCatalogActionPaths_1<SearchConditions, Item, ItemName> (
14+
PropertyConfig<SearchConditions, Item, ItemName> config,
1515
List<Item> items,
1616
optional CatalogReference catalogRef
1717
) {

0 commit comments

Comments
 (0)