Skip to content

Commit

Permalink
feat: add QuestionnaireControl (multiple yes/no questions)
Browse files Browse the repository at this point in the history
BREAKING CHANGE:
 - change to interaction model building flow. (controls must be added after custom content)
 - various additions and minor changes to built-in interaction model content

Squashed commit of the following:

commit d744f6c
Author: mliddell <mliddell@amazon.com>
Date:   Sun Dec 6 18:14:47 2020 -0800

    cleanup, export new publics

commit 0dad51a
Author: mliddell <mliddell@amazon.com>
Date:   Sun Dec 6 17:41:36 2020 -0800

    i18n strings, put response logging behind env-var

commit b019371
Author: mliddell <mliddell@amazon.com>
Date:   Sat Dec 5 18:16:30 2020 -0800

    fix: fix lint warnings

commit 218aec5
Author: mliddell <mliddell@amazon.com>
Date:   Sat Dec 5 18:12:06 2020 -0800

    chore: force upgrade highlight.js

commit 1310879
Author: mliddell <mliddell@amazon.com>
Date:   Sat Dec 5 18:01:19 2020 -0800

    added some todos

commit 3103314
Author: mliddell <mliddell@amazon.com>
Date:   Sat Dec 5 17:48:59 2020 -0800

    fix formatting

commit 3122b01
Author: mliddell <mliddell@amazon.com>
Date:   Sat Dec 5 17:48:09 2020 -0800

    reorganize and simplify how interaction-model build works

commit 7375763
Author: mliddell <mliddell@amazon.com>
Date:   Sat Dec 5 13:53:01 2020 -0800

    all tests passing

commit c2d4670
Author: mliddell <mliddell@amazon.com>
Date:   Sat Dec 5 13:47:56 2020 -0800

    visualLabel and tests for touch input

commit 5d6634a
Author: mliddell <mliddell@amazon.com>
Date:   Wed Dec 2 19:08:36 2020 -0800

    Generally working

commit 6f7613d
Author: mliddell <mliddell@amazon.com>
Date:   Wed Dec 2 10:55:01 2020 -0800

    wip

commit 2a68f25
Author: mliddell <mliddell@amazon.com>
Date:   Tue Nov 24 18:16:56 2020 -0800

    wip

commit 2bcdeaf
Author: mliddell <mliddell@amazon.com>
Date:   Fri Nov 20 14:21:50 2020 -0800

    wip

commit 3becee3
Author: mliddell <mliddell@amazon.com>
Date:   Tue Nov 3 16:46:54 2020 -0800

    feat: add QuestionnaireControl (basic facilities)

    QuestionnaireControl presents a series of questions to the user
    - each question must have the same set of allowed answers
    - intended to reduce friction by allowing fast data-entry and
      better user initiative to move between questions & be done with
      only partial input.

    This control is still in design/development.

    Squashed commit of the following:

    commit a65e450
    Author: mliddell <mliddell@amazon.com>
    Date:   Tue Nov 3 16:38:14 2020 -0800

        wip: fix formatting

    commit fa97a21
    Author: mliddell <mliddell@amazon.com>
    Date:   Tue Nov 3 16:37:01 2020 -0800

        wip: Added tests for Questionnaire. Improved async usage in other controls.

    commit 9af6b35
    Merge: ad07cfb bbcd95e
    Author: mliddell <mliddell@amazon.com>
    Date:   Sun Nov 1 14:01:43 2020 -0800

        wip: merged and continuiing...

    commit ad07cfb
    Author: mliddell <mliddell@amazon.com>
    Date:   Fri Oct 30 14:21:47 2020 -0700

        wip: adding question-asking initiative

    commit 76f39ff
    Author: mliddell <mliddell@amazon.com>
    Date:   Mon Oct 26 16:29:30 2020 -0700

        wip Demo with three columns, improved APL formatting

    commit a714dbe
    Author: mliddell <mliddell@amazon.com>
    Date:   Mon Oct 26 16:07:51 2020 -0700

        wip - APL improved

    commit 3c71204
    Author: mliddell <mliddell@amazon.com>
    Date:   Sun Oct 25 18:02:02 2020 -0700

        wip: general updates. auto-scrolling

    commit e1d4439
    Author: mliddell <mliddell@amazon.com>
    Date:   Sun Oct 25 16:51:02 2020 -0700

        wip: basic question prompts & value answers

    commit 1327704
    Author: mliddell <mliddell@amazon.com>
    Date:   Sun Oct 25 14:30:27 2020 -0700

        wip: basic apl touch-selection wired up

    commit a42dc2b
    Author: mliddell <mliddell@amazon.com>
    Date:   Sun Oct 25 13:06:04 2020 -0700

        wip: simplified questionnaire content props

    commit 4b93fac
    Author: mliddell <mliddell@amazon.com>
    Date:   Fri Oct 23 10:27:33 2020 -0700

        wip

    commit 68beedd
    Author: mliddell <mliddell@amazon.com>
    Date:   Tue Oct 20 11:01:37 2020 -0700

        chore_(.commitrc): change 'docs' to 'doc'

    commit a02210f
    Author: mliddell <mliddell@amazon.com>
    Date:   Tue Oct 20 11:00:20 2020 -0700

        docs_: jsDoc for ControlManagerProps

    commit 37f31ba
    Author: mliddell <mliddell@amazon.com>
    Date:   Tue Oct 20 09:23:03 2020 -0700

        wip

    commit b71b8ad
    Author: mliddell <mliddell@amazon.com>
    Date:   Mon Oct 19 18:06:31 2020 -0700

        checkpoint before rework of userActs to handleFuncs

    commit 2e0ff39
    Author: mliddell <mliddell@amazon.com>
    Date:   Sun Oct 18 17:10:32 2020 -0700

        wip_: progress on QuestionnaireControl

    commit c060561
    Author: mliddell <mliddell@amazon.com>
    Date:   Sat Oct 17 17:58:07 2020 -0700

        wip_: getting started with QuestionnaireControl

    commit 2914843
    Author: mliddell <mliddell@amazon.com>
    Date:   Sat Oct 17 12:33:34 2020 -0700

        wip: starting questionnaire

    commit 25e2986
    Author: mliddell <mliddell@amazon.com>
    Date:   Wed Oct 14 13:31:16 2020 -0700

        feat: a simple demo skill using multiple lists
  • Loading branch information
mliddell committed Dec 9, 2020
1 parent 3927e0f commit 65ed192
Show file tree
Hide file tree
Showing 44 changed files with 4,058 additions and 456 deletions.
4 changes: 2 additions & 2 deletions .commitlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
[
"chore",
"chore_",
"docs",
"docs_",
"doc",
"doc_",
"feat",
"feat_",
"fix",
Expand Down
30 changes: 29 additions & 1 deletion demo/Common/src/DemoRootControl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
import { IntentRequest, interfaces } from 'ask-sdk-model';
import {
AmazonIntent,
ContainerControl,
ContainerControlProps,
ContentAct,
ControlInput,
ControlResponseBuilder,
ControlResultBuilder,
InputUtil,
LiteralContentAct,
} from '../../../src';

/**
Expand Down Expand Up @@ -56,7 +59,12 @@ export class DemoRootControl extends ContainerControl {
}

// otherwise delegate to children
return this.canHandleByChild(input);
if (await this.canHandleByChild(input)) {
return true;
}

this.handleFunc = this.handleFallbackEtc;
return true;
}

async handle(input: ControlInput, resultBuilder: ControlResultBuilder): Promise<void> {
Expand All @@ -74,4 +82,24 @@ export class DemoRootControl extends ContainerControl {
async handleSessionEnded(input: ControlInput, resultBuilder: ControlResultBuilder) {
//nothing.
}

async handleFallbackEtc(input: ControlInput, resultBuilder: ControlResultBuilder) {
let requestDescription;
if (InputUtil.isIntent(input)) {
requestDescription = (input.request as IntentRequest).intent.name;
} else if (input.request.type === 'Alexa.Presentation.APL.UserEvent') {
requestDescription = '';
const event = input.request as interfaces.alexa.presentation.apl.UserEvent;
const args = (event.arguments ?? []).join(', ');
requestDescription = `APL UserEvent with params ${args}`;
} else {
requestDescription = 'Input of unknown type';
}

resultBuilder.addAct(
new LiteralContentAct(this, {
promptFragment: `${requestDescription} was not handled by any control.`,
}),
);
}
}
8 changes: 3 additions & 5 deletions demo/ListControl/YesNoMaybe/src/buildInteractionModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ const log = new Logger('HelloWorld:InteractionModel');

export namespace ListDemo1IM {
export const imGen = new ControlInteractionModelGenerator()
.buildCoreModelForControls(new ListDemo1.DemoControlManager())
.withInvocationName('control demos')
.withInvocationName('controls demo')
.addIntent({ name: 'AMAZON.StopIntent' })
.addIntent({ name: 'AMAZON.NavigateHomeIntent' })
.addIntent({ name: 'AMAZON.HelpIntent' })
Expand All @@ -41,9 +40,9 @@ export namespace ListDemo1IM {
name: 'HelloIntent',
samples: ['Say hello', 'Say hi'],
})

.addOrMergeSlotType(yesNoMaybeSlotType)
.addOrMergeSlotType(filteredYesNoMaybeSlotType);
.addOrMergeSlotType(filteredYesNoMaybeSlotType)
.buildCoreModelForControls(new ListDemo1.DemoControlManager());
}

// If launched directly, build and write to a file
Expand All @@ -52,4 +51,3 @@ if (require.main === module) {
ListDemo1IM.imGen.buildAndWrite('en-US-generated.json');
console.log('Wrote ./en-US-generated.json');
}

4 changes: 2 additions & 2 deletions demo/ListControl/YesNoMaybe/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ListControl } from '../../../../src/commonControls/listControl/ListCont
import { Control } from '../../../../src/controls/Control';
import { ControlManager } from '../../../../src/controls/ControlManager';
import { ControlHandler } from '../../../../src/runtime/ControlHandler';
import { IntentNameToValueMapper } from '../../../../src/utils/IntentUtils';
import { defaultIntentToValueMapper } from '../../../../src/utils/IntentUtils';
import { DemoRootControl } from '../../../Common/src/DemoRootControl';
import { filteredYesNoMaybeSlotType, yesNoMaybeSlotType } from './interactionModelTypes';

Expand All @@ -29,7 +29,7 @@ export namespace ListDemo1 {
targets: [Strings.Target.It, Strings.Target.Choice],
slotValueConflictExtensions: {
filteredSlotType: filteredYesNoMaybeSlotType.name!,
intentToValueMapper: (intent) => IntentNameToValueMapper(intent, ['yes', 'no']),
intentToValueMapper: (intent) => defaultIntentToValueMapper(intent),
},
},
prompts: {
Expand Down
195 changes: 195 additions & 0 deletions demo/QuestionnaireControl/src/buildInteractionModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
/*
* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

import { v1 } from 'ask-smapi-model';
import { MultipleLists } from '.';
import { ControlInteractionModelGenerator } from '../../../src/interactionModelGeneration/ControlInteractionModelGenerator';
import { Logger } from '../../../src/logging/Logger';

import SlotType = v1.skill.interactionModel.SlotType;
import TypeValue = v1.skill.interactionModel.TypeValue;
import Intent = v1.skill.interactionModel.Intent;

const log = new Logger('HelloWorld:InteractionModel');

export namespace TwoListsIM {
export const imGen = new ControlInteractionModelGenerator()
.withInvocationName('controls demo')
.addIntent({ name: 'AMAZON.StopIntent' })
.addIntent({ name: 'AMAZON.NavigateHomeIntent' })
.addIntent({ name: 'AMAZON.HelpIntent' })
.addIntent({ name: 'AMAZON.CancelIntent' })
.addIntent({ name: 'AMAZON.YesIntent' })
.addIntent({ name: 'AMAZON.NoIntent' })
.addIntent({ name: 'AMAZON.FallbackIntent' })
.setModelConfiguration({ fallbackIntentSensitivity: { level: 'HIGH' } })

.addOrMergeSlotType({
name: 'YesNo',
values: [
{
id: 'yes',
name: {
value: 'yes',
synonyms: ['yeah', 'yep'],
},
},
{
id: 'no',
name: {
value: 'no',
synonyms: ['nope', 'not ever'],
},
},
],
})

.addOrMergeSlotType({
name: 'FrequencyAnswer',
values: [
{
id: 'often',
name: {
value: 'often',
synonyms: [
'get',
'have',
'suffer',
'suffer from',
'often', // e.g. {I} {often} {cough}
'often have', // e.g. {I} {often have} {headache}
'often get',
'often suffer',
'often suffer from',
'fairly often have',
'fairly often get',
'fairly often suffer',
'fairly often suffer from',
'generally have',
'generally get',
'generally suffer',
'generally suffer from',
'most of the time I have',
'most of the time I get',
'most of the time I suffer',
'most of the time I suffer from',
'frequently',
'frequently get',
'frequently have',
'frequently suffer',
'frequently suffer from',
'always',
'always get',
'always have',
'always suffer',
'always suffer from',
],
},
},
{
id: 'rarely',
name: {
value: 'rarely',
synonyms: [
"don't", // e.g. {I} {don't} {cough}
"don't get", // e.g. {I} {don't get} {headaches}
"don't have",
"don't suffer",
"don't suffer from",
'rarely',
'rarely get',
'rarely have',
'rarely suffer',
'rarely suffer from',
'infrequently',
'infrequently get',
'infrequently have',
'infrequently suffer',
'infrequently suffer from',
'hardly ever',
'hardly ever get',
'hardly ever have',
'hardly ever suffer',
'hardly ever suffer from',
'never get',
'never have',
'never suffer',
'never suffer from',
],
},
},
{
id: 'skip',
name: {
value: 'skip',
synonyms: [
"don't know",
'unsure',
'pass',
'next',
"I honestly don't know",
'maybe',
'off and on',
'from time to time',
],
},
},
],
})

.addOrMergeSlotType({
name: 'target',
values: [
{
id: 'headache',
name: {
value: 'headache',
synonyms: [
'headaches',
'sore head',
'lots of headaches',
'bad headaches',
'really bad headaches',
],
},
},
{
id: 'cough',
name: {
value: 'cough',
synonyms: ['coughing', 'constant coughing', 'cough a lot', 'moderate cough'],
},
},
{
id: 'healthQuestionnaire',
name: {
value: 'healthQuestionnaire',
synonyms: [
'the health questionnaire',
'health questions',
'symptoms',
'symptom questions',
],
},
},
],
})
.buildCoreModelForControls(new MultipleLists.DemoControlManager());
}

// If launched directly, build and write to a file
if (require.main === module) {
// Build and write
TwoListsIM.imGen.buildAndWrite('en-US-generated.json');
console.log('Wrote ./en-US-generated.json');
}
79 changes: 79 additions & 0 deletions demo/QuestionnaireControl/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { SkillBuilders } from 'ask-sdk-core';
import { Control } from '../../..//src/controls/Control';
import { QuestionnaireControl } from '../../../src/commonControls/questionnaireControl/QuestionnaireControl';
import { ControlManager } from '../../../src/controls/ControlManager';
import { ControlHandler } from '../../../src/runtime/ControlHandler';
import { DemoRootControl } from '../../Common/src/DemoRootControl';

export namespace MultipleLists {
export class DemoControlManager extends ControlManager {
createControlTree(): Control {
const rootControl = new DemoRootControl({ id: 'root' });

// Call it MultiListControl?
// list one is "what day": mon, tues, wed
// list two is "how many?": a few, lots.

// Call it ManyListsControlWithSameChoices?... one 'list' but with lots of different questions associated.
// -- must be short lists.
// -- each list is an (id,targets) pair with associated
// prompts/reprompt/aplMappers
// Keep it as questionnaire for now and describe is as 'like a multi-list
// but with many special aspects'/

rootControl.addChild(
new QuestionnaireControl({
id: 'healthScreen',

questionnaireData: {
questions: [
{

id: 'headache',
targets: ['headache'],
//TODO: support functions on prompt/label/shortForm
prompt: 'Do you frequently have a headache?',
visualLabel: 'Frequent headache?',
promptShortForm: 'headache',
},
{
id: 'cough',
targets: ['cough'],
prompt: 'Have you been coughing a lot?',
visualLabel: 'Cough most days?',
promptShortForm: 'cough',
},
],
choices: [
{
id: 'yes',
//TODO: values: .. allow additional values. default to id
aplColumnHeader: 'yes', //TODO: default to id
prompt: 'yes', //TODO: default to id
},
{
id: 'no',
aplColumnHeader: 'no',
prompt: 'no',
},
], // TODO: should be consistent with ListControl. listItemIds vs choices.
},
interactionModel: {
slotType: 'YesNo', // TODO: allow multiple slotTypes? e.g. to support YesNo and Symptom.
filteredSlotType: 'none',
targets: ['builtin_it', 'builtin_questionnaire','healthQuestionnaire'], // this should just be the control targets. The question targets are in content.
},
dialog: {
confirmationRequired: false,
},
}),
);

return rootControl;
}
}
}

export const handler = SkillBuilders.custom()
.addRequestHandlers(new ControlHandler(new MultipleLists.DemoControlManager()))
.lambda();
Loading

0 comments on commit 65ed192

Please sign in to comment.