Index: ow/static/js/ow.js
===================================================================
--- ow/static/js/ow.js	(revision 6dc1846776f4da4706db290101d90756522a553b)
+++ ow/static/js/ow.js	(revision bd8eeb48868cef1c8fadf8663e70414a57526da0)
@@ -227,2 +227,147 @@
 
 };
+
+
+owjs.year_chart = function(spec) {
+
+    "use strict";
+
+    // parameters provided when creating an "instance" of the chart
+    var chart_selector = spec.chart_selector,
+        filters_selector = spec.filters_selector,
+        url = spec.url,
+        current_month = spec.current_month,
+        y_axis_labels = spec.y_axis_labels;
+
+    // Helpers
+    function select_x_axis_label(d) {
+        /* Given a value, return the label associated with it */
+        return d3.select('.x-axis-b')
+    	    .selectAll('text')
+    	    .filter(function(x) { return x == d.name; });
+    };
+
+    function get_y_value(d, filter_by) {
+        return Number(d[filter_by]);
+    };
+
+    function get_y_axis_label(filter_by) {
+        return y_axis_labels[filter_by];
+    };
+
+    // Methods
+    var filters_setup = function filters_setup() {
+        $(filters_selector).on('click', function(e) {
+            var filter_by = 'distance';
+            e.preventDefault();
+            filter_by = $(this).attr('class').split('-')[1]
+            var chart = d3.select(chart_selector);
+            chart.selectAll("*").remove();
+            render(filter_by);
+        });
+    };
+
+    var render = function render(filter_by) {
+        /*
+          Build a d3 bar chart, populated with data from the given url.
+        */
+        var chart = d3.select(chart_selector),
+            margin = {top: 20, right: 20, bottom: 30, left: 50},
+            width = +chart.attr("width") - margin.left - margin.right,
+            height = +chart.attr("height") - margin.top - margin.bottom,
+            g = chart.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")"),
+            x = d3.scaleBand().rangeRound([0, width]).padding(0.1),
+            y = d3.scaleLinear().rangeRound([height, 0]);
+
+        d3.json(url).then(function (data) {
+	    x.domain(data.map(function (d) {
+	        return d.name;
+	    }));
+
+	    y.domain([0, d3.max(data, function (d) {
+                return get_y_value(d, filter_by);
+	    })]);
+
+	    g.append("g")
+                .attr('class', 'x-axis-b')
+	        .attr("transform", "translate(0," + height + ")")
+                .call(d3.axisBottom(x))
+
+            g.append("g")
+	        .call(d3.axisLeft(y))
+	        .append("text")
+	        .attr("fill", "#000")
+	        .attr("transform", "rotate(-90)")
+	        .attr("y", 6)
+	        .attr("dy", "0.71em")
+	        .attr("text-anchor", "end")
+	        .text(get_y_axis_label(filter_by));
+
+	    g.selectAll(".bar")
+	        .data(data)
+	        .enter().append("rect")
+                .attr("class", function(d) {
+                    if (d.id == current_month){
+                        select_x_axis_label(d).attr('style', "font-weight: bold;");
+                        return 'bar current'
+                    }
+                    else {
+                        return 'bar'
+                    }
+                })
+	        .attr("x", function (d) {
+		    return x(d.name);
+	        })
+	        .attr("y", function (d) {
+		    return y(get_y_value(d, filter_by));
+	        })
+	        .attr("width", x.bandwidth())
+	        .attr("height", function (d) {
+		    return height - y(get_y_value(d, filter_by));
+	        })
+                .on('mouseover', function(d) {
+                    if (d.id != current_month){
+                        select_x_axis_label(d).attr('style', "font-weight: bold;");
+                    }
+  		})
+  		.on('mouseout', function(d) {
+                    if (d.id != current_month){
+        	        select_x_axis_label(d).attr('style', "font-weight: regular;");
+                    }
+                });
+
+            g.selectAll(".text")
+                .data(data)
+                .enter()
+                .append("text")
+                .attr("class","label")
+	        .attr("x", function (d) {
+		    return x(d.name) + x.bandwidth()/2;
+	        })
+	        .attr("y", function (d) {
+                    /*
+                      Get the value for the current bar, then get the maximum
+                      value to be displayed in the bar, which is used to
+                      calculate the proper position of the label for this bar,
+                      relatively to its height (1% above the bar)
+                     */
+                    var value = get_y_value(d, filter_by);
+                    var max = y.domain()[1];
+                    return y(value + y.domain()[1] * 0.01);
+	        })
+                .text(function(d) {
+                    var value = get_y_value(d, filter_by)
+                    if ( value > 0) {
+                        return value;
+                    }
+                });
+
+        });
+    };
+
+    var that = {}
+    that.filters_setup = filters_setup;
+    that.render = render;
+    return that
+
+};
Index: ow/templates/profile.pt
===================================================================
--- ow/templates/profile.pt	(revision 6dc1846776f4da4706db290101d90756522a553b)
+++ ow/templates/profile.pt	(revision bd8eeb48868cef1c8fadf8663e70414a57526da0)
@@ -81,7 +81,41 @@
       </div>
 
+      <div class="month-stats js-month-stats">
+        <svg width="600" height="300"></svg>
+        <div class="filters js-filters">
+          <a href="#" class="js-distance" i18n:translate="">distance</a>
+          <a href="#" class="js-time" i18n:translate="">time</a>
+          <a href="#" class="js-elevation" i18n:translate="">elevation</a>
+        </div>
+      </div>
+
     </div>
 
   </metal:content>
 
+  <metal:body-js metal:fill-slot="body-js">
+
+    <script src="${request.static_url('ow:static/components/d3/d3.min.js')}"></script>
+    <script src="${request.static_url('ow:static/js/ow.js')}"></script>
+
+    <script type="text/javascript">
+     var y_axis_labels = {
+         "distance": "Kilometers",
+         "time": "Hours",
+         "elevation": "Meters"
+     };
+
+     var year_chart = owjs.year_chart({
+         chart_selector: 'div.js-month-stats svg',
+         filters_selector: 'div.js-month-stats div.js-filters a',
+         url: "${request.resource_url(context, 'yearly')}",
+         current_month: "${current_month}",
+         y_axis_labels: y_axis_labels,
+     });
+     year_chart.render("distance");
+     year_chart.filters_setup();
+    </script>
+
+  </metal:body-js>
+
 </html>
