Changeset bd8eeb4 in OpenWorkouts-current


Ignore:
Timestamp:
Jan 29, 2019, 12:35:08 PM (4 years ago)
Author:
borja <borja@…>
Branches:
current, feature/docs, master
Children:
d4cabcc, ed7e9d7
Parents:
6dc1846
Message:

(#7) Yearly (last 12 months) per month chart in the user profile

with filters to see the chart for distance, time or elevation

Location:
ow
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • ow/static/js/ow.js

    r6dc1846 rbd8eeb4  
    227227
    228228};
     229
     230
     231owjs.year_chart = function(spec) {
     232
     233    "use strict";
     234
     235    // parameters provided when creating an "instance" of the chart
     236    var chart_selector = spec.chart_selector,
     237        filters_selector = spec.filters_selector,
     238        url = spec.url,
     239        current_month = spec.current_month,
     240        y_axis_labels = spec.y_axis_labels;
     241
     242    // Helpers
     243    function select_x_axis_label(d) {
     244        /* Given a value, return the label associated with it */
     245        return d3.select('.x-axis-b')
     246            .selectAll('text')
     247            .filter(function(x) { return x == d.name; });
     248    };
     249
     250    function get_y_value(d, filter_by) {
     251        return Number(d[filter_by]);
     252    };
     253
     254    function get_y_axis_label(filter_by) {
     255        return y_axis_labels[filter_by];
     256    };
     257
     258    // Methods
     259    var filters_setup = function filters_setup() {
     260        $(filters_selector).on('click', function(e) {
     261            var filter_by = 'distance';
     262            e.preventDefault();
     263            filter_by = $(this).attr('class').split('-')[1]
     264            var chart = d3.select(chart_selector);
     265            chart.selectAll("*").remove();
     266            render(filter_by);
     267        });
     268    };
     269
     270    var render = function render(filter_by) {
     271        /*
     272          Build a d3 bar chart, populated with data from the given url.
     273        */
     274        var chart = d3.select(chart_selector),
     275            margin = {top: 20, right: 20, bottom: 30, left: 50},
     276            width = +chart.attr("width") - margin.left - margin.right,
     277            height = +chart.attr("height") - margin.top - margin.bottom,
     278            g = chart.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")"),
     279            x = d3.scaleBand().rangeRound([0, width]).padding(0.1),
     280            y = d3.scaleLinear().rangeRound([height, 0]);
     281
     282        d3.json(url).then(function (data) {
     283            x.domain(data.map(function (d) {
     284                return d.name;
     285            }));
     286
     287            y.domain([0, d3.max(data, function (d) {
     288                return get_y_value(d, filter_by);
     289            })]);
     290
     291            g.append("g")
     292                .attr('class', 'x-axis-b')
     293                .attr("transform", "translate(0," + height + ")")
     294                .call(d3.axisBottom(x))
     295
     296            g.append("g")
     297                .call(d3.axisLeft(y))
     298                .append("text")
     299                .attr("fill", "#000")
     300                .attr("transform", "rotate(-90)")
     301                .attr("y", 6)
     302                .attr("dy", "0.71em")
     303                .attr("text-anchor", "end")
     304                .text(get_y_axis_label(filter_by));
     305
     306            g.selectAll(".bar")
     307                .data(data)
     308                .enter().append("rect")
     309                .attr("class", function(d) {
     310                    if (d.id == current_month){
     311                        select_x_axis_label(d).attr('style', "font-weight: bold;");
     312                        return 'bar current'
     313                    }
     314                    else {
     315                        return 'bar'
     316                    }
     317                })
     318                .attr("x", function (d) {
     319                    return x(d.name);
     320                })
     321                .attr("y", function (d) {
     322                    return y(get_y_value(d, filter_by));
     323                })
     324                .attr("width", x.bandwidth())
     325                .attr("height", function (d) {
     326                    return height - y(get_y_value(d, filter_by));
     327                })
     328                .on('mouseover', function(d) {
     329                    if (d.id != current_month){
     330                        select_x_axis_label(d).attr('style', "font-weight: bold;");
     331                    }
     332                })
     333                .on('mouseout', function(d) {
     334                    if (d.id != current_month){
     335                        select_x_axis_label(d).attr('style', "font-weight: regular;");
     336                    }
     337                });
     338
     339            g.selectAll(".text")
     340                .data(data)
     341                .enter()
     342                .append("text")
     343                .attr("class","label")
     344                .attr("x", function (d) {
     345                    return x(d.name) + x.bandwidth()/2;
     346                })
     347                .attr("y", function (d) {
     348                    /*
     349                      Get the value for the current bar, then get the maximum
     350                      value to be displayed in the bar, which is used to
     351                      calculate the proper position of the label for this bar,
     352                      relatively to its height (1% above the bar)
     353                     */
     354                    var value = get_y_value(d, filter_by);
     355                    var max = y.domain()[1];
     356                    return y(value + y.domain()[1] * 0.01);
     357                })
     358                .text(function(d) {
     359                    var value = get_y_value(d, filter_by)
     360                    if ( value > 0) {
     361                        return value;
     362                    }
     363                });
     364
     365        });
     366    };
     367
     368    var that = {}
     369    that.filters_setup = filters_setup;
     370    that.render = render;
     371    return that
     372
     373};
  • ow/templates/profile.pt

    r6dc1846 rbd8eeb4  
    8181      </div>
    8282
     83      <div class="month-stats js-month-stats">
     84        <svg width="600" height="300"></svg>
     85        <div class="filters js-filters">
     86          <a href="#" class="js-distance" i18n:translate="">distance</a>
     87          <a href="#" class="js-time" i18n:translate="">time</a>
     88          <a href="#" class="js-elevation" i18n:translate="">elevation</a>
     89        </div>
     90      </div>
     91
    8392    </div>
    8493
    8594  </metal:content>
    8695
     96  <metal:body-js metal:fill-slot="body-js">
     97
     98    <script src="${request.static_url('ow:static/components/d3/d3.min.js')}"></script>
     99    <script src="${request.static_url('ow:static/js/ow.js')}"></script>
     100
     101    <script type="text/javascript">
     102     var y_axis_labels = {
     103         "distance": "Kilometers",
     104         "time": "Hours",
     105         "elevation": "Meters"
     106     };
     107
     108     var year_chart = owjs.year_chart({
     109         chart_selector: 'div.js-month-stats svg',
     110         filters_selector: 'div.js-month-stats div.js-filters a',
     111         url: "${request.resource_url(context, 'yearly')}",
     112         current_month: "${current_month}",
     113         y_axis_labels: y_axis_labels,
     114     });
     115     year_chart.render("distance");
     116     year_chart.filters_setup();
     117    </script>
     118
     119  </metal:body-js>
     120
    87121</html>
Note: See TracChangeset for help on using the changeset viewer.