Changes in / [ed7e9d7:26220ba] in OpenWorkouts-current


Ignore:
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • bin/install

    red7e9d7 r26220ba  
    1010# Full path to the env
    1111env_path=${current}/env
    12 
    13 
    14 set_scripts_permissions() {
    15     # ensure the shell scripts we will need have proper permissions
    16     chmod u+x ${current}/bin/js_deps
    17     chmod u+x ${current}/bin/start
    18     chmod u+x ${current}/bin/screenshot_map
    19 }
    2012
    2113check_python3() {
     
    6355
    6456install_js_deps() {
     57    chmod +x ${current}/bin/js_deps
    6558    ${current}/bin/js_deps
    6659}
    6760
    6861setup_start_stop() {
     62    chmod +x ${current}/bin/start
    6963    echo "OpenWorkouts successfully installed in ${env_path}"
    7064    echo ""
     
    7973}
    8074
    81 set_script_permissions
    8275check_python3
    8376create_venv
  • ow/models/user.py

    red7e9d7 r26220ba  
    234234                        timedelta())
    235235        }
    236 
    237     @property
    238     def yearly_stats(self):
    239         """
    240         Return per-month stats for the last 12 months
    241         """
    242         # set the boundaries for looking for workouts afterwards,
    243         # we need the current date as the "end date" and one year
    244         # ago from that date. Then we set the start at the first
    245         # day of that month.
    246         end = datetime.now(timezone.utc)
    247         start = (end - timedelta(days=365)).replace(day=1)
    248 
    249         # build the stats, populating first the dict with empty values
    250         # for each month.
    251         stats = {}
    252         for days in range((end - start).days):
    253             day = (start + timedelta(days=days)).date()
    254             if (day.year, day.month) not in stats.keys():
    255                 stats[(day.year, day.month)] = {
    256                     'workouts': 0,
    257                     'time': timedelta(0),
    258                     'distance': Decimal(0),
    259                     'elevation': Decimal(0),
    260                     'sports': {}
    261                 }
    262 
    263         # now loop over workouts, filtering and then adding stats to the
    264         # proper place
    265         for workout in self.workouts():
    266             if start.date() <= workout.start.date() <= end.date():
    267                 # less typing, avoid long lines
    268                 month = stats[
    269                     (workout.start.date().year, workout.start.date().month)]
    270                 month['workouts'] += 1
    271                 month['time'] += workout.duration or timedelta(seconds=0)
    272                 month['distance'] += workout.distance or Decimal(0)
    273                 month['elevation'] += workout.uphill or Decimal(0)
    274                 if workout.sport not in month['sports']:
    275                     month['sports'][workout.sport] = {
    276                         'workouts': 0,
    277                         'time': timedelta(seconds=0),
    278                         'distance': Decimal(0),
    279                         'elevation': Decimal(0),
    280                     }
    281                 month['sports'][workout.sport]['workouts'] += 1
    282                 month['sports'][workout.sport]['time'] += (
    283                     workout.duration or timedelta(0))
    284                 month['sports'][workout.sport]['distance'] += (
    285                     workout.distance or Decimal(0))
    286                 month['sports'][workout.sport]['elevation'] += (
    287                     workout.uphill or Decimal(0))
    288 
    289         return stats
  • ow/static/js/ow.js

    red7e9d7 r26220ba  
    3131    var openstreetmap_attr = 'Map data &copy; <a href="http://www.osm.org">OpenStreetMap</a>';
    3232
    33     // Some vars reused through the code
     33    // Some constants reused through the code
    3434    var map;
    3535    var gpx;
     
    147147           Build a d3 bar chart, populated with data from the given url.
    148148         */
    149         var chart = d3.select(chart_selector),
     149        var chart = d3.select("svg"),
    150150            margin = {top: 20, right: 20, bottom: 30, left: 50},
    151151            width = +chart.attr("width") - margin.left - margin.right,
     
    227227
    228228};
    229 
    230 
    231 owjs.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/dashboard.pt

    red7e9d7 r26220ba  
    211211    <script type="text/javascript">
    212212     var week_chart = owjs.week_chart({
    213          chart_selector: 'div.js-week-stats svg',
     213         chart_selector: 'div.js-week-stats',
    214214         url: "${request.resource_url(context, 'week')}",
    215215         current_day_name: "${current_day_name}"
  • ow/templates/profile.pt

    red7e9d7 r26220ba  
    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 
    9283    </div>
    9384
    9485  </metal:content>
    9586
    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 
    12187</html>
  • ow/tests/views/test_user.py

    red7e9d7 r26220ba  
    268268        request = dummy_request
    269269        response = user_views.profile(john, request)
    270         assert len(response.keys()) == 1
    271         current_month = datetime.now(timezone.utc).strftime('%Y-%m')
    272         assert response['current_month'] == current_month
     270        assert response == {}
    273271
    274272    def test_login_get(self, dummy_request):
  • ow/views/user.py

    red7e9d7 r26220ba  
    11import json
    22from calendar import month_name
    3 from datetime import datetime, timezone
     3from datetime import datetime
    44
    55from pyramid.httpexceptions import HTTPFound
     
    146146
    147147    return {
    148         'current_year': datetime.now(timezone.utc).year,
    149         'current_day_name': datetime.now(timezone.utc).strftime('%a'),
     148        'current_year': datetime.now().year,
     149        'current_day_name': datetime.now().strftime('%a'),
    150150        'month_name': month_name,
    151151        'viewing_year': viewing_year,
     
    165165    basic info, stats, etc
    166166    """
    167     now = datetime.now(timezone.utc)
    168     return {
    169         'current_month': now.strftime('%Y-%m')
    170     }
     167    return {}
    171168
    172169
     
    253250                    charset='utf-8',
    254251                    body=json.dumps(json_stats))
    255 
    256 
    257 @view_config(
    258     context=User,
    259     permission='view',
    260     name='yearly')
    261 def last_months_stats(context, request):
    262     """
    263     Return a json-encoded stream with statistics for the last 12 months
    264     """
    265     stats = context.yearly_stats
    266     # this sets which month is 2 times in the stats, once this year, once
    267     # the previous year. We will show it a bit different in the UI (showing
    268     # the year too to prevent confusion)
    269     repeated_month = datetime.now(timezone.utc).date().month
    270     json_stats = []
    271     for month in stats:
    272         hms = timedelta_to_hms(stats[month]['time'])
    273         name = month_name[month[1]][:3]
    274         if month[1] == repeated_month:
    275             name += ' ' + str(month[0])
    276         month_stats = {
    277             'id': str(month[0]) + '-' + str(month[1]).zfill(2),
    278             'name': name,
    279             'time': str(hms[0]).zfill(2),
    280             'distance': int(round(stats[month]['distance'])),
    281             'elevation': int(stats[month]['elevation']),
    282             'workouts': stats[month]['workouts']
    283         }
    284         json_stats.append(month_stats)
    285     return Response(content_type='application/json',
    286                     charset='utf-8',
    287                     body=json.dumps(json_stats))
Note: See TracChangeset for help on using the changeset viewer.