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:

Face Login Signup

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:

Twitter friends

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:

Tweets

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:

Twitter Visualisation

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.