Inspired by the "Cancer Moonshot" program announced by the president to accelerate cancer research. Recently NCI (National Cancer Institute) announced their first stabilized version of Clinical Trials API to the public. Started working on that API to create a web application with search screen to provide more awareness to the public on available clinical trials.
See link below for more information about the Clinical Trials API : https://www.cancer.gov/syndication/api
Got an Idea to make use of this API in a better way using Alexa Skills Kit. Started with a very simple use case but finally completed the base version and submitting this to the community to make create better applications or contributing to my work to improve this application.
Realtime DemoTechnical Solution : PreparationVery important source for this skill is NCI Clinical Trials API. Luckily this API was built on JSON technology, It saves my life. Then spends most of the time to analyze the API to understand different ways to provide the filter parameters. As NCI maintaining all the world wide cancer trials information in their database, the result list is very huge and need to filter it once received.
Please check the links below for more information about NCI Clinical Trials API:
About the API : https://www.cancer.gov/syndication/api
How to use the API : https://clinicaltrialsapi.cancer.gov/v1/
Here is the sample response from the API for the URL : https://clinicaltrialsapi.cancer.gov/v1/clinical-trials/?current_trial_status=Active&sites.org_city=Stillwater&phase.phase=III
(Only showing main response keys)
{
"total": 25,
"trials": [{
"nci_id" : "NCI-2012-01995",
"brief_title":"Hormone Therapy with or without Everolimus in Treating Patients with Breast Cancer","official_title":"Phase III Randomized, Placebo-Controlled Clinical Trial Evaluating the Use of Adjuvant Endocrine Therapy +/- One Year of Everolimus in Patients with High-risk, Hormone Receptor-Positive and HER2/Neu Negative Breast Cancer. e^3 Breast Cancer Study-Evaluating Everolimus with Endocrine Therapy.",
"phase":{
"phase":"III",
"phase_other_text":null,
"phase_additional_qualifier_code":null
},
"sites":[{
"contact_email":null,
"contact_name":"Kevin Becker",
"contact_phone":"718-765-2500",
"org_address_line_1":"4802 Tenth Avenue",
"org_address_line_2":null,
"org_city":"Brooklyn",
"org_country":"United States",
"org_email":null,
"org_name":"Maimonides Medical Center",
"org_phone":"718-765-2500",
"org_postal_code":"11219",
"org_state_or_province":"NY",
"org_status":"ACTIVE",
"org_status_date":"2015-07-27",
"org_coordinates":{
"lat":40.6327,
"lon":-73.9967}
}],
"diseases":[{
"preferred_name":"Synchronous Bilateral Breast Carcinoma",
"display_name":"","parents":["C8287"],
]},
}]
There are more sections but we are going to use the following keys only for our application
- Trials
- Phase
- Sites (Nothing but Organization or Hospital Information)
- Diseases
If you observed the sites org_state_or_province value it is two letter abbreviation of the state. We are not sure user is going to say two letter code of the state all the time. I have used the following JSON object to convert state full name to two letter abbrevation.
module.exports = [
{
"name": "Alabama",
"abbreviation": "AL"
},
{
"name": "Alaska",
"abbreviation": "AK"
},
{
"name": "American Samoa",
"abbreviation": "AS"
},
{
"name": "Arizona",
"abbreviation": "AZ"
},
{
"name": "Arkansas",
"abbreviation": "AR"
},
{
"name": "California",
"abbreviation": "CA"
},
{
"name": "Colorado",
"abbreviation": "CO"
},
{
"name": "Connecticut",
"abbreviation": "CT"
},
{
"name": "Delaware",
"abbreviation": "DE"
},
{
"name": "District Of Columbia",
"abbreviation": "DC"
},
{
"name": "Federated States Of Micronesia",
"abbreviation": "FM"
},
{
"name": "Florida",
"abbreviation": "FL"
},
{
"name": "Georgia",
"abbreviation": "GA"
},
{
"name": "Guam",
"abbreviation": "GU"
},
{
"name": "Hawaii",
"abbreviation": "HI"
},
{
"name": "Idaho",
"abbreviation": "ID"
},
{
"name": "Illinois",
"abbreviation": "IL"
},
{
"name": "Indiana",
"abbreviation": "IN"
},
{
"name": "Iowa",
"abbreviation": "IA"
},
{
"name": "Kansas",
"abbreviation": "KS"
},
{
"name": "Kentucky",
"abbreviation": "KY"
},
{
"name": "Louisiana",
"abbreviation": "LA"
},
{
"name": "Maine",
"abbreviation": "ME"
},
{
"name": "Marshall Islands",
"abbreviation": "MH"
},
{
"name": "Maryland",
"abbreviation": "MD"
},
{
"name": "Massachusetts",
"abbreviation": "MA"
},
{
"name": "Michigan",
"abbreviation": "MI"
},
{
"name": "Minnesota",
"abbreviation": "MN"
},
{
"name": "Mississippi",
"abbreviation": "MS"
},
{
"name": "Missouri",
"abbreviation": "MO"
},
{
"name": "Montana",
"abbreviation": "MT"
},
{
"name": "Nebraska",
"abbreviation": "NE"
},
{
"name": "Nevada",
"abbreviation": "NV"
},
{
"name": "New Hampshire",
"abbreviation": "NH"
},
{
"name": "New Jersey",
"abbreviation": "NJ"
},
{
"name": "New Mexico",
"abbreviation": "NM"
},
{
"name": "New York",
"abbreviation": "NY"
},
{
"name": "North Carolina",
"abbreviation": "NC"
},
{
"name": "North Dakota",
"abbreviation": "ND"
},
{
"name": "Northern Mariana Islands",
"abbreviation": "MP"
},
{
"name": "Ohio",
"abbreviation": "OH"
},
{
"name": "Oklahoma",
"abbreviation": "OK"
},
{
"name": "Oregon",
"abbreviation": "OR"
},
{
"name": "Palau",
"abbreviation": "PW"
},
{
"name": "Pennsylvania",
"abbreviation": "PA"
},
{
"name": "Puerto Rico",
"abbreviation": "PR"
},
{
"name": "Rhode Island",
"abbreviation": "RI"
},
{
"name": "South Carolina",
"abbreviation": "SC"
},
{
"name": "South Dakota",
"abbreviation": "SD"
},
{
"name": "Tennessee",
"abbreviation": "TN"
},
{
"name": "Texas",
"abbreviation": "TX"
},
{
"name": "Utah",
"abbreviation": "UT"
},
{
"name": "Vermont",
"abbreviation": "VT"
},
{
"name": "Virgin Islands",
"abbreviation": "VI"
},
{
"name": "Virginia",
"abbreviation": "VA"
},
{
"name": "Washington",
"abbreviation": "WA"
},
{
"name": "West Virginia",
"abbreviation": "WV"
},
{
"name": "Wisconsin",
"abbreviation": "WI"
},
{
"name": "Wyoming",
"abbreviation": "WY"
}
];
One more limitation in NCI Clinical Trials API is we need to provide Latitude and Longitude to get the trials based on near by location
For field params which are geolocation coordinates (geo_point), please supply the following to the end of the field param:
- _lat - The latitude in decimal degrees and plus/minus.
- _lon - The longitude in decimal degrees and plus/minus.
- _dist - The radius to search within. Format must be an integer followed by a unit defined as:
mi - miles (for example 2mi)
km - kilometer (for example 5km)
clinical-trials?<field_param>_lat=<field_value_latitude>&<field_param>_lon=<field_value_longitude>&<field_param>_dist=<field_value_dist>
Example:
clinical-trials?sites.org_coordinates_lat=39.1292&sites.org_coordinates_lon=-77.2953&sites.org_coordinates_dist=100mi
I have used Google Maps Geocode API to get Latitude and Longitude based on the provided location. Please check the following link to more about the google API
https://developers.google.com/maps/documentation/geocoding
Now I am able to complete all the preparation to feed the data to Alexa Skills Kit.
Then I started learning the best practices to build Alexa application, As I haven't built any realtime solution before with an Alexa Skills Kit.
Following URLs helped me allot to jump start all the concepts:
- Requirements to build a service : https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/requirements-to-build-a-skill
- Voice Design HandBook : https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/alexa-skills-kit-voice-design-handbook
- SSML (Speech Synthesis Markup Language) : https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/speech-synthesis-markup-language-ssml-reference
- Built-in Library : https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/built-in-intent-ref/built-in-intent-library
- Standard Intents: https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/built-in-intent-ref/standard-intents
- Certification Requirements: https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/alexa-skills-kit-submission-checklist
Then I following the below link to build Trials Guider Skill using AWS Lambda function:
Used the following Node.js modules to improve Trials Guider Skill performance.
DeaSync module to turns async function into sync via JavaScript wrapper of Node event loop, see the links below for more information
Request moduel to handle HTTP/HTTPS calls in a better way, see the link below for more information
Technical Solution : Google Maps Geocode APII have created the following function to retrieve Latitude and Longitude based on the provided location
/**
* Uses Google Maps API, see link below for more information
* https://developers.google.com/maps/documentation/geocoding
**/
function getLatLongQueryString(address, mapsResponseCallback) {
console.log(" START: getLatLongQueryString ");
var endpoint = 'http://maps.googleapis.com/maps/api/geocode/json';
var queryString = '?address=' + address + '&sensor=false';
console.log('Maps URL: ' + endpoint + queryString);
request.get(endpoint + queryString, function (error, response, body) {
if (!error && response.statusCode == 200) {
//console.log(body) // Show the HTML for the Google homepage.
var mapsResponseObject = JSON.parse(body);
if (mapsResponseObject.error) {
//console.log(" Maps API Error: " + mapsResponseObject.error.message);
throw " some thing went wrong, Please try again!";
} else {
mapsResponseCallback(mapsResponseObject);
}
} else {
throw " some thing went wrong, Please try again!";
}
});
console.log(" END: getLatLongQueryString ");
}
This function simply convert the retrieved object into JSON object and then send it back to the caller function
Technical Solution : NCI Clinical Trials APICreated the following function to call the NCI Clinical Trials API based on the provided slots.
/**
* Uses Clinical Trials API, see link below for more information
* https://clinicaltrialsapi.cancer.gov/v1/
**/
function makeTrialsAPIRequest(intent, latLngQueryStr, guiderResponseCallback) {
console.log(" START: makeTrialsAPIRequest ");
var city = intent.slots.City.value,
state = intent.slots.State.value,
address = intent.slots.Address.value,
radius = intent.slots.Radius.value,
hospital = intent.slots.Hospital.value,
cancerType = intent.slots.CancerType.value;
var endpoint = 'https://clinicaltrialsapi.cancer.gov/v1/clinical-trials?current_trial_status=Active',
queryString = '';
if (city) {
queryString += '&sites.org_city=' + city;
}
if (state) {
queryString += '&sites.org_state_or_province=' + state;
}
if (hospital) {
queryString += '&sites.org_name=' + hospital;
}
if (cancerType) {
queryString += '&brief_title=' + cancerType;
}
if (latLngQueryStr) {
if (radius) {
queryString += latLngQueryStr + '&sites.org_coordinates_dist=' + radius + 'mi';
} else {
queryString += latLngQueryStr + '&sites.org_coordinates_dist=50mi';
}
} //Close LatLng
console.log(" Trials Query: " + endpoint + queryString);
if (queryString.length > 0) {
request.get(endpoint + queryString, function (error, response, body) {
var trialsResponseString = '';
if (!error && response.statusCode == 200) {
var trialsResponseObject = JSON.parse(body);
//console.log(" API Response String: " + trialsResponseString);
if (trialsResponseObject.error) {
console.log(" API Error: " + trialsResponseObject.error.message);
throw " some thing went wrong, Please try again!";
} else {
//console.log(" API Response: " + trialsResponseObject);
guiderResponseCallback(trialsResponseObject);
}
} else {
throw " some thing went wrong, Please try again!";
}
});
} else {
console.log(" Not found trials.");
throw " No trials found. Please try again!";
}
console.log(" END: makeTrialsAPIRequest ");
}
Technical Solution : Calling Google Maps Geocode API and NCI Clinical Trials API functionsOnce I am able to call the both API's with the provide user data then ended up with the following function by calling both aysnchronous HTTP/HTTPS calls.
/**
* Called when the user says something for trials search.
* Please check Sample Utterences for more information.
*
*/
function handleTrialsRequest(intent, session, callback) {
var speechOutput = " ",
cardText = "",
sessionAttributes = {},
resultTrials = [],
shouldEndSession = false,
repromptText = "",
latLngQueryStr,
filterByCity = "",
filterByState = "",
filterByCountry = "United States";
//This code will call google maps geocode api to get Latitude and Longitude if address slot found
if (intent.slots.Address.value) {
getLatLongQueryString(intent.slots.Address.value, function (mapsResponse) {
//console.log(" API Response : " + mapsResponseObject);
if (mapsResponse.results) {
if (mapsResponse.results[0].address_components) {
var totalCmpnts = Object.keys(mapsResponse.results[0].address_components).length;
for (var cmpntsIndex = 0; cmpntsIndex < totalCmpnts; cmpntsIndex++) {
var compntObj = mapsResponse.results[0].address_components[cmpntsIndex],
isLocality = false,
isState = false;
if (compntObj.types) {
var totalTypes = Object.keys(compntObj.types).length;
for (var typesIndex = 0; typesIndex < totalTypes; typesIndex++) {
if (compntObj.types[typesIndex] === " locality ") {
isLocality = true;
}
if (compntObj.types[typesIndex] === " administrative_area_level_1 ") {
isState = true;
}
}
}
if (isLocality) {
filterByCity = compntObj.short_name;
}
if (isState) {
filterByState = compntObj.short_name;
}
}
}
var latStr = mapsResponse.results[0].geometry.location.lat;
var lngStr = mapsResponse.results[0].geometry.location.lng;
console.log(" lat : " + latStr + " lng : " + lngStr);
latLngQueryStr = '&sites.org_coordinates_lat=' + latStr + '&sites.org_coordinates_lon=' + lngStr;
} else {
throw " Specified Address is not found!";
}
});
}else{
latLngQueryStr = "";
}
//Used DeaSync model to turn Google Maps API call to sync.Sometimes process is continuing to the next step before response coming from google api.
//This small piece of code saved me allot to make sure retrieving Latitude and Longitude before calling NCI Clinical Trials API
while (latLngQueryStr === undefined) {
require('deasync').runLoopOnce();
}
var totalTrials;
//NCI Clinical Trials API needs State in abbreviation (ex: two letter codes as MN for Minnesota).
//Following code will lookup the state abbreviation from predefined states array.Please check usaStates.js in src folder
if(intent.slots.State.value){
if(intent.slots.State.value.length >2){
for(var stateIndex = 0; stateIndex<usaStates.length;stateIndex++) {
if(intent.slots.State.value === usaStates[stateIndex].name){
intent.slots.State.value = usaStates[stateIndex].abbreviation;
}
}
}
}
//Calling makeTrialsAPIRequest to populate Trials information by calling NCI Clinical Trials API
makeTrialsAPIRequest(intent, latLngQueryStr, function (trialsResponse) {
if (trialsResponse.trials) {
totalTrials = Object.keys(trialsResponse.trials).length;
//session.trials = trialsResponse.trials;
//console.log(" Number of Trials: " + totalTrials);
for (var trialsIndex = 0; trialsIndex < totalTrials; trialsIndex++) {
var trialObj = trialsResponse.trials[trialsIndex],
resultTrialObj = {};
briefTitle = trialObj.brief_title,
primaryPurpose = trialObj.primary_purpose.primary_purpose_code,
phase = trialObj.phase.phase;
if(!filterByCity && intent.slots.City.value){
filterByCity = intent.slots.City.value;
}
if (!filterByState && intent.slots.State.value) {
filterByState = intent.slots.State.value;
}
if (filterByCountry) {
trialObj.sites = trialObj.sites.filter(function (site) {
return site.org_country === filterByCountry
});
}
if (filterByState) {
trialObj.sites = trialObj.sites.filter(function (site) {
return site.org_state_or_province === filterByState
});
}
if (filterByCity) {
trialObj.sites = trialObj.sites.filter(function (site) {
return site.org_city === filterByCity
});
}
if (intent.slots.Hospital.value) {
trialObj.sites = trialObj.sites.filter(function (site) {
return site.org_name === intent.slots.Hospital.value
});
}
var noOfDiseases = Object.keys(trialObj.diseases).length;
//console.log(" Number Of Diseases: " + noOfDiseases);
var diseases = [];
for (var diseasesIndex = 0; diseasesIndex < noOfDiseases; diseasesIndex++) {
diseases[diseasesIndex] = trialObj.diseases[diseasesIndex].preferred_name;
} //Close Diseases Loop
var resultSites = [],
noOfSites = Object.keys(trialObj.sites).length;
for (var sitesIndex = 0; sitesIndex < noOfSites; sitesIndex++) {
var sitesObj = trialObj.sites[sitesIndex],
resultSiteObj = {};
if ("ACTIVE" === sitesObj.org_status) {
var orgName,
orgPhone,
contactName,
contactPhone,
contactEmail,
orgStatusDate,
orgAddress = " ",
orgAddressCard = "";
if(sitesObj.org_name){
orgName = sitesObj.org_name;
}
if(sitesObj.org_phone){
orgPhone = sitesObj.org_phone;
}
if(sitesObj.org_status_date){
orgStatusDate = sitesObj.org_status_date;
}
if(sitesObj.contact_name){
contactName = sitesObj.contact_name;
}
if(sitesObj.contact_phone){
contactPhone = sitesObj.contact_phone;
}
if(sitesObj.contact_email){
contactEmail = sitesObj.contact_email;
}
if (sitesObj.org_address_line_1) {
orgAddress += sitesObj.org_address_line_1;
}
if (sitesObj.org_address_line_2) {
orgAddress += ", " + sitesObj.org_address_line_2;
}
if (sitesObj.org_city) {
orgAddress += ", " + sitesObj.org_city;
}
if (sitesObj.org_state_or_province) {
orgAddress += ", " + sitesObj.org_state_or_province;
}
if (sitesObj.org_country) {
orgAddress += ", " + sitesObj.org_country;
}
if (sitesObj.org_postal_code) {
orgAddressCard += orgAddress;
orgAddressCard += ", " + sitesObj.org_postal_code;
orgAddress += ", <say-as interpret-as=\"digits\">" + sitesObj.org_postal_code+"</say-as>";
}
resultSiteObj = {
"orgName": orgName,
"orgPhone": orgPhone,
"orgStatusDate": orgStatusDate,
"contactName": contactName,
"contactPhone": contactPhone,
"contactEmail": contactEmail,
"orgAddress": orgAddress,
"orgAddressCard":orgAddressCard
};
resultSites[sitesIndex] = resultSiteObj;
}//Close active sties
}//Close Sites loop
resultTrialObj = {
"trialName": briefTitle,
"primaryPurpose": primaryPurpose,
"phase": phase,
"diseases": diseases,
"sites":resultSites
};
resultTrials[trialsIndex] = resultTrialObj;
}//Close trials loop;
trialsResponse.trials = {};
}else{
totalTrials = 0;
} //Close Trials null checking
});
//Used DeaSync model to turn NCI Clinical Trials API call to sync. Sometimes process is continuing to the next step before response coming from the API.
//This small piece of code saved me allot to make sure retrieving trials information before moving further.
while (totalTrials === undefined) {
require('deasync').runLoopOnce();
}
//Preparing User response
if(totalTrials === 0){
speechOutput = "<s>no active trials found.</s> <s>say, start search</s> or <s>start trials guider</s> to <s>provide a new location.</s>";
repromptText = speechOutput;
cardText = "no active trials found. \n say, start search or \nstart trials guider to provide a new location.";
sessionAttributes = {
"speechOutput": speechOutput,
"repromptText": repromptText,
"cardText":cardText,
"userPromptedToContinue": false
};
}else{
if(resultTrials){
var noOfActiveTrials = Object.keys(resultTrials).length,
noOfSites = Object.keys(resultTrials[0].sites).length,
noOfDiseases = Object.keys(resultTrials[0].diseases).length;
speechOutput = "<s>found <say-as interpret-as=\"number\">"+noOfActiveTrials+"</say-as> active trials. </s>";
speechOutput +=" <s>trial </s><say-as interpret-as=\"number\">1</say-as> is <p> "+resultTrials[0].trialName+" .</p> <s>primary purpose is "+resultTrials[0].primaryPurpose+" </s>for <s>phase "+resultTrials[0].phase+" . </s>";
speechOutput += " <s>there are <say-as interpret-as=\"number\">"+noOfSites+"</say-as> locations and <say-as interpret-as=\"number\">"+noOfDiseases+"</say-as> diseases are exist.</s>";
speechOutput += " <s>say, next trial</s> or <s>say, diseases to know what diseases are treating,</s> or <s>say, locations to know hospital locations</s>";
repromptText = speechOutput;
cardText = "found "+noOfActiveTrials+" active trials. \n";
cardText +=" Trial 1 : \n"+resultTrials[0].trialName+". \n primary purpose is "+resultTrials[0].primaryPurpose+" for phase "+resultTrials[0].phase+". ";
cardText += " there are "+noOfSites+" locations and "+noOfDiseases+" diseases are exist.\n";
cardText += " say, next trial or \n say, diseases to know what diseases are treating, or \n say, locations to know hospital locations";
sessionAttributes = {
"speechOutput": speechOutput,
"repromptText": repromptText,
"cardText":cardText,
"currentTrialIndex": 1,
"currentDiseaseIndex":-1,
"currentSitesIndex":-1,
"userPromptedToContinue": true,
"trialsSearchResult":resultTrials
};
}else{
speechOutput = "<s>no active trials found.</s> <s>say, start search</s> or <s>start trials guider</s> to <s>provide a new location.</s>";
cardText = "no active trials found. \n say, start search or \nstart trials guider to provide a new location.";
repromptText = speechOutput;
sessionAttributes = {
"speechOutput": speechOutput,
"repromptText": repromptText,
"cardText":cardText,
"userPromptedToContinue": false
};
}
}
shouldEndSession = false;
callback(sessionAttributes,
buildSpeechletResponse(CARD_TITLE, speechOutput, repromptText, cardText, shouldEndSession));
}
Faced issues while calling these asynchronous functions, sometimes Google Maps Geocode API response came after NCI Clinical Trials API call completed.
Used the following code snippet by using DeaSync node.js module. How cool it is, It saves my life.
//Used DeaSync model to turn Google Maps API call to sync.Sometimes process is continuing to the next step before response coming from google api.
//This small piece of code saved me allot to make sure retrieving Latitude and Longitude before calling NCI Clinical Trials API
while (latLngQueryStr === undefined) {
require('deasync').runLoopOnce();
}
Applied the similar logic after NCI Clinical Trials API to make sure response processed before proceeding.
//Used DeaSync model to turn NCI Clinical Trials API call to sync. Sometimes process is continuing to the next step before response coming from the API.
//This small piece of code saved me allot to make sure retrieving trials information before moving further.
while (totalTrials === undefined) {
require('deasync').runLoopOnce();
}
Then implemented all the remaining functions to support all the Intents. Here is the onIntent function which handles all the user Intents.
/**
* Called when the user specifies an intent for this skill.
* Master piese which redirecting to the corresponding business functionality.
* Check Sample Utterences for more information.
*/
function onIntent(intentRequest, session, callback) {
console.log("onIntent requestId=" + intentRequest.requestId
+ ", sessionId=" + session.sessionId);
var intent = intentRequest.intent,
intentName = intentRequest.intent.name;
// dispatch custom intents to handlers here
if ("TrialsGuiderSearchIntent" === intentName) {
handleTrialsRequest(intent, session, callback);
} else if ("TrialDiseaseIntent" === intentName) {
handleDiseasesRequest(intent, session, callback);
} else if ("TrialSitesIntent" === intentName) {
handleSitesRequest(intent, session, callback);
} else if ("NextTrialIntent" === intentName) {
handleNextTrialRequest(intent, session, callback);
} else if ("AMAZON.StartOverIntent" === intentName) {
getWelcomeResponse(callback);
} else if ("AMAZON.RepeatIntent" === intentName) {
handleRepeatRequest(intent, session, callback);
} else if ("AMAZON.HelpIntent" === intentName) {
handleGetHelpRequest(intent, session, callback);
} else if ("AMAZON.StopIntent" === intentName) {
handleFinishSessionRequest(intent, session, callback);
} else if ("AMAZON.CancelIntent" === intentName) {
handleNextTrialRequest(intent, session, callback);
} else {
throw "Invalid intent";
}
}
Please check the GitHub repository for completed source code.
Technical Solution : Trials Guider ArchitecturePutting it all together, The skill is implemented in Node.js and runs in a Lambda function on AWS.
If you want to use Trials Guider to know the Cancer Clinical Trials in particular location. Say some thing like :
"Alexa, Open Trials Guider"
with this intent Alexa opens the Trials Guider skill if it is already activated in your device and responded with the welcome message.
Then say some thing like:
"Search trials in Stillwater"
Then Alexa will responded with the retrieved active trials information. See the demo for more information.
Usability : Voice User InterfacePlease check the following flow chart with basic Voice User Interface design.
To view the full image, Please click here
General Features- "Repeat" during search plays back the previous response.
- "Next" or "Next Trial" or "More Trials" at any time after the trials search, it will responded with the next trial information.
- "Cancel" after the trials search will jump to the next trial.
- "Stop" quits the skill and responded with the Thank You message and Good Bye!
- "Help" gives more information about how to search trials
Fill in Skill Information
- Name : Trials Guider
- Invocation Name : trials guider
Fill in Interaction Model
Please check the following interaction model in JSON format
{
"intents": [
{
"intent": "TrialsGuiderSearchIntent",
"slots": [
{
"name": "CancerType",
"type": "LIST_OF_CANCERS"
},
{
"name": "TrialsPhase",
"type": "AMAZON.NUMBER"
},
{
"name": "City",
"type": " AMAZON.US_CITY"
},
{
"name": "State",
"type": "AMAZON.US_STATE"
},
{
"name": "Address",
"type": "LIST_OF_ADDRESSES"
},
{
"name": "Radius",
"type": "AMAZON.NUMBER"
},
{
"name": "Hospital",
"type": "LIST_OF_HOSPITALS"
}
]
},
{
"intent": "TrialDiseaseIntent"
},
{
"intent": "TrialSitesIntent"
},
{
"intent": "NextTrialIntent"
},
{
"intent": "AMAZON.HelpIntent"
},
{
"intent": "AMAZON.StartOverIntent"
},
{
"intent": "AMAZON.StopIntent"
},
{
"intent": "AMAZON.CancelIntent"
},
{
"intent": "AMAZON.RepeatIntent"
}
]
}
Then created the sample utterances like below:
TrialsGuiderSearchIntent get trials in {City}
TrialsGuiderSearchIntent get trials in {City} {State}
TrialsGuiderSearchIntent get trials near by {Address}
TrialsGuiderSearchIntent get trials near by {Address} with in {Radius} miles
TrialsGuiderSearchIntent get trials at {Hospital}
TrialsGuiderSearchIntent get trials at {Hospital} {State}
TrialsGuiderSearchIntent search trials in {City}
TrialsGuiderSearchIntent search trials in {City} {State}
TrialsGuiderSearchIntent search trials near by {Address}
TrialsGuiderSearchIntent search trials near by {Address} with in {Radius} miles
TrialsGuiderSearchIntent search trials at {Hospital}
TrialsGuiderSearchIntent search trials at {Hospital} {State}
TrialsGuiderSearchIntent trials in {City}
TrialsGuiderSearchIntent trials in {City} {State}
TrialsGuiderSearchIntent trials near by {Address}
TrialsGuiderSearchIntent trials near by {Address} with in {Radius} miles
TrialsGuiderSearchIntent trials at {Hospital}
TrialsGuiderSearchIntent trials at {Hospital} {State}
TrialsGuiderSearchIntent get trials for {CancerType} cancer in {City}
TrialsGuiderSearchIntent get trials for {CancerType} cancer in {City} {State}
TrialsGuiderSearchIntent get trials for {CancerType} cancer near by {Address}
TrialsGuiderSearchIntent get trials for {CancerType} cancer near by {Address} with in {Radius} miles
TrialsGuiderSearchIntent get trials for {CancerType} cancer at {Hospital}
TrialsGuiderSearchIntent get trials for {CancerType} cancer at {Hospital} {State}
TrialsGuiderSearchIntent search trials for {CancerType} cancer in {City}
TrialsGuiderSearchIntent search trials for {CancerType} cancer in {City} {State}
TrialsGuiderSearchIntent search trials for {CancerType} cancer near by {Address}
TrialsGuiderSearchIntent search trials for {CancerType} cancer near by {Address} with in {Radius} miles
TrialsGuiderSearchIntent search trials for {CancerType} cancer at {Hospital}
TrialsGuiderSearchIntent search trials for {CancerType} cancer at {Hospital} {State}
TrialsGuiderSearchIntent trials for {CancerType} cancer in {City}
TrialsGuiderSearchIntent trials for {CancerType} cancer in {City} {State}
TrialsGuiderSearchIntent trials for {CancerType} cancer near by {Address}
TrialsGuiderSearchIntent trials for {CancerType} cancer near by {Address} with in {Radius} miles
TrialsGuiderSearchIntent trials for {CancerType} cancer at {Hospital}
TrialsGuiderSearchIntent trials for {CancerType} cancer at {Hospital} {State}
TrialsGuiderSearchIntent get {CancerType} cancer trials in {City}
TrialsGuiderSearchIntent get {CancerType} cancer trials in {City} {State}
TrialsGuiderSearchIntent get {CancerType} cancer trials near by {Address}
TrialsGuiderSearchIntent get {CancerType} cancer trials near by {Address} with in {Radius} miles
TrialsGuiderSearchIntent get {CancerType} cancer trials at {Hospital}
TrialsGuiderSearchIntent get {CancerType} cancer trials at {Hospital} {State}
TrialsGuiderSearchIntent search {CancerType} cancer trials in {City}
TrialsGuiderSearchIntent search {CancerType} cancer trials in {City} {State}
TrialsGuiderSearchIntent search {CancerType} cancer trials near by {Address}
TrialsGuiderSearchIntent search {CancerType} cancer trials near by {Address} with in {Radius} miles
TrialsGuiderSearchIntent search {CancerType} cancer trials at {Hospital}
TrialsGuiderSearchIntent search {CancerType} cancer trials at {Hospital} {State}
TrialsGuiderSearchIntent {CancerType} cancer trials in {City}
TrialsGuiderSearchIntent {CancerType} cancer trials in {City} {State}
TrialsGuiderSearchIntent {CancerType} cancer trials near by {Address}
TrialsGuiderSearchIntent {CancerType} cancer trials near by {Address} with in {Radius} miles
TrialsGuiderSearchIntent {CancerType} cancer trials at {Hospital}
TrialsGuiderSearchIntent {CancerType} cancer trials at {Hospital} {State}
TrialsGuiderSearchIntent get {CancerType} cancer phase {TrialsPhase} trials in {City}
TrialsGuiderSearchIntent get {CancerType} cancer phase {TrialsPhase} trials in {City} {State}
TrialsGuiderSearchIntent get {CancerType} cancer phase {TrialsPhase} trials near by {Address}
TrialsGuiderSearchIntent get {CancerType} cancer phase {TrialsPhase} trials near by {Address} with in {Radius} miles
TrialsGuiderSearchIntent get {CancerType} cancer phase {TrialsPhase} trials at {Hospital}
TrialsGuiderSearchIntent get {CancerType} cancer phase {TrialsPhase} trials at {Hospital} {State}
TrialsGuiderSearchIntent search {CancerType} cancer phase {TrialsPhase} trials in {City}
TrialsGuiderSearchIntent search {CancerType} cancer phase {TrialsPhase} trials in {City} {State}
TrialsGuiderSearchIntent search {CancerType} cancer phase {TrialsPhase} trials near by {Address}
TrialsGuiderSearchIntent search {CancerType} cancer phase {TrialsPhase} trials near by {Address} with in {Radius} miles
TrialsGuiderSearchIntent search {CancerType} cancer phase {TrialsPhase} trials at {Hospital}
TrialsGuiderSearchIntent search {CancerType} cancer phase {TrialsPhase} trials at {Hospital} {State}
TrialsGuiderSearchIntent {CancerType} cancer phase {TrialsPhase} trials in {City}
TrialsGuiderSearchIntent {CancerType} cancer phase {TrialsPhase} trials in {City} {State}
TrialsGuiderSearchIntent {CancerType} cancer phase {TrialsPhase} trials near by {Address}
TrialsGuiderSearchIntent {CancerType} cancer phase {TrialsPhase} trials near by {Address} with in {Radius} miles
TrialsGuiderSearchIntent {CancerType} cancer phase {TrialsPhase} trials at {Hospital}
TrialsGuiderSearchIntent {CancerType} cancer phase {TrialsPhase} trials at {Hospital} {State}
TrialsGuiderSearchIntent get phase {TrialsPhase} trials in {City}
TrialsGuiderSearchIntent get phase {TrialsPhase} trials in {City} {State}
TrialsGuiderSearchIntent get phase {TrialsPhase} trials near by {Address}
TrialsGuiderSearchIntent get phase {TrialsPhase} trials near by {Address} with in {Radius} miles
TrialsGuiderSearchIntent get phase {TrialsPhase} trials at {Hospital}
TrialsGuiderSearchIntent get phase {TrialsPhase} trials at {Hospital} {State}
TrialsGuiderSearchIntent search phase {TrialsPhase} trials in {City}
TrialsGuiderSearchIntent search phase {TrialsPhase} trials in {City} {State}
TrialsGuiderSearchIntent search phase {TrialsPhase} trials near by {Address}
TrialsGuiderSearchIntent search phase {TrialsPhase} trials near by {Address} with in {Radius} miles
TrialsGuiderSearchIntent search phase {TrialsPhase} trials at {Hospital}
TrialsGuiderSearchIntent search phase {TrialsPhase} trials at {Hospital} {State}
TrialsGuiderSearchIntent phase {TrialsPhase} trials in {City}
TrialsGuiderSearchIntent phase {TrialsPhase} trials in {City} {State}
TrialsGuiderSearchIntent phase {TrialsPhase} trials near by {Address}
TrialsGuiderSearchIntent phase {TrialsPhase} trials near by {Address} with in {Radius} miles
TrialsGuiderSearchIntent phase {TrialsPhase} trials at {Hospital}
TrialsGuiderSearchIntent phase {TrialsPhase} trials at {Hospital} {State}
TrialsGuiderSearchIntent get phase {TrialsPhase} {CancerType} cancer trials in {City}
TrialsGuiderSearchIntent get phase {TrialsPhase} {CancerType} cancer trials in {City} {State}
TrialsGuiderSearchIntent get phase {TrialsPhase} {CancerType} cancer trials near by {Address}
TrialsGuiderSearchIntent get phase {TrialsPhase} {CancerType} cancer trials near by {Address} with in {Radius} miles
TrialsGuiderSearchIntent get phase {TrialsPhase} {CancerType} cancer trials at {Hospital}
TrialsGuiderSearchIntent get phase {TrialsPhase} {CancerType} cancer trials at {Hospital} {State}
TrialsGuiderSearchIntent search phase {TrialsPhase} {CancerType} cancer trials in {City}
TrialsGuiderSearchIntent search phase {TrialsPhase} {CancerType} cancer trials in {City} {State}
TrialsGuiderSearchIntent search phase {TrialsPhase} {CancerType} cancer trials near by {Address}
TrialsGuiderSearchIntent search phase {TrialsPhase} {CancerType} cancer trials near by {Address} with in {Radius} miles
TrialsGuiderSearchIntent search phase {TrialsPhase} {CancerType} cancer trials at {Hospital}
TrialsGuiderSearchIntent search phase {TrialsPhase} {CancerType} cancer trials at {Hospital} {State}
TrialsGuiderSearchIntent phase {TrialsPhase} {CancerType} cancer trials in {City}
TrialsGuiderSearchIntent phase {TrialsPhase} {CancerType} cancer trials in {City} {State}
TrialsGuiderSearchIntent phase {TrialsPhase} {CancerType} cancer trials near by {Address}
TrialsGuiderSearchIntent phase {TrialsPhase} {CancerType} cancer trials near by {Address} with in {Radius} miles
TrialsGuiderSearchIntent phase {TrialsPhase} {CancerType} cancer trials at {Hospital}
TrialsGuiderSearchIntent phase {TrialsPhase} {CancerType} cancer trials at {Hospital} {State}
TrialDiseaseIntent diseases
TrialDiseaseIntent next disease
TrialDiseaseIntent next disease please
TrialDiseaseIntent more diseases
TrialSitesIntent sites
TrialSitesIntent sites please
TrialSitesIntent next site
TrialSitesIntent next site please
TrialSitesIntent more sites
TrialSitesIntent organizations
TrialSitesIntent organizations please
TrialSitesIntent next organization
TrialSitesIntent next organization please
TrialSitesIntent more organizations
TrialSitesIntent hospitals
TrialSitesIntent hospitals please
TrialSitesIntent next hospital
TrialSitesIntent next hospital please
TrialSitesIntent more hospitals
TrialSitesIntent locations
TrialSitesIntent locations please
TrialSitesIntent next location
TrialSitesIntent next location please
TrialSitesIntent more locations
NextTrialIntent trials
NextTrialIntent next trial
NextTrialIntent next trial please
NextTrialIntent more trials
AMAZON.StartOverIntent start trials guider
AMAZON.StartOverIntent start trials search
AMAZON.StartOverIntent start search
AMAZON.StartOverIntent start guider
AMAZON.StartOverIntent new trials
AMAZON.StartOverIntent new trials guider
AMAZON.StartOverIntent new trials search
AMAZON.StartOverIntent new search
AMAZON.StartOverIntent new guider
AMAZON.StartOverIntent open trials guider
AMAZON.StartOverIntent open trials search
AMAZON.StartOverIntent open search
AMAZON.StartOverIntent open trials
AMAZON.StartOverIntent open guider
AMAZON.StopIntent stop
AMAZON.StopIntent bye
AMAZON.StopIntent good bye
AMAZON.StopIntent shut down
After all skill set up completion without any errors. I was able to test manually.
TestingFinally tested with a real device and feeling happy that my code is responding to words.
PublishingFeeling proud to submit this skill for certification to get an approval from Amazon to publish it to the public. This is the first time I am submitting my Alexa Skill.
Followed the below link to take care of all the necessary steps.
Certification Requirements: https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/alexa-skills-kit-submission-checklist
Waiting to hear back from Amazon. Usually it will take 7 working business days if everything goes fine.
Comments