initial step 4 for online test

This commit is contained in:
GotPPay
2018-01-11 04:24:16 +01:00
parent 3214a2bea4
commit 7d79c03d15
8 changed files with 328 additions and 71 deletions

View File

@@ -12,8 +12,19 @@ const router = express.Router ();
var app = express (); var app = express ();
//User data for sending message, this is skill-related and will be in some skill container
var Name = null;
var Email = null;
var Message = null;
var State = 0; // states should be defined in seperate file. (Not sending message, Waiting for name, Waiting for email, Waiting for message)
//For now :
// 0 : Not sending Message
// 1 : Waiting for name
// 2 : Waiting for email
// 3 : Waiting for message
// ALWAYS setup the alexa app and attach it to express before anything else. // ALWAYS setup the alexa app and attach it to express before anything else.
var alexaApp = new alexa.app ('step3'); // this means we still work with one skill var alexaApp = new alexa.app ('saburly'); // this means we still work with one skill
alexaApp.express ({ alexaApp.express ({
expressApp: app, expressApp: app,
@@ -39,6 +50,7 @@ var updateIntentsJSON = function () {
.loadSkill (config.SKILL_DB_ID) .loadSkill (config.SKILL_DB_ID)
.then (skill => { .then (skill => {
skill.intents.map (intent => { skill.intents.map (intent => {
alexaApp.intent ( alexaApp.intent (
intent.intentName, intent.intentName,
{ {
@@ -50,9 +62,93 @@ var updateIntentsJSON = function () {
} }
); );
}); });
alexaApp.launch ((request, response) => { alexaApp.launch ((request, response) => {
return response.say (skill.invocationAnswer).shouldEndSession(false); return response.say (skill.invocationAnswer).shouldEndSession(false);
}); });
alexaApp.intent('EmailIntentLaunch',{
slots:[],
utterances: [
'I want to send a message',
'I would like to send a message',
'I would like to leave a message',
'Leave a message'
]
},
function(request,response){
Name = null;
Email = null;
Message = null;
State = 1;
return response.say('Ok. What is your name').shouldEndSession(false);
}
);
//TODO : Watch out for this intent. It will make trouble with other regular intents
//if other intents have utterance with just one slot like {Data}
//It should be taken care somwhere before this, to check if Email Intent is invoked
//This is problem only if we introduce slot options for regular intents for users
alexaApp.intent('EmailIntent',{
slots:{
'Name':'AMAZON.Person',
'Email' : 'AMAZON.LITERAL',
'Message': 'AMAZON.LITERAL',
'Data': 'AMAZON.LITERAL'
},
utterances: [
'My name is {Name}',
'I am {Name}',
'{Data}',
'My email is {Email}',
'Send replay to {Email}',
'My message is {Message}'
]
},
function(request,response){
if (!Name) Name = response.slots['Name'];
if (!Email) Email = response.slots['Email'];
if (!Message) Message = response.slots['Message'];
let Data = reponse.slots['Data'];
//TODO : Responses could be configurable for each skill ?
if (State === 1){
//Was waiting for name, so if Name is null, name is probably in Data
if ((!Name && Data) || Name){
//got the name, let's continue for the email
State = 2;
return response.say('Ok ' + Name + '. What is your email ?').shouldEndSession(false);
}else{
//Something is wrong, ask for name again
return response.say('Sorry, I didnt understand your name. Can you say it again ?').shouldEndSession(false);
}
}else if (State === 2){
//was waiting for email, so if Email is null, email is probably in Data
if ((!Email && Data) || Email){
//Got the email, first verify email and than continue to message
//TODO : verify email
State = 3;
return response.say('Great. Whats the message ?').shouldEndSession(false);
}else{
//Something is wrong, ask for the email again
return response.say('Sorry, I didnt understan you email. Can you say it again ?').shouldEndSession(false);
}
}else if (State === 3){
//Was waiting for message, so if Message is null, message is probably in Data
if ((!Message && Data) || Message){
//Ok, we got all informations. Exit email intent
State = 0;
//TODO : Send email
return response.say('Message sent. Someone will contact you ASAP');
}
}
}
);
}) })
.catch (err => { .catch (err => {
console.log (err); console.log (err);
@@ -92,14 +188,15 @@ router.get ('/deleteSkill/:skillID', async (req, res, next) => {
router.post ('/updateSkill/:id', async (req, res, next) => { router.post ('/updateSkill/:id', async (req, res, next) => {
let id = req.params.id; let id = req.params.id;
let skill = req.body; let dataFromWeb = JSON.stringify(req.body);
let skill = JSON.parse(dataFromWeb);
let updateOnAmazon = skill.updateOnAmazon;
delete skill.updateOnAmazon;
delete skill._id; delete skill._id;
console.log('id = ' + id); console.log('id = ' + id);
if (id !== '-1') { if (id !== '-1') {
amazonHelper if (updateOnAmazon){
.updateSkill (skill) amazonHelper.updateSkill(skill).then(amazonResult=>{
.then (amazonResult => {
console.log('amazon result : ' + amazonResult);
if (amazonResult === 200 || amazonResult === 202) { if (amazonResult === 200 || amazonResult === 202) {
//Skill uploaded, it's ok to update databaseI //Skill uploaded, it's ok to update databaseI
databaseHelper databaseHelper
@@ -109,16 +206,24 @@ router.post ('/updateSkill/:id', async (req, res, next) => {
updateIntentsJSON (); updateIntentsJSON ();
}) })
.catch (e => { .catch (e => {
res.json ({result: -1, message: 'ok'}); res.json ({result: -1, message: 'error'});
}); });
} else {
res.json ({result: -1, message: 'Amazon result ' + amazonResult});
} }
}) }).catch(e=>{
.catch (e => {
//skill upload went wrong, don't update database, send error
res.json ({result: -1, message: e}); res.json ({result: -1, message: e});
}); });
}else{
databaseHelper
.updateSkill (id, skill)
.then (result => {
res.json ({result: 0, message: 'ok'});
updateIntentsJSON ();
})
.catch (e => {
res.json ({result: -1, message: 'error'});
});
}
} else { } else {
//no new skills for now //no new skills for now
} }

View File

@@ -81,6 +81,47 @@ var generateInteractionModel = function (skill) {
allIntents.push ({name: intent.intentName, samples: intent.questions}); allIntents.push ({name: intent.intentName, samples: intent.questions});
}); });
//Special Email Intents :
allIntents.push({
name: 'EmailIntentLaunch',
slots:[],
samples: [
'I want to send a message',
'I would like to send a message',
'I would like to leave a message',
'Leave a message'
]
});
allIntents.push({
name: 'EmailIntent',
slots:[
{
name: 'Name',
type: 'AMAZON.Person'
},
{
name: 'Email',
type: 'AMAZON.LITERAL'
},
{
name: 'Message',
type: 'AMAZON.LITERAL'
},
{
name: 'Data',
type: 'AMAZON.LITERAL'
}
],
samples: [
'My name is {Name}',
'I am {Name}',
'{Data}',
'My email is {Email}',
'Send replay to {Email}',
'My message is {Message}'
]
});
result.interactionModel = {}; result.interactionModel = {};
result.interactionModel.languageModel = { result.interactionModel.languageModel = {

View File

@@ -4,9 +4,13 @@ import './css/popup.css';
import IntentList from './components/IntentList'; import IntentList from './components/IntentList';
import IntentDetails from './components/IntentDetails'; import IntentDetails from './components/IntentDetails';
import LaunchRequest from './components/LaunchRequest'; import LaunchRequest from './components/LaunchRequest';
import Contact from './components/Contact';
import Popup from 'react-popup'; import Popup from 'react-popup';
import {getSkill, updateSkill} from './lib/api' import {getSkill, updateSkill} from './lib/api'
import {
NEW_INTENT_SELECTED_INDEX,
LAUNCH_REQUEST_SELECTED_INDEX,
CONTACT_SELECTED_INDEX} from './config'
class App extends Component { class App extends Component {
@@ -20,8 +24,8 @@ class App extends Component {
invocationAnswer:'We are saburly', invocationAnswer:'We are saburly',
allIntents:[], allIntents:[],
selectedIntent: {intentName:'',questions:[''],answer:''}, selectedIntent: {intentName:'',questions:[''],answer:''},
selectedIndex:-1, selectedIndex:NEW_INTENT_SELECTED_INDEX,
launchRequest:false, contactEmail:'',
waiting: false waiting: false
}; };
@@ -30,7 +34,7 @@ class App extends Component {
if (jResult===undefined) return; if (jResult===undefined) return;
this.setState({ skillID:jResult.skillID,skillName:jResult.skillName, invocationName: jResult.invocationName, this.setState({ skillID:jResult.skillID,skillName:jResult.skillName, invocationName: jResult.invocationName,
invocationAnswer: jResult.invocationAnswer, invocationAnswer: jResult.invocationAnswer,
allIntents: jResult.intents}) allIntents: jResult.intents, contactEmail: jResult.contactEmail})
}) })
this.handleIntentClick = this.handleIntentClick.bind(this); this.handleIntentClick = this.handleIntentClick.bind(this);
@@ -41,62 +45,67 @@ class App extends Component {
this.handleSaveLaunchRequestClick = this.handleSaveLaunchRequestClick.bind(this); this.handleSaveLaunchRequestClick = this.handleSaveLaunchRequestClick.bind(this);
this.createSkill = this.createSkill.bind(this); this.createSkill = this.createSkill.bind(this);
this.sendSkill = this.sendSkill.bind(this); this.sendSkill = this.sendSkill.bind(this);
this.handleContactClick = this.handleContactClick.bind(this);
this.handleSaveEmailClick = this.handleSaveEmailClick.bind(this);
} }
render() { render() {
return(
if(this.state.launchRequest){ <div className="App">
return ( <Popup/>
<div className="App"> <div className="App-header">
<Popup/> <h1> Tell All </h1>
<div className="App-header">
<h1> Tell All </h1>
</div>
<IntentList allIntents={this.state.allIntents}
onLaunchRequestClick={this.handleLaunchRequestClick}
onIntentClick={this.handleIntentClick}
onAddIntentClick={this.handleAddIntentClick}
selectedIndex={this.state.selectedIndex}
waiting={this.state.waiting}>
</IntentList>
<LaunchRequest invocationName={this.state.invocationName}
invocationAnswer={this.state.invocationAnswer}
onSaveClick={this.handleSaveLaunchRequestClick}
waiting={this.state.waiting}>
</LaunchRequest>
</div> </div>
); <IntentList allIntents={this.state.allIntents}
}else{ onLaunchRequestClick={this.handleLaunchRequestClick}
return ( onContactClick={this.handleContactClick}
<div className="App"> onIntentClick={this.handleIntentClick}
<Popup/> onAddIntentClick={this.handleAddIntentClick}
<div className="App-header"> selectedIndex={this.state.selectedIndex}
<h1> Tell All </h1> waiting={this.state.waiting}>
</div> </IntentList>
<IntentList allIntents={this.state.allIntents} {(
onLaunchRequestClick={this.handleLaunchRequestClick} ()=>{
onIntentClick={this.handleIntentClick} if (this.state.selectedIndex===LAUNCH_REQUEST_SELECTED_INDEX){
onAddIntentClick={this.handleAddIntentClick} return (
selectedIndex={this.state.selectedIndex} <LaunchRequest invocationName={this.state.invocationName}
waiting={this.state.waiting}> invocationAnswer={this.state.invocationAnswer}
</IntentList> onSaveClick={this.handleSaveLaunchRequestClick}
<IntentDetails selectedIntent={this.state.selectedIntent} waiting={this.state.waiting}>
onDeleteIntentClick={this.handleDeleteIntentClick} </LaunchRequest>
onSaveIntentClick={this.handleSaveIntentClick} );
waiting={this.state.waiting}> }else if (this.state.selectedIndex===CONTACT_SELECTED_INDEX){
</IntentDetails> return (
</div> <Contact
); contactEmail={this.state.contactEmail}
} onSaveEmailClick={this.handleSaveEmailClick}
waiting={this.state.waiting}>
</Contact>
);
}else{
return(
<IntentDetails selectedIntent={this.state.selectedIntent}
onDeleteIntentClick={this.handleDeleteIntentClick}
onSaveIntentClick={this.handleSaveIntentClick}
waiting={this.state.waiting}>
</IntentDetails>
);
}
}
)()}
</div>
);
} }
createSkill(intents, name, answer){ createSkill(intents, name, answer, email, updateOnAmazon){
return { return {
_id: this.state._id, _id: this.state._id,
skillID: this.state.skillID, skillID: this.state.skillID,
intents: intents, intents: intents,
invocationName: (name===undefined) ? this.state.invocationName : name, invocationName: name,
invocationAnswer: (answer===undefined)? this.state.invocationAnswer: answer invocationAnswer: answer,
contactEmail: email,
updateOnAmazon: updateOnAmazon
}; };
} }
@@ -105,12 +114,21 @@ class App extends Component {
} }
handleLaunchRequestClick(){ handleLaunchRequestClick(){
this.setState({selectedIndex: -2, launchRequest:true}); this.setState({selectedIndex: LAUNCH_REQUEST_SELECTED_INDEX});
}
handleContactClick(){
this.setState({selectedIndex: CONTACT_SELECTED_INDEX})
} }
handleSaveLaunchRequestClick(name, answer){ handleSaveLaunchRequestClick(name, answer){
this.setState({waiting:true, invocationName:name, invocationAnswer: answer}); this.setState({waiting:true, invocationName:name, invocationAnswer: answer});
this.sendSkill(this.state.allIntents,true,{waiting:false},{waiting:false},name,answer); this.sendSkill(this.state.allIntents,true,{waiting:false},{waiting:false},name,answer,this.state.contactEmail,true);
}
handleSaveEmailClick(email){
this.setState({waiting:true});
this.sendSkill(this.state.allIntents,true,{contactEmail: email, waiting:false},{waiting:false},this.state.invocationName,this.state.invocationAnswer,email,false);
} }
handleDeleteIntentClick(selectedIntent){ handleDeleteIntentClick(selectedIntent){
@@ -128,7 +146,7 @@ class App extends Component {
this.setState({waiting:true}); this.setState({waiting:true});
let newState = {allIntents: newAllIntents, selectedIntent: {intentName:'', questions:[''],answer:''}, waiting:false}; let newState = {allIntents: newAllIntents, selectedIntent: {intentName:'', questions:[''],answer:''}, waiting:false};
this.sendSkill(newAllIntents,true,newState,{waiting:false}); this.sendSkill(newAllIntents,true,newState,{waiting:false},this.state.invocationName,this.state.invocationAnswer,this.state.contactEmail,true);
}catch(e){ }catch(e){
console.log("error : " + e); console.log("error : " + e);
@@ -142,7 +160,7 @@ class App extends Component {
let newAllIntents = JSON.parse(newAllIntentsJSON); let newAllIntents = JSON.parse(newAllIntentsJSON);
let newState = null; let newState = null;
if (this.state.selectedIndex === -1){ if (this.state.selectedIndex === NEW_INTENT_SELECTED_INDEX){
//new intent //new intent
newAllIntents.push(selectedIntent); newAllIntents.push(selectedIntent);
newState = {allIntents: newAllIntents, selectedIntent: selectedIntent, selectedIndex: newAllIntents.length-1, waiting:false}; newState = {allIntents: newAllIntents, selectedIntent: selectedIntent, selectedIndex: newAllIntents.length-1, waiting:false};
@@ -151,16 +169,16 @@ class App extends Component {
newState = {allIntents: newAllIntents, selectedIntent: selectedIntent, waiting: false}; newState = {allIntents: newAllIntents, selectedIntent: selectedIntent, waiting: false};
} }
this.setState({waiting:true}); this.setState({waiting:true});
this.sendSkill(newAllIntents, true, newState, {waiting:false}); this.sendSkill(newAllIntents, true, newState, {waiting:false}, this.state.invocationName,this.state.invocationAnswer,this.state.contactEmail, true);
} }
handleAddIntentClick(){ handleAddIntentClick(){
this.setState({allIntents: this.state.allIntents, selectedIndex: -1,launchRequest:false,selectedIntent: {intentName:'',questions:[''], answer:''}}); this.setState({allIntents: this.state.allIntents, selectedIndex: NEW_INTENT_SELECTED_INDEX,launchRequest:false,selectedIntent: {intentName:'',questions:[''], answer:''}});
} }
sendSkill(newAllIntents, showPopUp, resolveState, rejectState, newName, newAnswer){ sendSkill(newAllIntents, showPopUp, resolveState, rejectState, newName, newAnswer, email, updateOnAmazon){
return new Promise((resolve,reject)=>{ return new Promise((resolve,reject)=>{
updateSkill(this.createSkill(newAllIntents,newName,newAnswer)).then(l=>l.text()).then(result=>{ updateSkill(this.createSkill(newAllIntents,newName,newAnswer,email,updateOnAmazon)).then(l=>l.text()).then(result=>{
let jResult = JSON.parse(result); let jResult = JSON.parse(result);
if (jResult.result !== 0){ if (jResult.result !== 0){
if (showPopUp) Popup.alert('Model was not saved. Please try again'); if (showPopUp) Popup.alert('Model was not saved. Please try again');

View File

@@ -0,0 +1,48 @@
import React, { Component } from 'react';
import {Button, TextField} from 'react-md';
import '../css/Intent.css'
import {EMAIL_MAX_LENGTH} from '../config';
class Contact extends Component {
constructor(props){
super(props);
this.state = {contactEmail: props.contactEmail};
this.handleEmailEdit = this.handleEmailEdit.bind(this);
}
componentWillReceiveProps(props){
this.setState({contactEmail: props.contactEmail});
}
render() {
return (
<div className="LaunchRequestBox">
<h5 style={{textAlign:'left', marginTop: '30px', marginLeft: '20px'}}> Contact address will be used for direct messaging through Alexa </h5>
<TextField
id="contact email"
lineDirection="center"
label="Contact email"
className="md-cell md-cell--bottom"
style={{width:'60%', marginLeft: '20px'}}
maxLength={EMAIL_MAX_LENGTH}
onChange={this.handleEmailEdit}
value={this.state.contactEmail}/>
<br></br>
<br></br>
<br></br>
<Button style={{float:'right', marginRight: '20px'}} flat primary swapTheming
onClick={()=>{this.props.onSaveEmailClick(this.state.contactEmail)}}
disabled={this.props.waiting}>Save</Button>
</div>
);
}
handleEmailEdit(e){
if (e.length === EMAIL_MAX_LENGTH) return;
this.setState({contactEmail: e});
}
}
export default Contact;

View File

@@ -2,6 +2,9 @@ import React, { Component } from 'react';
import {Button} from 'react-md'; import {Button} from 'react-md';
import IntentItem from './IntentItem'; import IntentItem from './IntentItem';
import '../css/Intent.css' import '../css/Intent.css'
import {
LAUNCH_REQUEST_SELECTED_INDEX,
CONTACT_SELECTED_INDEX} from '../config'
class IntentList extends Component { class IntentList extends Component {
constructor (props){ constructor (props){
@@ -17,9 +20,15 @@ class IntentList extends Component {
render() { render() {
return ( return (
<div className="IntentList"> <div className="IntentList">
<Button className={this.props.selectedIndex===-2 ? "LaunchRequest-selected" : "LaunchRequest"} flat primary <Button className={this.props.selectedIndex===LAUNCH_REQUEST_SELECTED_INDEX ? "LaunchRequest-selected" : "LaunchRequest"} flat primary
onClick={this.props.onLaunchRequestClick} onClick={this.props.onLaunchRequestClick}
disabled={this.props.waiting} >Launch request</Button> disabled={this.props.waiting} >Launch request</Button>
<Button className={this.props.selectedIndex===CONTACT_SELECTED_INDEX ? "Contact-selected" : "Contact"} flat primary
onClick={this.props.onContactClick}
disabled={this.props.waiting} >Contact</Button>
<div className="IntentList-title"> <div className="IntentList-title">
<h3>Intents</h3> <h3>Intents</h3>
</div> </div>

View File

@@ -9,3 +9,9 @@ export const INTENT_TITLE_TOOLTIP_DELAY = 700;
export const INVOCATION_NAME_MAX_LENGTH = 15; export const INVOCATION_NAME_MAX_LENGTH = 15;
export const INVOCATION_ANSWER_MAX_LENGTH = 100; export const INVOCATION_ANSWER_MAX_LENGTH = 100;
export const EMAIL_MAX_LENGTH = 100;
export const NEW_INTENT_SELECTED_INDEX = -1;
export const LAUNCH_REQUEST_SELECTED_INDEX = -2;
export const CONTACT_SELECTED_INDEX = -3;

View File

@@ -24,6 +24,20 @@
height: 50px; height: 50px;
background-color: #f5f5f5; } background-color: #f5f5f5; }
.Contact {
text-align: left;
color: #009b8a;
width: 100%;
height: 50px;
background-color: #d8d8d8; }
.Contact-selected {
text-align: left;
color: #009b8a;
width: 100%;
height: 50px;
background-color: #f5f5f5; }
.AddIntent { .AddIntent {
float: right; float: right;
margin: 12px; } margin: 12px; }

View File

@@ -31,6 +31,22 @@ $minHeight : calc(100vh - 80px);
background-color: #f5f5f5; background-color: #f5f5f5;
} }
.Contact{
text-align: left;
color: #009b8a;
width: 100%;
height: 50px;
background-color: #d8d8d8
}
.Contact-selected{
text-align: left;
color: #009b8a;
width: 100%;
height: 50px;
background-color: #f5f5f5;
}
.AddIntent{ .AddIntent{
float: right; float: right;
margin: 12px; margin: 12px;