Saturday, 7 October 2017

Deploying a Node JS application using IBM Bluemix UI

The big moment. After several weeks I had put together a dashboard for myself to include;

  • My Local Teams last two games, next game and two games after
  • Next trains from Waterloo to Winchester
  • Journey time from Hursley to Winchester
  • Last five of my home timeline tweets
  • Share prices
I also added, off the entry page;
  • Cinema time for two of my nearby cinemas
  • An old JS application I created that lets you find out how many cities you have visited.
It did what I wanted it to do, but I had to have my laptop running to view it.
My next idea was to be able to show it online whenever I wanted so I could access it as and when I liked. At first I considered using a web hosting service like 1&1 (who I have another web address with) but decided I would instead make use of the IBM Bluemix service.

IBM Bluemix has a node SDK which has a plan of £0.0424 GBP/GB-Hour.

There are a few developerWorks articles that describe deploying a node js application:

An alternative method to the command line as shown in the three examples above, is to use the bluemix User Interface. Which is what I opted for this time, having created several application using the CLI before.

 1. Create a "Cloud Foundry Apps" -> SDK for Node.js as seen in the image above.
 2. Give your application a name, select the hosting country and select "create"

 3. The application will build and then start. You need to wait for it to show as "running" at which point it will be available. In my example, this is true for the url: https://mytest012345.mybluemix.net
 4. Within the applications bluemix "Application Details" page we can go to "Overview" and "enable" continuous delivery.
 5. This creates a toolchain which include a git repo and an eclipse browser.
 6. Select the eclipse tool and we can browse the files in the editor
standard SDK for node JS project
 7. Delete the public folder
 8. Zip your own public folder as well as your dependencies such as your node modules.
 9. Right click on your project folder and select "import -> File or Zip archive" and select the zip file you just created.
 10. The zip will be unzipped and the files and directories unpackaged into the project.
 11. Replace the "package.json" with your own and replace the README.md.
 12. Update the app.js file with your path routes, variables and dependencies
//ADD YOUR VARIABLES
//var abc = require("abc");
/*eslint-env node*/
//------------------------------------------------------------------------------
// node.js starter application for Bluemix
//------------------------------------------------------------------------------
// This application uses express as its web server
// for more info, see: http://expressjs.com
var express = require('express');
// cfenv provides access to your Cloud Foundry environment
// for more info, see: https://www.npmjs.com/package/cfenv
var cfenv = require('cfenv');
// create a new express server
var app = express();
// serve the files out of ./public as our main files
app.use(express.static(__dirname + '/public'));
/*app.get(/route1, function(req, res){

}); REPEAT WITH ALL ROUTES*/
// get the app environment from Cloud Foundry
var appEnv = cfenv.getAppEnv();
// start server on the specified port and binding host
app.listen(appEnv.port, '0.0.0.0', function() {
  // print a message when the server starts listening
  console.log("server starting on " + appEnv.url);
});
 13. We now need to commit the changes we have made. On the left panel, select the "git" option and commit the changes we have just made.
The second button is a link to commit changes to our git repository
 14. We can now redeploy the application from the eclipse toolkit using the play button.
The green circle shows the application is running, if red it would indicate an error. Next to this is a play button which deploys the app, the stop button stops the application, the third button opens the application in a new tab, second from right opens the logs and the final button opens the bluemix "application overview" page.

The application should now be running again.
*I would recommend for ease and speed to follow the command line functionality described in the links presented earlier*

So... I now have my application available.

Live Dashboard
For an added extra I opened the application on my phone:
In iOS we can press the box with an arrow coming out of it and we can save the page to our home screen.
This makes it available on the home screen making for quick access.

So, the dashboard is up, I can access it and it's being hosted on Bluemix. All is good with the world and another project is complete.

Have some questions? Think you can suggest some improvements? Feel free to email me here.
Alternatively leave a comment below. 

Quote API with node JS

As Winston Churchill once said: "It is a good thing for an uneducated man to read books of quotations."

Having almost completed all the content I want for my dashboard I decided to add a little more to the title which currently just says "Gally Home Dashboard". I remember back at my old Secondary School - de Ferrers Specialist Technology College we used to have an intranet that gave a "thought of the day." So I started looking at how I can add quotes to my dashboard.
Boring Title Page
Option 1: Create a JSON file that contains quotations I like.
var quotes= [{
 "name": "Winston Churchill",
 "quote": "It is a good thing for an uneducated man to read books of quotations."
},
...];
We can then access these quotes by accessing a random part of the array.
getQuote(randNo){
  var thisQuote = JSON.parse(quotes[randNo]);
  var returnString = thisQuote.name + ": " + thisQuote.quote;
  return returnString;
}
There are two downsides to this. 1. I'd have to populate a huge JSON 2. It would be full of quotes I already know and would leave me in a "quote bubble". Not very educational to the uneducated man.

Option 2: Don't reinvent the wheel. Use an API.
There are several APIs we can use;
 * Quotes on Design - This allows us to decide a filter on the order and the number of results.
JS:
$.getJSON("https://quotesondesign.com/wp-json/posts?filter[orderby]=rand&filter[posts_per_page]=1&callback=", function(a) {
  $("body").append(a[0].content + "<p>&mdash; " + a[0].title + "</p>")
});
Example from the webpage
Curl:
curl -X GET "https://quotesondesign.com/wp-json/posts?filter=rand&filter=1"
* Forismatic - Similar to quotes on design but also includes "expressions". Neither appears to have rate limits or need an authentication code to be used!
Results can come in
Curl:
curl -X GET "https://api.forismatic.com/api/1.0/?method=getQuote&key=&format=json&lang=en
The language can be set to english or russian (?)
key can be set to blank which gives a random quote.
response format comes in: json, jsonp, xml, html and plain text.

Output for english, random key and different formats:
html: 
<blockquote><strong><cite>We are what we repeatedly do. Excellence, then, is not an act, but a habit. </cite></strong><br/><small>Aristotle <address></address></small><blockquote>
json:
{"quoteText":"We know the truth, not only by the reason, but by the heart. ", "quoteAuthor":"Blaise Pascal", "senderName":"", "senderLink":"", "quoteLink":"http://forismatic.com/en/b917914ba7/"}
text:
You do not become good by trying to be good, but by finding the goodness that is already within you. (Eckhart Tolle) 
The second comes with a bit more freedom. In the end I made it match how I was doing requests throughout the rest of the dashboard (please see my other posts) and then self formatting to how I wanted it to look on the client side.

My Basic Flow:
 - Use request npm to match the curl command above (using json)
 - Send the json response to the client
 - Parse the json and format the output in the client JS
document.getElementById("randomQuote").innerHTML = quote.quoteAuthor + ": " + quote.quoteText; 
 - Some responses had no author, so we attribute them to "anon"
 if (quote.quoteAuthor === "") {
   quote.quoteAuthor = "Anon";
}

To what I believe are great results!


Any thoughts? Feel free to comment below. 

Sunday, 1 October 2017

Cinema Time API

Continuing on from the earlier success of my dashboard API's to date: Twitter, National Rail Enquiries as well as a google finance integration and a home made Burton Albion score API. Now, on request from Louise I have looked to display all the films and film times for the local cinema.

I did a quick google of "Cinema API" and came up with something called "moviesapi" which you can find here: http://moviesapi.herokuapp.com which is a screen scraping API for https://www.findanyfilm.com which is a website that lets film fans find how to download, watch, buy and rent films - all above the board may I add.

The API itself is a non authenticated, JSON returning API that appears to have no rate limit (or so far as I can tell). It's quite a simple idea, there are two GET API's, find a nearby cinema and list films from a specific cinema using the id we can get from the nearby cinema API. 

In curl we can do "curl -X GET "http://moviesapi.herokuapp.com/cinemas/find/SO23"" (my nearby area)
which returns the following data (I've cut it down for readability):
[{"venue_id":"7144","name":"VUE Eastleigh, Eastleigh","address":"VUE Eastleigh, Swan Centre, Wells Place, Eastleigh, Eastleigh, SO50 5SF","url":"https://www.myvue.com/cinema/eastleigh?SC_CMP=AFF_indtrust","distance":""}]
We can then use this "venue_id" to get the selected cinemas film list for all times on the same day. The same day part is a significant restriction, but works for a dashboard that only wants to show this reduced information.

In curl we can do "curl -X GET "http://moviesapi.herokuapp.com/cinemas/7144/showings"" which returns the following film data (I've cut it down again for readability):
[{"title":"The Emoji Movie","link":"","time":["10:00","12:15","13:20","15:10"]},{"title":"The Nut Job 2: Nutty By Nature","link":"","time":["10:00","11:45"]}]
For the purpose of displaying the full information for the selected cinema I have created a server side http request using the request node module (npm). My code looks like a getFilmTimes function which is itself a promise, I create the request options and which has a url and a method, send the the request and then resolve the parsed JSON body.
function getFilmTimes(venueId) {
return new Promise(function (resolve, reject) {
var postOptions = {
url: 'http://moviesapi.herokuapp.com/cinemas/' + venueId + '/showings',
method: 'GET'
};
request(postOptions, function cb(err, res, body) {
if (err) {
log.dashErr(JSON.stringify(err))
reject();
}
if (res.statusCode === 200) {
resolve(JSON.parse(body));
}
});
});
}
I want to get all the information as a single string (html formatted) so that I can push it to the browser and display on new lines each film and all the times associated to it.

To do this, I want to cycle through the results and add them to a string variable using the "async" node module (npm), it will also mean cycling the film times to add them to the end of the derived strings. See the code below:
var films = JSON.parse(body);
var sendFilms = "";
async.eachSeries(films, function cycleThroughFilms(currentFilm, filmCallback) {
var filmTimes = currentFilm.time;
sendFilms = sendFilms + currentFilm.title + ": ";
async.eachSeries(filmTimes, function cycleThroughTimes(currentTime, currentTimeCallback) {
sendFilms = sendFilms + currentTime + " ";
currentTimeCallback();
}, function afterTimesDone() {
sendFilms = sendFilms + "<br>";
});
filmCallback();
}, function afterFilmsProcessed(err) {
resolve(sendFilms);
});
The above code takes an array "films" and cycles through each set of results. We add first the film title to our global "sendFilms" variable before cycling the times array within the film array. This cycles "filmTimes" which is the current films times array and adds the current time to the global variable. When we have cycled through the film times we add a "<br>" html break line tag and then continue to the next film using callbacks available.

Upon completion of all the films within the array I resolve the promise and send the string on - eventually to the browser.

This worked well but was displaying film times that had already passed so to fix this we can add a little function to get the current time and compare the times as we process them to see if they have already started.
...
async.eachSeries(filmTimes, function cycleThroughTimes(currentTime, currentTimeCallback) {
var timenow = new Date().toISOString().substr(11, 5);
if (currentTime > timenow) {
sendFilms = sendFilms + currentTime + " ";
}
currentTimeCallback();
}, function afterTimesDone() {
...
With that we're all done:
The last thought is that we could remove the films that have no more showings for the day. But I personally like to see what might be available the next day throughout the day. I'll see how it feels.

Any questions? Anything you would change/improve upon. Give me an email on aiden.g@live.co.uk or leave a comment.