Changeset 5cf5787 in OpenWorkouts-current


Ignore:
Timestamp:
Feb 4, 2019, 12:37:35 PM (5 years ago)
Author:
borja <borja@…>
Branches:
current, feature/docs, master
Children:
3357e47
Parents:
63df989
Message:

Show weekly/monthly versions of the "last 12 months" workout stats chart

in the user profile page.

Location:
ow
Files:
4 edited

Legend:

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

    r63df989 r5cf5787  
    236236    var chart_selector = spec.chart_selector,
    237237        filters_selector = spec.filters_selector,
    238         url = spec.url,
     238        switcher_selector = spec.switcher_selector,
     239        urls = spec.urls,
    239240        current_month = spec.current_month,
    240         y_axis_labels = spec.y_axis_labels;
     241        current_week = spec.current_week,
     242        y_axis_labels = spec.y_axis_labels,
     243        filter_by = spec.filter_by,
     244        url = spec.url;
    241245
    242246    // Helpers
     
    256260    };
    257261
     262    function get_name_for_x(d) {
     263        if (d.week == undefined || d.week == 0) {
     264            return d.name;
     265        }
     266        else {
     267            return d.id.split('-')[2];
     268        }
     269    }
     270
    258271    // Methods
    259272    var filters_setup = function filters_setup() {
    260273        $(filters_selector).on('click', function(e) {
    261             var filter_by = 'distance';
    262274            e.preventDefault();
    263275            filter_by = $(this).attr('class').split('-')[1]
    264276            var chart = d3.select(chart_selector);
    265277            chart.selectAll("*").remove();
    266             render(filter_by);
    267         });
    268     };
    269 
    270     var render = function render(filter_by) {
     278            render(filter_by, url);
     279        });
     280    };
     281
     282    var switcher_setup = function switcher_setup() {
     283        $(switcher_selector).on('click', function(e) {
     284            e.preventDefault();
     285            url = $(this).attr('class').split('-')[1]
     286            var chart = d3.select(chart_selector);
     287            chart.selectAll("*").remove();
     288            render(filter_by, url);
     289        });
     290    };
     291
     292    var render = function render(filter_by, url) {
    271293        /*
    272294          Build a d3 bar chart, populated with data from the given url.
     
    280302            y = d3.scaleLinear().rangeRound([height, 0]);
    281303
    282         d3.json(url).then(function (data) {
     304        d3.json(urls[url]).then(function (data) {
    283305            x.domain(data.map(function (d) {
    284                 return d.name;
     306                return get_name_for_x(d);
     307                // return d.name;
    285308            }));
    286309
     
    308331                .enter().append("rect")
    309332                .attr("class", function(d) {
    310                     if (d.id == current_month){
     333                    var sel_week = current_month + '-' + current_week;
     334                    if (d.id == current_month || d.id == sel_week){
     335                        /* Bar for the currently selected month or week */
    311336                        select_x_axis_label(d).attr('style', "font-weight: bold;");
    312                         return 'bar current'
     337                        return 'bar current';
    313338                    }
    314339                    else {
    315                         return 'bar'
     340                        if (!current_week && d.id.indexOf(current_month) >=0 ) {
     341                            /*
     342                               User selected a month, then switched to weekly
     343                               view, we do highlight all the bars for weeks in
     344                               that month
     345                            */
     346                            select_x_axis_label(d).attr('style', "font-weight: bold;");
     347                            return 'bar current';
     348                        }
     349                        else {
     350                            /* Non-selected bar */
     351                            return 'bar';
     352                        }
     353
    316354                    }
    317355                })
    318356                .attr("x", function (d) {
    319                     return x(d.name);
     357                    return x(get_name_for_x(d));
    320358                })
    321359                .attr("y", function (d) {
     
    340378                });
    341379
    342             g.selectAll(".text")
    343                 .data(data)
    344                 .enter()
    345                 .append("text")
    346                 .attr("class","label")
    347                 .attr("x", function (d) {
    348                     return x(d.name) + x.bandwidth()/2;
    349                 })
    350                 .attr("y", function (d) {
    351                     /*
    352                       Get the value for the current bar, then get the maximum
    353                       value to be displayed in the bar, which is used to
    354                       calculate the proper position of the label for this bar,
    355                       relatively to its height (1% above the bar)
    356                      */
    357                     var value = get_y_value(d, filter_by);
    358                     var max = y.domain()[1];
    359                     return y(value + y.domain()[1] * 0.01);
    360                 })
    361                 .text(function(d) {
    362                     var value = get_y_value(d, filter_by)
    363                     if ( value > 0) {
    364                         return value;
    365                     }
    366                 });
    367 
     380            if (url == 'monthly') {
     381                g.selectAll(".text")
     382                    .data(data)
     383                    .enter()
     384                    .append("text")
     385                    .attr("class","label")
     386                    .attr("x", function (d) {
     387                        return x(get_name_for_x(d)) + x.bandwidth()/2;
     388                    })
     389                    .attr("y", function (d) {
     390                        /*
     391                          Get the value for the current bar, then get the maximum
     392                          value to be displayed in the bar, which is used to
     393                          calculate the proper position of the label for this bar,
     394                          relatively to its height (1% above the bar)
     395                        */
     396                        var value = get_y_value(d, filter_by);
     397                        var max = y.domain()[1];
     398                        return y(value + y.domain()[1] * 0.01);
     399                    })
     400                    .text(function(d) {
     401                        var value = get_y_value(d, filter_by)
     402                        if ( value > 0) {
     403                            return value;
     404                        }
     405                    });
     406            }
     407
     408            if (url == 'weekly') {
     409                g.selectAll(".tick")
     410                    .each(function (d, i) {
     411                        /*
     412                          Remove from the x-axis tickets those without letters
     413                          on them (useful for the weekly chart)
     414                        */
     415                        if (d !== parseInt(d, 10)) {
     416                            if(!d.match(/[a-z]/i)) {
     417                                this.remove();
     418                            }
     419                        }
     420                    });
     421            }
    368422        });
    369423    };
     
    371425    var that = {}
    372426    that.filters_setup = filters_setup;
     427    that.switcher_setup = switcher_setup;
    373428    that.render = render;
    374429    return that
  • ow/templates/profile.pt

    r63df989 r5cf5787  
    7272          <a href="#" class="js-time" i18n:translate="">time</a>
    7373          <a href="#" class="js-elevation" i18n:translate="">elevation</a>
     74        </div>
     75        <div class="switcher js-switcher">
     76          <a href="#" class="js-weekly" i18n:translate="">weekly</a>
     77          <a href="#" class="js-monthly" i18n:translate="">monthly</a>
    7478        </div>
    7579      </div>
     
    154158         chart_selector: 'div.js-month-stats svg',
    155159         filters_selector: 'div.js-month-stats div.js-filters a',
    156          url: "${request.resource_url(context, 'yearly')}",
     160         switcher_selector: 'div.js-month-stats div.js-switcher a',
     161         urls: {"monthly": "${request.resource_url(context, 'monthly')}",
     162                "weekly": "${request.resource_url(context, 'weekly')}"},
    157163         current_month: "${current_month}",
     164         current_week: "${current_week}",
    158165         y_axis_labels: y_axis_labels,
     166         filter_by: "distance",
     167         url: "${'monthly' if current_week is None else 'weekly'}",
    159168     });
    160      year_chart.render("distance");
     169     year_chart.render("distance", "${'monthly' if current_week is None else 'weekly'}");
    161170     year_chart.filters_setup();
     171     year_chart.switcher_setup();
    162172    </script>
    163173
  • ow/tests/views/test_user.py

    r63df989 r5cf5787  
    269269        # profile page for the current day (no workouts avalable)
    270270        response = user_views.profile(john, request)
    271         assert len(response.keys()) == 2
     271        assert len(response.keys()) == 3
    272272        current_month = datetime.now(timezone.utc).strftime('%Y-%m')
    273273        assert response['current_month'] == current_month
     274        assert response['current_week'] is None
    274275        assert response['workouts'] == []
    275276        # profile page for a previous date, that has workouts
     
    277278        request.GET['month'] = 8
    278279        response = user_views.profile(john, request)
    279         assert len(response.keys()) == 2
     280        assert len(response.keys()) == 3
    280281        assert response['current_month'] == '2015-08'
     282        assert response['current_week'] is None
     283        assert response['workouts'] == john.workouts(2015, 8)
     284        # same, passing a week, first on a week without workouts
     285        request.GET['year'] = 2015
     286        request.GET['month'] = 8
     287        request.GET['week'] = 25
     288        response = user_views.profile(john, request)
     289        assert len(response.keys()) == 3
     290        assert response['current_month'] == '2015-08'
     291        assert response['current_week'] is 25
     292        assert response['workouts'] == []
     293        # now in a week with workoutss
     294        request.GET['year'] = 2015
     295        request.GET['month'] = 8
     296        request.GET['week'] = 26
     297        response = user_views.profile(john, request)
     298        assert len(response.keys()) == 3
     299        assert response['current_month'] == '2015-08'
     300        assert response['current_week'] is 26
    281301        assert response['workouts'] == john.workouts(2015, 8)
    282302
  • ow/views/user.py

    r63df989 r5cf5787  
    168168    year = int(request.GET.get('year', now.year))
    169169    month = int(request.GET.get('month', now.month))
     170    week = request.GET.get('week', None)
    170171    return {
    171         'workouts': context.workouts(year, month),
     172        'workouts': context.workouts(year, month, week),
    172173        'current_month': '{year}-{month}'.format(
    173             year=str(year), month=str(month).zfill(2))
     174            year=str(year), month=str(month).zfill(2)),
     175        'current_week': week
    174176    }
    175177
     
    262264    context=User,
    263265    permission='view',
    264     name='yearly')
     266    name='monthly')
    265267def last_months_stats(context, request):
    266268    """
     
    294296                    charset='utf-8',
    295297                    body=json.dumps(json_stats))
     298
     299
     300@view_config(
     301    context=User,
     302    permission='view',
     303    name='weekly')
     304def last_weeks_stats(context, request):
     305    """
     306    Return a json-encoded stream with statistics for the last 12-months, but
     307    in a per-week basis
     308    """
     309    stats = context.weekly_year_stats
     310    # this sets which month is 2 times in the stats, once this year, once
     311    # the previous year. We will show it a bit different in the UI (showing
     312    # the year too to prevent confusion)
     313    repeated_month = datetime.now(timezone.utc).date().month
     314    json_stats = []
     315    for week in stats:
     316        hms = timedelta_to_hms(stats[week]['time'])
     317        name = month_name[week[1]][:3]
     318        if week[1] == repeated_month:
     319            name += ' ' + str(week[0])
     320        week_stats = {
     321            'id': '-'.join(
     322                [str(week[0]), str(week[1]).zfill(2), str(week[2])]),
     323            'week': str(week[3]),  # the number of week in the current month
     324            'name': name,
     325            'time': str(hms[0]).zfill(2),
     326            'distance': int(round(stats[week]['distance'])),
     327            'elevation': int(stats[week]['elevation']),
     328            'workouts': stats[week]['workouts'],
     329            'url': request.resource_url(
     330                context, 'profile',
     331                query={'year': str(week[0]),
     332                       'month': str(week[1]),
     333                       'week': str(week[2])},
     334                anchor='workouts')
     335        }
     336        json_stats.append(week_stats)
     337    return Response(content_type='application/json',
     338                    charset='utf-8',
     339                    body=json.dumps(json_stats))
Note: See TracChangeset for help on using the changeset viewer.