Skip to content
This repository has been archived by the owner on Aug 30, 2024. It is now read-only.

Adding tests #1

Merged
merged 11 commits into from
Apr 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 3 additions & 15 deletions lambda/conversation/billing.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,7 @@ const {
guard,
} = require('robot3')
const { fakePhoneNumber, fakeWebsite } = require('../constants')

const generateRandomBillAmount = () => {
const min = Math.ceil(0)
const max = Math.floor(500)
const decimalMax = Math.floor(99)

const dollars = Math.floor(Math.random() * (max - min + 1)) + min
const cents = Math.floor(Math.random() * (decimalMax - min + 1)) + min

return `$${dollars}.${cents}`
}

// replace this with an api call
const fetchBillForAddress = async () => ({ billAmount: generateRandomBillAmount() })
const { fetchBill } = require('../service/fetchBill')

const stateMap = {
fresh: state(
Expand All @@ -42,7 +29,7 @@ const stateMap = {
guard(({ resuming, conversationAttributes }) => resuming && conversationAttributes.confirmAddress?.correctAddress),
),
),
correctAddress: invoke(fetchBillForAddress,
correctAddress: invoke(fetchBill,
transition('done', 'returnBill',
reduce((ctx, { data: { billAmount }}) => ({ ...ctx, billAmount })),
),
Expand All @@ -69,6 +56,7 @@ const billing = {
}),
intent: 'Billing',
canInterrupt: true,
shouldBeUnique: true,
}

module.exports = { billing }
5 changes: 3 additions & 2 deletions lambda/conversation/estimatedRestoration.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,10 @@ const estimatedRestoration = {
stateMap,
transitionStates: [ 'confirmAddress', 'pickAnOutage' ],
dialogMap: {
askAboutHomeOrOther: ({ misunderstandingCount }) => dialog(
askAboutHomeOrOther: ({ misunderstandingCount = 0 }) => dialog(
`estimatedRestoration.homeOrOther.${misunderstandingCount > 0 ? 'misheard' : 'confirm'}`
),
selectOtherOutage: ({ misunderstandingCount }) => dialog(
selectOtherOutage: ({ misunderstandingCount = 0 }) => dialog(
`estimatedRestoration.otherLocation.${misunderstandingCount > 0 ? 'misheard' : 'confirm'}`,
{ options: utils.objectMapToSpeech(outageOptions) }
),
Expand All @@ -107,6 +107,7 @@ const estimatedRestoration = {
intent: 'EstimatedRestoration',
canInterrupt: true,
description: 'how long it will take to restore power',
shouldBeUnique: true,
}

module.exports = { estimatedRestoration }
129 changes: 107 additions & 22 deletions lambda/conversation/reportOutage.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,73 @@ const {
const { utils } = require('@ocelot-consulting/ocelot-voice-framework')
const { fakePhoneNumber, fakeWebsite } = require('../constants')

const callOutageApi = async () => {
console.log('outage reported')
const callOutageApi = async ({houseNumber, phoneNumber}) => {
console.log('outage reported', houseNumber, phoneNumber)

return [{
result: 'noOutage'
}, {
result: 'yesOutage',
impact: '300',
areaDescription: 'The pines neighborhood north of Park Street',
workDescription: 'Crews are on the scenen and expect repairs to complete in about an hour.'
}, {
result: 'badCombination'
}][houseNumber % 3]
}

const stateMap = {
fresh: state(
transition('processIntent', 'confirmAddress',
guard(({ conversationAttributes }) => !conversationAttributes.confirmAddress?.confirmedAddress),
transition('processIntent', 'askForHouseNumber')
),
askForHouseNumber: state(
transition('processIntent', 'askForTelephoneNumber',
guard((ctx, { intent }) => intent.name === 'ANumber'),
reduce((ctx, { intent } ) => ({ ...ctx, houseNumber: intent.slots.number.value, misunderstandingCount: 0 }))
),
transition('processIntent', 'incorrectAddress',
guard(({ conversationAttributes }) => !conversationAttributes.confirmAddress?.correctAddress),
transition('processIntent', 'goBack',
guard(({ misunderstandingCount }, { intent }) => intent.name === 'GoBackIntent' || misunderstandingCount > 3)
),
transition('processIntent', 'letYouKnow',
guard(({ conversationAttributes }) => conversationAttributes.confirmAddress?.correctAddress),
transition('processIntent', 'askForHouseNumber',
reduce(ctx => ({ ...ctx, misunderstandingCount: ctx.misunderstandingCount + 1 })),
),
),
confirmAddress: state(
immediate('incorrectAddress',
guard(({ resuming, conversationAttributes }) => resuming && !conversationAttributes.confirmAddress?.correctAddress),
askForTelephoneNumber: state(
transition('processIntent', 'gotAllData',
guard((ctx, { intent }) => intent.name === 'APhoneNumber'),
reduce((ctx, { intent } ) => ({ ...ctx, phoneNumber: intent.slots.phoneNumber.value, misunderstandingCount: 0 }))
),
transition('processIntent', 'goBack',
guard(({ misunderstandingCount }, { intent }) => intent.name === 'GoBackIntent' || misunderstandingCount > 3)
),
immediate('letYouKnow',
guard(({ resuming, conversationAttributes }) => resuming && conversationAttributes.confirmAddress?.correctAddress),
transition('processIntent', 'askForTelephoneNumber',
reduce(ctx => ({ ...ctx, misunderstandingCount: ctx.misunderstandingCount + 1 })),
),
),
letYouKnow: state(
transition('processIntent', 'gotAllData',
transition('processIntent', 'thanksForReporting',
guard((ctx, { intent }) => intent.name === 'YesNoIntent' && utils.getSlotValueId(intent.slots.yesNo) === 'yes'),
reduce(ctx => ({ ...ctx, letThemKnow: true }))
reduce(ctx => ({ ...ctx, letThemKnow: true, misunderstandingCount: 0 }))
),
transition('processIntent', 'gotAllData',
transition('processIntent', 'thanksForReporting',
guard((ctx, { intent }) => intent.name === 'YesNoIntent' && utils.getSlotValueId(intent.slots.yesNo) === 'no'),
reduce(ctx => ({ ...ctx, letThemKnow: false, misunderstandingCount: 0 }))
),
transition('processIntent', 'goBack',
guard(({ misunderstandingCount }, { intent }) => intent.name === 'GoBackIntent' || misunderstandingCount > 3)
),
transition('processIntent', 'letYouKnow',
reduce(ctx => ({ ...ctx, misunderstandingCount: ctx.misunderstandingCount + 1 })),
),
),
letYouKnowWOutageReport: state(
transition('processIntent', 'thanksForReporting',
guard((ctx, { intent }) => intent.name === 'YesNoIntent' && utils.getSlotValueId(intent.slots.yesNo) === 'yes'),
reduce(ctx => ({ ...ctx, letThemKnow: true, misunderstandingCount: 0 }))
),
transition('processIntent', 'thanksForReporting',
guard((ctx, { intent }) => intent.name === 'YesNoIntent' && utils.getSlotValueId(intent.slots.yesNo) === 'no'),
reduce(ctx => ({ ...ctx, letThemKnow: false }))
reduce(ctx => ({ ...ctx, letThemKnow: false, misunderstandingCount: 0 }))
),
transition('processIntent', 'goBack',
guard(({ misunderstandingCount }, { intent }) => intent.name === 'GoBackIntent' || misunderstandingCount > 3)
Expand All @@ -49,37 +84,87 @@ const stateMap = {
reduce(ctx => ({ ...ctx, misunderstandingCount: ctx.misunderstandingCount + 1 })),
),
),
reportAnOutage: state(
transition('processIntent', 'letYouKnow',
guard((ctx, { intent }) => intent.name === 'YesNoIntent' && utils.getSlotValueId(intent.slots.yesNo) === 'yes'),
reduce(ctx => ({ ...ctx, reportingOutage: true, misunderstandingCount: 0 }))
),
transition('processIntent', 'haveANiceDay',
guard((ctx, { intent }) => intent.name === 'YesNoIntent' && utils.getSlotValueId(intent.slots.yesNo) === 'no'),
reduce(ctx => ({ ...ctx, reportingOutage: false, misunderstandingCount: 0 }))
),
transition('processIntent', 'goBack',
guard(({ misunderstandingCount }, { intent }) => intent.name === 'GoBackIntent' || misunderstandingCount > 3)
),
transition('processIntent', 'reportAnOutage',
reduce(ctx => ({ ...ctx, misunderstandingCount: ctx.misunderstandingCount + 1 })),
),
),
tryAgain: state(
transition('processIntent', 'askForHouseNumber',
guard((ctx, { intent }) => intent.name === 'YesNoIntent' && utils.getSlotValueId(intent.slots.yesNo) === 'yes'),
reduce(ctx => ({ ...ctx, houseNumber: 0, phoneNumber: '', misunderstandingCount: 0 }))
),
transition('processIntent', 'goBack',
guard((ctx, { intent }) => intent.name === 'YesNoIntent' && utils.getSlotValueId(intent.slots.yesNo) === 'no'),
reduce(ctx => ({ ...ctx, misunderstandingCount: 0 }))
),
transition('processIntent', 'goBack',
guard(({ misunderstandingCount }, { intent }) => intent.name === 'GoBackIntent' || misunderstandingCount > 3)
),
transition('processIntent', 'tryAgain',
reduce(ctx => ({ ...ctx, misunderstandingCount: ctx.misunderstandingCount + 1 })),
),
),
gotAllData: invoke(callOutageApi,
transition('done', 'thanksForReporting'),
transition('done', 'reportAnOutage',
guard((ctx, {data: { result }}) => result === 'noOutage'),
reduce((ctx, { data }) => ({ ...ctx, attemptCount: ctx.attemptCount + 1 })),),
transition('done', 'letYouKnowWOutageReport',
guard((ctx, {data: { result }}) => result === 'yesOutage'),
reduce((ctx, { data }) => ({ ...ctx, attemptCount: ctx.attemptCount + 1, outageDetails: {...data} })),),
transition('done', 'tryAgain',
guard((ctx, {data: { result }}) => result === 'badCombination'),
reduce((ctx, { data }) => ({ ...ctx, attemptCount: ctx.attemptCount + 1 })),),
transition('error', 'error',
reduce((ctx, { error }) => ({ ...ctx, error })),
)
),
thanksForReporting: state(),
incorrectAddress: state(),
haveANiceDay: state(),
error: state(),
goBack: state(),
};

const reportOutage = {
handle: ({ dialog, sessionAttributes }) => ({
initialState: {
outageDetails: {},
attemptCount: 0,
houseNumber: 0,
phoneNumber: '',
reportAnOutage: '',
letThemKnow: '',
reportingOutage: '',
misunderstandingCount: 0,
error: '',
},
stateMap,
transitionStates: 'confirmAddress',
dialogMap: {
askForHouseNumber: ({ misunderstandingCount }) => dialog(`reportOutage.askForHouseNumber.${misunderstandingCount > 0 ? 'misheard' : 'confirm'}`),
askForTelephoneNumber: ({ misunderstandingCount }) => dialog(`reportOutage.askForTelephoneNumber.${misunderstandingCount > 0 ? 'misheard' : 'confirm'}`),
letYouKnow: ({ misunderstandingCount }) => dialog(`reportOutage.letYouKnow.${misunderstandingCount > 0 ? 'misheard' : 'confirm'}`),
letYouKnowWOutageReport: ({ misunderstandingCount, outageDetails }) => dialog(`reportOutage.letYouKnowWOutageReport.${misunderstandingCount > 0 ? 'misheard' : 'confirm'}`, {...outageDetails}),
reportAnOutage: ({ misunderstandingCount }) => dialog(`reportOutage.reportAnOutage.${misunderstandingCount > 0 ? 'misheard' : 'confirm'}`),
tryAgain: ({ misunderstandingCount, houseNumber, phoneNumber }) => dialog(`reportOutage.tryAgain.${misunderstandingCount > 0 ? 'misheard' : 'confirm'}`, {houseNumber, phoneNumber}),
thanksForReporting: ({ letThemKnow }) => dialog(`reportOutage.reply.${letThemKnow ? 'withContact' : 'noContact'}`),
incorrectAddress: () => dialog('reportOutage.wrongAddress', { website: fakeWebsite, phoneNumber: fakePhoneNumber }),
haveANiceDay: () => dialog(`reportOutage.reply.haveANiceDay`),
error: () => dialog('home.error'),
},
overrideResume: sessionAttributes.previousPoppedConversation === 'confirmAddress',
}),
intent: 'ReportOutage',
canInterrupt: true,
shouldBeUnique: true,
}

module.exports = { reportOutage }
52 changes: 49 additions & 3 deletions lambda/dialog/ReportOutageDialog.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,40 @@
const reportOutage = {
wrongAddress: [
`Sorry, we can't report an outage for you because we don't have the right address for your account. Please call <say-as interpret-as="telephone">{{phoneNumber}}</say-as> or visit {{website}} to report the outage`,
],
askForHouseNumber: {
confirm:[
`What is the house number for the house that you would like to report an outage at?`
],
misheard: [
`Please just say a number for the address you're trying to report an outage at.`
]
},
askForTelephoneNumber: {
confirm:[
`What is the telephone number for service at this address?`
],
misheard: [
`Please say the telephone number for this address as a ten digit number.`
]
},
tryAgain: {
confirm: [
`I'm sorry but house number {{houseNumber}} and phone number {{phoneNumber}} don't match our records. Would you like to try saying the numbers again?`
],
misheard: [
`Was that a yes or a no.`,
`I didn't catch that. You can respond yes or no.`,
`I didn't catch that. Would you like to try saying the numbers again?`
]
},
reportAnOutage: {
confirm: [
`There are no reported outages in your area. Would you like to report an outage at your address?`
],
misheard: [
`Was that a yes or a no.`,
`I didn't catch that. You can respond yes or no.`,
`I didn't catch that. Would you like to report an outage at your address?`
]
},
letYouKnow: {
confirm: [
`Would you like a notification when the power is restored?`,
Expand All @@ -14,12 +47,25 @@ const reportOutage = {
`I didn't catch that. Would you like us to notify you when the power is restored?`
]
},
letYouKnowWOutageReport: {
confirm: [
`There is currently an outage in your area affecting {{impact}} customers in {{areaDescription}}. {{workDescription}} Would you like a notification when the power is restored?`
],
misheard: [
`Was that a yes or a no.`,
`I didn't catch that. You can respond yes or no.`,
`I didn't catch that. Would you like us to notify you when the power is restored?`
]
},
reply: {
withContact: [
`Great! We received your outage report, and we'll let you know when power is restored. Thank you.`
],
noContact: [
`Great! We received your outage report. Thank you.`
],
haveANiceDay: [
`Thank you. Come back if there is a problem.`
]
}
}
Expand Down
32 changes: 16 additions & 16 deletions lambda/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion lambda/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"author": "Ocelot Consulting",
"license": "ISC",
"dependencies": {
"@ocelot-consulting/ocelot-voice-framework": "^1.1.74",
"@ocelot-consulting/ocelot-voice-framework": "^1.1.84",
"robot3": "^0.2.21"
},
"devDependencies": {
Expand Down
Loading