Merge pull request #93 from SamR1/add-elevation-data-and-new-sports
Add elevation data and new sports
							
								
								
									
										3
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						@@ -134,6 +134,9 @@ migrate-db:
 | 
				
			|||||||
recalculate:
 | 
					recalculate:
 | 
				
			||||||
	$(FLASK) recalculate
 | 
						$(FLASK) recalculate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					revision:
 | 
				
			||||||
 | 
						$(FLASK) db revision --directory $(MIGRATIONS) --message $(MIGRATION_MESSAGE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
run:
 | 
					run:
 | 
				
			||||||
	$(MAKE) P="run-server run-workers" make-p
 | 
						$(MAKE) P="run-server run-workers" make-p
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -157,6 +157,8 @@
 | 
				
			|||||||
      <span class="nt">"2017"</span><span class="p">:</span> <span class="p">{</span>
 | 
					      <span class="nt">"2017"</span><span class="p">:</span> <span class="p">{</span>
 | 
				
			||||||
        <span class="nt">"3"</span><span class="p">:</span> <span class="p">{</span>
 | 
					        <span class="nt">"3"</span><span class="p">:</span> <span class="p">{</span>
 | 
				
			||||||
          <span class="nt">"nb_workouts"</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
 | 
					          <span class="nt">"nb_workouts"</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
 | 
				
			||||||
 | 
					          <span class="nt">"total_ascent"</span><span class="p">:</span> <span class="mf">203.0</span><span class="p">,</span>
 | 
				
			||||||
 | 
					          <span class="nt">"total_ascent"</span><span class="p">:</span> <span class="mf">156.0</span><span class="p">,</span>
 | 
				
			||||||
          <span class="nt">"total_distance"</span><span class="p">:</span> <span class="mf">15.282</span><span class="p">,</span>
 | 
					          <span class="nt">"total_distance"</span><span class="p">:</span> <span class="mf">15.282</span><span class="p">,</span>
 | 
				
			||||||
          <span class="nt">"total_duration"</span><span class="p">:</span> <span class="mi">12341</span>
 | 
					          <span class="nt">"total_duration"</span><span class="p">:</span> <span class="mi">12341</span>
 | 
				
			||||||
        <span class="p">}</span>
 | 
					        <span class="p">}</span>
 | 
				
			||||||
@@ -164,11 +166,15 @@
 | 
				
			|||||||
      <span class="nt">"2019"</span><span class="p">:</span> <span class="p">{</span>
 | 
					      <span class="nt">"2019"</span><span class="p">:</span> <span class="p">{</span>
 | 
				
			||||||
        <span class="nt">"1"</span><span class="p">:</span> <span class="p">{</span>
 | 
					        <span class="nt">"1"</span><span class="p">:</span> <span class="p">{</span>
 | 
				
			||||||
          <span class="nt">"nb_workouts"</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
 | 
					          <span class="nt">"nb_workouts"</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
 | 
				
			||||||
 | 
					          <span class="nt">"total_ascent"</span><span class="p">:</span> <span class="mf">150.0</span><span class="p">,</span>
 | 
				
			||||||
 | 
					          <span class="nt">"total_ascent"</span><span class="p">:</span> <span class="mf">178.0</span><span class="p">,</span>
 | 
				
			||||||
          <span class="nt">"total_distance"</span><span class="p">:</span> <span class="mi">47</span><span class="p">,</span>
 | 
					          <span class="nt">"total_distance"</span><span class="p">:</span> <span class="mi">47</span><span class="p">,</span>
 | 
				
			||||||
          <span class="nt">"total_duration"</span><span class="p">:</span> <span class="mi">9960</span>
 | 
					          <span class="nt">"total_duration"</span><span class="p">:</span> <span class="mi">9960</span>
 | 
				
			||||||
        <span class="p">},</span>
 | 
					        <span class="p">},</span>
 | 
				
			||||||
        <span class="nt">"2"</span><span class="p">:</span> <span class="p">{</span>
 | 
					        <span class="nt">"2"</span><span class="p">:</span> <span class="p">{</span>
 | 
				
			||||||
          <span class="nt">"nb_workouts"</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
 | 
					          <span class="nt">"nb_workouts"</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
 | 
				
			||||||
 | 
					          <span class="nt">"total_ascent"</span><span class="p">:</span> <span class="mf">46.0</span><span class="p">,</span>
 | 
				
			||||||
 | 
					          <span class="nt">"total_ascent"</span><span class="p">:</span> <span class="mf">78.0</span><span class="p">,</span>
 | 
				
			||||||
          <span class="nt">"total_distance"</span><span class="p">:</span> <span class="mf">5.613</span><span class="p">,</span>
 | 
					          <span class="nt">"total_distance"</span><span class="p">:</span> <span class="mf">5.613</span><span class="p">,</span>
 | 
				
			||||||
          <span class="nt">"total_duration"</span><span class="p">:</span> <span class="mi">1267</span>
 | 
					          <span class="nt">"total_duration"</span><span class="p">:</span> <span class="mi">1267</span>
 | 
				
			||||||
        <span class="p">}</span>
 | 
					        <span class="p">}</span>
 | 
				
			||||||
@@ -266,16 +272,22 @@
 | 
				
			|||||||
    <span class="nt">"statistics"</span><span class="p">:</span> <span class="p">{</span>
 | 
					    <span class="nt">"statistics"</span><span class="p">:</span> <span class="p">{</span>
 | 
				
			||||||
      <span class="nt">"1"</span><span class="p">:</span> <span class="p">{</span>
 | 
					      <span class="nt">"1"</span><span class="p">:</span> <span class="p">{</span>
 | 
				
			||||||
        <span class="nt">"nb_workouts"</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
 | 
					        <span class="nt">"nb_workouts"</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
 | 
				
			||||||
 | 
					        <span class="nt">"total_ascent"</span><span class="p">:</span> <span class="mf">150.0</span><span class="p">,</span>
 | 
				
			||||||
 | 
					        <span class="nt">"total_ascent"</span><span class="p">:</span> <span class="mf">178.0</span><span class="p">,</span>
 | 
				
			||||||
        <span class="nt">"total_distance"</span><span class="p">:</span> <span class="mi">47</span><span class="p">,</span>
 | 
					        <span class="nt">"total_distance"</span><span class="p">:</span> <span class="mi">47</span><span class="p">,</span>
 | 
				
			||||||
        <span class="nt">"total_duration"</span><span class="p">:</span> <span class="mi">9960</span>
 | 
					        <span class="nt">"total_duration"</span><span class="p">:</span> <span class="mi">9960</span>
 | 
				
			||||||
      <span class="p">},</span>
 | 
					      <span class="p">},</span>
 | 
				
			||||||
      <span class="nt">"2"</span><span class="p">:</span> <span class="p">{</span>
 | 
					      <span class="nt">"2"</span><span class="p">:</span> <span class="p">{</span>
 | 
				
			||||||
        <span class="nt">"nb_workouts"</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
 | 
					        <span class="nt">"nb_workouts"</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
 | 
				
			||||||
 | 
					        <span class="nt">"total_ascent"</span><span class="p">:</span> <span class="mf">46.0</span><span class="p">,</span>
 | 
				
			||||||
 | 
					        <span class="nt">"total_ascent"</span><span class="p">:</span> <span class="mf">78.0</span><span class="p">,</span>
 | 
				
			||||||
        <span class="nt">"total_distance"</span><span class="p">:</span> <span class="mf">5.613</span><span class="p">,</span>
 | 
					        <span class="nt">"total_distance"</span><span class="p">:</span> <span class="mf">5.613</span><span class="p">,</span>
 | 
				
			||||||
        <span class="nt">"total_duration"</span><span class="p">:</span> <span class="mi">1267</span>
 | 
					        <span class="nt">"total_duration"</span><span class="p">:</span> <span class="mi">1267</span>
 | 
				
			||||||
      <span class="p">},</span>
 | 
					      <span class="p">},</span>
 | 
				
			||||||
      <span class="nt">"3"</span><span class="p">:</span> <span class="p">{</span>
 | 
					      <span class="nt">"3"</span><span class="p">:</span> <span class="p">{</span>
 | 
				
			||||||
        <span class="nt">"nb_workouts"</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
 | 
					        <span class="nt">"nb_workouts"</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
 | 
				
			||||||
 | 
					        <span class="nt">"total_ascent"</span><span class="p">:</span> <span class="mf">203.0</span><span class="p">,</span>
 | 
				
			||||||
 | 
					        <span class="nt">"total_ascent"</span><span class="p">:</span> <span class="mf">156.0</span><span class="p">,</span>
 | 
				
			||||||
        <span class="nt">"total_distance"</span><span class="p">:</span> <span class="mf">15.282</span><span class="p">,</span>
 | 
					        <span class="nt">"total_distance"</span><span class="p">:</span> <span class="mf">15.282</span><span class="p">,</span>
 | 
				
			||||||
        <span class="nt">"total_duration"</span><span class="p">:</span> <span class="mi">12341</span>
 | 
					        <span class="nt">"total_duration"</span><span class="p">:</span> <span class="mi">12341</span>
 | 
				
			||||||
      <span class="p">}</span>
 | 
					      <span class="p">}</span>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,6 @@ from fittrackee.application.utils import (
 | 
				
			|||||||
    update_app_config_from_database,
 | 
					    update_app_config_from_database,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from fittrackee.users.models import User
 | 
					from fittrackee.users.models import User
 | 
				
			||||||
from fittrackee.workouts.models import Sport
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def init_database(app: Flask) -> None:
 | 
					def init_database(app: Flask) -> None:
 | 
				
			||||||
@@ -17,30 +16,6 @@ def init_database(app: Flask) -> None:
 | 
				
			|||||||
    admin.admin = True
 | 
					    admin.admin = True
 | 
				
			||||||
    admin.timezone = 'Europe/Paris'
 | 
					    admin.timezone = 'Europe/Paris'
 | 
				
			||||||
    db.session.add(admin)
 | 
					    db.session.add(admin)
 | 
				
			||||||
    sport = Sport(label='Cycling (Sport)')
 | 
					 | 
				
			||||||
    sport.img = '/img/sports/cycling-sport.png'
 | 
					 | 
				
			||||||
    sport.is_default = True
 | 
					 | 
				
			||||||
    db.session.add(sport)
 | 
					 | 
				
			||||||
    sport = Sport(label='Cycling (Transport)')
 | 
					 | 
				
			||||||
    sport.img = '/img/sports/cycling-transport.png'
 | 
					 | 
				
			||||||
    sport.is_default = True
 | 
					 | 
				
			||||||
    db.session.add(sport)
 | 
					 | 
				
			||||||
    sport = Sport(label='Hiking')
 | 
					 | 
				
			||||||
    sport.img = '/img/sports/hiking.png'
 | 
					 | 
				
			||||||
    sport.is_default = True
 | 
					 | 
				
			||||||
    db.session.add(sport)
 | 
					 | 
				
			||||||
    sport = Sport(label='Mountain Biking')
 | 
					 | 
				
			||||||
    sport.img = '/img/sports/mountain-biking.png'
 | 
					 | 
				
			||||||
    sport.is_default = True
 | 
					 | 
				
			||||||
    db.session.add(sport)
 | 
					 | 
				
			||||||
    sport = Sport(label='Running')
 | 
					 | 
				
			||||||
    sport.img = '/img/sports/running.png'
 | 
					 | 
				
			||||||
    sport.is_default = True
 | 
					 | 
				
			||||||
    db.session.add(sport)
 | 
					 | 
				
			||||||
    sport = Sport(label='Walking')
 | 
					 | 
				
			||||||
    sport.img = '/img/sports/walking.png'
 | 
					 | 
				
			||||||
    sport.is_default = True
 | 
					 | 
				
			||||||
    db.session.add(sport)
 | 
					 | 
				
			||||||
    db.session.commit()
 | 
					    db.session.commit()
 | 
				
			||||||
    _, db_app_config = init_config()
 | 
					    _, db_app_config = init_config()
 | 
				
			||||||
    update_app_config_from_database(app, db_app_config)
 | 
					    update_app_config_from_database(app, db_app_config)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										20
									
								
								fittrackee/dist/asset-manifest.json
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -1,15 +1,15 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "files": {
 | 
					  "files": {
 | 
				
			||||||
    "main.css": "/static/css/main.b0b9a9d7.chunk.css",
 | 
					    "main.css": "/static/css/main.05f8352a.chunk.css",
 | 
				
			||||||
    "main.js": "/static/js/main.18465477.chunk.js",
 | 
					    "main.js": "/static/js/main.d87e6a43.chunk.js",
 | 
				
			||||||
    "main.js.map": "/static/js/main.18465477.chunk.js.map",
 | 
					    "main.js.map": "/static/js/main.d87e6a43.chunk.js.map",
 | 
				
			||||||
    "runtime-main.js": "/static/js/runtime-main.464ec2ca.js",
 | 
					    "runtime-main.js": "/static/js/runtime-main.464ec2ca.js",
 | 
				
			||||||
    "runtime-main.js.map": "/static/js/runtime-main.464ec2ca.js.map",
 | 
					    "runtime-main.js.map": "/static/js/runtime-main.464ec2ca.js.map",
 | 
				
			||||||
    "static/js/2.08d2122d.chunk.js": "/static/js/2.08d2122d.chunk.js",
 | 
					    "static/js/2.d97c563d.chunk.js": "/static/js/2.d97c563d.chunk.js",
 | 
				
			||||||
    "static/js/2.08d2122d.chunk.js.map": "/static/js/2.08d2122d.chunk.js.map",
 | 
					    "static/js/2.d97c563d.chunk.js.map": "/static/js/2.d97c563d.chunk.js.map",
 | 
				
			||||||
    "index.html": "/index.html",
 | 
					    "index.html": "/index.html",
 | 
				
			||||||
    "static/css/main.b0b9a9d7.chunk.css.map": "/static/css/main.b0b9a9d7.chunk.css.map",
 | 
					    "static/css/main.05f8352a.chunk.css.map": "/static/css/main.05f8352a.chunk.css.map",
 | 
				
			||||||
    "static/js/2.08d2122d.chunk.js.LICENSE.txt": "/static/js/2.08d2122d.chunk.js.LICENSE.txt",
 | 
					    "static/js/2.d97c563d.chunk.js.LICENSE.txt": "/static/js/2.d97c563d.chunk.js.LICENSE.txt",
 | 
				
			||||||
    "static/media/en.9e6dbfb0.svg": "/static/media/en.9e6dbfb0.svg",
 | 
					    "static/media/en.9e6dbfb0.svg": "/static/media/en.9e6dbfb0.svg",
 | 
				
			||||||
    "static/media/fr.d0f9280c.svg": "/static/media/fr.d0f9280c.svg",
 | 
					    "static/media/fr.d0f9280c.svg": "/static/media/fr.d0f9280c.svg",
 | 
				
			||||||
    "static/media/mail-send.619079f0.svg": "/static/media/mail-send.619079f0.svg",
 | 
					    "static/media/mail-send.619079f0.svg": "/static/media/mail-send.619079f0.svg",
 | 
				
			||||||
@@ -17,8 +17,8 @@
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
  "entrypoints": [
 | 
					  "entrypoints": [
 | 
				
			||||||
    "static/js/runtime-main.464ec2ca.js",
 | 
					    "static/js/runtime-main.464ec2ca.js",
 | 
				
			||||||
    "static/js/2.08d2122d.chunk.js",
 | 
					    "static/js/2.d97c563d.chunk.js",
 | 
				
			||||||
    "static/css/main.b0b9a9d7.chunk.css",
 | 
					    "static/css/main.05f8352a.chunk.css",
 | 
				
			||||||
    "static/js/main.18465477.chunk.js"
 | 
					    "static/js/main.d87e6a43.chunk.js"
 | 
				
			||||||
  ]
 | 
					  ]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								fittrackee/dist/img/sports/alpine-skiing.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 9.7 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								fittrackee/dist/img/sports/cross-country-skiing.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 6.5 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								fittrackee/dist/img/sports/electric-mountain-biking.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 16 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								fittrackee/dist/img/sports/rowing.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 7.1 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								fittrackee/dist/img/sports/trail.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 8.1 KiB  | 
							
								
								
									
										2
									
								
								fittrackee/dist/index.html
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -1 +1 @@
 | 
				
			|||||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="#000000"><link rel="manifest" href="/manifest.json"><link rel="shortcut icon" href="/favicon.ico"><link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fork-awesome@1.1.7/css/fork-awesome.min.css" integrity="sha256-gsmEoJAws/Kd3CjuOQzLie5Q3yshhvmo7YNtBG7aaEY=" crossorigin="anonymous"><link rel="stylesheet" href="https://cdn.jsdelivr.net/foundation-icons/3.0/foundation-icons.min.css"><link rel="stylesheet" href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css" integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ==" crossorigin=""><title>FitTrackee</title><link href="/static/css/main.b0b9a9d7.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script><script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script><script type="text/javascript">$(document).ready((function(){$("li.nav-item").click((function(){$("button.navbar-toggler").toggleClass("collapsed"),$("#navbarSupportedContent").toggleClass("show")}))}))</script><script>!function(e){function t(t){for(var n,i,l=t[0],f=t[1],a=t[2],p=0,s=[];p<l.length;p++)i=l[p],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&s.push(o[i][0]),o[i]=0;for(n in f)Object.prototype.hasOwnProperty.call(f,n)&&(e[n]=f[n]);for(c&&c(t);s.length;)s.shift()();return u.push.apply(u,a||[]),r()}function r(){for(var e,t=0;t<u.length;t++){for(var r=u[t],n=!0,l=1;l<r.length;l++){var f=r[l];0!==o[f]&&(n=!1)}n&&(u.splice(t--,1),e=i(i.s=r[0]))}return e}var n={},o={1:0},u=[];function i(t){if(n[t])return n[t].exports;var r=n[t]={i:t,l:!1,exports:{}};return e[t].call(r.exports,r,r.exports,i),r.l=!0,r.exports}i.m=e,i.c=n,i.d=function(e,t,r){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,t){if(1&t&&(e=i(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(i.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)i.d(r,n,function(t){return e[t]}.bind(null,n));return r},i.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="/";var l=this.webpackJsonpfittrackee_client=this.webpackJsonpfittrackee_client||[],f=l.push.bind(l);l.push=t,l=l.slice();for(var a=0;a<l.length;a++)t(l[a]);var c=f;r()}([])</script><script src="/static/js/2.08d2122d.chunk.js"></script><script src="/static/js/main.18465477.chunk.js"></script></body></html>
 | 
					<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="#000000"><link rel="manifest" href="/manifest.json"><link rel="shortcut icon" href="/favicon.ico"><link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fork-awesome@1.1.7/css/fork-awesome.min.css" integrity="sha256-gsmEoJAws/Kd3CjuOQzLie5Q3yshhvmo7YNtBG7aaEY=" crossorigin="anonymous"><link rel="stylesheet" href="https://cdn.jsdelivr.net/foundation-icons/3.0/foundation-icons.min.css"><link rel="stylesheet" href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css" integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ==" crossorigin=""><title>FitTrackee</title><link href="/static/css/main.05f8352a.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script><script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script><script type="text/javascript">$(document).ready((function(){$("li.nav-item").click((function(){$("button.navbar-toggler").toggleClass("collapsed"),$("#navbarSupportedContent").toggleClass("show")}))}))</script><script>!function(e){function t(t){for(var n,i,l=t[0],f=t[1],a=t[2],p=0,s=[];p<l.length;p++)i=l[p],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&s.push(o[i][0]),o[i]=0;for(n in f)Object.prototype.hasOwnProperty.call(f,n)&&(e[n]=f[n]);for(c&&c(t);s.length;)s.shift()();return u.push.apply(u,a||[]),r()}function r(){for(var e,t=0;t<u.length;t++){for(var r=u[t],n=!0,l=1;l<r.length;l++){var f=r[l];0!==o[f]&&(n=!1)}n&&(u.splice(t--,1),e=i(i.s=r[0]))}return e}var n={},o={1:0},u=[];function i(t){if(n[t])return n[t].exports;var r=n[t]={i:t,l:!1,exports:{}};return e[t].call(r.exports,r,r.exports,i),r.l=!0,r.exports}i.m=e,i.c=n,i.d=function(e,t,r){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,t){if(1&t&&(e=i(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(i.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)i.d(r,n,function(t){return e[t]}.bind(null,n));return r},i.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="/";var l=this.webpackJsonpfittrackee_client=this.webpackJsonpfittrackee_client||[],f=l.push.bind(l);l.push=t,l=l.slice();for(var a=0;a<l.length;a++)t(l[a]);var c=f;r()}([])</script><script src="/static/js/2.d97c563d.chunk.js"></script><script src="/static/js/main.d87e6a43.chunk.js"></script></body></html>
 | 
				
			||||||
							
								
								
									
										2
									
								
								fittrackee/dist/static/css/main.05f8352a.chunk.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										1
									
								
								fittrackee/dist/static/css/main.05f8352a.chunk.css.map
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										1
									
								
								fittrackee/dist/static/js/2.d97c563d.chunk.js.map
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										2
									
								
								fittrackee/dist/static/js/main.d87e6a43.chunk.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										1
									
								
								fittrackee/dist/static/js/main.d87e6a43.chunk.js.map
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@@ -51,6 +51,19 @@ def upgrade():
 | 
				
			|||||||
    )
 | 
					    )
 | 
				
			||||||
    # ### end Alembic commands ###
 | 
					    # ### end Alembic commands ###
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    op.execute(
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        INSERT INTO sports (label, img, is_default)
 | 
				
			||||||
 | 
					        VALUES 
 | 
				
			||||||
 | 
					            ('Cycling (Sport)', '/img/sports/cycling-sport.png', True),
 | 
				
			||||||
 | 
					            ('Cycling (Transport)', '/img/sports/cycling-transport.png', True),
 | 
				
			||||||
 | 
					            ('Hiking', '/img/sports/hiking.png', True),
 | 
				
			||||||
 | 
					            ('Mountain Biking', '/img/sports/mountain-biking.png', True),
 | 
				
			||||||
 | 
					            ('Running', '/img/sports/running.png', True),
 | 
				
			||||||
 | 
					            ('Walking', '/img/sports/walking.png', True)
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def downgrade():
 | 
					def downgrade():
 | 
				
			||||||
    # ### commands auto generated by Alembic - please adjust! ###
 | 
					    # ### commands auto generated by Alembic - please adjust! ###
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,43 @@
 | 
				
			|||||||
 | 
					""" Add new sports
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Revision ID: cee0830497f8
 | 
				
			||||||
 | 
					Revises: 4e8597c50064
 | 
				
			||||||
 | 
					Create Date: 2021-08-25 13:58:52.333603
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					from alembic import op
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# revision identifiers, used by Alembic.
 | 
				
			||||||
 | 
					revision = 'cee0830497f8'
 | 
				
			||||||
 | 
					down_revision = '4e8597c50064'
 | 
				
			||||||
 | 
					branch_labels = None
 | 
				
			||||||
 | 
					depends_on = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def upgrade():
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    op.execute(
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        INSERT INTO sports (label, img, is_active)
 | 
				
			||||||
 | 
					        VALUES 
 | 
				
			||||||
 | 
					            ('Mountain Biking (Electric)', '/img/sports/electric-mountain-biking.png', True),
 | 
				
			||||||
 | 
					            ('Trail', '/img/sports/trail.png', True),
 | 
				
			||||||
 | 
					            ('Skiing (Alpine)', '/img/sports/alpine-skiing.png', True),
 | 
				
			||||||
 | 
					            ('Skiing (Cross Country)', '/img/sports/cross-country-skiing.png', True),
 | 
				
			||||||
 | 
					            ('Rowing', '/img/sports/rowing.png', True)
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def downgrade():
 | 
				
			||||||
 | 
					    op.execute(
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        DELETE FROM sports
 | 
				
			||||||
 | 
					        WHERE label = 'Mountain Biking (Electric)'
 | 
				
			||||||
 | 
					           OR label = 'Trail'
 | 
				
			||||||
 | 
					           OR label = 'Skiing (Alpine)'
 | 
				
			||||||
 | 
					           OR label = 'Skiing (Cross Country)'
 | 
				
			||||||
 | 
					           OR label = 'Rowing';
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
							
								
								
									
										18
									
								
								fittrackee/tests/fixtures/fixtures_workouts.py
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -108,8 +108,11 @@ def seven_workouts_user_1() -> Workout:
 | 
				
			|||||||
    )
 | 
					    )
 | 
				
			||||||
    workout.ave_speed = float(workout.distance) / (1024 / 3600)
 | 
					    workout.ave_speed = float(workout.distance) / (1024 / 3600)
 | 
				
			||||||
    workout.moving = workout.duration
 | 
					    workout.moving = workout.duration
 | 
				
			||||||
 | 
					    workout.ascent = 120
 | 
				
			||||||
 | 
					    workout.descent = 200
 | 
				
			||||||
    db.session.add(workout)
 | 
					    db.session.add(workout)
 | 
				
			||||||
    db.session.flush()
 | 
					    db.session.flush()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    workout = Workout(
 | 
					    workout = Workout(
 | 
				
			||||||
        user_id=1,
 | 
					        user_id=1,
 | 
				
			||||||
        sport_id=1,
 | 
					        sport_id=1,
 | 
				
			||||||
@@ -119,8 +122,11 @@ def seven_workouts_user_1() -> Workout:
 | 
				
			|||||||
    )
 | 
					    )
 | 
				
			||||||
    workout.ave_speed = float(workout.distance) / (3456 / 3600)
 | 
					    workout.ave_speed = float(workout.distance) / (3456 / 3600)
 | 
				
			||||||
    workout.moving = workout.duration
 | 
					    workout.moving = workout.duration
 | 
				
			||||||
 | 
					    workout.ascent = 100
 | 
				
			||||||
 | 
					    workout.descent = 80
 | 
				
			||||||
    db.session.add(workout)
 | 
					    db.session.add(workout)
 | 
				
			||||||
    db.session.flush()
 | 
					    db.session.flush()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    workout = Workout(
 | 
					    workout = Workout(
 | 
				
			||||||
        user_id=1,
 | 
					        user_id=1,
 | 
				
			||||||
        sport_id=1,
 | 
					        sport_id=1,
 | 
				
			||||||
@@ -130,8 +136,11 @@ def seven_workouts_user_1() -> Workout:
 | 
				
			|||||||
    )
 | 
					    )
 | 
				
			||||||
    workout.ave_speed = float(workout.distance) / (1024 / 3600)
 | 
					    workout.ave_speed = float(workout.distance) / (1024 / 3600)
 | 
				
			||||||
    workout.moving = workout.duration
 | 
					    workout.moving = workout.duration
 | 
				
			||||||
 | 
					    workout.ascent = 80
 | 
				
			||||||
 | 
					    workout.descent = 100
 | 
				
			||||||
    db.session.add(workout)
 | 
					    db.session.add(workout)
 | 
				
			||||||
    db.session.flush()
 | 
					    db.session.flush()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    workout = Workout(
 | 
					    workout = Workout(
 | 
				
			||||||
        user_id=1,
 | 
					        user_id=1,
 | 
				
			||||||
        sport_id=1,
 | 
					        sport_id=1,
 | 
				
			||||||
@@ -141,8 +150,11 @@ def seven_workouts_user_1() -> Workout:
 | 
				
			|||||||
    )
 | 
					    )
 | 
				
			||||||
    workout.ave_speed = float(workout.distance) / (600 / 3600)
 | 
					    workout.ave_speed = float(workout.distance) / (600 / 3600)
 | 
				
			||||||
    workout.moving = workout.duration
 | 
					    workout.moving = workout.duration
 | 
				
			||||||
 | 
					    workout.ascent = 120
 | 
				
			||||||
 | 
					    workout.descent = 180
 | 
				
			||||||
    db.session.add(workout)
 | 
					    db.session.add(workout)
 | 
				
			||||||
    db.session.flush()
 | 
					    db.session.flush()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    workout = Workout(
 | 
					    workout = Workout(
 | 
				
			||||||
        user_id=1,
 | 
					        user_id=1,
 | 
				
			||||||
        sport_id=1,
 | 
					        sport_id=1,
 | 
				
			||||||
@@ -152,8 +164,11 @@ def seven_workouts_user_1() -> Workout:
 | 
				
			|||||||
    )
 | 
					    )
 | 
				
			||||||
    workout.ave_speed = float(workout.distance) / (1000 / 3600)
 | 
					    workout.ave_speed = float(workout.distance) / (1000 / 3600)
 | 
				
			||||||
    workout.moving = workout.duration
 | 
					    workout.moving = workout.duration
 | 
				
			||||||
 | 
					    workout.ascent = 100
 | 
				
			||||||
 | 
					    workout.descent = 200
 | 
				
			||||||
    db.session.add(workout)
 | 
					    db.session.add(workout)
 | 
				
			||||||
    db.session.flush()
 | 
					    db.session.flush()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    workout = Workout(
 | 
					    workout = Workout(
 | 
				
			||||||
        user_id=1,
 | 
					        user_id=1,
 | 
				
			||||||
        sport_id=1,
 | 
					        sport_id=1,
 | 
				
			||||||
@@ -163,8 +178,11 @@ def seven_workouts_user_1() -> Workout:
 | 
				
			|||||||
    )
 | 
					    )
 | 
				
			||||||
    workout.ave_speed = float(workout.distance) / (6000 / 3600)
 | 
					    workout.ave_speed = float(workout.distance) / (6000 / 3600)
 | 
				
			||||||
    workout.moving = workout.duration
 | 
					    workout.moving = workout.duration
 | 
				
			||||||
 | 
					    workout.ascent = 40
 | 
				
			||||||
 | 
					    workout.descent = 20
 | 
				
			||||||
    db.session.add(workout)
 | 
					    db.session.add(workout)
 | 
				
			||||||
    db.session.flush()
 | 
					    db.session.flush()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    workout = Workout(
 | 
					    workout = Workout(
 | 
				
			||||||
        user_id=1,
 | 
					        user_id=1,
 | 
				
			||||||
        sport_id=1,
 | 
					        sport_id=1,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -110,6 +110,8 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2017': {
 | 
					            '2017': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 2,
 | 
					                    'nb_workouts': 2,
 | 
				
			||||||
 | 
					                    'total_ascent': 220.0,
 | 
				
			||||||
 | 
					                    'total_descent': 280.0,
 | 
				
			||||||
                    'total_distance': 15.0,
 | 
					                    'total_distance': 15.0,
 | 
				
			||||||
                    'total_duration': 4480,
 | 
					                    'total_duration': 4480,
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -117,11 +119,15 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2018': {
 | 
					            '2018': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 5,
 | 
					                    'nb_workouts': 5,
 | 
				
			||||||
 | 
					                    'total_ascent': 340.0,
 | 
				
			||||||
 | 
					                    'total_descent': 500.0,
 | 
				
			||||||
                    'total_distance': 39.0,
 | 
					                    'total_distance': 39.0,
 | 
				
			||||||
                    'total_duration': 11624,
 | 
					                    'total_duration': 11624,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                '2': {
 | 
					                '2': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 0.0,
 | 
				
			||||||
 | 
					                    'total_descent': 0.0,
 | 
				
			||||||
                    'total_distance': 12.0,
 | 
					                    'total_distance': 12.0,
 | 
				
			||||||
                    'total_duration': 6000,
 | 
					                    'total_duration': 6000,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
@@ -151,11 +157,15 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2018': {
 | 
					            '2018': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 40.0,
 | 
				
			||||||
 | 
					                    'total_descent': 20.0,
 | 
				
			||||||
                    'total_distance': 8.0,
 | 
					                    'total_distance': 8.0,
 | 
				
			||||||
                    'total_duration': 6000,
 | 
					                    'total_duration': 6000,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                '2': {
 | 
					                '2': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 0.0,
 | 
				
			||||||
 | 
					                    'total_descent': 0.0,
 | 
				
			||||||
                    'total_distance': 12.0,
 | 
					                    'total_distance': 12.0,
 | 
				
			||||||
                    'total_duration': 6000,
 | 
					                    'total_duration': 6000,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
@@ -186,11 +196,15 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2018': {
 | 
					            '2018': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 40.0,
 | 
				
			||||||
 | 
					                    'total_descent': 20.0,
 | 
				
			||||||
                    'total_distance': 8.0,
 | 
					                    'total_distance': 8.0,
 | 
				
			||||||
                    'total_duration': 6000,
 | 
					                    'total_duration': 6000,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                '2': {
 | 
					                '2': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 0.0,
 | 
				
			||||||
 | 
					                    'total_descent': 0.0,
 | 
				
			||||||
                    'total_distance': 12.0,
 | 
					                    'total_distance': 12.0,
 | 
				
			||||||
                    'total_duration': 6000,
 | 
					                    'total_duration': 6000,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
@@ -220,6 +234,8 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2017': {
 | 
					            '2017': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 2,
 | 
					                    'nb_workouts': 2,
 | 
				
			||||||
 | 
					                    'total_ascent': 220.0,
 | 
				
			||||||
 | 
					                    'total_descent': 280.0,
 | 
				
			||||||
                    'total_distance': 15.0,
 | 
					                    'total_distance': 15.0,
 | 
				
			||||||
                    'total_duration': 4480,
 | 
					                    'total_duration': 4480,
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -227,11 +243,15 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2018': {
 | 
					            '2018': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 5,
 | 
					                    'nb_workouts': 5,
 | 
				
			||||||
 | 
					                    'total_ascent': 340.0,
 | 
				
			||||||
 | 
					                    'total_descent': 500.0,
 | 
				
			||||||
                    'total_distance': 39.0,
 | 
					                    'total_distance': 39.0,
 | 
				
			||||||
                    'total_duration': 11624,
 | 
					                    'total_duration': 11624,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                '2': {
 | 
					                '2': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 0.0,
 | 
				
			||||||
 | 
					                    'total_descent': 0.0,
 | 
				
			||||||
                    'total_distance': 12.0,
 | 
					                    'total_distance': 12.0,
 | 
				
			||||||
                    'total_duration': 6000,
 | 
					                    'total_duration': 6000,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
@@ -261,11 +281,15 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2018': {
 | 
					            '2018': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 40.0,
 | 
				
			||||||
 | 
					                    'total_descent': 20.0,
 | 
				
			||||||
                    'total_distance': 8.0,
 | 
					                    'total_distance': 8.0,
 | 
				
			||||||
                    'total_duration': 6000,
 | 
					                    'total_duration': 6000,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                '2': {
 | 
					                '2': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 0.0,
 | 
				
			||||||
 | 
					                    'total_descent': 0.0,
 | 
				
			||||||
                    'total_distance': 12.0,
 | 
					                    'total_distance': 12.0,
 | 
				
			||||||
                    'total_duration': 6000,
 | 
					                    'total_duration': 6000,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
@@ -296,11 +320,15 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2018': {
 | 
					            '2018': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 40.0,
 | 
				
			||||||
 | 
					                    'total_descent': 20.0,
 | 
				
			||||||
                    'total_distance': 8.0,
 | 
					                    'total_distance': 8.0,
 | 
				
			||||||
                    'total_duration': 6000,
 | 
					                    'total_duration': 6000,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                '2': {
 | 
					                '2': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 0.0,
 | 
				
			||||||
 | 
					                    'total_descent': 0.0,
 | 
				
			||||||
                    'total_distance': 12.0,
 | 
					                    'total_distance': 12.0,
 | 
				
			||||||
                    'total_duration': 6000,
 | 
					                    'total_duration': 6000,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
@@ -330,6 +358,8 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2017-03': {
 | 
					            '2017-03': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 120.0,
 | 
				
			||||||
 | 
					                    'total_descent': 200.0,
 | 
				
			||||||
                    'total_distance': 5.0,
 | 
					                    'total_distance': 5.0,
 | 
				
			||||||
                    'total_duration': 1024,
 | 
					                    'total_duration': 1024,
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -337,6 +367,8 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2017-06': {
 | 
					            '2017-06': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 100.0,
 | 
				
			||||||
 | 
					                    'total_descent': 80.0,
 | 
				
			||||||
                    'total_distance': 10.0,
 | 
					                    'total_distance': 10.0,
 | 
				
			||||||
                    'total_duration': 3456,
 | 
					                    'total_duration': 3456,
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -344,6 +376,8 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2018-01': {
 | 
					            '2018-01': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 80.0,
 | 
				
			||||||
 | 
					                    'total_descent': 100.0,
 | 
				
			||||||
                    'total_distance': 10.0,
 | 
					                    'total_distance': 10.0,
 | 
				
			||||||
                    'total_duration': 1024,
 | 
					                    'total_duration': 1024,
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -351,6 +385,8 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2018-02': {
 | 
					            '2018-02': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 2,
 | 
					                    'nb_workouts': 2,
 | 
				
			||||||
 | 
					                    'total_ascent': 220.0,
 | 
				
			||||||
 | 
					                    'total_descent': 380.0,
 | 
				
			||||||
                    'total_distance': 11.0,
 | 
					                    'total_distance': 11.0,
 | 
				
			||||||
                    'total_duration': 1600,
 | 
					                    'total_duration': 1600,
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -358,11 +394,15 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2018-04': {
 | 
					            '2018-04': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 40.0,
 | 
				
			||||||
 | 
					                    'total_descent': 20.0,
 | 
				
			||||||
                    'total_distance': 8.0,
 | 
					                    'total_distance': 8.0,
 | 
				
			||||||
                    'total_duration': 6000,
 | 
					                    'total_duration': 6000,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                '2': {
 | 
					                '2': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 0.0,
 | 
				
			||||||
 | 
					                    'total_descent': 0.0,
 | 
				
			||||||
                    'total_distance': 12.0,
 | 
					                    'total_distance': 12.0,
 | 
				
			||||||
                    'total_duration': 6000,
 | 
					                    'total_duration': 6000,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
@@ -370,6 +410,8 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2018-05': {
 | 
					            '2018-05': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 0.0,
 | 
				
			||||||
 | 
					                    'total_descent': 0.0,
 | 
				
			||||||
                    'total_distance': 10.0,
 | 
					                    'total_distance': 10.0,
 | 
				
			||||||
                    'total_duration': 3000,
 | 
					                    'total_duration': 3000,
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -399,6 +441,8 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2017-03': {
 | 
					            '2017-03': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 120.0,
 | 
				
			||||||
 | 
					                    'total_descent': 200.0,
 | 
				
			||||||
                    'total_distance': 5.0,
 | 
					                    'total_distance': 5.0,
 | 
				
			||||||
                    'total_duration': 1024,
 | 
					                    'total_duration': 1024,
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -406,6 +450,8 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2017-06': {
 | 
					            '2017-06': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 100.0,
 | 
				
			||||||
 | 
					                    'total_descent': 80.0,
 | 
				
			||||||
                    'total_distance': 10.0,
 | 
					                    'total_distance': 10.0,
 | 
				
			||||||
                    'total_duration': 3456,
 | 
					                    'total_duration': 3456,
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -413,6 +459,8 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2018-01': {
 | 
					            '2018-01': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 80.0,
 | 
				
			||||||
 | 
					                    'total_descent': 100.0,
 | 
				
			||||||
                    'total_distance': 10.0,
 | 
					                    'total_distance': 10.0,
 | 
				
			||||||
                    'total_duration': 1024,
 | 
					                    'total_duration': 1024,
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -420,6 +468,8 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2018-02': {
 | 
					            '2018-02': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 2,
 | 
					                    'nb_workouts': 2,
 | 
				
			||||||
 | 
					                    'total_ascent': 220.0,
 | 
				
			||||||
 | 
					                    'total_descent': 380.0,
 | 
				
			||||||
                    'total_distance': 11.0,
 | 
					                    'total_distance': 11.0,
 | 
				
			||||||
                    'total_duration': 1600,
 | 
					                    'total_duration': 1600,
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -427,11 +477,15 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2018-04': {
 | 
					            '2018-04': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 40.0,
 | 
				
			||||||
 | 
					                    'total_descent': 20.0,
 | 
				
			||||||
                    'total_distance': 8.0,
 | 
					                    'total_distance': 8.0,
 | 
				
			||||||
                    'total_duration': 6000,
 | 
					                    'total_duration': 6000,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                '2': {
 | 
					                '2': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 0.0,
 | 
				
			||||||
 | 
					                    'total_descent': 0.0,
 | 
				
			||||||
                    'total_distance': 12.0,
 | 
					                    'total_distance': 12.0,
 | 
				
			||||||
                    'total_duration': 6000,
 | 
					                    'total_duration': 6000,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
@@ -439,6 +493,8 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2018-05': {
 | 
					            '2018-05': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 0.0,
 | 
				
			||||||
 | 
					                    'total_descent': 0.0,
 | 
				
			||||||
                    'total_distance': 10.0,
 | 
					                    'total_distance': 10.0,
 | 
				
			||||||
                    'total_duration': 3000,
 | 
					                    'total_duration': 3000,
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -468,11 +524,15 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2018-04': {
 | 
					            '2018-04': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 40.0,
 | 
				
			||||||
 | 
					                    'total_descent': 20.0,
 | 
				
			||||||
                    'total_distance': 8.0,
 | 
					                    'total_distance': 8.0,
 | 
				
			||||||
                    'total_duration': 6000,
 | 
					                    'total_duration': 6000,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                '2': {
 | 
					                '2': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 0.0,
 | 
				
			||||||
 | 
					                    'total_descent': 0.0,
 | 
				
			||||||
                    'total_distance': 12.0,
 | 
					                    'total_distance': 12.0,
 | 
				
			||||||
                    'total_duration': 6000,
 | 
					                    'total_duration': 6000,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
@@ -502,6 +562,8 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2017-03-19': {
 | 
					            '2017-03-19': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 120.0,
 | 
				
			||||||
 | 
					                    'total_descent': 200.0,
 | 
				
			||||||
                    'total_distance': 5.0,
 | 
					                    'total_distance': 5.0,
 | 
				
			||||||
                    'total_duration': 1024,
 | 
					                    'total_duration': 1024,
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -509,6 +571,8 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2017-05-28': {
 | 
					            '2017-05-28': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 100.0,
 | 
				
			||||||
 | 
					                    'total_descent': 80.0,
 | 
				
			||||||
                    'total_distance': 10.0,
 | 
					                    'total_distance': 10.0,
 | 
				
			||||||
                    'total_duration': 3456,
 | 
					                    'total_duration': 3456,
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -516,6 +580,8 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2017-12-31': {
 | 
					            '2017-12-31': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 80.0,
 | 
				
			||||||
 | 
					                    'total_descent': 100.0,
 | 
				
			||||||
                    'total_distance': 10.0,
 | 
					                    'total_distance': 10.0,
 | 
				
			||||||
                    'total_duration': 1024,
 | 
					                    'total_duration': 1024,
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -523,6 +589,8 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2018-02-18': {
 | 
					            '2018-02-18': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 2,
 | 
					                    'nb_workouts': 2,
 | 
				
			||||||
 | 
					                    'total_ascent': 220.0,
 | 
				
			||||||
 | 
					                    'total_descent': 380.0,
 | 
				
			||||||
                    'total_distance': 11.0,
 | 
					                    'total_distance': 11.0,
 | 
				
			||||||
                    'total_duration': 1600,
 | 
					                    'total_duration': 1600,
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -530,11 +598,15 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2018-04-01': {
 | 
					            '2018-04-01': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 40.0,
 | 
				
			||||||
 | 
					                    'total_descent': 20.0,
 | 
				
			||||||
                    'total_distance': 8.0,
 | 
					                    'total_distance': 8.0,
 | 
				
			||||||
                    'total_duration': 6000,
 | 
					                    'total_duration': 6000,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                '2': {
 | 
					                '2': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 0.0,
 | 
				
			||||||
 | 
					                    'total_descent': 0.0,
 | 
				
			||||||
                    'total_distance': 12.0,
 | 
					                    'total_distance': 12.0,
 | 
				
			||||||
                    'total_duration': 6000,
 | 
					                    'total_duration': 6000,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
@@ -542,6 +614,8 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2018-05-06': {
 | 
					            '2018-05-06': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 0.0,
 | 
				
			||||||
 | 
					                    'total_descent': 0.0,
 | 
				
			||||||
                    'total_distance': 10.0,
 | 
					                    'total_distance': 10.0,
 | 
				
			||||||
                    'total_duration': 3000,
 | 
					                    'total_duration': 3000,
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -571,11 +645,15 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2018-04-01': {
 | 
					            '2018-04-01': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 40.0,
 | 
				
			||||||
 | 
					                    'total_descent': 20.0,
 | 
				
			||||||
                    'total_distance': 8.0,
 | 
					                    'total_distance': 8.0,
 | 
				
			||||||
                    'total_duration': 6000,
 | 
					                    'total_duration': 6000,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                '2': {
 | 
					                '2': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 0.0,
 | 
				
			||||||
 | 
					                    'total_descent': 0.0,
 | 
				
			||||||
                    'total_distance': 12.0,
 | 
					                    'total_distance': 12.0,
 | 
				
			||||||
                    'total_duration': 6000,
 | 
					                    'total_duration': 6000,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
@@ -605,6 +683,8 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2017-03-20': {
 | 
					            '2017-03-20': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 120.0,
 | 
				
			||||||
 | 
					                    'total_descent': 200.0,
 | 
				
			||||||
                    'total_distance': 5.0,
 | 
					                    'total_distance': 5.0,
 | 
				
			||||||
                    'total_duration': 1024,
 | 
					                    'total_duration': 1024,
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -612,6 +692,8 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2017-05-29': {
 | 
					            '2017-05-29': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 100.0,
 | 
				
			||||||
 | 
					                    'total_descent': 80.0,
 | 
				
			||||||
                    'total_distance': 10.0,
 | 
					                    'total_distance': 10.0,
 | 
				
			||||||
                    'total_duration': 3456,
 | 
					                    'total_duration': 3456,
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -619,6 +701,8 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2018-01-01': {
 | 
					            '2018-01-01': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 80.0,
 | 
				
			||||||
 | 
					                    'total_descent': 100.0,
 | 
				
			||||||
                    'total_distance': 10.0,
 | 
					                    'total_distance': 10.0,
 | 
				
			||||||
                    'total_duration': 1024,
 | 
					                    'total_duration': 1024,
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -626,6 +710,8 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2018-02-19': {
 | 
					            '2018-02-19': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 2,
 | 
					                    'nb_workouts': 2,
 | 
				
			||||||
 | 
					                    'total_ascent': 220.0,
 | 
				
			||||||
 | 
					                    'total_descent': 380.0,
 | 
				
			||||||
                    'total_distance': 11.0,
 | 
					                    'total_distance': 11.0,
 | 
				
			||||||
                    'total_duration': 1600,
 | 
					                    'total_duration': 1600,
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -633,11 +719,15 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2018-03-26': {
 | 
					            '2018-03-26': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 40.0,
 | 
				
			||||||
 | 
					                    'total_descent': 20.0,
 | 
				
			||||||
                    'total_distance': 8.0,
 | 
					                    'total_distance': 8.0,
 | 
				
			||||||
                    'total_duration': 6000,
 | 
					                    'total_duration': 6000,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                '2': {
 | 
					                '2': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 0.0,
 | 
				
			||||||
 | 
					                    'total_descent': 0.0,
 | 
				
			||||||
                    'total_distance': 12.0,
 | 
					                    'total_distance': 12.0,
 | 
				
			||||||
                    'total_duration': 6000,
 | 
					                    'total_duration': 6000,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
@@ -645,6 +735,8 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2018-05-07': {
 | 
					            '2018-05-07': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 0.0,
 | 
				
			||||||
 | 
					                    'total_descent': 0.0,
 | 
				
			||||||
                    'total_distance': 10.0,
 | 
					                    'total_distance': 10.0,
 | 
				
			||||||
                    'total_duration': 3000,
 | 
					                    'total_duration': 3000,
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -674,11 +766,15 @@ class TestGetStatsByTime(ApiTestCaseMixin):
 | 
				
			|||||||
            '2018-03-26': {
 | 
					            '2018-03-26': {
 | 
				
			||||||
                '1': {
 | 
					                '1': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 40.0,
 | 
				
			||||||
 | 
					                    'total_descent': 20.0,
 | 
				
			||||||
                    'total_distance': 8.0,
 | 
					                    'total_distance': 8.0,
 | 
				
			||||||
                    'total_duration': 6000,
 | 
					                    'total_duration': 6000,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                '2': {
 | 
					                '2': {
 | 
				
			||||||
                    'nb_workouts': 1,
 | 
					                    'nb_workouts': 1,
 | 
				
			||||||
 | 
					                    'total_ascent': 0.0,
 | 
				
			||||||
 | 
					                    'total_descent': 0.0,
 | 
				
			||||||
                    'total_distance': 12.0,
 | 
					                    'total_distance': 12.0,
 | 
				
			||||||
                    'total_duration': 6000,
 | 
					                    'total_duration': 6000,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
@@ -709,11 +805,15 @@ class TestGetStatsBySport(ApiTestCaseMixin):
 | 
				
			|||||||
        assert data['data']['statistics'] == {
 | 
					        assert data['data']['statistics'] == {
 | 
				
			||||||
            '1': {
 | 
					            '1': {
 | 
				
			||||||
                'nb_workouts': 7,
 | 
					                'nb_workouts': 7,
 | 
				
			||||||
 | 
					                'total_ascent': 560.0,
 | 
				
			||||||
 | 
					                'total_descent': 780.0,
 | 
				
			||||||
                'total_distance': 54.0,
 | 
					                'total_distance': 54.0,
 | 
				
			||||||
                'total_duration': 16104,
 | 
					                'total_duration': 16104,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            '2': {
 | 
					            '2': {
 | 
				
			||||||
                'nb_workouts': 1,
 | 
					                'nb_workouts': 1,
 | 
				
			||||||
 | 
					                'total_ascent': 0.0,
 | 
				
			||||||
 | 
					                'total_descent': 0.0,
 | 
				
			||||||
                'total_distance': 12.0,
 | 
					                'total_distance': 12.0,
 | 
				
			||||||
                'total_duration': 6000,
 | 
					                'total_duration': 6000,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
@@ -741,6 +841,8 @@ class TestGetStatsBySport(ApiTestCaseMixin):
 | 
				
			|||||||
        assert data['data']['statistics'] == {
 | 
					        assert data['data']['statistics'] == {
 | 
				
			||||||
            '1': {
 | 
					            '1': {
 | 
				
			||||||
                'nb_workouts': 7,
 | 
					                'nb_workouts': 7,
 | 
				
			||||||
 | 
					                'total_ascent': 560.0,
 | 
				
			||||||
 | 
					                'total_descent': 780.0,
 | 
				
			||||||
                'total_distance': 54.0,
 | 
					                'total_distance': 54.0,
 | 
				
			||||||
                'total_duration': 16104,
 | 
					                'total_duration': 16104,
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -67,6 +67,8 @@ def get_workouts(
 | 
				
			|||||||
                        'nb_workouts': 0,
 | 
					                        'nb_workouts': 0,
 | 
				
			||||||
                        'total_distance': 0.0,
 | 
					                        'total_distance': 0.0,
 | 
				
			||||||
                        'total_duration': 0,
 | 
					                        'total_duration': 0,
 | 
				
			||||||
 | 
					                        'total_ascent': 0.0,
 | 
				
			||||||
 | 
					                        'total_descent': 0.0,
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                workouts_list_by_sport[sport_id]['nb_workouts'] += 1
 | 
					                workouts_list_by_sport[sport_id]['nb_workouts'] += 1
 | 
				
			||||||
                workouts_list_by_sport[sport_id]['total_distance'] += float(
 | 
					                workouts_list_by_sport[sport_id]['total_distance'] += float(
 | 
				
			||||||
@@ -75,6 +77,14 @@ def get_workouts(
 | 
				
			|||||||
                workouts_list_by_sport[sport_id][
 | 
					                workouts_list_by_sport[sport_id][
 | 
				
			||||||
                    'total_duration'
 | 
					                    'total_duration'
 | 
				
			||||||
                ] += convert_timedelta_to_integer(workout.moving)
 | 
					                ] += convert_timedelta_to_integer(workout.moving)
 | 
				
			||||||
 | 
					                if workout.ascent:
 | 
				
			||||||
 | 
					                    workouts_list_by_sport[sport_id]['total_ascent'] += float(
 | 
				
			||||||
 | 
					                        workout.ascent
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                if workout.descent:
 | 
				
			||||||
 | 
					                    workouts_list_by_sport[sport_id]['total_descent'] += float(
 | 
				
			||||||
 | 
					                        workout.descent
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # filter_type == 'by_time'
 | 
					            # filter_type == 'by_time'
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
@@ -110,6 +120,8 @@ def get_workouts(
 | 
				
			|||||||
                        'nb_workouts': 0,
 | 
					                        'nb_workouts': 0,
 | 
				
			||||||
                        'total_distance': 0.0,
 | 
					                        'total_distance': 0.0,
 | 
				
			||||||
                        'total_duration': 0,
 | 
					                        'total_duration': 0,
 | 
				
			||||||
 | 
					                        'total_ascent': 0.0,
 | 
				
			||||||
 | 
					                        'total_descent': 0.0,
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                workouts_list_by_time[time_period][sport_id][
 | 
					                workouts_list_by_time[time_period][sport_id][
 | 
				
			||||||
                    'nb_workouts'
 | 
					                    'nb_workouts'
 | 
				
			||||||
@@ -120,7 +132,14 @@ def get_workouts(
 | 
				
			|||||||
                workouts_list_by_time[time_period][sport_id][
 | 
					                workouts_list_by_time[time_period][sport_id][
 | 
				
			||||||
                    'total_duration'
 | 
					                    'total_duration'
 | 
				
			||||||
                ] += convert_timedelta_to_integer(workout.moving)
 | 
					                ] += convert_timedelta_to_integer(workout.moving)
 | 
				
			||||||
 | 
					                if workout.ascent:
 | 
				
			||||||
 | 
					                    workouts_list_by_time[time_period][sport_id][
 | 
				
			||||||
 | 
					                        'total_ascent'
 | 
				
			||||||
 | 
					                    ] += float(workout.ascent)
 | 
				
			||||||
 | 
					                if workout.descent:
 | 
				
			||||||
 | 
					                    workouts_list_by_time[time_period][sport_id][
 | 
				
			||||||
 | 
					                        'total_descent'
 | 
				
			||||||
 | 
					                    ] += float(workout.descent)
 | 
				
			||||||
        return {
 | 
					        return {
 | 
				
			||||||
            'status': 'success',
 | 
					            'status': 'success',
 | 
				
			||||||
            'data': {
 | 
					            'data': {
 | 
				
			||||||
@@ -171,6 +190,8 @@ def get_workouts_by_time(
 | 
				
			|||||||
            "2017": {
 | 
					            "2017": {
 | 
				
			||||||
              "3": {
 | 
					              "3": {
 | 
				
			||||||
                "nb_workouts": 2,
 | 
					                "nb_workouts": 2,
 | 
				
			||||||
 | 
					                "total_ascent": 203.0,
 | 
				
			||||||
 | 
					                "total_ascent": 156.0,
 | 
				
			||||||
                "total_distance": 15.282,
 | 
					                "total_distance": 15.282,
 | 
				
			||||||
                "total_duration": 12341
 | 
					                "total_duration": 12341
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
@@ -178,11 +199,15 @@ def get_workouts_by_time(
 | 
				
			|||||||
            "2019": {
 | 
					            "2019": {
 | 
				
			||||||
              "1": {
 | 
					              "1": {
 | 
				
			||||||
                "nb_workouts": 3,
 | 
					                "nb_workouts": 3,
 | 
				
			||||||
 | 
					                "total_ascent": 150.0,
 | 
				
			||||||
 | 
					                "total_ascent": 178.0,
 | 
				
			||||||
                "total_distance": 47,
 | 
					                "total_distance": 47,
 | 
				
			||||||
                "total_duration": 9960
 | 
					                "total_duration": 9960
 | 
				
			||||||
              },
 | 
					              },
 | 
				
			||||||
              "2": {
 | 
					              "2": {
 | 
				
			||||||
                "nb_workouts": 1,
 | 
					                "nb_workouts": 1,
 | 
				
			||||||
 | 
					                "total_ascent": 46.0,
 | 
				
			||||||
 | 
					                "total_ascent": 78.0,
 | 
				
			||||||
                "total_distance": 5.613,
 | 
					                "total_distance": 5.613,
 | 
				
			||||||
                "total_duration": 1267
 | 
					                "total_duration": 1267
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
@@ -268,16 +293,22 @@ def get_workouts_by_sport(
 | 
				
			|||||||
          "statistics": {
 | 
					          "statistics": {
 | 
				
			||||||
            "1": {
 | 
					            "1": {
 | 
				
			||||||
              "nb_workouts": 3,
 | 
					              "nb_workouts": 3,
 | 
				
			||||||
 | 
					              "total_ascent": 150.0,
 | 
				
			||||||
 | 
					              "total_ascent": 178.0,
 | 
				
			||||||
              "total_distance": 47,
 | 
					              "total_distance": 47,
 | 
				
			||||||
              "total_duration": 9960
 | 
					              "total_duration": 9960
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "2": {
 | 
					            "2": {
 | 
				
			||||||
              "nb_workouts": 1,
 | 
					              "nb_workouts": 1,
 | 
				
			||||||
 | 
					              "total_ascent": 46.0,
 | 
				
			||||||
 | 
					              "total_ascent": 78.0,
 | 
				
			||||||
              "total_distance": 5.613,
 | 
					              "total_distance": 5.613,
 | 
				
			||||||
              "total_duration": 1267
 | 
					              "total_duration": 1267
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "3": {
 | 
					            "3": {
 | 
				
			||||||
              "nb_workouts": 2,
 | 
					              "nb_workouts": 2,
 | 
				
			||||||
 | 
					              "total_ascent": 203.0,
 | 
				
			||||||
 | 
					              "total_ascent": 156.0,
 | 
				
			||||||
              "total_distance": 15.282,
 | 
					              "total_distance": 15.282,
 | 
				
			||||||
              "total_duration": 12341
 | 
					              "total_duration": 12341
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,9 +5,9 @@
 | 
				
			|||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
    "@mapbox/togeojson": "^0.16.0",
 | 
					    "@mapbox/togeojson": "^0.16.0",
 | 
				
			||||||
    "connected-react-router": "^6.9.1",
 | 
					    "connected-react-router": "^6.9.1",
 | 
				
			||||||
    "date-fns": "^2.22.1",
 | 
					    "date-fns": "^2.23.0",
 | 
				
			||||||
    "history": "^4.10.1",
 | 
					    "history": "^4.10.1",
 | 
				
			||||||
    "i18next": "^20.3.2",
 | 
					    "i18next": "^20.6.0",
 | 
				
			||||||
    "i18next-browser-languagedetector": "^6.1.2",
 | 
					    "i18next-browser-languagedetector": "^6.1.2",
 | 
				
			||||||
    "i18next-xhr-backend": "^3.2.2",
 | 
					    "i18next-xhr-backend": "^3.2.2",
 | 
				
			||||||
    "leaflet": "^1.7.1",
 | 
					    "leaflet": "^1.7.1",
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										
											BIN
										
									
								
								fittrackee_client/public/img/sports/alpine-skiing.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 9.7 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								fittrackee_client/public/img/sports/cross-country-skiing.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 6.5 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								fittrackee_client/public/img/sports/electric-mountain-biking.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 16 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								fittrackee_client/public/img/sports/rowing.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 7.1 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								fittrackee_client/public/img/sports/trail.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 8.1 KiB  | 
@@ -358,9 +358,15 @@ label {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.record-table table, .record-table th, .record-table td{
 | 
					.record-table table, .record-table th, .record-table td{
 | 
				
			||||||
  font-size: 0.9em;
 | 
					  font-size: 0.85em;
 | 
				
			||||||
  padding: 0.1em;
 | 
					  padding: 0.1em;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					@media only screen and (min-width: 1200px) {
 | 
				
			||||||
 | 
					  .record-table table, .record-table th, .record-table td{
 | 
				
			||||||
 | 
					    font-size: 0.9em;
 | 
				
			||||||
 | 
					    padding: 0.1em;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.remaining-chars {
 | 
					.remaining-chars {
 | 
				
			||||||
  font-size: 0.8em;
 | 
					  font-size: 0.8em;
 | 
				
			||||||
@@ -485,6 +491,15 @@ label {
 | 
				
			|||||||
  margin-bottom: 15px;
 | 
					  margin-bottom: 15px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.col-with-map {
 | 
				
			||||||
 | 
					  font-size: .87em;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					@media only screen and (min-width: 1200px) {
 | 
				
			||||||
 | 
					  .col-with-map {
 | 
				
			||||||
 | 
					    font-size: 1em;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.workout-details {
 | 
					.workout-details {
 | 
				
			||||||
  font-size: 0.95em;
 | 
					  font-size: 0.95em;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,7 @@ import { formatDuration } from '../../../utils/stats'
 | 
				
			|||||||
const formatValue = (displayedData, value) =>
 | 
					const formatValue = (displayedData, value) =>
 | 
				
			||||||
  displayedData === 'duration'
 | 
					  displayedData === 'duration'
 | 
				
			||||||
    ? formatDuration(value, true)
 | 
					    ? formatDuration(value, true)
 | 
				
			||||||
    : displayedData === 'distance'
 | 
					    : ['distance', 'ascent', 'descent'].includes(displayedData)
 | 
				
			||||||
    ? value.toFixed(2)
 | 
					    ? value.toFixed(2)
 | 
				
			||||||
    : value
 | 
					    : value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,7 +28,7 @@ export default class StatsCharts extends React.PureComponent {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  render() {
 | 
					  render() {
 | 
				
			||||||
    const { displayedData } = this.state
 | 
					    const { displayedData } = this.state
 | 
				
			||||||
    const { sports, stats, t } = this.props
 | 
					    const { sports, stats, t, withElevation } = this.props
 | 
				
			||||||
    if (Object.keys(stats).length === 0) {
 | 
					    if (Object.keys(stats).length === 0) {
 | 
				
			||||||
      return t('common:No workouts.')
 | 
					      return t('common:No workouts.')
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -53,6 +53,28 @@ export default class StatsCharts extends React.PureComponent {
 | 
				
			|||||||
            />
 | 
					            />
 | 
				
			||||||
            {t('statistics:duration')}
 | 
					            {t('statistics:duration')}
 | 
				
			||||||
          </label>
 | 
					          </label>
 | 
				
			||||||
 | 
					          {withElevation && (
 | 
				
			||||||
 | 
					            <>
 | 
				
			||||||
 | 
					              <label className="radioLabel col">
 | 
				
			||||||
 | 
					                <input
 | 
				
			||||||
 | 
					                  type="radio"
 | 
				
			||||||
 | 
					                  name="ascent"
 | 
				
			||||||
 | 
					                  checked={displayedData === 'ascent'}
 | 
				
			||||||
 | 
					                  onChange={e => this.handleRadioChange(e)}
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					                {t('statistics:ascent')}
 | 
				
			||||||
 | 
					              </label>
 | 
				
			||||||
 | 
					              <label className="radioLabel col">
 | 
				
			||||||
 | 
					                <input
 | 
				
			||||||
 | 
					                  type="radio"
 | 
				
			||||||
 | 
					                  name="descent"
 | 
				
			||||||
 | 
					                  checked={displayedData === 'descent'}
 | 
				
			||||||
 | 
					                  onChange={e => this.handleRadioChange(e)}
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					                {t('statistics:descent')}
 | 
				
			||||||
 | 
					              </label>
 | 
				
			||||||
 | 
					            </>
 | 
				
			||||||
 | 
					          )}
 | 
				
			||||||
          <label className="radioLabel col">
 | 
					          <label className="radioLabel col">
 | 
				
			||||||
            <input
 | 
					            <input
 | 
				
			||||||
              type="radio"
 | 
					              type="radio"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,6 +40,7 @@ class Statistics extends React.PureComponent {
 | 
				
			|||||||
      displayEmpty,
 | 
					      displayEmpty,
 | 
				
			||||||
      t,
 | 
					      t,
 | 
				
			||||||
      user,
 | 
					      user,
 | 
				
			||||||
 | 
					      withElevation,
 | 
				
			||||||
    } = this.props
 | 
					    } = this.props
 | 
				
			||||||
    if (!displayEmpty && Object.keys(statistics).length === 0) {
 | 
					    if (!displayEmpty && Object.keys(statistics).length === 0) {
 | 
				
			||||||
      return <span>{t('common:No workouts.')}</span>
 | 
					      return <span>{t('common:No workouts.')}</span>
 | 
				
			||||||
@@ -51,7 +52,14 @@ class Statistics extends React.PureComponent {
 | 
				
			|||||||
      displayedSports,
 | 
					      displayedSports,
 | 
				
			||||||
      user.weekm
 | 
					      user.weekm
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    return <StatsChart sports={sports} stats={stats} t={t} />
 | 
					    return (
 | 
				
			||||||
 | 
					      <StatsChart
 | 
				
			||||||
 | 
					        sports={sports}
 | 
				
			||||||
 | 
					        stats={stats}
 | 
				
			||||||
 | 
					        t={t}
 | 
				
			||||||
 | 
					        withElevation={withElevation}
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,12 @@ export default class Statistics extends React.Component {
 | 
				
			|||||||
      <div className="card workout-card">
 | 
					      <div className="card workout-card">
 | 
				
			||||||
        <div className="card-header">{t('dashboard:This month')}</div>
 | 
					        <div className="card-header">{t('dashboard:This month')}</div>
 | 
				
			||||||
        <div className="card-body">
 | 
					        <div className="card-body">
 | 
				
			||||||
          <Stats displayEmpty={false} statsParams={this.state} t={t} />
 | 
					          <Stats
 | 
				
			||||||
 | 
					            displayEmpty={false}
 | 
				
			||||||
 | 
					            statsParams={this.state}
 | 
				
			||||||
 | 
					            t={t}
 | 
				
			||||||
 | 
					            withElevation={false}
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,7 +29,7 @@ export default function WorkoutCard(props) {
 | 
				
			|||||||
              <StaticMap workout={workout} />
 | 
					              <StaticMap workout={workout} />
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
          )}
 | 
					          )}
 | 
				
			||||||
          <div className="col">
 | 
					          <div className={`col${workout.map ? ' col-with-map' : ''}`}>
 | 
				
			||||||
            <p>
 | 
					            <p>
 | 
				
			||||||
              <i className="fa fa-clock-o" aria-hidden="true" />{' '}
 | 
					              <i className="fa fa-clock-o" aria-hidden="true" />{' '}
 | 
				
			||||||
              {t('workouts:Duration')}: {workout.moving}
 | 
					              {t('workouts:Duration')}: {workout.moving}
 | 
				
			||||||
@@ -43,7 +43,25 @@ export default function WorkoutCard(props) {
 | 
				
			|||||||
              )}
 | 
					              )}
 | 
				
			||||||
              <i className="fa fa-road" aria-hidden="true" />{' '}
 | 
					              <i className="fa fa-road" aria-hidden="true" />{' '}
 | 
				
			||||||
              {t('workouts:Distance')}: {workout.distance} km
 | 
					              {t('workouts:Distance')}: {workout.distance} km
 | 
				
			||||||
 | 
					              <br />
 | 
				
			||||||
            </p>
 | 
					            </p>
 | 
				
			||||||
 | 
					            {workout.min_alt && workout.max_alt && (
 | 
				
			||||||
 | 
					              <p>
 | 
				
			||||||
 | 
					                <i className="fi-mountains custom-fa" />
 | 
				
			||||||
 | 
					                {t('workouts:Min. altitude')}: {workout.min_alt}m
 | 
				
			||||||
 | 
					                <br />
 | 
				
			||||||
 | 
					                {t('workouts:Max. altitude')}: {workout.max_alt}m
 | 
				
			||||||
 | 
					                <br />
 | 
				
			||||||
 | 
					              </p>
 | 
				
			||||||
 | 
					            )}
 | 
				
			||||||
 | 
					            {workout.ascent && workout.descent && (
 | 
				
			||||||
 | 
					              <p>
 | 
				
			||||||
 | 
					                <i className="fa fa-location-arrow custom-fa" />
 | 
				
			||||||
 | 
					                {t('workouts:Ascent')}: {workout.ascent}m
 | 
				
			||||||
 | 
					                <br />
 | 
				
			||||||
 | 
					                {t('workouts:Descent')}: {workout.descent}m
 | 
				
			||||||
 | 
					              </p>
 | 
				
			||||||
 | 
					            )}
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -175,6 +175,7 @@ class Statistics extends React.Component {
 | 
				
			|||||||
                displayedSports={displayedSports}
 | 
					                displayedSports={displayedSports}
 | 
				
			||||||
                statsParams={statsParams}
 | 
					                statsParams={statsParams}
 | 
				
			||||||
                t={t}
 | 
					                t={t}
 | 
				
			||||||
 | 
					                withElevation
 | 
				
			||||||
              />
 | 
					              />
 | 
				
			||||||
              <div className="row chart-workouts">
 | 
					              <div className="row chart-workouts">
 | 
				
			||||||
                {translatedSports.map(sport => (
 | 
					                {translatedSports.map(sport => (
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,6 +21,8 @@ export default class WorkoutsList extends React.PureComponent {
 | 
				
			|||||||
                <th scope="col">{t('workouts:Duration')}</th>
 | 
					                <th scope="col">{t('workouts:Duration')}</th>
 | 
				
			||||||
                <th scope="col">{t('workouts:Ave. speed')}</th>
 | 
					                <th scope="col">{t('workouts:Ave. speed')}</th>
 | 
				
			||||||
                <th scope="col">{t('workouts:Max. speed')}</th>
 | 
					                <th scope="col">{t('workouts:Max. speed')}</th>
 | 
				
			||||||
 | 
					                <th scope="col">{t('workouts:Ascent')}</th>
 | 
				
			||||||
 | 
					                <th scope="col">{t('workouts:Descent')}</th>
 | 
				
			||||||
              </tr>
 | 
					              </tr>
 | 
				
			||||||
            </thead>
 | 
					            </thead>
 | 
				
			||||||
            <tbody>
 | 
					            <tbody>
 | 
				
			||||||
@@ -85,6 +87,18 @@ export default class WorkoutsList extends React.PureComponent {
 | 
				
			|||||||
                      </span>
 | 
					                      </span>
 | 
				
			||||||
                      {workout.max_speed} km/h
 | 
					                      {workout.max_speed} km/h
 | 
				
			||||||
                    </td>
 | 
					                    </td>
 | 
				
			||||||
 | 
					                    <td className="text-right">
 | 
				
			||||||
 | 
					                      <span className="heading-span-absolute">
 | 
				
			||||||
 | 
					                        {t('workouts:Ascent')}
 | 
				
			||||||
 | 
					                      </span>
 | 
				
			||||||
 | 
					                      {workout.ascent === null ? '' : `${workout.ascent} m`}
 | 
				
			||||||
 | 
					                    </td>
 | 
				
			||||||
 | 
					                    <td className="text-right">
 | 
				
			||||||
 | 
					                      <span className="heading-span-absolute">
 | 
				
			||||||
 | 
					                        {t('workouts:Descent')}
 | 
				
			||||||
 | 
					                      </span>
 | 
				
			||||||
 | 
					                      {workout.descent === null ? '' : `${workout.descent} m`}
 | 
				
			||||||
 | 
					                    </td>
 | 
				
			||||||
                  </tr>
 | 
					                  </tr>
 | 
				
			||||||
                ))}
 | 
					                ))}
 | 
				
			||||||
            </tbody>
 | 
					            </tbody>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,11 @@
 | 
				
			|||||||
  "Cycling (Transport)": "Cycling (Transport)",
 | 
					  "Cycling (Transport)": "Cycling (Transport)",
 | 
				
			||||||
  "Hiking": "Hiking",
 | 
					  "Hiking": "Hiking",
 | 
				
			||||||
  "Mountain Biking": "Mountain Biking",
 | 
					  "Mountain Biking": "Mountain Biking",
 | 
				
			||||||
 | 
					  "Mountain Biking (Electric)": "Mountain Biking (Electric)",
 | 
				
			||||||
  "Running": "Running",
 | 
					  "Running": "Running",
 | 
				
			||||||
  "Walking": "Walking"
 | 
					  "Walking": "Walking",
 | 
				
			||||||
 | 
					  "Trail" : "Trail",
 | 
				
			||||||
 | 
					  "Skiing (Alpine)" : "Skiing (Alpine)",
 | 
				
			||||||
 | 
					  "Skiing (Cross Country)" : "Skiing (Cross Country)",
 | 
				
			||||||
 | 
					  "Rowing" : "Rowing"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,8 @@
 | 
				
			|||||||
  "workouts": "workouts",
 | 
					  "workouts": "workouts",
 | 
				
			||||||
  "distance": "distance",
 | 
					  "distance": "distance",
 | 
				
			||||||
  "duration": "duration",
 | 
					  "duration": "duration",
 | 
				
			||||||
 | 
					  "ascent": "ascent",
 | 
				
			||||||
 | 
					  "descent": "descent",
 | 
				
			||||||
  "month": "month",
 | 
					  "month": "month",
 | 
				
			||||||
  "Statistics": "Statistics",
 | 
					  "Statistics": "Statistics",
 | 
				
			||||||
  "year": "year",
 | 
					  "year": "year",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,11 @@
 | 
				
			|||||||
  "Cycling (Transport)": "Vélo (Transport)",
 | 
					  "Cycling (Transport)": "Vélo (Transport)",
 | 
				
			||||||
  "Hiking": "Randonnée",
 | 
					  "Hiking": "Randonnée",
 | 
				
			||||||
  "Mountain Biking": "VTT",
 | 
					  "Mountain Biking": "VTT",
 | 
				
			||||||
 | 
					  "Mountain Biking (Electric)": "VTT (Electrique)",  
 | 
				
			||||||
  "Running": "Course",
 | 
					  "Running": "Course",
 | 
				
			||||||
  "Walking": "Marche"
 | 
					  "Walking": "Marche",
 | 
				
			||||||
 | 
					  "Trail" : "Trail",
 | 
				
			||||||
 | 
					  "Skiing (Alpine)" : "Ski (Alpin)",
 | 
				
			||||||
 | 
					  "Skiing (Cross Country)" : "Ski (Randonnée)",
 | 
				
			||||||
 | 
					  "Rowing" : "Aviron"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,8 @@
 | 
				
			|||||||
  "workouts": "séances",
 | 
					  "workouts": "séances",
 | 
				
			||||||
  "distance": "distance",
 | 
					  "distance": "distance",
 | 
				
			||||||
  "duration": "durée",
 | 
					  "duration": "durée",
 | 
				
			||||||
 | 
					  "ascent": "dénivelé +",
 | 
				
			||||||
 | 
					  "descent": "dénivelé -",
 | 
				
			||||||
  "month": "mois",
 | 
					  "month": "mois",
 | 
				
			||||||
  "Statistics": "Statistiques",
 | 
					  "Statistics": "Statistiques",
 | 
				
			||||||
  "year": "année",
 | 
					  "year": "année",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,13 +5,13 @@
 | 
				
			|||||||
  "Add a workout": "Ajouter une séance",
 | 
					  "Add a workout": "Ajouter une séance",
 | 
				
			||||||
  "Are you sure you want to delete this workout?": "Etes-vous sûr de vouloir supprimer cette séance ?",
 | 
					  "Are you sure you want to delete this workout?": "Etes-vous sûr de vouloir supprimer cette séance ?",
 | 
				
			||||||
  "Ave. speed": "Vitesse moyenne",
 | 
					  "Ave. speed": "Vitesse moyenne",
 | 
				
			||||||
  "Ascent": "Dénivelé positif",
 | 
					  "Ascent": "Dénivelé +",
 | 
				
			||||||
  "Average speed": "Vitesse moyenne",
 | 
					  "Average speed": "Vitesse moyenne",
 | 
				
			||||||
  "Chart": "Analyse",
 | 
					  "Chart": "Analyse",
 | 
				
			||||||
  "data from gpx, without any cleaning": "données issues du fichier gpx, sans correction",
 | 
					  "data from gpx, without any cleaning": "données issues du fichier gpx, sans correction",
 | 
				
			||||||
  "Date": "Date",
 | 
					  "Date": "Date",
 | 
				
			||||||
  "Delete workout": "Supprimer l'séance",
 | 
					  "Delete workout": "Supprimer l'séance",
 | 
				
			||||||
  "Descent": "Dénivelé négatif",
 | 
					  "Descent": "Dénivelé -",
 | 
				
			||||||
  "Distance": "Distance",
 | 
					  "Distance": "Distance",
 | 
				
			||||||
  "distance": "distance",
 | 
					  "distance": "distance",
 | 
				
			||||||
  "Duration": "Durée",
 | 
					  "Duration": "Durée",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,6 +39,10 @@ export const formatValue = (displayedData, value) =>
 | 
				
			|||||||
    ? `${value.toFixed(2)} km`
 | 
					    ? `${value.toFixed(2)} km`
 | 
				
			||||||
    : displayedData === 'duration'
 | 
					    : displayedData === 'duration'
 | 
				
			||||||
    ? formatDuration(value)
 | 
					    ? formatDuration(value)
 | 
				
			||||||
 | 
					    : displayedData === 'ascent'
 | 
				
			||||||
 | 
					    ? `${value.toFixed(2)} km`
 | 
				
			||||||
 | 
					    : displayedData === 'descent'
 | 
				
			||||||
 | 
					    ? `${value.toFixed(2)} km`
 | 
				
			||||||
    : value
 | 
					    : value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const dateIncrement = (duration, day) => {
 | 
					const dateIncrement = (duration, day) => {
 | 
				
			||||||
@@ -69,6 +73,8 @@ export const formatStats = (stats, sports, params, displayedSports, weekm) => {
 | 
				
			|||||||
  const nbWorkoutsStats = []
 | 
					  const nbWorkoutsStats = []
 | 
				
			||||||
  const distanceStats = []
 | 
					  const distanceStats = []
 | 
				
			||||||
  const durationStats = []
 | 
					  const durationStats = []
 | 
				
			||||||
 | 
					  const ascentStats = []
 | 
				
			||||||
 | 
					  const descentStats = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  for (
 | 
					  for (
 | 
				
			||||||
    let day = startDate(params.duration, params.start, weekm);
 | 
					    let day = startDate(params.duration, params.start, weekm);
 | 
				
			||||||
@@ -83,6 +89,8 @@ export const formatStats = (stats, sports, params, displayedSports, weekm) => {
 | 
				
			|||||||
    const dataNbWorkouts = { date: xAxis }
 | 
					    const dataNbWorkouts = { date: xAxis }
 | 
				
			||||||
    const dataDistance = { date: xAxis }
 | 
					    const dataDistance = { date: xAxis }
 | 
				
			||||||
    const dataDuration = { date: xAxis }
 | 
					    const dataDuration = { date: xAxis }
 | 
				
			||||||
 | 
					    const dataAscent = { date: xAxis }
 | 
				
			||||||
 | 
					    const dataDescent = { date: xAxis }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (stats[date]) {
 | 
					    if (stats[date]) {
 | 
				
			||||||
      Object.keys(stats[date])
 | 
					      Object.keys(stats[date])
 | 
				
			||||||
@@ -94,17 +102,23 @@ export const formatStats = (stats, sports, params, displayedSports, weekm) => {
 | 
				
			|||||||
          dataNbWorkouts[sportLabel] = stats[date][sportId].nb_workouts
 | 
					          dataNbWorkouts[sportLabel] = stats[date][sportId].nb_workouts
 | 
				
			||||||
          dataDistance[sportLabel] = stats[date][sportId].total_distance
 | 
					          dataDistance[sportLabel] = stats[date][sportId].total_distance
 | 
				
			||||||
          dataDuration[sportLabel] = stats[date][sportId].total_duration
 | 
					          dataDuration[sportLabel] = stats[date][sportId].total_duration
 | 
				
			||||||
 | 
					          dataAscent[sportLabel] = stats[date][sportId].total_ascent / 1000
 | 
				
			||||||
 | 
					          dataDescent[sportLabel] = stats[date][sportId].total_descent / 1000
 | 
				
			||||||
          return null
 | 
					          return null
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    nbWorkoutsStats.push(dataNbWorkouts)
 | 
					    nbWorkoutsStats.push(dataNbWorkouts)
 | 
				
			||||||
    distanceStats.push(dataDistance)
 | 
					    distanceStats.push(dataDistance)
 | 
				
			||||||
    durationStats.push(dataDuration)
 | 
					    durationStats.push(dataDuration)
 | 
				
			||||||
 | 
					    ascentStats.push(dataAscent)
 | 
				
			||||||
 | 
					    descentStats.push(dataDescent)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
    workouts: nbWorkoutsStats,
 | 
					    workouts: nbWorkoutsStats,
 | 
				
			||||||
    distance: distanceStats,
 | 
					    distance: distanceStats,
 | 
				
			||||||
    duration: durationStats,
 | 
					    duration: durationStats,
 | 
				
			||||||
 | 
					    ascent: ascentStats,
 | 
				
			||||||
 | 
					    descent: descentStats,
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,13 +4,17 @@ import togeojson from '@mapbox/togeojson'
 | 
				
			|||||||
import { getDateWithTZ } from './index'
 | 
					import { getDateWithTZ } from './index'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const workoutColors = [
 | 
					export const workoutColors = [
 | 
				
			||||||
  '#55a8a3',
 | 
					  '#55a8a3', // Cycling (sport)
 | 
				
			||||||
  '#98C3A9',
 | 
					  '#98C3A9', // Cycling (transport)
 | 
				
			||||||
  '#D0838A',
 | 
					  '#D0838A', // Hiking
 | 
				
			||||||
  '#ECC77E',
 | 
					  '#ECC77E', // Mountain bike
 | 
				
			||||||
  '#926692',
 | 
					  '#926692', // Running
 | 
				
			||||||
  '#929292',
 | 
					  '#929292', // Walking
 | 
				
			||||||
  '#428bca',
 | 
					  '#f7af88', // Mountain Biking (Electric)
 | 
				
			||||||
 | 
					  '#fbc2a6', // Trail
 | 
				
			||||||
 | 
					  '#67a4bd', // Skiing (Alpine)
 | 
				
			||||||
 | 
					  '#9498d0', // Skiing (Cross Country)
 | 
				
			||||||
 | 
					  '#fad783', // Rowing
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const recordsLabels = [
 | 
					export const recordsLabels = [
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3999,7 +3999,7 @@ data-urls@^2.0.0:
 | 
				
			|||||||
    whatwg-mimetype "^2.3.0"
 | 
					    whatwg-mimetype "^2.3.0"
 | 
				
			||||||
    whatwg-url "^8.0.0"
 | 
					    whatwg-url "^8.0.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
date-fns@^2.22.1:
 | 
					date-fns@^2.23.0:
 | 
				
			||||||
  version "2.25.0"
 | 
					  version "2.25.0"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.25.0.tgz#8c5c8f1d958be3809a9a03f4b742eba894fc5680"
 | 
					  resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.25.0.tgz#8c5c8f1d958be3809a9a03f4b742eba894fc5680"
 | 
				
			||||||
  integrity sha512-ovYRFnTrbGPD4nqaEqescPEv1mNwvt+UTqI3Ay9SzNtey9NZnYu6E2qCcBBgJ6/2VF1zGGygpyTDITqpQQ5e+w==
 | 
					  integrity sha512-ovYRFnTrbGPD4nqaEqescPEv1mNwvt+UTqI3Ay9SzNtey9NZnYu6E2qCcBBgJ6/2VF1zGGygpyTDITqpQQ5e+w==
 | 
				
			||||||
@@ -5781,7 +5781,7 @@ i18next-xhr-backend@^3.2.2:
 | 
				
			|||||||
  dependencies:
 | 
					  dependencies:
 | 
				
			||||||
    "@babel/runtime" "^7.5.5"
 | 
					    "@babel/runtime" "^7.5.5"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
i18next@^20.3.2:
 | 
					i18next@^20.6.0:
 | 
				
			||||||
  version "20.6.1"
 | 
					  version "20.6.1"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/i18next/-/i18next-20.6.1.tgz#535e5f6e5baeb685c7d25df70db63bf3cc0aa345"
 | 
					  resolved "https://registry.yarnpkg.com/i18next/-/i18next-20.6.1.tgz#535e5f6e5baeb685c7d25df70db63bf3cc0aa345"
 | 
				
			||||||
  integrity sha512-yCMYTMEJ9ihCwEQQ3phLo7I/Pwycf8uAx+sRHwwk5U9Aui/IZYgQRyMqXafQOw5QQ7DM1Z+WyEXWIqSuJHhG2A==
 | 
					  integrity sha512-yCMYTMEJ9ihCwEQQ3phLo7I/Pwycf8uAx+sRHwwk5U9Aui/IZYgQRyMqXafQOw5QQ7DM1Z+WyEXWIqSuJHhG2A==
 | 
				
			||||||
 
 | 
				
			|||||||