diff --git a/docs/api/sports.html b/docs/api/sports.html index 049b68be..0f18d41c 100644 --- a/docs/api/sports.html +++ b/docs/api/sports.html @@ -147,34 +147,52 @@ "data": { "sports": [ { + "color": null, "id": 1, "is_active": true, - "label": "Cycling (Sport)" + "is_active_for_user": true, + "label": "Cycling (Sport)", + "stopped_speed_threshold": 1 }, { + "color": null, "id": 2, "is_active": true, - "label": "Cycling (Transport)" + "is_active_for_user": true, + "label": "Cycling (Transport)", + "stopped_speed_threshold": 1 }, { + "color": null, "id": 3, "is_active": true, - "label": "Hiking" + "is_active_for_user": true, + "label": "Hiking", + "stopped_speed_threshold": 0.1 }, { + "color": null, "id": 4, "is_active": true, - "label": "Mountain Biking" + "is_active_for_user": true, + "label": "Mountain Biking", + "stopped_speed_threshold": 1 }, { + "color": null, "id": 5, "is_active": true, - "label": "Running" + "is_active_for_user": true, + "label": "Running", + "stopped_speed_threshold": 0.1 }, { + "color": null, "id": 6, "is_active": true, - "label": "Walking" + "is_active_for_user": true, + "label": "Walking", + "stopped_speed_threshold": 0.1 } ] }, @@ -192,40 +210,58 @@ "data": { "sports": [ { + "color": null, "has_workouts": true, "id": 1, "is_active": true, - "label": "Cycling (Sport)" + "is_active_for_user": true, + "label": "Cycling (Sport)", + "stopped_speed_threshold": 1 }, { + "color": null, "has_workouts": false, "id": 2, "is_active": true, - "label": "Cycling (Transport)" + "is_active_for_user": true, + "label": "Cycling (Transport)", + "stopped_speed_threshold": 1 }, { + "color": null, "has_workouts": false, "id": 3, "is_active": true, - "label": "Hiking" + "is_active_for_user": true, + "label": "Hiking", + "stopped_speed_threshold": 0.1 }, { + "color": null, "has_workouts": false, "id": 4, "is_active": true, - "label": "Mountain Biking" + "is_active_for_user": true, + "label": "Mountain Biking", + "stopped_speed_threshold": 1 }, { + "color": null, "has_workouts": false, "id": 5, "is_active": true, - "label": "Running" + "is_active_for_user": true, + "label": "Running", + "stopped_speed_threshold": 0.1 }, { + "color": null, "has_workouts": false, "id": 6, "is_active": true, - "label": "Walking" + "is_active_for_user": true, + "label": "Walking", + "stopped_speed_threshold": 0.1 } ] }, @@ -278,9 +314,12 @@ "data": { "sports": [ { + "color": null, "id": 1, "is_active": true, - "label": "Cycling (Sport)" + "is_active_for_user": true, + "label": "Cycling (Sport)", + "stopped_speed_threshold": 1 } ] }, @@ -298,10 +337,13 @@ "data": { "sports": [ { + "color": null, "has_workouts": false, "id": 1, "is_active": true, - "label": "Cycling (Sport)" + "is_active_for_user": true, + "label": "Cycling (Sport)", + "stopped_speed_threshold": 1 } ] }, @@ -371,10 +413,13 @@ Authenticated user must be an admin

"data": { "sports": [ { + "color": null, "has_workouts": false, "id": 1, "is_active": false, - "label": "Cycling (Sport)" + "is_active_for_user": false, + "label": "Cycling (Sport)", + "stopped_speed_threshold": 1 } ] }, diff --git a/docs/searchindex.js b/docs/searchindex.js index a2199228..a57fcb28 100644 --- a/docs/searchindex.js +++ b/docs/searchindex.js @@ -1 +1 @@ -Search.setIndex({docnames:["api/auth","api/configuration","api/index","api/records","api/sports","api/stats","api/users","api/workouts","changelog","features","index","installation","troubleshooting/administrator","troubleshooting/index","troubleshooting/user"],envversion:{"sphinx.domains.c":2,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":4,"sphinx.domains.index":1,"sphinx.domains.javascript":2,"sphinx.domains.math":2,"sphinx.domains.python":3,"sphinx.domains.rst":2,"sphinx.domains.std":2,sphinx:56},filenames:["api/auth.rst","api/configuration.rst","api/index.rst","api/records.rst","api/sports.rst","api/stats.rst","api/users.rst","api/workouts.rst","changelog.md","features.rst","index.rst","installation.rst","troubleshooting/administrator.rst","troubleshooting/index.rst","troubleshooting/user.rst"],objects:{"":{"/api/auth/login":[0,0,1,"post--api-auth-login"],"/api/auth/logout":[0,1,1,"get--api-auth-logout"],"/api/auth/password/reset-request":[0,0,1,"post--api-auth-password-reset-request"],"/api/auth/password/update":[0,0,1,"post--api-auth-password-update"],"/api/auth/picture":[0,0,1,"post--api-auth-picture"],"/api/auth/profile":[0,1,1,"get--api-auth-profile"],"/api/auth/profile/edit":[0,0,1,"post--api-auth-profile-edit"],"/api/auth/profile/edit/preferences":[0,0,1,"post--api-auth-profile-edit-preferences"],"/api/auth/profile/edit/sports":[0,0,1,"post--api-auth-profile-edit-sports"],"/api/auth/register":[0,0,1,"post--api-auth-register"],"/api/config":[1,3,1,"patch--api-config"],"/api/ping":[1,1,1,"get--api-ping"],"/api/records":[3,1,1,"get--api-records"],"/api/sports":[4,1,1,"get--api-sports"],"/api/sports/(int:sport_id)":[4,3,1,"patch--api-sports-(int-sport_id)"],"/api/stats/(user_name)/by_sport":[5,1,1,"get--api-stats-(user_name)-by_sport"],"/api/stats/(user_name)/by_time":[5,1,1,"get--api-stats-(user_name)-by_time"],"/api/stats/all":[5,1,1,"get--api-stats-all"],"/api/users":[6,1,1,"get--api-users"],"/api/users/(user_name)":[6,3,1,"patch--api-users-(user_name)"],"/api/users/(user_name)/picture":[6,1,1,"get--api-users-(user_name)-picture"],"/api/workouts":[7,0,1,"post--api-workouts"],"/api/workouts/(string:workout_short_id)":[7,3,1,"patch--api-workouts-(string-workout_short_id)"],"/api/workouts/(string:workout_short_id)/chart_data":[7,1,1,"get--api-workouts-(string-workout_short_id)-chart_data"],"/api/workouts/(string:workout_short_id)/chart_data/segment/(int:segment_id)":[7,1,1,"get--api-workouts-(string-workout_short_id)-chart_data-segment-(int-segment_id)"],"/api/workouts/(string:workout_short_id)/gpx":[7,1,1,"get--api-workouts-(string-workout_short_id)-gpx"],"/api/workouts/(string:workout_short_id)/gpx/segment/(int:segment_id)":[7,1,1,"get--api-workouts-(string-workout_short_id)-gpx-segment-(int-segment_id)"],"/api/workouts/map/(map_id)":[7,1,1,"get--api-workouts-map-(map_id)"],"/api/workouts/no_gpx":[7,0,1,"post--api-workouts-no_gpx"],"DEFAULT_STATICMAP \ud83c\udd95":[11,4,1,"envvar-DEFAULT_STATICMAP"],APP_LOG:[11,4,1,"-"],APP_SECRET_KEY:[11,4,1,"-"],APP_SETTINGS:[11,4,1,"-"],APP_WORKERS:[11,4,1,"-"],DATABASE_DISABLE_POOLING:[11,4,1,"-"],DATABASE_URL:[11,4,1,"-"],EMAIL_URL:[11,4,1,"-"],FLASK_APP:[11,4,1,"-"],HOST:[11,4,1,"-"],MAP_ATTRIBUTION:[11,4,1,"-"],PORT:[11,4,1,"-"],REDIS_URL:[11,4,1,"-"],SENDER_EMAIL:[11,4,1,"-"],TILE_SERVER_URL:[11,4,1,"-"],UI_URL:[11,4,1,"-"],UPLOAD_FOLDER:[11,4,1,"-"],VUE_APP_API_URL:[11,4,1,"-"],WEATHER_API_KEY:[11,4,1,"-"],WORKERS_PROCESSES:[11,4,1,"-"]},"/api/workouts/map_tile/(s)/(z)/(x)/(y)":{png:[7,1,1,"get--api-workouts-map_tile-(s)-(z)-(x)-(y).png"]}},objnames:{"0":["http","post","HTTP post"],"1":["http","get","HTTP get"],"2":["http","delete","HTTP delete"],"3":["http","patch","HTTP patch"],"4":["std","envvar","environment variable"]},objtypes:{"0":"http:post","1":"http:get","2":"http:delete","3":"http:patch","4":"std:envvar"},terms:{"0":[0,1,3,4,5,6,7,9,10,11],"00":[0,3,6,7],"000000":0,"01":[0,3,5,6,7,10],"02":[7,10],"03":[6,7,10],"04":[7,10],"06":5,"07":[0,3,6,7,10],"08":[0,3,6],"09":[0,6,10],"0mb":[0,7],"1":[0,1,3,4,5,6,7,9,10,11],"10":[0,1,3,6,7,10,11],"100":[7,9],"1000":5,"104":8,"1048576":1,"10485760":1,"109":8,"11":[0,3,6,8,9],"12":[0,3,6,7,10],"1232004":7,"12341":5,"1234538":7,"1267":5,"127":11,"13":[7,8],"14":[0,6,7,8],"15":[5,7,10],"150":5,"156":5,"1563529507772":7,"16":[7,10],"17":[7,10,11],"178":5,"18":[0,3,6,7,8],"19":10,"2":[0,1,3,4,5,6,7,10,11],"20":6,"200":[0,1,3,4,5,6,7],"201":[0,7],"2017":[5,7],"2018":[5,7,10],"2019":[0,3,5,6,7,10],"2020":10,"2021":10,"203":5,"204":[0,6,7],"21":10,"22":[7,8],"23":10,"24":8,"25":[8,11],"26":8,"27":[0,6],"279":7,"28":8,"280":7,"282":5,"29":10,"2930":7,"2e1ee2c":8,"3":[0,4,5,6,7,9,10,11],"30":[5,10],"3000":11,"301":11,"31":[7,10],"33":8,"34":8,"34614d5":8,"36":8,"37":8,"39":7,"4":[0,4,6,7,10,11],"40":8,"400":[0,1,4,7],"401":[0,1,3,4,5,6,7],"403":[0,1,4,5,6,7],"404":[0,4,5,6,7],"41":8,"4109":7,"413":[0,7],"43":8,"44":[7,8],"443":11,"45":7,"46":[5,8],"465":11,"47":[5,7,8],"4c3fc34":8,"5":[1,4,5,6,7,9,10,11],"50":[0,6,8],"500":[0,1,4,6,7],"5000":11,"5078118":7,"5079733":7,"51":7,"53":8,"54":8,"5432":11,"57":[7,8],"58":[0,6,8],"587":11,"59":8,"6":[0,4,6,7,10],"613":5,"62":8,"63":7,"64":8,"66":8,"67":[0,6],"7":[7,10,11],"70":8,"71":8,"72":8,"7380":7,"75":8,"78":5,"79":8,"8":[0,7,10],"80":[8,11],"8025":11,"81":8,"82":8,"83":8,"84":8,"895":[0,6],"9":[0,3,6,10,11],"91":8,"93":8,"95":8,"98":8,"9960":5,"boolean":[0,1,6],"default":[5,6,7,8,9,11],"do":[1,4,5,6,7],"export":10,"float":[0,7],"import":[8,11],"int":[4,7],"new":[0,9,11],"null":[0,6,7],"return":[3,7,11],"short":7,"static":[8,11],"switch":8,"true":[0,1,4,6,8,11],"try":[0,6,7],A:[6,8,9],AS:[0,3,6,7],For:11,If:[9,11],In:8,It:[8,10,12],NO:[0,6,7],NOT:[0,4,7],No:[0,6,7,8,10],Not:[4,5,6,7],OF:11,ON:11,On:11,One:11,TO:11,The:[8,9,11],There:11,To:11,WITH:11,With:11,_:8,__main__:11,_blank:11,access:11,account:[6,8],acit:7,activ:[4,8,9,10,11],ad:[0,8],adapt:11,add:[6,8,9,10],addit:11,address:11,admin:[0,1,3,4,5,6,7,8,11],administr:[0,6,7,10,13],after:[8,11],again:[0,1,3,4,5,6,7],agplv3:8,all:[3,4,5,6,11],allow:[0,1,7,8,10],along:7,alpin:9,alreadi:0,also:[8,10,11],altitud:8,alwai:11,an:[1,4,6,7,8,9,11],android:10,anoth:[6,11],anymor:8,apach:10,api:[0,1,3,4,5,6,7,8,9,10,11],apikei:11,app:10,app_log:11,app_secret_kei:11,app_set:11,app_work:11,applic:[0,1,3,4,5,6,7,8,9,10,11],ar:[3,7,8,9,10,11,12],arch:11,archiv:[1,8,9,11],archlinux:11,asc:6,ascent:7,attribut:11,auth:[0,1,3,4,5,6,7],auth_token:0,auth_user_id:[1,3,4,5,6,7],authent:[1,2,3,4,5,6,7,10],author:[0,1,3,4,5,6,7],avail:[0,3,9,10,11],ave_spe:7,ave_speed_from:7,ave_speed_to:7,averag:[3,7,8,9],axi:7,b862a77:8,b:11,background:8,backup:11,bad:[0,1,4,7],bearer:[0,1,3,4,5,6,7],becom:8,been:8,befor:[8,11],begin:11,better:8,bike:[4,7,8,9],bin:11,bio:[0,6],biographi:0,birth:[0,8],birth_dat:[0,6],bound:7,build:11,by_sport:5,by_tim:5,c:7,calcul:8,calendar:[8,9],can:[6,8,9,10,11,12],cannot:8,cd:11,chang:[9,10,11],charact:[0,8],chart:[7,8,9,11],chart_data:7,check:[1,11,12],choos:8,client:[8,11],client_max_body_s:11,clone:11,code:[0,1,3,4,5,6,7],color:[0,8],com:[0,6,11],complet:8,config:[1,11],configur:[2,8,9,10,11],confirm:0,contact:[0,6,7],contain:[8,9,11],content:[0,1,3,4,5,6,7],contributor:[1,11],control:8,coordin:11,copi:[1,11],copyright:[1,11],correctli:8,countri:9,cp:11,creat:[0,7,8,9,11],create_app:11,created_at:[0,6],creation:[8,9],creation_d:7,credenti:[0,11],criteria:6,cross:9,current:8,custom:11,cycl:[4,8,9],d:[0,5,7],dai:8,dark:11,darkski:[8,9],dashboard:[8,10],data:[0,1,3,4,5,6,7,8,10,11],databas:[8,11,12],database_disable_pool:11,database_url:[8,11],date:[0,5,7,8,9],db:11,debian:11,default_staticmap:[8,11],defin:9,definit:8,delet:[0,6,7,8,9],depend:[8,11],deploy:10,desc:7,descent:7,describ:11,descript:11,detail:[6,8,10],develop:10,dialect:12,differ:8,directli:11,directori:[8,11],disabl:[0,8,9,11],displai:[7,8,9,10,11,12],distanc:[3,7,8,9],distance_from:7,distance_to:7,distribut:11,docker:[8,10],document:[8,10,11],doe:[0,5,6,7,8],don:0,down:8,download:11,dramatiq:11,drop:8,due:8,durat:[3,7,8,9],duration_from:7,duration_to:7,dure:[0,7],easi:8,edit:[0,8,9],electr:9,elev:[7,8,9,11],els:11,email:[0,6,8],email_url:11,empti:8,en:[0,6],enabl:[1,9],encount:11,end:[5,7],endpoint:[1,8],engin:[8,11,12],english:9,enter:[8,9],entiti:[0,7],entri:11,env:11,environ:[8,10,12],error:[0,1,4,6,7,8,9,11,12],europ:[0,6],evalu:[8,11],even:[8,9],exampl:[0,1,3,4,5,6,7,10,11],exc:12,exce:[0,7],exceed:8,except:6,execstart:11,exhaust:10,exist:[0,5,6,7,9,10,11],expir:[0,1,3,4,5,6,7],extens:[0,7],fa33f4d996844a5c73ecd1ae24456ab8:7,fail:[8,13],fals:[0,1,4,6,7,11],farest:[3,8,9],fd:[0,3,6,7],featur:[10,11],fetch:11,file:[0,1,7,8,9,10,11],filter:[8,9],first:[0,10],first_nam:[0,6],fitotrack:10,fittracke:[9,11,13],fittrackee_cli:11,fittrackee_init_data:11,fittrackee_upgrade_db:11,fittrackee_work:11,fix:10,flask:11,flask_app:11,flaticon:11,follow:[3,9,11,12],forbidden:[0,1,4,5,6,7],forecast:11,form:[0,7],format:[0,5,7],former:11,forrunn:10,forward:11,found:[0,4,5,6,7],fr:6,frame:5,freepik:11,french:[9,10],fri:7,from:[1,3,4,5,6,7,8,10],fullchain:11,gener:[8,11],get:[0,1,3,4,5,6,7],gif:0,git:11,github:11,given:11,gmt:[0,3,6,7],gpl:10,gpx:[7,8,9,10,11],gpx_limit_import:1,gpxpy:[0,9,11],grant:11,greater:8,gunicorn:[11,12],gz:11,h:[7,9],ha:6,handl:[0,9,11],has_workout:4,have:[1,4,5,6,7,8],he:6,header:[0,1,3,4,5,6,7],health:1,heavi:[10,11],hexadecim:0,hi:[6,8,9],hike:[4,8,9],home:11,host:11,href:[1,11],http:[0,1,3,4,5,6,7,11],hvybqybra7wwxpastwr4v2:[0,3,6],i18n:8,icon:11,id:[0,1,3,4,5,6,7],imag:[0,6,7,8,11],improv:10,incorrect:8,index:7,info:0,inform:[8,10,11],init:11,initi:11,initialis:11,insid:11,instal:[8,10],instanc:[1,9,11],instead:9,integ:[1,3,4,5,6,7],interceptor:8,interfac:11,intern:[0,1,4,6,7],introduc:8,invalid:[0,1,3,4,5,6,7,8],io:11,ip:11,is_act:[0,4],is_registration_en:1,issu:[10,11],jan:7,javascript:8,john_do:6,jpeg:6,jpg:0,js:11,json:[0,1,3,4,5,6,7],jul:[0,3,6,7],keep:[8,10,11],kei:[8,9,11],kjxavsturjvoah2wvcegef:7,km:[7,9],label:4,languag:[0,6],larg:[0,7,9],larger:11,last:[0,11],last_nam:[0,6],latitud:7,layer:[8,11],ld:[0,3,6,7],leaflet:[7,11],least:7,less:8,librari:11,licens:[8,10],like:[9,11],limit:[8,9],link:11,linux:11,list:[8,10,11],listen:11,load:[8,12],local:[8,10,11],localhost:11,locat:[0,6,11],log:[0,1,3,4,5,6,7,10,11,12],logfil:11,login:0,logout:[0,8],longer:[8,12],longest:[3,8,9],longitud:7,m:[0,5,7],made:11,mai:[9,10,11],mailhog:11,major:8,make:[8,11],makefil:11,manag:8,mandatori:[7,8,11],map:[7,8,9,10],map_attribut:[1,11],map_id:7,map_til:7,match:0,max:[1,6,7,8],max_alt:7,max_single_file_s:1,max_spe:7,max_speed_from:7,max_speed_to:7,max_us:1,max_zip_file_s:1,maxim:7,maximum:[3,8,9],messag:[0,1,7,8],method:11,min:8,min_alt:7,minim:7,minor:10,miss:8,mobil:10,model:8,modifi:[6,8],modification_d:7,modul:11,mon:7,mondai:[0,5,9],montain:8,month:[5,8,9],more:[8,9,10,11],morn:7,mountain:[4,9],mous:8,move:[7,8],movement:8,mpwoadmin:11,ms:[0,3,6,7],multi:11,multipart:[0,7],must:[0,1,4,8,9,11,12],mv:11,n:0,name:[0,5,6,11],nano:11,nb_sport:[0,6],nb_workout:[0,5,6],necessari:[9,11],need:11,network:11,next_workout:7,nginx:[9,11],no_gpx:7,non:[4,10],noopen:11,noreferr:11,nosuchmoduleerror:12,note:[7,8,9,11],now:[8,9,11],number:[1,6,7,8,9,11],oauth:[0,1,3,4,5,6,7],object:[0,1,4,6,7],ok:[0,1,3,4,5,6,7],one:[6,7,9],onli:[6,7,8,9,11],open:[10,11],openstreetmap:[1,8,11],opentrack:10,option:11,order:[6,7,8],order_bi:6,org:[1,11],os:11,other:[6,11],out:0,outdoor:[8,10,11],over:8,overwrit:11,own:[6,10],owner:[8,9],packag:[8,11],page:[6,7],pagin:[6,7],par_pag:6,paramet:[0,1,3,4,5,6,7,8,9],pari:[0,6],pars:11,part:[0,7],pass:11,password:[0,8,9,11],password_conf:0,patch:[1,4,6,7],path:11,paus:[7,8],payload:[0,1,4,7],pem:11,per:[6,7,8],per_pag:[6,7],permiss:[1,4,5,6,7],pg_dump:11,pictur:[0,6,7],ping:1,pip:11,pipenv:8,pleas:[0,1,3,4,5,6,7,8],plugin:12,png:[0,7,11],poetri:[8,11],point:[8,11],pong:1,pool:11,port:11,possibl:[8,10],post:[0,7],postgr:[8,12],postgresql:[8,11,12],prefer:0,prerequisit:10,previous_workout:7,privileg:11,privkei:11,process:[0,9,11],productionconfig:11,profil:0,project:11,provid:[0,1,3,4,5,6,7,8,9,11],proxi:11,proxy_add_x_forwarded_for:11,proxy_pass:11,proxy_redirect:11,proxy_set_head:11,pull:11,purpos:[8,11],pwd:11,py:11,pypi:10,python:[8,11],q:6,queri:[5,6,7],queue:11,r:11,rais:9,read:8,real:11,rebuild:8,rechart:7,recommend:11,record:[0,2,6,7,8,9,10],record_typ:[0,3,6,7],redi:[8,11],redis_url:11,refactor:8,regist:[0,1,11],registr:[0,1,8,9],rel:11,relat:11,releas:[10,11],remain:8,remote_addr:11,remov:9,renam:8,replac:[8,11],repo:11,repositori:11,request:[0,1,3,4,5,6,7],request_uri:11,requir:0,reset:[0,8,9],respons:[0,1,3,4,5,6,7],restart:11,restartsec:11,right:[6,8,9],rout:8,row:9,run:[4,8,9,11],runner:10,s:[6,7,8,11],sam:[0,6],same:8,samr1:11,sat:6,save:[8,9],schema:11,search:8,second:7,secret:11,see:[8,9,10,11],seem:8,segment:[7,8,9],segment_id:7,select:[0,7],send:[8,11],sender:11,sender_email:11,serv:11,server:[0,1,4,6,7,8,9,10],server_nam:11,servic:11,set:[8,9],sever:[10,11],shell:11,should:11,show:8,shown:8,side:8,signatur:[0,1,3,4,5,6,7],simpl:11,simplifi:8,sinc:11,singl:[1,6],size:[0,1,7,8,9],ski:9,sky:11,slow:8,smtp:11,some:[6,7,8,10,11],sorri:0,sort:[6,7],sourc:10,spawn:11,speed:[0,3,7,8,9,11],spinner:8,sport:[0,2,5,7,8,9,10,11],sport_id:[0,3,4,5,6,7],sports_list:[0,6],sql:11,sqlalchemi:[8,11,12],ssl:11,ssl_certif:11,ssl_certificate_kei:11,standard:[8,11],standarderror:11,standardoutput:11,start:[0,5,7,8,9,11,13],startlimitintervalsec:11,starttl:11,stat:[5,8],staticmap:11,statist:[2,10],statu:[0,1,3,4,5,6,7],step:11,still:10,stop:[0,8,9,11],stopped_speed_threshold:0,store:10,street:10,string:[0,4,5,6,7],subdomain:7,success:[0,1,3,4,5,6,7],successfulli:0,sun:[0,3,6,7],sundai:[5,7,9],support:[8,9,12],syslog:11,syslogidentifi:11,system:11,systemd:11,t:[0,12],tar:11,target:11,task:11,term:[8,11],test:11,textarea:8,than:8,thei:11,them:10,thi:[7,8,9,10,11],threshold:[0,8,9],thunderforest:[8,11],tile:[7,8],tile_server_url:11,time:[0,5,7,8,9],timezon:[0,6,8],titl:7,tl:11,todo:14,token:[0,1,3,4,5,6,7],too:[0,7],tool:11,tooltip:8,total:8,total_asc:5,total_dist:[0,5,6],total_dur:[0,5,6],track:10,trail:9,transport:[4,8,9],troubleshoot:10,type:[0,1,3,4,5,6,7,11],typescript:11,u:11,ui:8,ui_url:11,unauthor:[0,1,3,4,5,6,7],under:[10,11],unencrypt:11,unit:11,unreleas:10,unstabl:[10,11],up:[9,10],updat:[0,1,4,6,7,8,9,11],upload:[8,9,11],upload_fold:11,uploads_dir_s:5,url:[8,11,12],us:[0,6,7,8,9,10,11],user:[0,1,2,3,4,5,7,8,9,10,11,13],user_id:0,user_nam:[5,6],usernam:[0,6,11],uuid:[7,8],v0:11,v3:10,valid:[0,1,3,4,5,6,7,11],valu:[0,3,6,7],variabl:[8,10,12],venv:11,version:[1,10,11],view:8,virtualenv:11,vue3:11,vue:11,vue_app_api_url:11,vuex:11,wa:8,wai:11,walk:[4,8,9],wantedbi:11,warn:8,weather:[8,9,11],weather_api:11,weather_api_kei:11,weather_end:7,weather_start:7,web:[0,1,3,4,5,6,7,10,11],week:[0,5,8,9],weekend:8,weekm:[0,5],were:8,wget:11,when:[0,1,8,11],where:11,which:9,with_gpx:7,without:[5,6,7,8,9,10],worker:11,workers_process:11,workingdirectori:11,workout:[0,2,5,8,10],workout_d:[0,3,6,7],workout_id:[0,3,6,7],workout_short_id:7,workouts_count:6,written:11,www:[1,11],x:[7,11],xxxx:11,xzf:11,y:[0,5,7,11],yai:11,yarn:11,year:5,yet:10,you:[1,4,5,6,7,10],your:[6,10,11],z:[7,11],zip:[1,7,8,9],zone:0,zoom:7},titles:["Authentication","Configuration","API documentation","Records","Sports","Statistics","Users","Workouts","Change log","Features","FitTrackee","Installation","Administrator","Troubleshooting","User"],titleterms:{"0":8,"01":8,"02":8,"03":8,"04":8,"07":8,"09":8,"1":8,"10":8,"12":8,"15":8,"16":8,"17":8,"19":8,"2":8,"2018":8,"2019":8,"2020":8,"2021":8,"21":8,"23":8,"29":8,"3":8,"30":8,"31":8,"4":8,"5":8,"6":8,"7":8,"8":8,"9":8,"new":8,account:9,administr:[8,9,12],api:2,authent:0,avail:8,bug:8,chang:8,close:8,configur:1,content:10,dashboard:9,deploy:11,detail:9,dev:11,develop:11,docker:11,document:2,email:11,endpoint:[2,13],environ:11,fail:12,featur:[8,9],first:8,fittracke:[8,10,12],fix:8,french:8,from:11,improv:8,instal:11,issu:8,list:9,log:8,map:11,minor:8,misc:8,prerequisit:11,prod:11,product:11,pull:8,pypi:[8,11],record:3,releas:8,request:8,server:11,sourc:11,sport:4,start:12,statist:[5,8,9],tabl:10,tile:11,translat:9,troubleshoot:13,unreleas:8,upgrad:11,user:[6,14],variabl:11,version:8,workout:[7,9]}}) \ No newline at end of file +Search.setIndex({docnames:["api/auth","api/configuration","api/index","api/records","api/sports","api/stats","api/users","api/workouts","changelog","features","index","installation","troubleshooting/administrator","troubleshooting/index","troubleshooting/user"],envversion:{"sphinx.domains.c":2,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":4,"sphinx.domains.index":1,"sphinx.domains.javascript":2,"sphinx.domains.math":2,"sphinx.domains.python":3,"sphinx.domains.rst":2,"sphinx.domains.std":2,sphinx:56},filenames:["api/auth.rst","api/configuration.rst","api/index.rst","api/records.rst","api/sports.rst","api/stats.rst","api/users.rst","api/workouts.rst","changelog.md","features.rst","index.rst","installation.rst","troubleshooting/administrator.rst","troubleshooting/index.rst","troubleshooting/user.rst"],objects:{"":{"/api/auth/login":[0,0,1,"post--api-auth-login"],"/api/auth/logout":[0,1,1,"get--api-auth-logout"],"/api/auth/password/reset-request":[0,0,1,"post--api-auth-password-reset-request"],"/api/auth/password/update":[0,0,1,"post--api-auth-password-update"],"/api/auth/picture":[0,0,1,"post--api-auth-picture"],"/api/auth/profile":[0,1,1,"get--api-auth-profile"],"/api/auth/profile/edit":[0,0,1,"post--api-auth-profile-edit"],"/api/auth/profile/edit/preferences":[0,0,1,"post--api-auth-profile-edit-preferences"],"/api/auth/profile/edit/sports":[0,0,1,"post--api-auth-profile-edit-sports"],"/api/auth/register":[0,0,1,"post--api-auth-register"],"/api/config":[1,3,1,"patch--api-config"],"/api/ping":[1,1,1,"get--api-ping"],"/api/records":[3,1,1,"get--api-records"],"/api/sports":[4,1,1,"get--api-sports"],"/api/sports/(int:sport_id)":[4,3,1,"patch--api-sports-(int-sport_id)"],"/api/stats/(user_name)/by_sport":[5,1,1,"get--api-stats-(user_name)-by_sport"],"/api/stats/(user_name)/by_time":[5,1,1,"get--api-stats-(user_name)-by_time"],"/api/stats/all":[5,1,1,"get--api-stats-all"],"/api/users":[6,1,1,"get--api-users"],"/api/users/(user_name)":[6,3,1,"patch--api-users-(user_name)"],"/api/users/(user_name)/picture":[6,1,1,"get--api-users-(user_name)-picture"],"/api/workouts":[7,0,1,"post--api-workouts"],"/api/workouts/(string:workout_short_id)":[7,3,1,"patch--api-workouts-(string-workout_short_id)"],"/api/workouts/(string:workout_short_id)/chart_data":[7,1,1,"get--api-workouts-(string-workout_short_id)-chart_data"],"/api/workouts/(string:workout_short_id)/chart_data/segment/(int:segment_id)":[7,1,1,"get--api-workouts-(string-workout_short_id)-chart_data-segment-(int-segment_id)"],"/api/workouts/(string:workout_short_id)/gpx":[7,1,1,"get--api-workouts-(string-workout_short_id)-gpx"],"/api/workouts/(string:workout_short_id)/gpx/segment/(int:segment_id)":[7,1,1,"get--api-workouts-(string-workout_short_id)-gpx-segment-(int-segment_id)"],"/api/workouts/map/(map_id)":[7,1,1,"get--api-workouts-map-(map_id)"],"/api/workouts/no_gpx":[7,0,1,"post--api-workouts-no_gpx"],"DEFAULT_STATICMAP \ud83c\udd95":[11,4,1,"envvar-DEFAULT_STATICMAP"],APP_LOG:[11,4,1,"-"],APP_SECRET_KEY:[11,4,1,"-"],APP_SETTINGS:[11,4,1,"-"],APP_WORKERS:[11,4,1,"-"],DATABASE_DISABLE_POOLING:[11,4,1,"-"],DATABASE_URL:[11,4,1,"-"],EMAIL_URL:[11,4,1,"-"],FLASK_APP:[11,4,1,"-"],HOST:[11,4,1,"-"],MAP_ATTRIBUTION:[11,4,1,"-"],PORT:[11,4,1,"-"],REDIS_URL:[11,4,1,"-"],SENDER_EMAIL:[11,4,1,"-"],TILE_SERVER_URL:[11,4,1,"-"],UI_URL:[11,4,1,"-"],UPLOAD_FOLDER:[11,4,1,"-"],VUE_APP_API_URL:[11,4,1,"-"],WEATHER_API_KEY:[11,4,1,"-"],WORKERS_PROCESSES:[11,4,1,"-"]},"/api/workouts/map_tile/(s)/(z)/(x)/(y)":{png:[7,1,1,"get--api-workouts-map_tile-(s)-(z)-(x)-(y).png"]}},objnames:{"0":["http","post","HTTP post"],"1":["http","get","HTTP get"],"2":["http","delete","HTTP delete"],"3":["http","patch","HTTP patch"],"4":["std","envvar","environment variable"]},objtypes:{"0":"http:post","1":"http:get","2":"http:delete","3":"http:patch","4":"std:envvar"},terms:{"0":[0,1,3,4,5,6,7,9,10,11],"00":[0,3,6,7],"000000":0,"01":[0,3,5,6,7,10],"02":[7,10],"03":[6,7,10],"04":[7,10],"06":5,"07":[0,3,6,7,10],"08":[0,3,6],"09":[0,6,10],"0mb":[0,7],"1":[0,1,3,4,5,6,7,9,10,11],"10":[0,1,3,6,7,10,11],"100":[7,9],"1000":5,"104":8,"1048576":1,"10485760":1,"109":8,"11":[0,3,6,8,9],"12":[0,3,6,7,10],"1232004":7,"12341":5,"1234538":7,"1267":5,"127":11,"13":[7,8],"14":[0,6,7,8],"15":[5,7,10],"150":5,"156":5,"1563529507772":7,"16":[7,10],"17":[7,10,11],"178":5,"18":[0,3,6,7,8],"19":10,"2":[0,1,3,4,5,6,7,10,11],"20":6,"200":[0,1,3,4,5,6,7],"201":[0,7],"2017":[5,7],"2018":[5,7,10],"2019":[0,3,5,6,7,10],"2020":10,"2021":10,"203":5,"204":[0,6,7],"21":10,"22":[7,8],"23":10,"24":8,"25":[8,11],"26":8,"27":[0,6],"279":7,"28":8,"280":7,"282":5,"29":10,"2930":7,"2e1ee2c":8,"3":[0,4,5,6,7,9,10,11],"30":[5,10],"3000":11,"301":11,"31":[7,10],"33":8,"34":8,"34614d5":8,"36":8,"37":8,"39":7,"4":[0,4,6,7,10,11],"40":8,"400":[0,1,4,7],"401":[0,1,3,4,5,6,7],"403":[0,1,4,5,6,7],"404":[0,4,5,6,7],"41":8,"4109":7,"413":[0,7],"43":8,"44":[7,8],"443":11,"45":7,"46":[5,8],"465":11,"47":[5,7,8],"4c3fc34":8,"5":[1,4,5,6,7,9,10,11],"50":[0,6,8],"500":[0,1,4,6,7],"5000":11,"5078118":7,"5079733":7,"51":7,"53":8,"54":8,"5432":11,"57":[7,8],"58":[0,6,8],"587":11,"59":8,"6":[0,4,6,7,10],"613":5,"62":8,"63":7,"64":8,"66":8,"67":[0,6],"7":[7,10,11],"70":8,"71":8,"72":8,"7380":7,"75":8,"78":5,"79":8,"8":[0,7,10],"80":[8,11],"8025":11,"81":8,"82":8,"83":8,"84":8,"895":[0,6],"9":[0,3,6,10,11],"91":8,"93":8,"95":8,"98":8,"9960":5,"boolean":[0,1,6],"default":[5,6,7,8,9,11],"do":[1,4,5,6,7],"export":10,"float":[0,7],"import":[8,11],"int":[4,7],"new":[0,9,11],"null":[0,4,6,7],"return":[3,7,11],"short":7,"static":[8,11],"switch":8,"true":[0,1,4,6,8,11],"try":[0,6,7],A:[6,8,9],AS:[0,3,6,7],For:11,If:[9,11],In:8,It:[8,10,12],NO:[0,6,7],NOT:[0,4,7],No:[0,6,7,8,10],Not:[4,5,6,7],OF:11,ON:11,On:11,One:11,TO:11,The:[8,9,11],There:11,To:11,WITH:11,With:11,_:8,__main__:11,_blank:11,access:11,account:[6,8],acit:7,activ:[4,8,9,10,11],ad:[0,8],adapt:11,add:[6,8,9,10],addit:11,address:11,admin:[0,1,3,4,5,6,7,8,11],administr:[0,6,7,10,13],after:[8,11],again:[0,1,3,4,5,6,7],agplv3:8,all:[3,4,5,6,11],allow:[0,1,7,8,10],along:7,alpin:9,alreadi:0,also:[8,10,11],altitud:8,alwai:11,an:[1,4,6,7,8,9,11],android:10,anoth:[6,11],anymor:8,apach:10,api:[0,1,3,4,5,6,7,8,9,10,11],apikei:11,app:10,app_log:11,app_secret_kei:11,app_set:11,app_work:11,applic:[0,1,3,4,5,6,7,8,9,10,11],ar:[3,7,8,9,10,11,12],arch:11,archiv:[1,8,9,11],archlinux:11,asc:6,ascent:7,attribut:11,auth:[0,1,3,4,5,6,7],auth_token:0,auth_user_id:[1,3,4,5,6,7],authent:[1,2,3,4,5,6,7,10],author:[0,1,3,4,5,6,7],avail:[0,3,9,10,11],ave_spe:7,ave_speed_from:7,ave_speed_to:7,averag:[3,7,8,9],axi:7,b862a77:8,b:11,background:8,backup:11,bad:[0,1,4,7],bearer:[0,1,3,4,5,6,7],becom:8,been:8,befor:[8,11],begin:11,better:8,bike:[4,7,8,9],bin:11,bio:[0,6],biographi:0,birth:[0,8],birth_dat:[0,6],bound:7,build:11,by_sport:5,by_tim:5,c:7,calcul:8,calendar:[8,9],can:[6,8,9,10,11,12],cannot:8,cd:11,chang:[9,10,11],charact:[0,8],chart:[7,8,9,11],chart_data:7,check:[1,11,12],choos:8,client:[8,11],client_max_body_s:11,clone:11,code:[0,1,3,4,5,6,7],color:[0,4,8],com:[0,6,11],complet:8,config:[1,11],configur:[2,8,9,10,11],confirm:0,contact:[0,6,7],contain:[8,9,11],content:[0,1,3,4,5,6,7],contributor:[1,11],control:8,coordin:11,copi:[1,11],copyright:[1,11],correctli:8,countri:9,cp:11,creat:[0,7,8,9,11],create_app:11,created_at:[0,6],creation:[8,9],creation_d:7,credenti:[0,11],criteria:6,cross:9,current:8,custom:11,cycl:[4,8,9],d:[0,5,7],dai:8,dark:11,darkski:[8,9],dashboard:[8,10],data:[0,1,3,4,5,6,7,8,10,11],databas:[8,11,12],database_disable_pool:11,database_url:[8,11],date:[0,5,7,8,9],db:11,debian:11,default_staticmap:[8,11],defin:9,definit:8,delet:[0,6,7,8,9],depend:[8,11],deploy:10,desc:7,descent:7,describ:11,descript:11,detail:[6,8,10],develop:10,dialect:12,differ:8,directli:11,directori:[8,11],disabl:[0,8,9,11],displai:[7,8,9,10,11,12],distanc:[3,7,8,9],distance_from:7,distance_to:7,distribut:11,docker:[8,10],document:[8,10,11],doe:[0,5,6,7,8],don:0,down:8,download:11,dramatiq:11,drop:8,due:8,durat:[3,7,8,9],duration_from:7,duration_to:7,dure:[0,7],easi:8,edit:[0,8,9],electr:9,elev:[7,8,9,11],els:11,email:[0,6,8],email_url:11,empti:8,en:[0,6],enabl:[1,9],encount:11,end:[5,7],endpoint:[1,8],engin:[8,11,12],english:9,enter:[8,9],entiti:[0,7],entri:11,env:11,environ:[8,10,12],error:[0,1,4,6,7,8,9,11,12],europ:[0,6],evalu:[8,11],even:[8,9],exampl:[0,1,3,4,5,6,7,10,11],exc:12,exce:[0,7],exceed:8,except:6,execstart:11,exhaust:10,exist:[0,5,6,7,9,10,11],expir:[0,1,3,4,5,6,7],extens:[0,7],fa33f4d996844a5c73ecd1ae24456ab8:7,fail:[8,13],fals:[0,1,4,6,7,11],farest:[3,8,9],fd:[0,3,6,7],featur:[10,11],fetch:11,file:[0,1,7,8,9,10,11],filter:[8,9],first:[0,10],first_nam:[0,6],fitotrack:10,fittracke:[9,11,13],fittrackee_cli:11,fittrackee_init_data:11,fittrackee_upgrade_db:11,fittrackee_work:11,fix:10,flask:11,flask_app:11,flaticon:11,follow:[3,9,11,12],forbidden:[0,1,4,5,6,7],forecast:11,form:[0,7],format:[0,5,7],former:11,forrunn:10,forward:11,found:[0,4,5,6,7],fr:6,frame:5,freepik:11,french:[9,10],fri:7,from:[1,3,4,5,6,7,8,10],fullchain:11,gener:[8,11],get:[0,1,3,4,5,6,7],gif:0,git:11,github:11,given:11,gmt:[0,3,6,7],gpl:10,gpx:[7,8,9,10,11],gpx_limit_import:1,gpxpy:[0,9,11],grant:11,greater:8,gunicorn:[11,12],gz:11,h:[7,9],ha:6,handl:[0,9,11],has_workout:4,have:[1,4,5,6,7,8],he:6,header:[0,1,3,4,5,6,7],health:1,heavi:[10,11],hexadecim:0,hi:[6,8,9],hike:[4,8,9],home:11,host:11,href:[1,11],http:[0,1,3,4,5,6,7,11],hvybqybra7wwxpastwr4v2:[0,3,6],i18n:8,icon:11,id:[0,1,3,4,5,6,7],imag:[0,6,7,8,11],improv:10,incorrect:8,index:7,info:0,inform:[8,10,11],init:11,initi:11,initialis:11,insid:11,instal:[8,10],instanc:[1,9,11],instead:9,integ:[1,3,4,5,6,7],interceptor:8,interfac:11,intern:[0,1,4,6,7],introduc:8,invalid:[0,1,3,4,5,6,7,8],io:11,ip:11,is_act:[0,4],is_active_for_us:4,is_registration_en:1,issu:[10,11],jan:7,javascript:8,john_do:6,jpeg:6,jpg:0,js:11,json:[0,1,3,4,5,6,7],jul:[0,3,6,7],keep:[8,10,11],kei:[8,9,11],kjxavsturjvoah2wvcegef:7,km:[7,9],label:4,languag:[0,6],larg:[0,7,9],larger:11,last:[0,11],last_nam:[0,6],latitud:7,layer:[8,11],ld:[0,3,6,7],leaflet:[7,11],least:7,less:8,librari:11,licens:[8,10],like:[9,11],limit:[8,9],link:11,linux:11,list:[8,10,11],listen:11,load:[8,12],local:[8,10,11],localhost:11,locat:[0,6,11],log:[0,1,3,4,5,6,7,10,11,12],logfil:11,login:0,logout:[0,8],longer:[8,12],longest:[3,8,9],longitud:7,m:[0,5,7],made:11,mai:[9,10,11],mailhog:11,major:8,make:[8,11],makefil:11,manag:8,mandatori:[7,8,11],map:[7,8,9,10],map_attribut:[1,11],map_id:7,map_til:7,match:0,max:[1,6,7,8],max_alt:7,max_single_file_s:1,max_spe:7,max_speed_from:7,max_speed_to:7,max_us:1,max_zip_file_s:1,maxim:7,maximum:[3,8,9],messag:[0,1,7,8],method:11,min:8,min_alt:7,minim:7,minor:10,miss:8,mobil:10,model:8,modifi:[6,8],modification_d:7,modul:11,mon:7,mondai:[0,5,9],montain:8,month:[5,8,9],more:[8,9,10,11],morn:7,mountain:[4,9],mous:8,move:[7,8],movement:8,mpwoadmin:11,ms:[0,3,6,7],multi:11,multipart:[0,7],must:[0,1,4,8,9,11,12],mv:11,n:0,name:[0,5,6,11],nano:11,nb_sport:[0,6],nb_workout:[0,5,6],necessari:[9,11],need:11,network:11,next_workout:7,nginx:[9,11],no_gpx:7,non:[4,10],noopen:11,noreferr:11,nosuchmoduleerror:12,note:[7,8,9,11],now:[8,9,11],number:[1,6,7,8,9,11],oauth:[0,1,3,4,5,6,7],object:[0,1,4,6,7],ok:[0,1,3,4,5,6,7],one:[6,7,9],onli:[6,7,8,9,11],open:[10,11],openstreetmap:[1,8,11],opentrack:10,option:11,order:[6,7,8],order_bi:6,org:[1,11],os:11,other:[6,11],out:0,outdoor:[8,10,11],over:8,overwrit:11,own:[6,10],owner:[8,9],packag:[8,11],page:[6,7],pagin:[6,7],par_pag:6,paramet:[0,1,3,4,5,6,7,8,9],pari:[0,6],pars:11,part:[0,7],pass:11,password:[0,8,9,11],password_conf:0,patch:[1,4,6,7],path:11,paus:[7,8],payload:[0,1,4,7],pem:11,per:[6,7,8],per_pag:[6,7],permiss:[1,4,5,6,7],pg_dump:11,pictur:[0,6,7],ping:1,pip:11,pipenv:8,pleas:[0,1,3,4,5,6,7,8],plugin:12,png:[0,7,11],poetri:[8,11],point:[8,11],pong:1,pool:11,port:11,possibl:[8,10],post:[0,7],postgr:[8,12],postgresql:[8,11,12],prefer:0,prerequisit:10,previous_workout:7,privileg:11,privkei:11,process:[0,9,11],productionconfig:11,profil:0,project:11,provid:[0,1,3,4,5,6,7,8,9,11],proxi:11,proxy_add_x_forwarded_for:11,proxy_pass:11,proxy_redirect:11,proxy_set_head:11,pull:11,purpos:[8,11],pwd:11,py:11,pypi:10,python:[8,11],q:6,queri:[5,6,7],queue:11,r:11,rais:9,read:8,real:11,rebuild:8,rechart:7,recommend:11,record:[0,2,6,7,8,9,10],record_typ:[0,3,6,7],redi:[8,11],redis_url:11,refactor:8,regist:[0,1,11],registr:[0,1,8,9],rel:11,relat:11,releas:[10,11],remain:8,remote_addr:11,remov:9,renam:8,replac:[8,11],repo:11,repositori:11,request:[0,1,3,4,5,6,7],request_uri:11,requir:0,reset:[0,8,9],respons:[0,1,3,4,5,6,7],restart:11,restartsec:11,right:[6,8,9],rout:8,row:9,run:[4,8,9,11],runner:10,s:[6,7,8,11],sam:[0,6],same:8,samr1:11,sat:6,save:[8,9],schema:11,search:8,second:7,secret:11,see:[8,9,10,11],seem:8,segment:[7,8,9],segment_id:7,select:[0,7],send:[8,11],sender:11,sender_email:11,serv:11,server:[0,1,4,6,7,8,9,10],server_nam:11,servic:11,set:[8,9],sever:[10,11],shell:11,should:11,show:8,shown:8,side:8,signatur:[0,1,3,4,5,6,7],simpl:11,simplifi:8,sinc:11,singl:[1,6],size:[0,1,7,8,9],ski:9,sky:11,slow:8,smtp:11,some:[6,7,8,10,11],sorri:0,sort:[6,7],sourc:10,spawn:11,speed:[0,3,7,8,9,11],spinner:8,sport:[0,2,5,7,8,9,10,11],sport_id:[0,3,4,5,6,7],sports_list:[0,6],sql:11,sqlalchemi:[8,11,12],ssl:11,ssl_certif:11,ssl_certificate_kei:11,standard:[8,11],standarderror:11,standardoutput:11,start:[0,5,7,8,9,11,13],startlimitintervalsec:11,starttl:11,stat:[5,8],staticmap:11,statist:[2,10],statu:[0,1,3,4,5,6,7],step:11,still:10,stop:[0,8,9,11],stopped_speed_threshold:[0,4],store:10,street:10,string:[0,4,5,6,7],subdomain:7,success:[0,1,3,4,5,6,7],successfulli:0,sun:[0,3,6,7],sundai:[5,7,9],support:[8,9,12],syslog:11,syslogidentifi:11,system:11,systemd:11,t:[0,12],tar:11,target:11,task:11,term:[8,11],test:11,textarea:8,than:8,thei:11,them:10,thi:[7,8,9,10,11],threshold:[0,8,9],thunderforest:[8,11],tile:[7,8],tile_server_url:11,time:[0,5,7,8,9],timezon:[0,6,8],titl:7,tl:11,todo:14,token:[0,1,3,4,5,6,7],too:[0,7],tool:11,tooltip:8,total:8,total_asc:5,total_dist:[0,5,6],total_dur:[0,5,6],track:10,trail:9,transport:[4,8,9],troubleshoot:10,type:[0,1,3,4,5,6,7,11],typescript:11,u:11,ui:8,ui_url:11,unauthor:[0,1,3,4,5,6,7],under:[10,11],unencrypt:11,unit:11,unreleas:10,unstabl:[10,11],up:[9,10],updat:[0,1,4,6,7,8,9,11],upload:[8,9,11],upload_fold:11,uploads_dir_s:5,url:[8,11,12],us:[0,6,7,8,9,10,11],user:[0,1,2,3,4,5,7,8,9,10,11,13],user_id:0,user_nam:[5,6],usernam:[0,6,11],uuid:[7,8],v0:11,v3:10,valid:[0,1,3,4,5,6,7,11],valu:[0,3,6,7],variabl:[8,10,12],venv:11,version:[1,10,11],view:8,virtualenv:11,vue3:11,vue:11,vue_app_api_url:11,vuex:11,wa:8,wai:11,walk:[4,8,9],wantedbi:11,warn:8,weather:[8,9,11],weather_api:11,weather_api_kei:11,weather_end:7,weather_start:7,web:[0,1,3,4,5,6,7,10,11],week:[0,5,8,9],weekend:8,weekm:[0,5],were:8,wget:11,when:[0,1,8,11],where:11,which:9,with_gpx:7,without:[5,6,7,8,9,10],worker:11,workers_process:11,workingdirectori:11,workout:[0,2,5,8,10],workout_d:[0,3,6,7],workout_id:[0,3,6,7],workout_short_id:7,workouts_count:6,written:11,www:[1,11],x:[7,11],xxxx:11,xzf:11,y:[0,5,7,11],yai:11,yarn:11,year:5,yet:10,you:[1,4,5,6,7,10],your:[6,10,11],z:[7,11],zip:[1,7,8,9],zone:0,zoom:7},titles:["Authentication","Configuration","API documentation","Records","Sports","Statistics","Users","Workouts","Change log","Features","FitTrackee","Installation","Administrator","Troubleshooting","User"],titleterms:{"0":8,"01":8,"02":8,"03":8,"04":8,"07":8,"09":8,"1":8,"10":8,"12":8,"15":8,"16":8,"17":8,"19":8,"2":8,"2018":8,"2019":8,"2020":8,"2021":8,"21":8,"23":8,"29":8,"3":8,"30":8,"31":8,"4":8,"5":8,"6":8,"7":8,"8":8,"9":8,"new":8,account:9,administr:[8,9,12],api:2,authent:0,avail:8,bug:8,chang:8,close:8,configur:1,content:10,dashboard:9,deploy:11,detail:9,dev:11,develop:11,docker:11,document:2,email:11,endpoint:[2,13],environ:11,fail:12,featur:[8,9],first:8,fittracke:[8,10,12],fix:8,french:8,from:11,improv:8,instal:11,issu:8,list:9,log:8,map:11,minor:8,misc:8,prerequisit:11,prod:11,product:11,pull:8,pypi:[8,11],record:3,releas:8,request:8,server:11,sourc:11,sport:4,start:12,statist:[5,8,9],tabl:10,tile:11,translat:9,troubleshoot:13,unreleas:8,upgrad:11,user:[6,14],variabl:11,version:8,workout:[7,9]}}) \ No newline at end of file diff --git a/fittrackee/tests/fixtures/fixtures_users.py b/fittrackee/tests/fixtures/fixtures_users.py index 217d92c9..d1f7ce15 100644 --- a/fittrackee/tests/fixtures/fixtures_users.py +++ b/fittrackee/tests/fixtures/fixtures_users.py @@ -96,3 +96,17 @@ def user_sport_1_preference( db.session.add(user_sport) db.session.commit() return user_sport + + +@pytest.fixture() +def user_admin_sport_1_preference( + user_1_admin: User, sport_1_cycling: Sport +) -> UserSportPreference: + user_sport = UserSportPreference( + user_id=user_1_admin.id, + sport_id=sport_1_cycling.id, + stopped_speed_threshold=sport_1_cycling.stopped_speed_threshold, + ) + db.session.add(user_sport) + db.session.commit() + return user_sport diff --git a/fittrackee/tests/workouts/test_sports_api.py b/fittrackee/tests/workouts/test_sports_api.py index 8612e744..765e883e 100644 --- a/fittrackee/tests/workouts/test_sports_api.py +++ b/fittrackee/tests/workouts/test_sports_api.py @@ -2,7 +2,8 @@ import json from flask import Flask -from fittrackee.users.models import User +from fittrackee import db +from fittrackee.users.models import User, UserSportPreference from fittrackee.workouts.models import Sport, Workout from ..api_test_case import ApiTestCaseMixin @@ -11,6 +12,9 @@ expected_sport_1_cycling_result = { 'id': 1, 'label': 'Cycling', 'is_active': True, + 'is_active_for_user': True, + 'color': None, + 'stopped_speed_threshold': 1, } expected_sport_1_cycling_admin_result = expected_sport_1_cycling_result.copy() expected_sport_1_cycling_admin_result['has_workouts'] = False @@ -19,6 +23,9 @@ expected_sport_2_running_result = { 'id': 2, 'label': 'Running', 'is_active': True, + 'is_active_for_user': True, + 'color': None, + 'stopped_speed_threshold': 0.1, } expected_sport_2_running_admin_result = expected_sport_2_running_result.copy() expected_sport_2_running_admin_result['has_workouts'] = False @@ -27,6 +34,9 @@ expected_sport_1_cycling_inactive_result = { 'id': 1, 'label': 'Cycling', 'is_active': False, + 'is_active_for_user': False, + 'color': None, + 'stopped_speed_threshold': 1, } expected_sport_1_cycling_inactive_admin_result = ( expected_sport_1_cycling_inactive_result.copy() @@ -108,6 +118,39 @@ class TestGetSports(ApiTestCaseMixin): data['data']['sports'][1] == expected_sport_2_running_admin_result ) + def test_it_gets_sports_with_auth_user_preferences( + self, + app: Flask, + user_1_admin: User, + sport_1_cycling: Sport, + sport_2_running: Sport, + user_admin_sport_1_preference: UserSportPreference, + ) -> None: + user_admin_sport_1_preference.color = '#000000' + user_admin_sport_1_preference.stopped_speed_threshold = 0.5 + user_admin_sport_1_preference.is_active = False + db.session.commit() + + client, auth_token = self.get_test_client_and_auth_token( + app, as_admin=True + ) + + response = client.get( + '/api/sports', + headers=dict(Authorization=f'Bearer {auth_token}'), + ) + + data = json.loads(response.data.decode()) + assert response.status_code == 200 + assert 'success' in data['status'] + assert len(data['data']['sports']) == 2 + assert data['data']['sports'][0]['color'] == '#000000' + assert data['data']['sports'][0]['stopped_speed_threshold'] == 0.5 + assert data['data']['sports'][0]['is_active_for_user'] is False + assert ( + data['data']['sports'][1] == expected_sport_2_running_admin_result + ) + class TestGetSport(ApiTestCaseMixin): def test_it_gets_a_sport( @@ -126,6 +169,26 @@ class TestGetSport(ApiTestCaseMixin): assert len(data['data']['sports']) == 1 assert data['data']['sports'][0] == expected_sport_1_cycling_result + def test_it_gets_a_sport_with_preferences( + self, + app: Flask, + user_1: User, + sport_1_cycling: Sport, + user_sport_1_preference: UserSportPreference, + ) -> None: + client, auth_token = self.get_test_client_and_auth_token(app) + + response = client.get( + '/api/sports/1', + headers=dict(Authorization=f'Bearer {auth_token}'), + ) + + data = json.loads(response.data.decode()) + assert response.status_code == 200 + assert 'success' in data['status'] + assert len(data['data']['sports']) == 1 + assert data['data']['sports'][0] == expected_sport_1_cycling_result + def test_it_returns_404_if_sport_does_not_exist( self, app: Flask, user_1: User ) -> None: @@ -202,6 +265,7 @@ class TestUpdateSport(ApiTestCaseMixin): assert 'success' in data['status'] assert len(data['data']['sports']) == 1 assert data['data']['sports'][0]['is_active'] is False + assert data['data']['sports'][0]['is_active_for_user'] is False assert data['data']['sports'][0]['has_workouts'] is False def test_it_enables_a_sport( @@ -224,6 +288,7 @@ class TestUpdateSport(ApiTestCaseMixin): assert 'success' in data['status'] assert len(data['data']['sports']) == 1 assert data['data']['sports'][0]['is_active'] is True + assert data['data']['sports'][0]['is_active_for_user'] is True assert data['data']['sports'][0]['has_workouts'] is False def test_it_disables_a_sport_with_workouts( @@ -249,6 +314,7 @@ class TestUpdateSport(ApiTestCaseMixin): assert 'success' in data['status'] assert len(data['data']['sports']) == 1 assert data['data']['sports'][0]['is_active'] is False + assert data['data']['sports'][0]['is_active_for_user'] is False assert data['data']['sports'][0]['has_workouts'] is True def test_it_enables_a_sport_with_workouts( @@ -275,8 +341,63 @@ class TestUpdateSport(ApiTestCaseMixin): assert 'success' in data['status'] assert len(data['data']['sports']) == 1 assert data['data']['sports'][0]['is_active'] is True + assert data['data']['sports'][0]['is_active_for_user'] is True assert data['data']['sports'][0]['has_workouts'] is True + def test_it_disables_a_sport_with_preferences( + self, + app: Flask, + user_1_admin: User, + sport_1_cycling: Sport, + user_admin_sport_1_preference: UserSportPreference, + ) -> None: + client, auth_token = self.get_test_client_and_auth_token( + app, as_admin=True + ) + + response = client.patch( + '/api/sports/1', + content_type='application/json', + data=json.dumps(dict(is_active=False)), + headers=dict(Authorization=f'Bearer {auth_token}'), + ) + + data = json.loads(response.data.decode()) + assert response.status_code == 200 + assert 'success' in data['status'] + assert len(data['data']['sports']) == 1 + assert data['data']['sports'][0]['is_active'] is False + assert data['data']['sports'][0]['is_active_for_user'] is False + assert data['data']['sports'][0]['is_active_for_user'] is False + assert data['data']['sports'][0]['has_workouts'] is False + + def test_it_enables_a_sport_with_preferences( + self, + app: Flask, + user_1_admin: User, + sport_1_cycling: Sport, + user_admin_sport_1_preference: UserSportPreference, + ) -> None: + sport_1_cycling.is_active = False + client, auth_token = self.get_test_client_and_auth_token( + app, as_admin=True + ) + + response = client.patch( + '/api/sports/1', + content_type='application/json', + data=json.dumps(dict(is_active=True)), + headers=dict(Authorization=f'Bearer {auth_token}'), + ) + + data = json.loads(response.data.decode()) + assert response.status_code == 200 + assert 'success' in data['status'] + assert len(data['data']['sports']) == 1 + assert data['data']['sports'][0]['is_active'] is True + assert data['data']['sports'][0]['is_active_for_user'] is True + assert data['data']['sports'][0]['has_workouts'] is False + def test_returns_error_if_user_has_no_admin_rights( self, app: Flask, user_1: User, sport_1_cycling: Sport ) -> None: diff --git a/fittrackee/tests/workouts/test_sports_model.py b/fittrackee/tests/workouts/test_sports_model.py index 0129bc99..417f2959 100644 --- a/fittrackee/tests/workouts/test_sports_model.py +++ b/fittrackee/tests/workouts/test_sports_model.py @@ -2,7 +2,8 @@ from typing import Dict, Optional from flask import Flask -from fittrackee.users.models import User +from fittrackee import db +from fittrackee.users.models import User, UserSportPreference from fittrackee.workouts.models import Sport, Workout @@ -15,10 +16,13 @@ class TestSportModel: assert 'Cycling' == sport.label assert '' == str(sport) - serialized_sport = sport.serialize(is_admin) + serialized_sport = sport.serialize(is_admin=is_admin) assert 1 == serialized_sport['id'] assert 'Cycling' == serialized_sport['label'] assert serialized_sport['is_active'] is True + assert serialized_sport['is_active_for_user'] is True + assert serialized_sport['color'] is None + assert serialized_sport['stopped_speed_threshold'] == 1 return serialized_sport def test_sport_model(self, app: Flask, sport_1_cycling: Sport) -> None: @@ -44,3 +48,87 @@ class TestSportModel: ) -> None: serialized_sport = self.assert_sport_model(sport_1_cycling, True) assert serialized_sport['has_workouts'] is True + + +class TestSportModelWithPreferences: + def test_sport_model_with_color_preference( + self, + app: Flask, + user_1: User, + sport_1_cycling: Sport, + user_sport_1_preference: UserSportPreference, + ) -> None: + user_sport_1_preference.color = '#00000' + + serialized_sport = sport_1_cycling.serialize( + sport_preferences=user_sport_1_preference.serialize() + ) + assert serialized_sport['id'] == 1 + assert serialized_sport['label'] == 'Cycling' + assert serialized_sport['is_active'] is True + assert serialized_sport['is_active_for_user'] is True + assert serialized_sport['color'] == '#00000' + assert serialized_sport['stopped_speed_threshold'] == 1 + assert 'has_workouts' not in serialized_sport + + def test_sport_model_with_is_active_preference( + self, + app: Flask, + user_1: User, + sport_1_cycling: Sport, + user_sport_1_preference: UserSportPreference, + ) -> None: + user_sport_1_preference.is_active = False + + serialized_sport = sport_1_cycling.serialize( + sport_preferences=user_sport_1_preference.serialize() + ) + assert serialized_sport['id'] == 1 + assert serialized_sport['label'] == 'Cycling' + assert serialized_sport['is_active'] is True + assert serialized_sport['is_active_for_user'] is False + assert serialized_sport['color'] is None + assert serialized_sport['stopped_speed_threshold'] == 1 + assert 'has_workouts' not in serialized_sport + + def test_inactive_sport_model_with_is_active_preference( + self, + app: Flask, + user_1: User, + sport_1_cycling: Sport, + user_sport_1_preference: UserSportPreference, + ) -> None: + sport_1_cycling.is_active = False + user_sport_1_preference.is_active = True + + serialized_sport = sport_1_cycling.serialize( + sport_preferences=user_sport_1_preference.serialize() + ) + assert serialized_sport['id'] == 1 + assert serialized_sport['label'] == 'Cycling' + assert serialized_sport['is_active'] is False + assert serialized_sport['is_active_for_user'] is False + assert serialized_sport['color'] is None + assert serialized_sport['stopped_speed_threshold'] == 1 + assert 'has_workouts' not in serialized_sport + + def test_sport_model_with_threshold_preference( + self, + app: Flask, + user_1: User, + sport_1_cycling: Sport, + user_sport_1_preference: UserSportPreference, + ) -> None: + user_sport_1_preference.stopped_speed_threshold = 0.5 + db.session.commit() + + serialized_sport = sport_1_cycling.serialize( + sport_preferences=user_sport_1_preference.serialize() + ) + assert serialized_sport['id'] == 1 + assert serialized_sport['label'] == 'Cycling' + assert serialized_sport['is_active'] is True + assert serialized_sport['is_active_for_user'] is True + assert serialized_sport['color'] is None + assert serialized_sport['stopped_speed_threshold'] == 0.5 + assert 'has_workouts' not in serialized_sport diff --git a/fittrackee/users/models.py b/fittrackee/users/models.py index 9c602b9c..1f640344 100644 --- a/fittrackee/users/models.py +++ b/fittrackee/users/models.py @@ -40,11 +40,6 @@ class User(BaseModel): 'Record', lazy=True, backref=db.backref('user', lazy='joined') ) language = db.Column(db.String(50), nullable=True) - sport_preferences = db.relationship( - 'UserSportPreference', - lazy=True, - backref=db.backref('user', lazy='joined'), - ) def __repr__(self) -> str: return f'' diff --git a/fittrackee/workouts/models.py b/fittrackee/workouts/models.py index 6083475f..0a075f7e 100644 --- a/fittrackee/workouts/models.py +++ b/fittrackee/workouts/models.py @@ -85,11 +85,30 @@ class Sport(BaseModel): def __init__(self, label: str) -> None: self.label = label - def serialize(self, is_admin: Optional[bool] = False) -> Dict: + def serialize( + self, + is_admin: Optional[bool] = False, + sport_preferences: Optional[Dict] = None, + ) -> Dict: serialized_sport = { 'id': self.id, 'label': self.label, 'is_active': self.is_active, + 'is_active_for_user': ( + self.is_active + if sport_preferences is None + else (sport_preferences['is_active'] and self.is_active) + ), + 'color': ( + None + if sport_preferences is None + else sport_preferences['color'] + ), + 'stopped_speed_threshold': ( + self.stopped_speed_threshold + if sport_preferences is None + else sport_preferences['stopped_speed_threshold'] + ), } if is_admin: serialized_sport['has_workouts'] = len(self.workouts) > 0 diff --git a/fittrackee/workouts/sports.py b/fittrackee/workouts/sports.py index 0283578c..e91cf973 100644 --- a/fittrackee/workouts/sports.py +++ b/fittrackee/workouts/sports.py @@ -11,7 +11,7 @@ from fittrackee.responses import ( handle_error_and_return_response, ) from fittrackee.users.decorators import authenticate, authenticate_as_admin -from fittrackee.users.models import User +from fittrackee.users.models import User, UserSportPreference from .models import Sport @@ -44,34 +44,52 @@ def get_sports(auth_user_id: int) -> Dict: "data": { "sports": [ { + "color": null, "id": 1, "is_active": true, - "label": "Cycling (Sport)" + "is_active_for_user": true, + "label": "Cycling (Sport)", + "stopped_speed_threshold": 1 }, { + "color": null, "id": 2, "is_active": true, - "label": "Cycling (Transport)" + "is_active_for_user": true, + "label": "Cycling (Transport)", + "stopped_speed_threshold": 1 }, { + "color": null, "id": 3, "is_active": true, - "label": "Hiking" + "is_active_for_user": true, + "label": "Hiking", + "stopped_speed_threshold": 0.1 }, { + "color": null, "id": 4, "is_active": true, - "label": "Mountain Biking" + "is_active_for_user": true, + "label": "Mountain Biking", + "stopped_speed_threshold": 1 }, { + "color": null, "id": 5, "is_active": true, - "label": "Running" + "is_active_for_user": true, + "label": "Running", + "stopped_speed_threshold": 0.1 }, { + "color": null, "id": 6, "is_active": true, - "label": "Walking" + "is_active_for_user": true, + "label": "Walking", + "stopped_speed_threshold": 0.1 } ] }, @@ -89,40 +107,58 @@ def get_sports(auth_user_id: int) -> Dict: "data": { "sports": [ { + "color": null, "has_workouts": true, "id": 1, "is_active": true, - "label": "Cycling (Sport)" + "is_active_for_user": true, + "label": "Cycling (Sport)", + "stopped_speed_threshold": 1 }, { + "color": null, "has_workouts": false, "id": 2, "is_active": true, - "label": "Cycling (Transport)" + "is_active_for_user": true, + "label": "Cycling (Transport)", + "stopped_speed_threshold": 1 }, { + "color": null, "has_workouts": false, "id": 3, "is_active": true, - "label": "Hiking" + "is_active_for_user": true, + "label": "Hiking", + "stopped_speed_threshold": 0.1 }, { + "color": null, "has_workouts": false, "id": 4, "is_active": true, - "label": "Mountain Biking" + "is_active_for_user": true, + "label": "Mountain Biking", + "stopped_speed_threshold": 1 }, { + "color": null, "has_workouts": false, "id": 5, "is_active": true, - "label": "Running" + "is_active_for_user": true, + "label": "Running", + "stopped_speed_threshold": 0.1 }, { + "color": null, "has_workouts": false, "id": 6, "is_active": true, - "label": "Walking" + "is_active_for_user": true, + "label": "Walking", + "stopped_speed_threshold": 0.1 } ] }, @@ -142,9 +178,22 @@ def get_sports(auth_user_id: int) -> Dict: """ user = User.query.filter_by(id=int(auth_user_id)).first() sports = Sport.query.order_by(Sport.id).all() + sports_data = [] + for sport in sports: + sport_preferences = UserSportPreference.query.filter_by( + user_id=user.id, sport_id=sport.id + ).first() + sports_data.append( + sport.serialize( + is_admin=user.admin, + sport_preferences=sport_preferences.serialize() + if sport_preferences + else None, + ) + ) return { 'status': 'success', - 'data': {'sports': [sport.serialize(user.admin) for sport in sports]}, + 'data': {'sports': sports_data}, } @@ -174,9 +223,12 @@ def get_sport(auth_user_id: int, sport_id: int) -> Union[Dict, HttpResponse]: "data": { "sports": [ { + "color": null, "id": 1, "is_active": true, - "label": "Cycling (Sport)" + "is_active_for_user": true, + "label": "Cycling (Sport)", + "stopped_speed_threshold": 1 } ] }, @@ -194,10 +246,13 @@ def get_sport(auth_user_id: int, sport_id: int) -> Union[Dict, HttpResponse]: "data": { "sports": [ { + "color": null, "has_workouts": false, "id": 1, "is_active": true, - "label": "Cycling (Sport)" + "is_active_for_user": true, + "label": "Cycling (Sport)", + "stopped_speed_threshold": 1 } ] }, @@ -234,9 +289,21 @@ def get_sport(auth_user_id: int, sport_id: int) -> Union[Dict, HttpResponse]: user = User.query.filter_by(id=int(auth_user_id)).first() sport = Sport.query.filter_by(id=sport_id).first() if sport: + sport_preferences = UserSportPreference.query.filter_by( + user_id=user.id, sport_id=sport.id + ).first() return { 'status': 'success', - 'data': {'sports': [sport.serialize(user.admin)]}, + 'data': { + 'sports': [ + sport.serialize( + is_admin=user.admin, + sport_preferences=sport_preferences.serialize() + if sport_preferences + else None, + ) + ] + }, } return DataNotFoundErrorResponse('sports') @@ -270,10 +337,13 @@ def update_sport( "data": { "sports": [ { + "color": null, "has_workouts": false, "id": 1, "is_active": false, - "label": "Cycling (Sport)" + "is_active_for_user": false, + "label": "Cycling (Sport)", + "stopped_speed_threshold": 1 } ] }, @@ -317,15 +387,28 @@ def update_sport( return InvalidPayloadErrorResponse() try: + user = User.query.filter_by(id=int(auth_user_id)).first() sport = Sport.query.filter_by(id=sport_id).first() if not sport: return DataNotFoundErrorResponse('sports') sport.is_active = sport_data.get('is_active') db.session.commit() + sport_preferences = UserSportPreference.query.filter_by( + user_id=user.id, sport_id=sport.id + ).first() return { 'status': 'success', - 'data': {'sports': [sport.serialize(True)]}, + 'data': { + 'sports': [ + sport.serialize( + is_admin=user.admin, + sport_preferences=sport_preferences.serialize() + if sport_preferences + else None, + ) + ] + }, } except (exc.IntegrityError, exc.OperationalError, ValueError) as e: