Index: ow/static/css/openworkouts.css
===================================================================
--- ow/static/css/openworkouts.css	(revision 421f05f48c5d18f659fbf167e4267039b6ff4fb1)
+++ ow/static/css/openworkouts.css	(revision 5bdfbfbaa6861a929676d8453ef30d2b9fd55660)
@@ -12,2 +12,35 @@
     background-color: #eeeeee;
 }
+
+/* dashboard, weekly stats */
+
+span.week_totals_left {
+    padding-right: 5px;
+    border-right: 1px solid #e1e1e1;
+}
+
+span.week_totals_right {
+    padding-left: 5px;
+}
+
+.x-axis path, .x-axis line {
+    fill: none;
+    stroke: none;
+}
+
+.bar {
+    fill: #f8b5be;
+}
+
+.bar:hover {
+    fill: #ee4056;
+}
+
+.current {
+    fill: #ee4056;
+}
+
+text.label {
+    font-size: 0.6em;
+    text-anchor: middle;
+}
Index: ow/static/js/ow.js
===================================================================
--- ow/static/js/ow.js	(revision 421f05f48c5d18f659fbf167e4267039b6ff4fb1)
+++ ow/static/js/ow.js	(revision 5bdfbfbaa6861a929676d8453ef30d2b9fd55660)
@@ -123,2 +123,106 @@
 
 };
+
+
+owjs.week_chart = function(spec) {
+
+    "use strict";
+
+    // parameters provided when creating an "instance" of the chart
+    var chart_selector = spec.chart_selector,
+        url = spec.url,
+        current_day_name = spec.current_day_name
+
+    // Helpers
+    function select_x_axis_label(d) {
+        /* Given a value, return the label associated with it */
+        return d3.select('.x-axis')
+    	    .selectAll('text')
+    	    .filter(function(x) { return x == d.name; });
+    }
+
+    // Methods
+    var render = function render() {
+        /*
+           Build a d3 bar chart, populated with data from the given url.
+         */
+        var chart = d3.select("svg"),
+            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 Number(d.distance);
+	    })]);
+
+	    g.append("g")
+                .attr('class', 'x-axis')
+	        .attr("transform", "translate(0," + height + ")")
+	        .call(d3.axisBottom(x))
+
+	    g.selectAll(".bar")
+	        .data(data)
+	        .enter().append("rect")
+                .attr("class", function(d) {
+                    if (d.name == current_day_name){
+                        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(Number(d.distance));
+	        })
+	        .attr("width", x.bandwidth())
+	        .attr("height", function (d) {
+		    return height - y(Number(d.distance));
+	        })
+                .on('mouseover', function(d) {
+                    if (d.name != current_day_name){
+                        select_x_axis_label(d).attr('style', "font-weight: bold;");
+                    }
+  		})
+  		.on('mouseout', function(d) {
+                    if (d.name != current_day_name){
+        	        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) {
+                    return y(Number(d.distance) + 5);
+	        })
+                .text(function(d) {
+                    if (Number(d.distance) > 0) {
+                        return d.distance;
+                    }
+                });
+
+        });
+    };
+
+    var that = {}
+    that.render = render;
+    return that
+
+};
Index: ow/templates/dashboard.pt
===================================================================
--- ow/templates/dashboard.pt	(revision 421f05f48c5d18f659fbf167e4267039b6ff4fb1)
+++ ow/templates/dashboard.pt	(revision 5bdfbfbaa6861a929676d8453ef30d2b9fd55660)
@@ -142,4 +142,61 @@
               </ul>
           </tal:activity_tree>
+
+          <tal:stats>
+
+            <div class="week-stats js-week-stats">
+              <h3><tal:t i18n:translate="">This week</tal:t></h3>
+              <h4 tal:define="totals context.week_totals">
+                <span class="week_totals_left">
+                  <tal:d tal:content="round(totals['distance'])"></tal:d>
+                  <tal:t i18n:translate="">kms</tal:t>
+                </span>
+                <span class="week_totals_right">
+                  <tal:hms tal:define="hms timedelta_to_hms(totals['time'])">
+                    <tal:h tal:content="str(hms[0]).zfill(2)"></tal:h>
+                    <tal:t i18n:translate="">hours</tal:t>,
+                    <tal:h tal:content="str(hms[1]).zfill(2)"></tal:h>
+                    <tal:t i18n:translate="">min.</tal:t>
+                  </tal:hms>
+                </span>
+              </h4>
+              <svg width="300" height="200"></svg>
+              <style>
+
+              </style>
+            </div>
+
+            <div class="user-stats">
+              <tal:year-stats tal:repeat="year context.activity_years">
+                <h3><a href="" tal:content="year"></a></h3>
+                <ul tal:define="stats context.stats(year)">
+                  <li>
+                    <span i18n:translate="">Workouts:</span>
+                    <span tal:content="stats['workouts']"></span>
+                  </li>
+                  <li>
+                    <span i18n:translate="">Distance:</span>
+                    <span tal:content="round(stats['distance'])"></span> kms
+                  </li>
+                  <li>
+                    <span i18n:translate="">Time:</span>
+                    <tal:hms tal:define="hms timedelta_to_hms(stats['time'])">
+                      <span>
+                        <tal:h tal:content="str(hms[0]).zfill(2)"></tal:h>
+                        <tal:t i18n:translate="">hours</tal:t>,
+                        <tal:h tal:content="str(hms[1]).zfill(2)"></tal:h>
+                        <tal:t i18n:translate="">min.</tal:t>
+                      </span>
+                    </tal:hms>
+                  </li>
+                  <li>
+                    <span i18n:translate="">Elevation:</span>
+                    <span tal:content="stats['elevation']"></span> m
+                  </li>
+                </ul>
+              </tal:year-stats>
+            </div>
+          </tal:stats>
+
       </aside>
 
@@ -148,3 +205,18 @@
   </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 week_chart = owjs.week_chart({
+         chart_selector: 'div.js-week-stats',
+         url: "${request.resource_url(context, 'week')}",
+         current_day_name: "${current_day_name}"
+     });
+     week_chart.render();
+    </script>
+
+  </metal:body-js>
+
 </html>
Index: ow/tests/views/test_user.py
===================================================================
--- ow/tests/views/test_user.py	(revision 421f05f48c5d18f659fbf167e4267039b6ff4fb1)
+++ ow/tests/views/test_user.py	(revision 5bdfbfbaa6861a929676d8453ef30d2b9fd55660)
@@ -153,6 +153,8 @@
         request = dummy_request
         response = user_views.dashboard(john, request)
-        assert len(response) == 4
-        assert 'month_name' in response.keys()
+        assert len(response) == 6
+        assert 'month_name' in response.keys()
+        assert response['current_year'] == datetime.now().year
+        assert response['current_day_name'] == datetime.now().strftime('%a')
         # this user has a single workout, in 2015
         assert response['viewing_year'] == 2015
@@ -168,6 +170,8 @@
         request.GET['year'] = 2015
         response = user_views.dashboard(john, request)
-        assert len(response) == 4
-        assert 'month_name' in response.keys()
+        assert len(response) == 6
+        assert 'month_name' in response.keys()
+        assert response['current_year'] == datetime.now().year
+        assert response['current_day_name'] == datetime.now().strftime('%a')
         # this user has a single workout, in 2015
         assert response['viewing_year'] == 2015
@@ -177,6 +181,8 @@
         request.GET['year'] = 2000
         response = user_views.dashboard(john, request)
-        assert len(response) == 4
-        assert 'month_name' in response.keys()
+        assert len(response) == 6
+        assert 'month_name' in response.keys()
+        assert response['current_year'] == datetime.now().year
+        assert response['current_day_name'] == datetime.now().strftime('%a')
         # this user has a single workout, in 2015
         assert response['viewing_year'] == 2000
@@ -195,6 +201,8 @@
         request.GET['month'] = 6
         response = user_views.dashboard(john, request)
-        assert len(response) == 4
-        assert 'month_name' in response.keys()
+        assert len(response) == 6
+        assert 'month_name' in response.keys()
+        assert response['current_year'] == datetime.now().year
+        assert response['current_day_name'] == datetime.now().strftime('%a')
         # this user has a single workout, in 2015
         assert response['viewing_year'] == 2015
@@ -204,6 +212,8 @@
         request.GET['month'] = 2
         response = user_views.dashboard(john, request)
-        assert len(response) == 4
-        assert 'month_name' in response.keys()
+        assert len(response) == 6
+        assert 'month_name' in response.keys()
+        assert response['current_year'] == datetime.now().year
+        assert response['current_day_name'] == datetime.now().strftime('%a')
         # this user has a single workout, in 2015
         assert response['viewing_year'] == 2015
@@ -214,6 +224,8 @@
         request.GET['month'] = 6
         response = user_views.dashboard(john, request)
-        assert len(response) == 4
-        assert 'month_name' in response.keys()
+        assert len(response) == 6
+        assert 'month_name' in response.keys()
+        assert response['current_year'] == datetime.now().year
+        assert response['current_day_name'] == datetime.now().strftime('%a')
         # this user has a single workout, in 2015
         assert response['viewing_year'] == 2010
@@ -230,6 +242,8 @@
         request.GET['month'] = 5
         response = user_views.dashboard(john, request)
-        assert len(response) == 4
-        assert 'month_name' in response.keys()
+        assert len(response) == 6
+        assert 'month_name' in response.keys()
+        assert response['current_year'] == datetime.now().year
+        assert response['current_day_name'] == datetime.now().strftime('%a')
         # this user has a single workout, in 2015
         assert response['viewing_year'] == 2015
@@ -239,6 +253,8 @@
         request.GET['month'] = 6
         response = user_views.dashboard(john, request)
-        assert len(response) == 4
-        assert 'month_name' in response.keys()
+        assert len(response) == 6
+        assert 'month_name' in response.keys()
+        assert response['current_year'] == datetime.now().year
+        assert response['current_day_name'] == datetime.now().strftime('%a')
         # this user has a single workout, in 2015
         assert response['viewing_year'] == 2015
Index: ow/views/user.py
===================================================================
--- ow/views/user.py	(revision 421f05f48c5d18f659fbf167e4267039b6ff4fb1)
+++ ow/views/user.py	(revision 5bdfbfbaa6861a929676d8453ef30d2b9fd55660)
@@ -146,4 +146,6 @@
 
     return {
+        'current_year': datetime.now().year,
+        'current_day_name': datetime.now().strftime('%a'),
         'month_name': month_name,
         'viewing_year': viewing_year,
Index: ow/views/workout.py
===================================================================
--- ow/views/workout.py	(revision 421f05f48c5d18f659fbf167e4267039b6ff4fb1)
+++ ow/views/workout.py	(revision 5bdfbfbaa6861a929676d8453ef30d2b9fd55660)
@@ -1,2 +1,3 @@
+from decimal import Decimal
 from datetime import datetime, timedelta, time, timezone
 
@@ -121,4 +122,6 @@
                                  tzinfo=timezone.utc)
         context.start = start
+        # ensure distance is a decimal
+        context.distance = Decimal(context.distance)
         catalog = get_catalog(context)
         reindex_object(catalog, context)
