HustleTime Development: Final Destination

Something that folks using HustleTime have asked for is a way to see the destination of each train that’s arriving.

The MTA provides this data on their station arrival boards, they often read something like this:

1. (G) Church Ave          4min
2. (F) Coney Island        8min
3. (G) Court Square        9min
4. (F) Jamaica 179 Street  10min
5. (F) Kings Hwy           12min

This is actually a useful bit of information, and it brings me to question how I organize HustleTime’s ArrivalNotifications.

There are a couple of issues here:
1) Even though the MTA gives out information in terms of North and South, human beings don’t think that way. Straphangers generally think in terms of Uptown and Downtown, or even Manhattan bound, Queens bound, etc.
2) I’m limited (figuratively) by real estate. I don’t have a whole lot of room in this format to give users more information.

So what I’ve decided I’m going to do is organize this as option. I’ll let users choose if they want the “compact” (i.e. current) version of the Arrival Notification or let them see a more detailed “to be named” notification, and I’m currently debating between 2 layouts:

There are pluses and minuses to both layouts… What I like about the top one is that the timetable is intuitive, you’re seeing the departures in order, regardless of their destination. It is possible in this view, however to show only incoming uptown trains, or in very busy stations (like West4th or Atlantic avenue’s Pacific street platform) to not see particular trains at all, even when ignoring.

The bottom ArrivalNotification separates out Northbound and Southbound departures, showing the next 2 in either direction, so addressing the issues I have with the top notification.

It’s also worth noting that both of these options will infringe on the map space, so I may need to work out how much less of the map will remain visible with the larger view enabled.

Regardless of the Ui I choose to implement, I need to set up some things on the back end to allow for this information to passed to the front end.

GTFS

GTFS is particularly inextricable to me, and according to GTFS documentation trips should have a key called stop_headsign that “appears on signage identifying the trip’s destination to riders.”

But this is the MTA, and there’s no such key in the feeds, so I need to figure out how the MTA actually gets that information on their signs.

When a feed is decoded there’s an entity called :stop_time_update that contains what’s essentially an array of all the information for a particular train along its route.

if I take the last element of this array and assign it to a variable, I can preserve this station as I iterate through each element in that array, where I pull out all the actual departure data:

last_stop = entity.trip_update.stop_time_update.last[:stop_id]

.
.
.
  for entity in lineFeed.entity do
    last_stop = entity.trip_update.stop_time_update.last[:stop_id]
    for upd in entity.trip_update.stop_time_update do
      if upd.departure
        if upd.departure.time && upd.departure.time > (timeNow + 5)
          oneArrival = {train: entity.trip_update.to_hash[:trip][:route_id], time: upd.departure.time, station: upd.stop_id, destination: last_stop}
          data_array.push(oneArrival)
        end
      end
.
.
.

Basically what I’m doing here is hanging on to the last stop of each :trip_update as I iterate through and add it into the arrivals as a key called destination

Now with the Redis data containing the final stops for each arrival, I can do the business of sending that to the front end and render SOMETHING in the app.

I’ll get to that later.

You can get HustleTime on both Android and iPhone free!

HustleTime Development: A tiny improvement

Sometimes I’m underground and there isn’t an arrivals board in sight, or it’s far down the other end of the platform or whatever the case, and I just can’t see it.

I open HustleTime, and it hiccups – I don’t have a strong connection, and it’s still retrieving data from the HustleTime API.

This is an unavoidable condition of urban subterranean travel: you’ll sometimes have a weak or no signal, and you really can’t do anything about it.

What I’ve decided to do is to indicate to the user that they’re device is still communicating by an animated icon in the top left of the screen.

searching…

The idea is simple: While the app is inside the fetch promise of retrieving data, this little alert turns on temporarily, and once it’s done retrieving data, it turns off the alert.

this.setState({searchingIndicator: true})
      fetch(<fetch stuff>)
        .then(resp => resp.json())
        .then(json => this.props.dispatchedFetchStations(json))
        .then(resp => this.setState({searchingIndicator: false}))

.
.
.

  showAlerts = () => {
    if (this.state.searchingIndicator == true){
      if (Object.values(this.state.shown).includes(false)) {
        return <View style={{position: 'absolute', top: 65, left: 15}}>
          <Image source={wifiRed} style={{width: 25, height: 25}} />
          <Image source={ignoreAlert} style={{width: 25, height: 25 }} />
        </View>
      } else {
        return <View style={{position: 'absolute', top: 65, left: 15}}>
          <Image source={wifiRed} style={{width: 25, height: 25}} />
        </View>
      }
    } else {
      if (Object.values(this.state.shown).includes(false)) {
        return <View style={{position: 'absolute', top: 65, left: 15}}>
          <Image source={ignoreAlert} style={{width: 25, height: 25 }} />
        </View>
      } else {
        null
      }
    }
  }

Now I have a way to show various indicators/alerts to the user as needed, and it’s a very simple process.

Look out for this little update in the next HT release.

Download HustleTime on android or iPhone free!

HustleTime Development: Status page (api)

I created the HustleTime back end with a rails api, and when I actually deployed it to Digital Ocean, I left some of the wrong settings in place and I ended up with this:

What the HELL is this?

That was what was on hustletime.paulbomba.com if you happened to look there… it’s a long list of pregenerated routes that don’t really lead anywhere. They are routes that are vestiges of prior versions of code, and I never went in and cleaned things up.

I also thought it might be helpful to have a view at that address. I wanted to have something there in case someone visited the URL.

I decided that I’d put a status page up, where you can see the current seed status of HustleTime’s data.

In order to do that, I had to do a couple of things.

a- create a view
b- put the assets together
c- clean up the routes
d- create a route for the new page
e-figure out how to collect and store latest seed information

One of the things about rails api is that each controller has inheritance that is namespaced for the api

class Api::V1::SomeController < ApplicationController

So when I am creating a controller for the view that I want, I need to set up the class a little differently and inherit direction from ActionController

class StatusPagesController < ActionController::Base

Now, I’ll create a folder in views called status_pages and drop an .erb file in there.

Finally, I’ll clean up my routes and get rid of all those unnecessary old routes and put a route to the status page in the correct namespace

Rails.application.routes.draw do
 root 'status_pages#index'
 resources :status_pages
.
.
.

easy.

Now that we have the status page view configured, I need to figure out what I’m actually putting there. I basically want it to tell me when any of the various feeds have reseeded.

I experimented with postgres, but I decided Redis is the way to go for this, so what I’ll do is create a number of keys in Redis for each feed, and when that feed gets updated, I’ll assign that timestamp to that key in redis, and when I render the page, I’ll check that particular key for the timestamp.

I’ll need to create a helpermethod in the controller so I can call it via erb in the view

app/controllers/status_pages_controller

class StatusPagesController < ActionController::Base

def get_last_sync(route)
  StatusPage.check_redis(route)
end

helper_method :get_last_sync
end

and I’ll need to define the class function in the model:

class StatusPage

require_relative './data_helper.rb'
require 'date'

  def self.check_redis(route)
    feed_time = DataHelper.get_last_time(route).to_i
    time_now = DateTime.now.strftime('%s').to_i
    difference = (time_now - feed_time)/60
    difference
  end
end

DataHelper.rb is where I put all my Redis methods, so I’ll create a method there to get the latest time from a feed.

/DataHelper.rb	

def self.get_last_time(route)
	time = JSON.parse(@@redis.get(route))
	time
end

Ok, so now I can get the last seed time from a particular route. All I need is a way to store that in redis. So let me create a method that will put that key into Redis for me.

def self.store_update_time(route, time)
	@@redis.set(route, time)
end

Now, whenever we reseed, if that particular feed is stored, I’ll store the updated time.
before:

require 'date'
time_now = DateTime.now.strftime('%s').to_i

.
.
.

begin
  feed123456S = Transit_realtime::FeedMessage.decode(data123456S)
rescue Protobuf::InvalidWireType
  errors = errors + ' skipped: 123456S (wireType)'
end

and after…

begin
  feed123456S = Transit_realtime::FeedMessage.decode(data123456S)
  DataHelper.update_time('data123456S', time_now) // pass feed and time
rescue Protobuf::InvalidWireType
  errors = errors + ' skipped: 123456S (wireType)'
end

The seeding helper file is a little complicated, so I’ll spare you the minutiae, but what’s happening here is that I download a data feed from the MTA api, and it needs to be decoded with protobuf, and something that happens quite often is the data is somehow corrupted and throws a wireType error.
So, if the decoding is successful, and we have data, I will tell DataHelper.rb that we’ve reseeded successfully.

you can learn more about my adventures with wireType errors here.

Now that all this is set up, I can go ahead and actually create the view. It’s super basic at this point, but that’s what I’m going for. I need to continue to experiment and learn as I go.

It’s not pretty, but it gets the job done- much like the rest of HustleTime.

Remember…
You can get HustleTime on both Android and iPhone free!