Skip to content

Commit

Permalink
Merge 2ba1342 into a9342f5
Browse files Browse the repository at this point in the history
  • Loading branch information
barbeau authored Mar 12, 2017
2 parents a9342f5 + 2ba1342 commit a81b428
Show file tree
Hide file tree
Showing 9 changed files with 250 additions and 37 deletions.
15 changes: 13 additions & 2 deletions src/main/java/org/onebusaway/alexa/AuthedSpeechlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,20 @@ private SpeechletResponse tellArrivals(Session session) throws SpeechletExceptio
response.getCurrentTime(), userData.getSpeakClockTime(), timeZone, routesToFilter);

log.info("Full text output: " + output);
StorageUtil.saveOutputForRepeat(output, obaDao, userData);

// Build the full text response to the user
StringBuilder builder = new StringBuilder();
builder.append(SpeechUtil.getAnnounceFeaturev1_1_0Text(session));
builder.append(output);

// Save that we've already read the tutorial info to the user
userData.setAnnouncedIntroduction(1L);
userData.setAnnouncedFeaturesv1_1_0(1L);
obaDao.saveUserData(userData);

StorageUtil.saveOutputForRepeat(builder.toString(), obaDao, userData);
PlainTextOutputSpeech out = new PlainTextOutputSpeech();
out.setText(output);
out.setText(builder.toString());
return SpeechletResponse.newTellResponse(out);
}

Expand Down
2 changes: 2 additions & 0 deletions src/main/java/org/onebusaway/alexa/SessionAttribute.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ public class SessionAttribute {
public static final String TIME_ZONE = "timeZone";
public static final String DIALOG_ROUTES_TO_ASK_ABOUT = "foundRoutes";
public static final String DIALOG_ROUTES_TO_FILTER = "dialogRoutesToFilter";
public static final String ANNOUNCED_INTRODUCTION = "announcedIntroduction";
public static final String ANNOUNCED_FEATURES_V1_1_0 = "announcedFeaturesv1_1_0";

// Strangely, we can't save HashSets or HashMaps to sessions (Amazon Alexa converts them to ArrayLists, which
// generates a ClassCastException when trying to retrieve them. This prevents us from saving route filters to sessions.
Expand Down
23 changes: 23 additions & 0 deletions src/main/java/org/onebusaway/alexa/storage/ObaUserDataItem.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ public class ObaUserDataItem {
@DynamoDBAttribute(attributeName = "LastAccessTime")
private long lastAccessTime;

/**
* 0 for false, 1 for true (Apparently DynamoDB doesn't persist booleans)
*/
@Getter
@Setter
@DynamoDBAttribute(attributeName = "SpeakClockTime")
Expand All @@ -103,6 +106,26 @@ public class ObaUserDataItem {
@DynamoDBAttribute(attributeName = "RoutesToFilterOut")
private HashMap<String, HashSet<String>> routesToFilterOut;

/**
* Whether or not we've given the user the introduction to OneBusAway when they first use the skill
* <p>
* 0 for false, 1 for true (Apparently DynamoDB doesn't persist booleans)
*/
@Getter
@Setter
@DynamoDBAttribute(attributeName = "AnnouncedIntroduction")
private long announcedIntroduction;

/**
* Whether or not we've given the user an update for what's new in v1.1.0
* <p>
* 0 for false, 1 for true (Apparently DynamoDB doesn't persist booleans)
*/
@Getter
@Setter
@DynamoDBAttribute(attributeName = "AnnouncedFeatures-v1_1_0")
private long announcedFeaturesv1_1_0;

@DynamoDBVersionAttribute
private Long version;
}
6 changes: 6 additions & 0 deletions src/main/java/org/onebusaway/alexa/util/SessionUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,5 +76,11 @@ public static void populateAttributes(Session session, ObaUserDataItem userData)
if (session.getAttribute(TIME_ZONE) == null) {
session.setAttribute(TIME_ZONE, userData.getTimeZone());
}
if (session.getAttribute(ANNOUNCED_INTRODUCTION) == null) {
session.setAttribute(ANNOUNCED_INTRODUCTION, userData.getAnnouncedIntroduction());
}
if (session.getAttribute(ANNOUNCED_FEATURES_V1_1_0) == null) {
session.setAttribute(ANNOUNCED_FEATURES_V1_1_0, userData.getAnnouncedFeaturesv1_1_0());
}
}
}
79 changes: 70 additions & 9 deletions src/main/java/org/onebusaway/alexa/util/SpeechUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.amazon.speech.ui.Reprompt;
import lombok.extern.log4j.Log4j;
import org.apache.http.util.TextUtils;
import org.onebusaway.alexa.SessionAttribute;
import org.onebusaway.alexa.storage.ObaDao;
import org.onebusaway.alexa.storage.ObaUserDataItem;
import org.onebusaway.io.client.elements.ObaArrivalInfo;
Expand All @@ -30,14 +31,17 @@

import java.util.*;

import static org.onebusaway.alexa.SessionAttribute.STOP_ID;

/**
* Utilities for speech-related actions
*/
@Log4j
public class SpeechUtil {

private static final String HELP_REAL_TIME_VS_STATIC_TEXT = "All predictions are based on real-time information unless they are followed by the words 'according to the schedule'. ";
private static final String HELP_CLOCK_TIME_TEXT = "I can also tell you times in a clock format such as 10:25 AM. You can enable this by saying " +
"`enable clock times`. ";
private static final String HELP_FILTER_ROUTES_TEXT = "You can ask me to filter out certain routes for the currently selected stop by saying `filter routes`. ";

private final static Reprompt cityReprompt;
private final static Reprompt stopNumReprompt;

Expand Down Expand Up @@ -120,7 +124,7 @@ public static HashSet getRoutesToFilter(ObaDao obaDao, Session session) {
if (optUserData.isPresent()) {
routeFilters = optUserData.get().getRoutesToFilterOut();
if (routeFilters != null) {
String stopId = (String) session.getAttribute(STOP_ID);
String stopId = (String) session.getAttribute(SessionAttribute.STOP_ID);
if (stopId == null) {
// Try to get Stop ID from DAO
stopId = optUserData.get().getStopId();
Expand Down Expand Up @@ -165,16 +169,73 @@ public static SpeechletResponse getHelpMessage() {
out.setText("The One Bus Away skill serves up fresh, real-time transit information " +
"at a stop of your choice. You've already configured your city and stop, " +
"so to hear predictions just open the skill or ask me for arrivals. " +
getRealtimeVsStaticText() +
"You can filter out certain routes for the currently selected stop by saying `filter routes`. " +
"I can also tell you times in a clock format such as 10:25 AM. You can enable this by saying " +
"`enable clock times`, and disable it by saying `disable clock times`. " +
HELP_REAL_TIME_VS_STATIC_TEXT +
HELP_FILTER_ROUTES_TEXT +
HELP_CLOCK_TIME_TEXT +
"If you'd like to change your city or stop, say `set my city` or `set my stop`, followed by the city or stop number. " +
"If you need additional help, please contact me using email at alexa at One Bus Away dot org");
return SpeechletResponse.newTellResponse(out);
}

public static String getRealtimeVsStaticText() {
return "All predictions are based on real-time information unless they are followed by the words 'according to the schedule'. ";
/**
* Returns the text that should be read to the user if this is the first time they've opened the skill, or an empty
* string if they've already heard this info.
* <p>
* After the call to this method, session attribute ANNOUNCED_INTRODUCTION will be 1.
*
* @param session if session attribute ANNOUNCED_INTRODUCTION is 0 will return intro text, if is 1 will return empty string
* @return intro text if session attribute ANNOUNCED_INTRODUCTION is 0, or empty string if is 1
*/
public static String getIntroductionText(Session session) {
Long introductionText = null;
Object introductionTextObject = session.getAttribute(SessionAttribute.ANNOUNCED_INTRODUCTION);
if (introductionTextObject instanceof Integer) {
// This happens if it's never been set before - ignore it and use default 0 value
introductionText = 0L;
} else if (introductionTextObject instanceof Long) {
introductionText = (Long) introductionTextObject;
}

if (introductionText == null || introductionText == 0L) {
// We haven't told the user about general OBA features yet - update the session and return the text
session.setAttribute(SessionAttribute.ANNOUNCED_INTRODUCTION, 1L);
return "Great. I am ready to tell you about the next bus. You can always ask me for arrival times " +
"by saying 'open One Bus Away', and filter routes for your currently selected stop by saying 'filter routes'. " +
HELP_REAL_TIME_VS_STATIC_TEXT +
"You can learn more about other features by asking me for help. ";
} else {
return "";
}
}

/**
* Returns the text that should be read to the user if this is the first time they've used v1.1.0 to tell them about new
* features, or an empty string if they've already heard this info.
* <p>
* After the call to this method, session attribute ANNOUNCED_FEATURES_V1_1_0 will be 1.
*
* @param session if session attribute ANNOUNCED_FEATURES_V1_1_0 is 0 will return feature description for v1.1.0, if is 1 will return empty string
* @return v1.1.0 feature text if session attribute ANNOUNCED_FEATURES_V1_1_0 is 0, or empty string if is 1
*/
public static String getAnnounceFeaturev1_1_0Text(Session session) {
Long announcedFeaturesv1_1_0 = null;
Object announcedFeaturesv1_1_0Object = session.getAttribute(SessionAttribute.ANNOUNCED_FEATURES_V1_1_0);
if (announcedFeaturesv1_1_0Object instanceof Integer) {
// This happens if it's never been set before - ignore it and use default 0 value
announcedFeaturesv1_1_0 = 0L;
} else if (announcedFeaturesv1_1_0Object instanceof Long) {
announcedFeaturesv1_1_0 = (Long) announcedFeaturesv1_1_0Object;
}

if (announcedFeaturesv1_1_0 == null || announcedFeaturesv1_1_0 == 0L) {
// We haven't told the user about new v1.1.0 features yet - update the session and return the text
session.setAttribute(SessionAttribute.ANNOUNCED_FEATURES_V1_1_0, 1L);
return "Guess what! I learned some new tricks at transportation camp over spring break. If you want me to " +
"filter routes for your currently selected stop, say 'filter routes'. " +
HELP_CLOCK_TIME_TEXT +
"Right now, ";
} else {
return "";
}
}
}
27 changes: 17 additions & 10 deletions src/main/java/org/onebusaway/alexa/util/StorageUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -91,22 +91,23 @@ public static SpeechletResponse finishOnboard(Session session, String cityName,
// This code path is current used for the SetCityIntent if this isn't the users first time using the skill
// And, we can't store HashMaps in sessions (they get converted to ArrayLists by Alexa)
// So, try to get route filters from persisted data in case the user has previously set them
HashSet routesToFilter = SpeechUtil.getRoutesToFilter(obaDao, session);
HashSet<String> routesToFilter = SpeechUtil.getRoutesToFilter(obaDao, session);

String arrivalInfoText = SpeechUtil.getArrivalText(response.getArrivalInfo(), ARRIVALS_SCAN_MINS,
response.getCurrentTime(), speakClockTime, timeZone, routesToFilter);

log.info("Full arrival text output: " + arrivalInfoText);
String outText = String.format("Ok, your stop number is %s in the %s region. " +
"Great. I am ready to tell you about the next bus. You can always ask me for arrival times " +
"by saying 'open One Bus Away', and filter routes for your currently selected stop by saying 'filter routes'. " +
SpeechUtil.getRealtimeVsStaticText() +
"You can learn more about other features by asking me for help. " +
"Right now, %s",
stopCode, region.getName(), arrivalInfoText);

// Build the full text response to the user
StringBuilder builder = new StringBuilder();
builder.append(String.format("Ok, your stop number is %s in the %s region. ", stopCode, region.getName()));
builder.append(SpeechUtil.getIntroductionText(session));
builder.append(String.format("Right now, %s", arrivalInfoText));
String outText = builder.toString();

createOrUpdateUser(session, cityName, stopId, region.getId(), region.getName(), region.getObaBaseUrl(), outText,
System.currentTimeMillis(), speakClockTime, timeZone, obaDao);
System.currentTimeMillis(), speakClockTime, timeZone, 1L,
1L, obaDao);

PlainTextOutputSpeech out = new PlainTextOutputSpeech();
out.setText(outText);
Expand All @@ -126,11 +127,13 @@ public static SpeechletResponse finishOnboard(Session session, String cityName,
* @param lastAccessTime
* @param speakClockTime
* @param timeZone
* @param announcedIntroduction
* @param announcedFeaturesv1_1_0
* @param obaDao
*/
public static void createOrUpdateUser(Session session, String cityName, String stopId, long regionId, String regionName,
String regionObaBaseUrl, String previousResponse, long lastAccessTime,
long speakClockTime, TimeZone timeZone, ObaDao obaDao) {
long speakClockTime, TimeZone timeZone, long announcedIntroduction, long announcedFeaturesv1_1_0, ObaDao obaDao) {
Optional<ObaUserDataItem> optUserData = obaDao.getUserData(session);
if (optUserData.isPresent()) {
ObaUserDataItem userData = optUserData.get();
Expand All @@ -143,6 +146,8 @@ public static void createOrUpdateUser(Session session, String cityName, String s
userData.setLastAccessTime(lastAccessTime);
userData.setSpeakClockTime(speakClockTime);
userData.setTimeZone(timeZone.getID());
userData.setAnnouncedIntroduction(announcedIntroduction);
userData.setAnnouncedFeaturesv1_1_0(announcedFeaturesv1_1_0);
obaDao.saveUserData(userData);
} else {
ObaUserDataItem userData = new ObaUserDataItem(
Expand All @@ -157,6 +162,8 @@ public static void createOrUpdateUser(Session session, String cityName, String s
speakClockTime,
timeZone.getID(),
new HashMap<>(),
announcedIntroduction,
announcedFeaturesv1_1_0,
null
);
obaDao.saveUserData(userData);
Expand Down
2 changes: 1 addition & 1 deletion src/test/java/config/UnitTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public AnonSpeechlet anonSpeechlet() {

@Bean
public ObaUserDataItem testUserData() {
return new ObaUserDataItem("test-user-id", "Seattle", "test-stop-id", 1, "Puget Sound", "http://api.pugetsound.onebusaway.org/", "", System.currentTimeMillis(), 0, "America/Los_Angeles", new HashMap<>(), null);
return new ObaUserDataItem("test-user-id", "Seattle", "test-stop-id", 1, "Puget Sound", "http://api.pugetsound.onebusaway.org/", "", System.currentTimeMillis(), 0, "America/Los_Angeles", new HashMap<>(), 0, 0, null);
}

@Bean
Expand Down
Loading

0 comments on commit a81b428

Please sign in to comment.