Skip to main content

Flask by Example – Custom Angular Directive with D3

Let’s look at what we currently have…

Current User Interface

Start Redis in a terminal window:
$ redis-server
Then get your process worker going in another window:
$ cd flask-by-example
$ python worker.py
17:11:39 RQ worker started, version 0.4.6
17:11:39
17:11:39 *** Listening on default...
Finally, in a third window, fire up the app:
$ cd flask-by-example
$ python manage.py runserver
You should see your word counter working. Now we can add in a custom Angular Directive to display the results in a D3 chart.

Angular Directive

Start by adding the D3 library (v3) to the index.html file:
<!-- scripts -->
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="//code.jquery.com/jquery-2.2.1.min.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
<script src="{{ url_for('static', filename='main.js') }}"></script>
Now let’s set up a new custom Directive.
Angular Directives are markers on a DOM element, which allow us to insert sections of HTML with specific events and attributes attached to it. Let’s build out the first part of our Directive by adding the following code just below the controller in main.js:
.directive('wordCountChart', ['$parse', function ($parse) {
  return {
    restrict: 'E',
    replace: true,
    template: '<div id="chart"></div>',
    link: function (scope) {}
   };
}]);
restrict: 'E' creates a Directive that is restricted to an HTML element. replace: truesimply replaces the HTML Directive with the HTML in the template. The link function gives us access to variables in the scope defined in the controller.
Next, add a watch function to “watch” for any changes to the variables and respond appropriately. Add this to the link function like so:
link: function (scope) {
  scope.$watch('wordcounts', function() {
    // add code here
  }, true);
}
Finally, add the Directive just below the closing divider to <div class="row">:
<br>
<word-count-chart data="wordcounts"></word-count-chart>
With the Directive set up, let’s turn our attention to the D3 library…

D3 Bar Chart

D3 is a powerful library that utilizes HTML, CSS, and SVG to display data on the DOM and JavaScript to make it interactive. We will use it to create a basic bar chart.

Step 1: Functional Logic

Add the following to the watch function within the Angular Directive:
scope.$watch('wordcounts', function() {
  var data = scope.wordcounts;
  for (var word in data) {
    d3.select('#chart')
      .append('div')
      .selectAll('div')
      .data(word[0])
      .enter()
      .append('div');
  }
}, true);
Now, whenever scope.wordcounts changes, this function is fired, which updates the DOM. Since an object is returned from the AJAX request, we iterate through it to add the specific data to the chart. Essentially, every word is appended to a new div via a data join.
Try running the code.
What happens? Nothing shows up, right? Check out the DOM in Chrome’s Developer Tools, after you submit a new site. You should see a number of nested divs. We just need to add styles…

Step 2: Styling the Bar Chart

Start with some simple CSS:
#chart {
  overflow-y: scroll;
}

#chart {
  background: #eee;
  padding: 3px;
}

#chart div {
  width: 0;
  transition: all 1s ease-out;
  -moz-transition: all 1s ease-out;
  -webkit-transition: all 1s ease-out;
}

#chart div {
  height: 30px;
  font: 15px;
  background-color: #006dcc;
  text-align: right;
  padding: 3px;
  color: white;
  box-shadow: 2px 2px 2px gray;
}
Make sure to include this at the top of HTML page, after the Bootstrap stylesheet:
<link rel="stylesheet" type="text/css" href="../static/main.css">
Fire up the app in our browser. What’s happening now?
When you search for a website, you should now see a grey area with some thin blue bars on the left hand side. So you can see that we are generating a bar for each data element we’re getting back - 10 in total. However, we need to modify our D3 code in order to increase the width of each bar so they are readable.

Step 3: Making the Bar Chart More Interactive

We can chain this on to our existing code and use the D3 style function:
scope.$watch('wordcounts', function() {
  var data = scope.wordcounts;
  for (var word in data) {
    d3.select('#chart')
      .append('div')
      .selectAll('div')
      .data(word[0])
      .enter()
      .append('div')
      .style('width', function() {
        return (data[word] * 20) + 'px';
      })
      .text(function(d){
        return word;
      });
  }
}, true);
Now we are dynamically creating a width based on the numeric value of how often a word shows up on a webpage:
.style('width', function() {
  return (data[word] * 20) + 'px';
})
.text(function(d){
  return word;
});
The style is calculated by returning the value associated with each word, multiplying that number by 20, and then converting it into pixels. We can also add text to each bar element by inserting the string value of the word along with how often it shows up on the page.
Try this out. You should see something like:
There’s still one thing missing though. What happens when you search for a new website? Try it. The new chart is appended beneath the previous one. We need to clear out our chart div before a new one is created.
Update the link function in the Directive:
link: function (scope) {
  scope.$watch('wordcounts', function() {
    d3.select('#chart').selectAll('*').remove();
    var data = scope.wordcounts;
    for (var word in data) {
      d3.select('#chart')
        .append('div')
        .selectAll('div')
        .data(word[0])
        .enter()
        .append('div')
        .style('width', function() {
          return (data[word] * 20) + 'px';
        })
        .text(function(d){
          return word;
        });
    }
  }, true);
}
d3.select('#chart').selectAll('*').remove(); simply clears out the chart each time the $scope.watch function is fired. Now we have a chart that is cleared before each new use, and we have a fully functional word count application!!
Test it out!

Conclusion and Next Steps

That’s it. Push your changes to the staging and production servers. Let’s review what we tackled:
  1. We started with the configuration and workflow, setting up staging and production servers
  2. From there, we added the basic functionality - web scraping, data analysis - and set up a task queue with Redis
  3. With the back-end functionality set up, attention turned to the front-end where we added Angular, built a custom Directive, and added D3 into the mix
We have an MVP, but there’s still much to be done:
  1. Refactor, refactor, refactor!
  2. Write tests
  3. Handle errors and exceptions
  4. Abstract out state in the Angular app to a Service
  5. Work on the UI and UX
Want to help? Add a feature, write part 9, get paid, and become Internet famous!
Links:

Popular posts from this blog

How to read or extract text data from passport using python utility.

Hi ,  Lets get start with some utility which can be really helpful in extracting the text data from passport documents which can be images, pdf.  So instead of jumping to code directly lets understand the MRZ, & how it works basically. MRZ Parser :                 A machine-readable passport (MRP) is a machine-readable travel document (MRTD) with the data on the identity page encoded in optical character recognition format Most travel passports worldwide are MRPs.  It can have 2 lines or 3 lines of machine-readable data. This method allows to process MRZ written in accordance with ICAO Document 9303 (endorsed by the International Organization for Standardization and the International Electrotechnical Commission as ISO/IEC 7501-1)). Some applications will need to be able to scan such data of someway, so one of the easiest methods is to recognize it from an image file. I 'll show you how to retrieve the MRZ infor...

How to generate class diagrams pictures in a Django/Open-edX project from console

A class diagram in the Unified Modeling Language ( UML ) is a type of static structure diagram that describes the structure of a system by showing the system’s classes, their attributes, operations (or methods), and the relationships among objects. https://github.com/django-extensions/django-extensions Step 1:   Install django extensions Command:  pip install django-extensions Step 2:  Add to installed apps INSTALLED_APPS = ( ... 'django_extensions' , ... ) Step 3:  Install diagrams generators You have to choose between two diagram generators: Graphviz or Dotplus before using the command or you will get: python manage.py graph_models -a -o myapp_models.png Note:  I prefer to use   pydotplus   as it easier to install than Graphviz and its dependencies so we use   pip install pydotplus . Command:  pip install pydotplus Step 4:  Generate diagrams Now we have everything installed...

How to Remove course from Open-edX

Go to vagrant  => 1. In the edx-platform directory:  - cd /edx/app/edxapp/edx-platform 2. Run the following Django management command:   - sudo -u www-data /edx/bin/python.edxapp /edx/bin/manage.edxapp lms dump_course_ids --settings aws    - sudo -u www-data /edx/bin/python.edxapp /edx/bin/manage.edxapp lms dump_course_ids --settings=devstack 3. Find the course ID which you'd like to delete in the resulting list of course IDs. 4. Copy the course ID into the following command and run it:  - sudo -u www-data /edx/bin/python.edxapp /edx/bin/manage.edxapp cms delete_course <COURSE_ID> --settings aws  -   sudo -u www-data /edx/bin/python.edxapp /edx/bin/manage.edxapp cms delete_course <COURSE_ID> --settings=devstack  - You'll be asked to verify the deletion . To verify the deletion, run the command from step 2 above and ensure that the course ID is not in the list. Help reference : ...