DAT602 – Face Login – Twitter Visualisations
I have developed a functioning face login system based on Microsoft’s Azure Face API, Node.js and Express. After users have logged in using the face recognition system, I would like them to be able to access some personalised content from their social media platforms.
The first platform I looked at was Twitter and I made use of Tolga Tezel’s Twit package (Tezel, no date).
The first step to displaying some information from Twitter was to allow user’s to add their Twitter handle as part of the signup information they provide and store this in the Mongo database along with their username and password:

Getting Twitter Friends
Getting the friends of a Twitter user is a two-step process. Firstly, the IDs of the friends of the user are retrieved via the Twitter API using the friends/ids API call (Twitter, no date):
// Get the user IDs of 100 friends function getFriends(screen_name, next) { twitter.get('friends/ids', { screen_name: screen_name, count: 100 }, function(err, data) { // If we have the IDs, we can look up user information if (!err && data) { lookupUsers(data.ids, next); } // Otherwise, return with error else { next(err); } }); }
The list of friend IDs are then used to lookup the details for each user using the Twitter API users/lookup API call:
// Get user information for the array of user IDs provided function lookupUsers(user_ids, next) { twitter.get('users/lookup', { user_id: user_ids.join() }, function(err, data) { // If we have user information, we can pass it along to render if (!err && data) { // We'll fill this array with the friend data you need var friends_array = new Array(); for (index in data) { // Get your friend's join date and do some leading zero magic var date = new Date(data[index].created_at); var date_str = date.getFullYear() + '-' + ('0' + (date.getMonth()+1)).slice(-2) + '-' + ('0' + date.getDate()).slice(-2); // Push the info to an array friends_array.push({ 'name' : data[index].name, 'screen_name' : data[index].screen_name, 'created_at' : date_str, 'profile_image' : data[index].profile_image_url, 'link_color' : data[index].profile_link_color }); } // The callback function defined in the getFriends call next(err, friends_array); } // Otherwise, return with error else { next(err); } }); }
With these two functions in place, a new route can be created to call these functions and render a view with the logged-in user’s Twitter friends:
// twitter friends app.get('/twitter', isLoggedIn, function(req, res){ // get twitter screen name of currently logged-in user var user = req.user; var screen_name = user.local.twitter; // get user's friend information getFriends(screen_name, function(err, data) { // Render the page with our Twitter data if (!err && data) { res.render('twitter.hbs', { user: req.user, friends: data }); } // Otherwise, render an error page else { res.send(err.message); } }); });
The code for the Handlebars view that renders the Twitter friend information:
<h1><i class="fab fa-twitter mb-4"></i> Twitter Friends</h1> {{#grouped_each 3 friends}} <div class="row"> {{#each this }} <img class="align-self-start mr-3" src="{{this.profile_image}}" style="border-bottom: 3px solid #{{this.link_color}}"> <div class="media-body"> <a href="twitter/{{this.screen_name}}"><p><strong>@{{this.screen_name}}</strong></a></br> {{this.name}}</p> </div> {{/each}} </div> {{/grouped_each}}
Which renders as:

Note: In order to render the column layout, I registered a Handlbars helper function with the main server.js
file:
// handlebars helper (columns) hbs.registerHelper('grouped_each', function(every, context, options) { var out = "", subcontext = [], i; if (context && context.length > 0) { for (i = 0; i < context.length; i++) { if (i > 0 && i % every === 0) { out += options.fn(subcontext); subcontext = []; } subcontext.push(context[i]); } out += options.fn(subcontext); } return out; });
Getting Tweets that mention Twitter Friends
The statuses/user_timeline Twitter API call “returns a collection of the most recent Tweets posted by the user indicated by the screen_name or user_id parameters.”
I have access to the screen_name from the previous code, so this can be passed to the Twiter statuses/user_timeline Twitter API call via a querystring parameter as the code below demonstrates:
// tweets app.get('/twitter/:screen_name', isLoggedIn, function(req, res) { // get friend's tweets twitter.get('statuses/user_timeline', { screen_name: req.params.screen_name, count: 10 }, function(err, data) { // Render the page with our Twitter data if (!err && data) { console.log(data); res.render('tweets.hbs', { user: req.user, screen_name: req.params.screen_name, profile_link_color: data[0].user.profile_link_color, profile_image_url: data[0].user.profile_image_url, profile_banner_url: data[0].user.profile_banner_url, tweets: data }); } // Otherwise, render an error page else { res.send(err.message); } }); });
The returned tweet data is passed to a view and rendered using the following code:
<style> #banner { background-image:url({{profile_banner_url}}); width: 1024px; height:300px; background-repeat:no-repeat; background-size:cover; background-position: center; } </style> <h1><i class="fab fa-twitter mb-4"></i> Tweets</h1> <div class="row"> <div id="banner"></div> </div> <div class="row mt-1 pb-1 pt-1" style="border-bottom: 3px solid #{{profile_link_color}}"> <img class="align-self-start mr-3" src="{{profile_image_url}}"> <div class="media-body"> <h2 class="mt-1">@{{screen_name}}</h2> </div> </div> {{#each tweets}} <div class="row mt-2 mb-2 pb-2 pt-2" style="border-bottom: 1px solid silver;"> <i class="fab fa-twitter mr-2"></i> <div class="media-body"> {{this.text}} </div> </div> {{/each}}
Which renders as:

Visualisation Using Vis.js
I also decided to create a more creative Twitter visualisation and decided to use Vis.js.
Vis.js is a”dynamic, browser based visualization library. The library is designed to be easy to use, to handle large amounts of dynamic data, and to enable manipulation of and interaction with the data. The library consists of the components DataSet, Timeline, Network, Graph2d and Graph3d.”
(Vis.js, 2017)
I used the timeline component of vis.js to develop a visualisation displaying a timeline of when the logged-in user’s friends joined Twitter.
The code in routes.js
is the same as that used above to get a user’s Twitter friends:
// twitter visualisation test app.get('/twitter-vis', isLoggedIn, function(req, res){ // get twitter screen name of currently logged-in user var user = req.user; var screen_name = user.local.twitter; // get user's friend information getFriends(screen_name, function(err, data) { // Render the page with our Twitter data if (!err && data) { res.render('twitter.ejs', { user: req.user, friends: data }); } // Otherwise, render an error page else { res.send(err.message); } }); });
The data returned by the getFriends
function is passed to a view:
<!doctype html> <html lang="en"> <head> <!-- Required meta tags --> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <!-- Bootstrap CSS --> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"> <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.5.0/css/all.css"> <script src="http://visjs.org/dist/vis.js"></script> <link href="http://visjs.org/dist/vis.css" rel="stylesheet" type="text/css" /> <style> .starter-template { padding: 5rem 1.5rem; } .user-image { float: left; } .user-image img { width: 30px; height: 30px; } .user-info { float: right; text-align: left; margin-left: 5px; } </style> <body> <header> <nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top"> <a class="navbar-brand" href="/"><i class="far fa-grin-wink"></i> Face Login</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault" aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarsExampleDefault"> <ul class="navbar-nav mr-auto"> <% if (!user) { %> <li class="nav-item"> <a class="nav-link" href="/login">Login</a> </li> <li class="nav-item"> <a class="nav-link" href="/signup">Signup</a> </li> <% } %> <% if (user) { %> <li class="nav-item"> <a class="nav-link" href="/profile">Profile</a> </li> <li class="nav-item"> <a class="nav-link" href="/logout">Logout</a> </li> <% } %> </ul> </div> </nav> </header> <main role="main" class="container"> <div class="starter-template"> <h2><i class="fab fa-twitter"></i> Twitter Friends</h2> <div id="visualization"></div> <script> // Create the dataset by looping over the friends array var items = new vis.DataSet([ <% for(var i=0; i<friends.length; i++) {%> { id: <%= i %>, start: '<%= friends[i].created_at %>', content: '<div class="user-image"><img src="<%= friends[i].profile_image %>" style="border-bottom: 3px solid #<%= friends[i].link_color %>;" /></div><div class="user-info"><b><%= friends[i].name %></b><br />@<%= friends[i].screen_name %></div>' }<% if (i != friends.length-1) {%>,<%}%> <% } %> ]); // Reference to the visualization container var container = document.getElementById('visualization') // Get today's date for max range var date = new Date(); var options = { height: '500px', min: new Date(2006, 0, 21), // lower limit of visible range max: new Date(date.getFullYear(), date.getMonth()+2, date.getDate()) // upper limit of visible range }; // Create the timeline! var timeline = new vis.Timeline(container, items, options); </script> </div> </main> <footer class="footer"> <div class="container"> <span class="text-muted"><i class="far fa-grin-wink"></i> DAT602 - Face Login</span> </div> </footer> <!-- Optional JavaScript --> <!-- jQuery first, then Popper.js, then Bootstrap JS --> <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"></script> </body> </html>
The Javascript at the end of this view creates a vis.js dataset by looping through the friends data which is, in turn, used to create the timeline, which renders as:

Bibliography
Twitter (no date) Follow, search, and get users. Available at: https://developer.twitter.com/en/docs/accounts-and-users/follow-search-get-users/api-reference/get-friends-ids (Accessed: 23 November 2018).
Vis.js (2017) Vis.js. Available at http://visjs.org/ (Accessed: 23 November 2018).
Leave a Reply