Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Major API refactor and additional features/fixes #9

Merged
merged 10 commits into from
Jan 29, 2018

Conversation

andywaplinger
Copy link
Collaborator

@andywaplinger andywaplinger commented Jan 23, 2018

Fixes #7, fixes #3.

Major Changes

  • Updated API to MBTA API V3; V2 will be deprecated in April 2018
  • Split API calls across multiple functions with deferred callbacks to get all required data
    • Requires the use of jQuery 3 for deferred callbacks
  • Modified names of some subway stops to better reflect what they're typically called; users using affected stops will need to update the name in their config
  • Added commuter rail stops and bus stops
  • Added ability to switch been predictions and schedules using predictedTimes boolean
  • Added ability to display departure time using showDepartTime boolean
    • Corrected the use of departure time vs arrival time
  • Added colorIcons boolean so vehicle icons reflect their line color

Minor Changes

  • Trips are sorted by ETA, not departure time
  • Trips filtered by ETA when maxTime is set
  • Added bus route number to trip description
  • Fixed alerts and enabled marquee scrolling
  • Added dictionary for trip directions (more info below)
  • Added "mins" or "min" to ETA time so it's more human-readable
  • Accounted for trips with no specified arrival or departure times
  • Accounted for no alerts at a stop if alerts are turned on

Known Issues

  • When there are multiple alerts on a trip, only the first alert is shown
  • Direction filtering is not yet functional, but some groundwork has been laid

@edward-shen
Copy link
Owner

Thanks so much for the PR!

I'll do a proper review soon, but for now some quick notes:

Resolves #7. Does this resolve #4, #3, and #1? Did I forget anything?

I took a quick review at the changes with the API V3: looks like we can remove the updateInterval limit since each key can handle 1k request/minute. This is more of a note for myself, but you're free to make a change that limits it to 1 (split-second updating is a little unnecessary in all use cases).

I'm still quite new to Boston; what are some colloquial examples of subway stops? And can you tell me which ones were changed? (For documentation purposes)

Can you also update the readme for getting an API v3 key? I think we just need to change the old link to https://api-v3.mbta.com/register

@andywaplinger
Copy link
Collaborator Author

You're welcome!

I'd say it definitely solves #3 and #7, and solves or, at least, helps #4. I'm doing more testing now and I wouldn't say that #1 is fixed. The alerts work better, but still only work well in full-width positions. I'll have to dig further into that.

I just tested setting the limit to 1s and quickly received 429 Too Many Requests. I'd recommend keeping updateInterval, but I can change it to 10s default.

Most of the names I changed had "Station" at the end when that's typically dropped, i.e. "Riverside Station" is now "Riverside", or "Boylston Street Station" is just "Boylston". Here's a sheet with the updated stations - updated_stops.xlsx

I'll also update the URL in the readme.

@edward-shen
Copy link
Owner

It's default limited to 10s, so you shouldn't need to worry about that.

@edward-shen
Copy link
Owner

Made some minor changes, you might need to merge changes from master to resolve conflicts.

@andywaplinger
Copy link
Collaborator Author

andywaplinger commented Jan 25, 2018

Cool, I merged your changes. Should be good to go now.

Edit: made a commit error originally, but it should be fixed.

Copy link
Owner

@edward-shen edward-shen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you could fix and/or explain some of the notes I've made, I would greatly appreciate it. Sorry it took so long, there was a lot more code than I expected and I got caught up with work.

MMM-MBTA.js Outdated
}
if (this.config.direction.includes("Eastbound")) {
this.filterDirection.push("1");
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't a switch case be better here? We can then use fall-through to make this a lot cleaner.

Copy link
Collaborator Author

@andywaplinger andywaplinger Jan 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, just like this?

switch (this.config.direction) {
    case "Southbound":
    case "Westbound":
    case "Outbound":
        this.filterDirection.push("0");
        break;
    case "Northbound":
    case "Eastbound":
    case "Inbound":
        this.filterDirection.push("1");
        break;
}

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup!

MMM-MBTA.js Outdated
break;
default:
descCell.innerHTML += " | " + routeId;
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are SL1-4 and CT1-3? I'm assuming CT# stands for Commuter Train, but what are SL1?

Can you add some comments?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those are their public-facing route names. Aside from those, every other bus uses its routeId as it's public route name.

MMM-MBTA.js Outdated
arrTimeCell.innerHTML = "No arrival";
} else {
arrTimeCell.innerHTML = moment.unix(this.stationData[i].preArr).format("h:mm");
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think doing this is cleaner:

if (isNaN(this.stationData[i].preArr) === true) {
    arrTimeCell.innertHTML = "No arrival";
} else {
    if (config.timerFormat === 24) {
        arrTimeCell.innerHTML = moment.unix(this.stationData[i].preArr).format("H:mm");
    } else {
        arrTimeCell.innerHTML = moment.unix(this.stationData[i].preArr).format("h:mm");
    }
}

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does no arrival mean? When does a mode of transportation show this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Changed.

Sometimes arrival_time is null. If it's a bus/subway, I'm not sure why. But if it's a train, it's typically because it already arrived and is waiting to depart.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh god, we need to check for null?

If that's the case, we can check nullity just by doing the following:

if (!this.stationData[i].preArr) {
    ...
}

This is because null is a falsey value, so when you negative null, it returns true.

Thanks javascript.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, right, one of my favorite things about JS...

MMM-MBTA.js Outdated
depTimeCell.innerHTML = moment.unix(this.stationData[i].preDt).format("h:mm");
}
}
row.appendChild(depTimeCell);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sometimes departure_time also comes in null. No idea why.

MMM-MBTA.js Outdated

function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused function, please remove.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Slipped through

MMM-MBTA.js Outdated
if (!this.config.predictedTimes) {
url += "&page[limit]=25";
url += "&filter[min_time]=" + moment().format("HH:mm");
url += "&filter[max_time]=" + moment().add(3, 'h').format("HH:mm");
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please leave a comment explaining this is necessary, for documentation's sake?

Copy link
Collaborator Author

@andywaplinger andywaplinger Jan 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

Page and time limits necessary because otherwise "schedules" endpoint gets every single schedule

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I meant to clarify moment.add(3, 'h'), but that's also good to have too

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, gotcha. Does that comment cover enough ground for you? Or do you want me to another specifically about limiting the time to 3 hours from the current time?

MMM-MBTA.js Outdated
alertUrl += "?api_key=" + this.config.apikey;

return alertUrl;
},
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be a good idea to make some sort of enumeration type and merge tripURL and routeURL with this.

JS enumerations don't really exist, but there are ways to emulate them.

Regardless, I feel like making a switch statement would make reduce the amount of duplicated code.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed using a single function.

MMM-MBTA.js Outdated
preDt: "None",
preArr: "None",
preETA: "None"
});
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little uncomfortable requiring our code to have a "default" state, because that implies that our code implicitly requires our stationData requires a minimum length of one--it's a code smell, and a very smelly one at that.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there isn't one station, then getDom() skips the for loop that renders all trip data. I can include an if statement before the loop that catches when stationData.length === 0 and renders just the symbolCell and descCell, unless you have a different idea.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed - opted for the if statement.

MMM-MBTA.js Outdated
}
preDt = parseInt(moment(data.data[pred].attributes.departure_time).format("X"));
preArr = parseInt(moment(data.data[pred].attributes.arrival_time).format("X"));
preETA = moment(data.data[pred].attributes.arrival_time).diff(moment(), 'seconds') - 30; //Better safe than sorry?
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to be truthful and inaccurate, or lie and "in good faith"?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haha, why not both? There's already the disclaimer, which is good. We could also include that we've added a 30s buffer to help ensure they're on time.

MMM-MBTA.js Outdated
preDt: "None",
preArr: "None",
preETA: "None"
});
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rather check if the stationData is empty when we're rendering, rather than making an "fake" station.

It's a misrepresentation of our data definition.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

@edward-shen
Copy link
Owner

Can you make sure you're pushing to your changes to github? I can't see local git changes!

@andywaplinger
Copy link
Collaborator Author

Oh yeah, sorry, I've been making a few of these changes before pushing. I'll push what I have now.

- Change `direction` filtering to switch case
- Clean up description, arrival, departure, and alerts
- Allow users to retrieve as many schedules as they want
- Retrieve all alerts from all predictions
- Changed icon when no upcoming trips; icon is red when `colorIcons` is `true`
Copy link
Owner

@edward-shen edward-shen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just some last few style remarks!

One last thing: Can you provide me with a list of stations that show all your new features? It's for testing purposes, so I can triple check your code.

After I do that, then it should be on the home stretch!

MMM-MBTA.js Outdated

return alertUrl;
return url
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing semi-colon

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

MMM-MBTA.js Outdated
preAwayCell.innerHTML = minutes + ":" + seconds;
} else {
preAwayCell.innerHTML = seconds;
if (isNaN(this.stationData[i].routeId) === false) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing here, can just be

if (this.stationData[i].routeId)  { ... }

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean? Only bus routes use straight numbers. Everything else is alphanumeric.

I can replace it with if ($.isNumeric(this.stationData[i].routeId)) instead so it's a little cleaner and clearer. Checking whether something is not not a number is kinda dumb.

MMM-MBTA.js Outdated
var seconds = preETATime % 60;

if (this.config.showMinutesOnly) {
if (isNaN(minutes) === true) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also a null check?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. Just replaced it with if (!minutes).

MMM-MBTA.js Outdated
routeId = data.data[pred].relationships.route.data.id;
tripId = data.data[pred].relationships.trip.data.id;
alertIds = data.data[pred].relationships.alerts.data;
debugger
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extraneous debugger

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

MMM-MBTA.js Outdated
alert = alertsParse[i].data.attributes.header;
alertsArray.push(alert);
}
};
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extra semi colon

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually if you want this to look cleaner you can do a for...of loop:

for (let alert of alertsParse) {
   if (alert.data) {
        alertsArray.push(alert.data.attributes.header);
    }
}

Copy link
Owner

@edward-shen edward-shen Jan 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional ofc, but nice learning point and could help make code cleaner in the future.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm all about cleaner code. I'll change it.

@edward-shen
Copy link
Owner

Would like to thank you again for such a large contribution to the repo; in fact, it looks like after this merge, you'll have more lines of code than me! 😆

LMK when you're done with the last few changes, otherwise LGTM.

@andywaplinger
Copy link
Collaborator Author

You're very welcome! It was a really useful learning experience for me, and I think it's a great little app. I'm happy to help make it even better. 😄

Let's merge this! 👍

@andywaplinger
Copy link
Collaborator Author

For station testing, I've been using North Station (multiple subway lines and typically has one or two alerts), Central Square (typically has no alerts), South Station (buses, subways, and trains, sometimes with alerts), and random bus and commuter rail stops. Every feature should work, regardless of the stop. 😄

@edward-shen edward-shen merged commit 7cf8b99 into edward-shen:master Jan 29, 2018
@edward-shen
Copy link
Owner

edward-shen commented Jan 29, 2018

Congratulations on your first PR! 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Option to switch between predicted and scheduled time Show text when no routes are available
2 participants