13029 lines
410 KiB
Python
13029 lines
410 KiB
Python
import time
|
|
import colorsys
|
|
import timestring
|
|
import zipfile
|
|
import bleach
|
|
import arrow
|
|
import pytz
|
|
import operator
|
|
import warnings
|
|
import urllib
|
|
import yaml
|
|
from PIL import Image
|
|
from numbers import Number
|
|
from django.views.generic.base import TemplateView
|
|
from django.contrib.auth import views as auth_views
|
|
from django.db.models import Q
|
|
from django import template
|
|
from django.db import IntegrityError, transaction
|
|
from django.views.decorators.csrf import csrf_exempt
|
|
from matplotlib.backends.backend_agg import FigureCanvas
|
|
import gc
|
|
|
|
from django.shortcuts import render
|
|
from django.http import (
|
|
HttpResponse, HttpResponseRedirect,
|
|
HttpResponseForbidden, HttpResponseNotAllowed,
|
|
HttpResponseNotFound,Http404
|
|
)
|
|
from django.contrib.auth import authenticate, login, logout
|
|
from rowers.forms import (
|
|
LoginForm,DocumentsForm,UploadOptionsForm,ImageForm,CourseForm,
|
|
TeamUploadOptionsForm,WorkFlowLeftPanelForm,WorkFlowMiddlePanelForm,
|
|
WorkFlowLeftPanelElement,WorkFlowMiddlePanelElement,
|
|
LandingPageForm,PlannedSessionSelectForm,WorkoutSessionSelectForm,
|
|
PlannedSessionTeamForm,PlannedSessionTeamMemberForm,
|
|
)
|
|
from django.core.urlresolvers import reverse
|
|
from django.core.exceptions import PermissionDenied
|
|
from django.template import RequestContext
|
|
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
|
from django.conf import settings
|
|
from django.utils.datastructures import MultiValueDictKeyError
|
|
from django.utils import timezone,translation
|
|
from django.core.mail import send_mail, BadHeaderError
|
|
from rowers.forms import (
|
|
SummaryStringForm,IntervalUpdateForm,StrokeDataForm,
|
|
StatsOptionsForm,PredictedPieceForm,DateRangeForm,DeltaDaysForm,
|
|
EmailForm, RegistrationForm, RegistrationFormTermsOfService,
|
|
RegistrationFormUniqueEmail,RegistrationFormSex,
|
|
CNsummaryForm,UpdateWindForm,
|
|
UpdateStreamForm,WorkoutMultipleCompareForm,ChartParamChoiceForm,
|
|
FusionMetricChoiceForm,BoxPlotChoiceForm,MultiFlexChoiceForm,
|
|
TrendFlexModalForm,WorkoutSplitForm,WorkoutJoinParamForm,
|
|
PlannedSessionMultipleCloneForm,SessionDateShiftForm,
|
|
)
|
|
from rowers.models import (
|
|
Workout, User, Rower, WorkoutForm,FavoriteChart,
|
|
PlannedSession, DeactivateUserForm,DeleteUserForm
|
|
)
|
|
from rowers.models import (
|
|
RowerPowerForm,RowerForm,GraphImage,AdvancedWorkoutForm,
|
|
RowerPowerZonesForm,AccountRowerForm,UserForm,StrokeData,
|
|
Team,TeamForm,TeamInviteForm,TeamInvite,TeamRequest,
|
|
WorkoutComment,WorkoutCommentForm,RowerExportForm,
|
|
CalcAgePerformance,PowerTimeFitnessMetric,PlannedSessionForm,
|
|
PlannedSessionFormSmall,GeoCourseEditForm,
|
|
)
|
|
from rowers.models import (
|
|
FavoriteForm,BaseFavoriteFormSet,SiteAnnouncement,BasePlannedSessionFormSet
|
|
)
|
|
from rowers.metrics import rowingmetrics,defaultfavoritecharts
|
|
from rowers import metrics
|
|
from rowers import courses
|
|
import rowers.uploads as uploads
|
|
from django.forms.formsets import formset_factory
|
|
from django.forms import modelformset_factory
|
|
import StringIO
|
|
from django.contrib.auth.decorators import login_required,user_passes_test
|
|
from time import strftime,strptime,mktime,time,daylight
|
|
import os,sys
|
|
import datetime
|
|
import iso8601
|
|
import c2stuff
|
|
from c2stuff import C2NoTokenError,c2_open
|
|
from runkeeperstuff import RunKeeperNoTokenError,runkeeper_open
|
|
from sporttracksstuff import SportTracksNoTokenError,sporttracks_open
|
|
from tpstuff import TPNoTokenError,tp_open
|
|
from iso8601 import ParseError
|
|
import stravastuff
|
|
from stravastuff import StravaNoTokenError
|
|
import sporttracksstuff
|
|
import underarmourstuff
|
|
from underarmourstuff import UnderArmourNoTokenError,underarmour_open
|
|
import tpstuff
|
|
import runkeeperstuff
|
|
import ownapistuff
|
|
from ownapistuff import TEST_CLIENT_ID, TEST_CLIENT_SECRET, TEST_REDIRECT_URI
|
|
from rowsandall_app.settings import (
|
|
C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET,
|
|
STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET,
|
|
SPORTTRACKS_CLIENT_ID, SPORTTRACKS_REDIRECT_URI,
|
|
SPORTTRACKS_CLIENT_SECRET,
|
|
UNDERARMOUR_CLIENT_ID, UNDERARMOUR_REDIRECT_URI,
|
|
UNDERARMOUR_CLIENT_SECRET,UNDERARMOUR_CLIENT_KEY,
|
|
RUNKEEPER_CLIENT_ID,RUNKEEPER_REDIRECT_URI,RUNKEEPER_CLIENT_SECRET,
|
|
TP_CLIENT_ID,TP_REDIRECT_URI,TP_CLIENT_KEY,TP_CLIENT_SECRET,
|
|
)
|
|
|
|
from rowers.tasks_standalone import addcomment2
|
|
from django.contrib import messages
|
|
from async_messages import messages as a_messages
|
|
|
|
import requests
|
|
import json
|
|
from rest_framework.renderers import JSONRenderer
|
|
from rest_framework.parsers import JSONParser
|
|
from rowers.rows import handle_uploaded_file,handle_uploaded_image
|
|
from rowers.plannedsessions import *
|
|
from rowers.tasks import handle_makeplot,handle_otwsetpower,handle_sendemailtcx,handle_sendemailcsv
|
|
from rowers.tasks import (
|
|
handle_sendemail_unrecognized,handle_sendemailnewcomment,
|
|
handle_sendemailsummary,
|
|
handle_sendemailnewresponse, handle_updatedps,
|
|
handle_updatecp,long_test_task,long_test_task2,
|
|
handle_zip_file,handle_getagegrouprecords,
|
|
handle_updatefitnessmetric,
|
|
handle_sendemail_userdeleted,
|
|
)
|
|
|
|
from scipy.signal import savgol_filter
|
|
from django.shortcuts import render_to_response
|
|
from Cookie import SimpleCookie
|
|
from shutil import copyfile,move
|
|
import types
|
|
from rowingdata import rower as rrower
|
|
from rowingdata import main as rmain
|
|
from rowingdata import rowingdata as rrdata
|
|
from rowingdata import make_cumvalues
|
|
from rowingdata import summarydata
|
|
import pandas as pd
|
|
import numpy as np
|
|
import matplotlib.pyplot as plt
|
|
|
|
from pytz import timezone as tz,utc
|
|
import dateutil
|
|
import mpld3
|
|
from mpld3 import plugins
|
|
import stravalib
|
|
from stravalib.exc import ActivityUploadFailed,TimeoutExceeded
|
|
from weather import get_wind_data,get_airport_code,get_metar_data
|
|
|
|
from oauth2_provider.models import Application,Grant,AccessToken
|
|
|
|
import django_rq
|
|
queue = django_rq.get_queue('default')
|
|
queuelow = django_rq.get_queue('low')
|
|
queuehigh = django_rq.get_queue('low')
|
|
|
|
import redis
|
|
import threading
|
|
from redis import StrictRedis,Redis
|
|
from rq.exceptions import NoSuchJobError
|
|
from rq.registry import StartedJobRegistry
|
|
from rq import Queue,cancel_job
|
|
|
|
from django.core.cache import cache
|
|
from django_mailbox.models import Message,Mailbox,MessageAttachment
|
|
|
|
|
|
# Utility to get stroke data in a JSON response
|
|
class JSONResponse(HttpResponse):
|
|
def __init__(self, data, **kwargs):
|
|
content = JSONRenderer().render(data)
|
|
kwargs['content_type'] = 'application/json'
|
|
super(JSONResponse, self).__init__(content, **kwargs)
|
|
|
|
|
|
|
|
|
|
def getrower(user):
|
|
try:
|
|
r = Rower.objects.get(user=user)
|
|
except Rower.DoesNotExist:
|
|
r = Rower(user=user)
|
|
r.save()
|
|
|
|
return r
|
|
|
|
|
|
def get_workout(id):
|
|
try:
|
|
w = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
return w
|
|
|
|
def get_workout_permitted(user,id):
|
|
w = get_workout(id)
|
|
|
|
if (checkworkoutuser(user,w)==False):
|
|
raise PermissionDenied("Access denied")
|
|
|
|
return w
|
|
|
|
def getvalue(data):
|
|
perc = 0
|
|
total = 1
|
|
done = 0
|
|
id = 0
|
|
session_key = 'noot'
|
|
for i in data.iteritems():
|
|
if i[0] == 'total':
|
|
total = float(i[1])
|
|
if i[0] == 'done':
|
|
done = float(i[1])
|
|
if i[0] == 'id':
|
|
id = i[1]
|
|
if i[0] == 'session_key':
|
|
session_key = i[1]
|
|
|
|
return total,done,id,session_key
|
|
|
|
class SessionTaskListener(threading.Thread):
|
|
def __init__(self, r, channels):
|
|
threading.Thread.__init__(self)
|
|
self.redis = r
|
|
self.pubsub = self.redis.pubsub()
|
|
self.pubsub.subscribe(channels)
|
|
|
|
def work(self, item):
|
|
|
|
try:
|
|
data = json.loads(item['data'])
|
|
total,done,id,session_key = getvalue(data)
|
|
perc = int(100.*done/total)
|
|
cache.set(id,perc,3600)
|
|
|
|
except TypeError:
|
|
pass
|
|
|
|
def run(self):
|
|
for item in self.pubsub.listen():
|
|
if item['data'] == "KILL":
|
|
self.pubsub.unsubscribe()
|
|
print self, "unsubscribed and finished"
|
|
break
|
|
else:
|
|
self.work(item)
|
|
|
|
|
|
queuefailed = Queue("failed",connection=Redis())
|
|
redis_connection = StrictRedis()
|
|
r = Redis()
|
|
|
|
# this doesn't yet work on production
|
|
#if settings.DEBUG:
|
|
# client = SessionTaskListener(r,['tasks'])
|
|
# client.start()
|
|
|
|
|
|
rq_registry = StartedJobRegistry(queue.name,connection=redis_connection)
|
|
rq_registryhigh = StartedJobRegistry(queuehigh.name,connection=redis_connection)
|
|
rq_registrylow = StartedJobRegistry(queuelow.name,connection=redis_connection)
|
|
|
|
from rq.job import Job
|
|
|
|
from rest_framework_swagger.views import get_swagger_view
|
|
from rest_framework.renderers import JSONRenderer
|
|
from rest_framework.parsers import JSONParser
|
|
from rest_framework.response import Response
|
|
from rowers.serializers import RowerSerializer,WorkoutSerializer
|
|
from rest_framework import status,permissions,generics
|
|
from rest_framework.decorators import api_view, renderer_classes
|
|
|
|
from permissions import IsOwnerOrNot
|
|
|
|
import plots
|
|
import mailprocessing
|
|
|
|
from io import BytesIO
|
|
from scipy.special import lambertw
|
|
|
|
from dataprep import timedeltaconv
|
|
from dataprep import getsmallrowdata_db
|
|
|
|
from scipy.interpolate import griddata
|
|
|
|
#LOCALTIMEZONE = tz('Etc/UTC')
|
|
USER_LANGUAGE = 'en-US'
|
|
|
|
from interactiveplots import *
|
|
from rowers.celery import result as celery_result
|
|
|
|
# Define the API documentation
|
|
schema_view = get_swagger_view(title='Rowsandall API')
|
|
|
|
def remove_asynctask(request,id):
|
|
try:
|
|
oldtasks = request.session['async_tasks']
|
|
except KeyError:
|
|
oldtasks = []
|
|
|
|
newtasks = []
|
|
for task in oldtasks:
|
|
if id not in task[0]:
|
|
newtasks += [(task[0],task[1])]
|
|
|
|
request.session['async_tasks'] = newtasks
|
|
|
|
def get_job_result(jobid):
|
|
if settings.DEBUG:
|
|
result = celery_result.AsyncResult(jobid).result
|
|
else:
|
|
running_job_ids = rq_registry.get_job_ids()
|
|
if len(running_job_ids) and jobid in running_job_ids:
|
|
# job is running
|
|
return None
|
|
else:
|
|
# job is ready
|
|
try:
|
|
job = Job.fetch(jobid,connection=redis_connection)
|
|
result = job.result
|
|
except NoSuchJobError:
|
|
return None
|
|
|
|
return result
|
|
|
|
verbose_job_status = {
|
|
'updatecp': 'Critical Power Calculation for Ergometer Workouts',
|
|
'updatecpwater': 'Critical Power Calculation for OTW Workouts',
|
|
'otwsetpower': 'Rowing Physics OTW Power Calculation',
|
|
'agegrouprecords': 'Calculate age group records',
|
|
'make_plot': 'Create static chart',
|
|
'long_test_task': 'Long Test Task',
|
|
'long_test_task2': 'Long Test Task 2',
|
|
}
|
|
|
|
def get_job_status(jobid):
|
|
if settings.DEBUG:
|
|
job = celery_result.AsyncResult(jobid)
|
|
jobresult = job.result
|
|
|
|
if 'fail' in job.status.lower():
|
|
jobresult = '0'
|
|
summary = {
|
|
'status': job.status,
|
|
'result': jobresult,
|
|
}
|
|
else:
|
|
try:
|
|
job = Job.fetch(jobid,connection=redis_connection)
|
|
summary = {
|
|
'status':job.status,
|
|
'result':job.result,
|
|
}
|
|
except NoSuchJobError:
|
|
summary = {
|
|
'status': 'success',
|
|
'result': 1,
|
|
}
|
|
|
|
try:
|
|
if 'fail' in summary['status'].lower():
|
|
summary['failed'] = True
|
|
else:
|
|
summary['failed'] = False
|
|
|
|
if 'success' in summary['status'].lower():
|
|
summary['finished'] = True
|
|
elif 'finished' in summary['status'].lower():
|
|
summary['finished'] = True
|
|
else:
|
|
summary['finished'] = False
|
|
except AttributeError:
|
|
summary = {
|
|
'status': 'failed',
|
|
'result': 0,
|
|
'finished': True,
|
|
'failed': True
|
|
}
|
|
|
|
return summary
|
|
|
|
def kill_async_job(request,id='aap'):
|
|
if settings.DEBUG:
|
|
job = celery_result.AsyncResult(id)
|
|
job.revoke()
|
|
else:
|
|
try:
|
|
cancel_job(id,connection=redis_connection)
|
|
except NoSuchJobError:
|
|
pass
|
|
|
|
remove_asynctask(request,id)
|
|
cache.delete(id)
|
|
url = reverse(session_jobs_status)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
@login_required()
|
|
def test_job_view(request,aantal=100):
|
|
|
|
session_key = request.session._session_key
|
|
|
|
job = myqueue(queuehigh,long_test_task,int(aantal),
|
|
session_key=session_key)
|
|
|
|
|
|
try:
|
|
request.session['async_tasks'] += [(job.id,'long_test_task')]
|
|
except KeyError:
|
|
request.session['async_tasks'] = [(job.id,'long_test_task')]
|
|
|
|
url = reverse(session_jobs_status)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
@login_required()
|
|
def test_job_view2(request,aantal=100):
|
|
|
|
|
|
job = myqueue(queuehigh,long_test_task2,int(aantal),
|
|
secret=settings.PROGRESS_CACHE_SECRET)
|
|
|
|
|
|
try:
|
|
request.session['async_tasks'] += [(job.id,'long_test_task2')]
|
|
except KeyError:
|
|
request.session['async_tasks'] = [(job.id,'long_test_task2')]
|
|
|
|
url = reverse(session_jobs_status)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
@csrf_exempt
|
|
def post_progress(request,id=None,value=0):
|
|
if request.method == 'POST':
|
|
try:
|
|
secret = request.POST['secret']
|
|
except KeyError:
|
|
return HttpResponse('Access Denied',status=401)
|
|
if secret == settings.PROGRESS_CACHE_SECRET:
|
|
if not id:
|
|
try:
|
|
id = request.POST['id']
|
|
except KeyError:
|
|
return HttpResponse('Invalid request',400)
|
|
try:
|
|
value = request.POST['value']
|
|
except KeyError:
|
|
pass
|
|
|
|
cache.set(id,value,3600)
|
|
# test
|
|
result = cache.get(id)
|
|
|
|
return HttpResponse('progress cached '+str(result),
|
|
status=201)
|
|
else: # secret not given
|
|
return HttpResponse('access denied',status=401)
|
|
|
|
else: # request method is not POST
|
|
return HttpResponse('GET method not allowed',status=405)
|
|
|
|
def get_all_queued_jobs(userid=0):
|
|
r = StrictRedis()
|
|
|
|
jobs = []
|
|
|
|
celerykeys = r.keys('celery*')
|
|
for key in celerykeys:
|
|
id= key[17:]
|
|
job = celery_result.AsyncResult(id)
|
|
jobresult = job.result
|
|
if 'fail' in job.status.lower():
|
|
jobresult = '0'
|
|
jobs.append(
|
|
(id,{
|
|
'status':job.status,
|
|
'result':jobresult,
|
|
'function':'',
|
|
'meta':job.info,
|
|
}))
|
|
|
|
ids = [j.id for j in queue.jobs]
|
|
ids += [j.id for j in queuehigh.jobs]
|
|
ids += [j.id for j in queuelow.jobs]
|
|
ids += [j.id for j in queuefailed.jobs]
|
|
|
|
|
|
for id in ids:
|
|
job = Job.fetch(id,connection=redis_connection)
|
|
jobs.append(
|
|
(id,{
|
|
'status':job.get_status(),
|
|
'result':job.result,
|
|
'function':job.func_name,
|
|
'meta':job.meta,
|
|
}))
|
|
|
|
return jobs
|
|
|
|
def get_stored_tasks_status(request):
|
|
try:
|
|
taskids = request.session['async_tasks']
|
|
except KeyError:
|
|
taskids = []
|
|
|
|
taskstatus = []
|
|
for id,func_name in reversed(taskids):
|
|
progress = 0
|
|
cached_progress = cache.get(id)
|
|
finished = get_job_status(id)['finished']
|
|
if finished:
|
|
cache.set(id,100)
|
|
progress = 100
|
|
elif cached_progress>0:
|
|
progress = cached_progress
|
|
else:
|
|
progress = 0
|
|
|
|
this_task_status = {
|
|
'id':id,
|
|
'status':get_job_status(id)['status'],
|
|
'failed':get_job_status(id)['failed'],
|
|
'finished':get_job_status(id)['finished'],
|
|
'func_name':func_name,
|
|
'verbose': verbose_job_status[func_name],
|
|
'progress': progress,
|
|
}
|
|
|
|
taskstatus.append(this_task_status)
|
|
|
|
|
|
return taskstatus
|
|
|
|
@login_required()
|
|
def get_thumbnails(request,id):
|
|
row = get_workout_permitted(request.user,id)
|
|
|
|
|
|
r = getrower(request.user)
|
|
result = request.user.is_authenticated() and ispromember(request.user)
|
|
if result:
|
|
promember=1
|
|
if request.user == row.user.user:
|
|
mayedit=1
|
|
|
|
comments = WorkoutComment.objects.filter(workout=row)
|
|
|
|
aantalcomments = len(comments)
|
|
|
|
workouttype = 'ote'
|
|
if row.workouttype in ('water','coastal'):
|
|
workouttype = 'otw'
|
|
|
|
try:
|
|
favorites = FavoriteChart.objects.filter(user=r,
|
|
workouttype__in=[workouttype,'both']).order_by("id")
|
|
maxfav = len(favorites)-1
|
|
except:
|
|
favorites = None
|
|
maxfav = 0
|
|
|
|
charts = []
|
|
|
|
charts = thumbnails_set(r,id,favorites)
|
|
try:
|
|
if charts[0]['script'] == '':
|
|
charts = []
|
|
except IndexError:
|
|
charts = []
|
|
|
|
|
|
return JSONResponse(charts)
|
|
|
|
@login_required()
|
|
def deactivate_user(request):
|
|
pk = request.user.id
|
|
user = User.objects.get(pk=pk)
|
|
user_form = DeactivateUserForm(instance=user)
|
|
if request.user.is_authenticated() and request.user.id == user.id:
|
|
if request.method == "POST":
|
|
user_form = DeactivateUserForm(request.POST, instance=user)
|
|
if user_form.is_valid():
|
|
deactivate_user = user_form.save(commit=False)
|
|
user.is_active = False
|
|
deactivate_user.save()
|
|
url = reverse(auth_views.logout_then_login)
|
|
return HttpResponseRedirect(url)
|
|
return render(request, "userprofile_deactivate.html", {
|
|
"user_form": user_form,
|
|
})
|
|
else:
|
|
raise PermissionDenied
|
|
|
|
@login_required()
|
|
def user_gdpr_optin(request):
|
|
r = getrower(request.user)
|
|
r.gdproptin = False
|
|
r.gdproptindate = None
|
|
r.save()
|
|
nexturl = request.GET.get('next','/rowers/list-workouts/')
|
|
if r.gdproptin:
|
|
return HttpResponseRedirect(nexturl)
|
|
|
|
return render(request,'gdpr_optin.html',{
|
|
"next": nexturl
|
|
})
|
|
|
|
@login_required()
|
|
def user_gdpr_confirm(request):
|
|
r = getrower(request.user)
|
|
r.gdproptin = True
|
|
r.gdproptindate = timezone.now()
|
|
r.save()
|
|
|
|
nexturl = request.GET.get('next','/rowers/list-workouts/')
|
|
|
|
return HttpResponseRedirect(nexturl)
|
|
|
|
|
|
|
|
@login_required()
|
|
def remove_user(request):
|
|
pk = request.user.id
|
|
user = User.objects.get(pk=pk)
|
|
user_form = DeleteUserForm(instance=user)
|
|
if request.user.is_authenticated() and request.user.id == user.id:
|
|
if request.method == "POST":
|
|
user_form = DeleteUserForm(request.POST,instance=user)
|
|
if user_form.is_valid():
|
|
cd = user_form.cleaned_data
|
|
name = user.first_name+' '+user.last_name
|
|
email = user.email
|
|
|
|
|
|
if cd['delete_user']:
|
|
user.delete()
|
|
res = myqueue(queuehigh,
|
|
handle_sendemail_userdeleted,
|
|
name, email)
|
|
|
|
url = reverse(auth_views.logout_then_login)
|
|
return HttpResponseRedirect(url)
|
|
return render(request, "userprofile_delete.html", {
|
|
"user_form": user_form,
|
|
})
|
|
else:
|
|
raise PermissionDenied
|
|
|
|
|
|
@login_required()
|
|
def get_testscript(request,id):
|
|
row = get_workout_permitted(request.user,id)
|
|
r = getrower(request.user)
|
|
|
|
object = {
|
|
"script":"""
|
|
<div id="id_script">
|
|
<script>alert("hi")</script>
|
|
</div>
|
|
""",
|
|
"div":"""
|
|
<div id="id_div">
|
|
Hoi
|
|
</div>
|
|
"""
|
|
}
|
|
|
|
|
|
return JSONResponse([object,object])
|
|
|
|
@login_required()
|
|
def session_jobs_view(request):
|
|
taskstatus = get_stored_tasks_status(request)
|
|
|
|
return HttpResponse(json.dumps(taskstatus))
|
|
|
|
@login_required()
|
|
def session_jobs_status(request):
|
|
taskstatus = get_stored_tasks_status(request)
|
|
|
|
return render(request,
|
|
'async_tasks.html',
|
|
{'taskstatus':taskstatus})
|
|
|
|
# Test if row data include candidates
|
|
def rowhascoordinates(row):
|
|
# create interactive plot
|
|
f1 = row.csvfilename
|
|
u = row.user.user
|
|
r = getrower(u)
|
|
rowdata = rdata(f1)
|
|
hascoordinates = 1
|
|
if rowdata != 0:
|
|
try:
|
|
latitude = rowdata.df[' latitude']
|
|
|
|
if not latitude.std():
|
|
hascoordinates = 0
|
|
except KeyError,AttributeError:
|
|
hascoordinates = 0
|
|
|
|
else:
|
|
hascoordinates = 0
|
|
|
|
return hascoordinates
|
|
|
|
# Custom error pages with Rowsandall headers
|
|
def error500_view(request):
|
|
response = render_to_response('500.html', {},
|
|
context_instance = RequestContext(request))
|
|
|
|
response.status_code = 500
|
|
return response
|
|
|
|
def error404_view(request):
|
|
response = render_to_response('404.html', {},
|
|
context_instance = RequestContext(request))
|
|
|
|
response.status_code = 404
|
|
return response
|
|
|
|
def error400_view(request):
|
|
response = render_to_response('400.html', {},
|
|
context_instance = RequestContext(request))
|
|
|
|
response.status_code = 400
|
|
return response
|
|
|
|
def error403_view(request):
|
|
response = render_to_response('403.html', {},
|
|
context_instance = RequestContext(request))
|
|
|
|
response.status_code = 403
|
|
return response
|
|
|
|
# Wrapper around the rowingdata call to catch some exceptions
|
|
# Checks for CSV file, then for gzipped CSV file, and if all fails, returns 0
|
|
def rdata(file,rower=rrower()):
|
|
try:
|
|
res = rrdata(csvfile=file,rower=rower)
|
|
except IOError, IndexError:
|
|
try:
|
|
res = rrdata(csvfile=file+'.gz',rower=rower)
|
|
except IOError, IndexError:
|
|
res = 0
|
|
|
|
return res
|
|
|
|
# Query to get teams managed and member of
|
|
def get_my_teams(user):
|
|
try:
|
|
therower = Rower.objects.get(user=user)
|
|
teams1 = therower.team.all()
|
|
teams2 = Team.objects.filter(manager=user)
|
|
teams = list(set(teams1).union(set(teams2)))
|
|
except TypeError:
|
|
teams = []
|
|
|
|
return teams
|
|
|
|
# Used for the interval editor - translates seconds to a time object
|
|
def get_time(second):
|
|
if (second<=0) or (second>1e9):
|
|
hours = 0
|
|
minutes=0
|
|
sec=0
|
|
microsecond = 0
|
|
elif math.isnan(second):
|
|
hours = 0
|
|
minutes=0
|
|
sec=0
|
|
microsecond = 0
|
|
else:
|
|
days = int(second/(24.*3600.)) % (24*3600)
|
|
hours = int((second-24.*3600.*days)/3600.) % 24
|
|
minutes = int((second-3600.*(hours+24.*days))/60.) % 60
|
|
sec = int(second-3600.*(hours+24.*days)-60.*minutes) % 60
|
|
microsecond = int(1.0e6*(second-3600.*(hours+24.*days)-60.*minutes-sec))
|
|
return datetime.time(hours,minutes,sec,microsecond)
|
|
|
|
|
|
# get the workout ID from the SportTracks URI
|
|
def getidfromsturi(uri,length=8):
|
|
return uri[len(uri)-length:]
|
|
|
|
def splituadata(lijst):
|
|
t = []
|
|
y = []
|
|
for d in lijst:
|
|
t.append(d[0])
|
|
y.append(d[1])
|
|
|
|
return np.array(t),np.array(y)
|
|
|
|
def splitrunkeeperlatlongdata(lijst,tname,latname,lonname):
|
|
t = []
|
|
lat = []
|
|
lon = []
|
|
for d in lijst:
|
|
t.append(d[tname])
|
|
lat.append(d[latname])
|
|
lon.append(d[lonname])
|
|
|
|
return [np.array(t),np.array(lat),np.array(lon)]
|
|
|
|
def splitrunkeeperdata(lijst,xname,yname):
|
|
x = []
|
|
y = []
|
|
for d in lijst:
|
|
x.append(d[xname])
|
|
y.append(d[yname])
|
|
|
|
return [np.array(x),np.array(y)]
|
|
|
|
# Splits SportTracks data which is one long sequence of
|
|
# [t,[lat,lon],t2,[lat2,lon2] ...]
|
|
# to [t,t2,t3, ...], [[lat,long],[lat2,long2],...
|
|
def splitstdata(lijst):
|
|
t = []
|
|
latlong = []
|
|
while len(lijst)>=2:
|
|
t.append(lijst[0])
|
|
latlong.append(lijst[1])
|
|
lijst = lijst[2:]
|
|
|
|
return [np.array(t),np.array(latlong)]
|
|
|
|
from utils import (
|
|
geo_distance,serialize_list,deserialize_list,uniqify,
|
|
str2bool,range_to_color_hex,absolute,myqueue,get_call,
|
|
calculate_age,rankingdistances,rankingdurations,
|
|
is_ranking_piece,my_dict_from_instance,wavg
|
|
)
|
|
|
|
import datautils
|
|
|
|
from rowers.models import checkworkoutuser,checkaccessuser
|
|
|
|
# Check if a user is a Coach member
|
|
def iscoachmember(user):
|
|
if not user.is_anonymous():
|
|
try:
|
|
r = Rower.objects.get(user=user)
|
|
except Rower.DoesNotExist:
|
|
r = Rower(user=user)
|
|
r.save()
|
|
|
|
result = user.is_authenticated() and (r.rowerplan=='coach')
|
|
else:
|
|
result = False
|
|
|
|
return result
|
|
|
|
# Check if a user can create planned sessions
|
|
def hasplannedsessions(user):
|
|
if not user.is_anonymous():
|
|
try:
|
|
r = Rower.objects.get(user=user)
|
|
except Rower.DoesNotExist:
|
|
r = Rower(user=user)
|
|
r.save()
|
|
|
|
result = user.is_authenticated() and (r.rowerplan=='coach' or r.rowerplan=='plan')
|
|
else:
|
|
result = False
|
|
|
|
return result
|
|
|
|
# Check if a user is a Pro member
|
|
def ispromember(user):
|
|
if not user.is_anonymous():
|
|
try:
|
|
r = Rower.objects.get(user=user)
|
|
except Rower.DoesNotExists:
|
|
r = Rower(user=user)
|
|
r.save()
|
|
|
|
result = user.is_authenticated() and (r.rowerplan=='pro' or r.rowerplan=='coach' or r.rowerplan=='plan')
|
|
if not result and r.protrialexpires:
|
|
result = user.is_authenticated() and r.rowerplan=='basic' and r.protrialexpires >= datetime.date.today()
|
|
else:
|
|
result = False
|
|
return result
|
|
|
|
# More User/Rower utils
|
|
def add_defaultfavorites(r):
|
|
for c in defaultfavoritecharts:
|
|
f = FavoriteChart(user=r,
|
|
yparam1=c['yparam1'],
|
|
yparam2=c['yparam2'],
|
|
xparam=c['xparam'],
|
|
plottype=c['plottype'],
|
|
workouttype=c['workouttype'],
|
|
reststrokes=c['reststrokes'],
|
|
notes=c['notes'])
|
|
|
|
f.save()
|
|
return 1
|
|
|
|
|
|
# User registration
|
|
def rower_register_view(request):
|
|
if request.method == 'POST':
|
|
#form = RegistrationFormUniqueEmail(request.POST)
|
|
form = RegistrationFormSex(request.POST)
|
|
if form.is_valid():
|
|
first_name = form.cleaned_data['first_name']
|
|
last_name = form.cleaned_data['last_name']
|
|
email = form.cleaned_data['email']
|
|
password = form.cleaned_data['password1']
|
|
username = form.cleaned_data['username']
|
|
sex = form.cleaned_data['sex']
|
|
birthdate = form.cleaned_data['birthdate']
|
|
weightcategory = form.cleaned_data['weightcategory']
|
|
theuser = User.objects.create_user(username,password=password)
|
|
theuser.first_name = first_name
|
|
theuser.last_name = last_name
|
|
theuser.email = email
|
|
theuser.save()
|
|
|
|
therower = Rower(user=theuser,sex=sex,birthdate=birthdate,
|
|
weightcategory=weightcategory)
|
|
|
|
therower.save()
|
|
|
|
# create default favorite charts
|
|
add_defaultfavorites(therower)
|
|
|
|
# Create Sample workout
|
|
f = 'media/testdata.csv.gz'
|
|
timestr = strftime("%Y%m%d-%H%M%S")
|
|
f2 = f[:-7]+timestr+'.csv.gz'
|
|
copyfile(f,f2)
|
|
|
|
response = dataprep.new_workout_from_file(therower,f2,
|
|
title='New User Sample Data',
|
|
notes='This is an example workout to get you started')
|
|
newworkoutid = response[0]
|
|
w = Workout.objects.get(id=newworkoutid)
|
|
w.startdatetime = timezone.now()
|
|
w.save()
|
|
|
|
# Create and send email
|
|
fullemail = first_name + " " + last_name + " " + "<" + email + ">"
|
|
subject = "Thank you for registering on rowsandall.com"
|
|
message = """
|
|
Thank you for registering on rowsandall.com. You can now login using the credentials you provided. The first thing you should do is go to the Settings page and make yourself familiar with the various options available to personalize your experience on the website. To get there, click the button with your first name on it in the upper right corner of the opening page.
|
|
|
|
As a minimum to get you started, enter your data in the Heart Rate Zones and Power Zones sections and your Functional Threshold Power. Then check your Account Information to make sure it is accurate and reflects your preferences.
|
|
|
|
For additional details on these settings and the buttons at the bottom of the page, read the settings tutorial at http://analytics.rowsandall.com/2017/11/02/rowsandall-settings-page-tutorial/.
|
|
|
|
Also check out our instructional videos at http://rowsandall.com/rowers/videos.
|
|
|
|
This website is a labor of love "by rowers, for rowers". If you find it to be useful, please help us cover our hosting costs and gain access to additional functionality by signing on as a Pro member: https://rowsandall.com/rowers/promembership
|
|
|
|
Oh, one more thing. The site is currently in beta and is developing fast. Bear with us. Don't hesitate to contact me at info@rowsandall.com if anything is broken or doesn't seem to work as advertised.
|
|
"""
|
|
|
|
send_mail(subject, message,
|
|
'Sander Roosendaal <info@rowsandall.com>',
|
|
[fullemail])
|
|
|
|
subject2 = "New User"
|
|
message2 = "New user registered.\n"
|
|
message2 += fullemail + "\n"
|
|
message2 += "User name: "+username
|
|
|
|
send_mail(subject2, message2,
|
|
'Rowsandall Server <info@rowsandall.com>',
|
|
['roosendaalsander@gmail.com'])
|
|
|
|
return HttpResponseRedirect('/rowers/register/thankyou/')
|
|
|
|
else:
|
|
return render(request,
|
|
"registration_form.html",
|
|
{'form':form})
|
|
else:
|
|
form = RegistrationFormSex()
|
|
return render(request,
|
|
"registration_form.html",
|
|
{'form':form,})
|
|
|
|
# Shows email form and sends it if submitted
|
|
def sendmail(request):
|
|
if request.method == 'POST':
|
|
form = EmailForm(request.POST)
|
|
if form.is_valid():
|
|
firstname = form.cleaned_data['firstname']
|
|
lastname = form.cleaned_data['lastname']
|
|
email = form.cleaned_data['email']
|
|
subject = form.cleaned_data['subject']
|
|
botcheck = form.cleaned_data['botcheck'].lower()
|
|
message = form.cleaned_data['message']
|
|
if botcheck == 'yes':
|
|
try:
|
|
fullemail = firstname + " " + lastname + " " + "<" + email + ">"
|
|
send_mail(subject, message, fullemail, ['info@rowsandall.com'])
|
|
return HttpResponseRedirect('/rowers/email/thankyou/')
|
|
except:
|
|
return HttpResponseRedirect('/rowers/email/')
|
|
else:
|
|
messages.error(request,'You have to answer YES to the question')
|
|
return HttpResponseRedirect('/rowers/email/')
|
|
else:
|
|
return HttpResponseRedirect('/rowers/email/')
|
|
else:
|
|
return HttpResponseRedirect('/rowers/email/')
|
|
|
|
@login_required()
|
|
def start_trial_view(request):
|
|
r = getrower(request.user)
|
|
|
|
if r.protrialexpires is not None:
|
|
messages.error(request,'You do not qualify for a trial')
|
|
url = '/rowers/promembership'
|
|
return HttpResponseRedirect(url)
|
|
|
|
r.protrialexpires = datetime.date.today()+datetime.timedelta(13)
|
|
r.save()
|
|
|
|
url = reverse(workouts_view)
|
|
|
|
messages.info(request,'We have started your 14 day trial period')
|
|
|
|
subject2 = "User started Pro Trial"
|
|
message2 = "User Started Pro Trial.\n"
|
|
message2 += request.user.email + "\n"
|
|
message2 += "User name: "+request.user.username
|
|
|
|
send_mail(subject2, message2,
|
|
'Rowsandall Server <info@rowsandall.com>',
|
|
['roosendaalsander@gmail.com'])
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
# Create workout data from Strava or Concept2
|
|
# data and create the associated Workout object and save it
|
|
def add_workout_from_strokedata(user,importid,data,strokedata,
|
|
source='c2',splitdata=None,
|
|
workoutsource='concept2'):
|
|
try:
|
|
workouttype = data['type']
|
|
except KeyError:
|
|
workouttype = 'rower'
|
|
|
|
if workouttype not in [x[0] for x in Workout.workouttypes]:
|
|
workouttype = 'other'
|
|
try:
|
|
comments = data['comments']
|
|
except:
|
|
comments = ' '
|
|
|
|
# comments = "Imported data \n %s" % comments
|
|
# comments = "Imported data \n"+comments # str(comments)
|
|
try:
|
|
thetimezone = tz(data['timezone'])
|
|
except:
|
|
thetimezone = 'UTC'
|
|
|
|
r = getrower(user)
|
|
try:
|
|
rowdatetime = iso8601.parse_date(data['date_utc'])
|
|
except KeyError:
|
|
rowdatetime = iso8601.parse_date(data['start_date'])
|
|
except ParseError:
|
|
rowdatetime = iso8601.parse_date(data['date'])
|
|
|
|
|
|
try:
|
|
c2intervaltype = data['workout_type']
|
|
|
|
except KeyError:
|
|
c2intervaltype = ''
|
|
|
|
try:
|
|
title = data['name']
|
|
except KeyError:
|
|
title = ""
|
|
try:
|
|
t = data['comments'].split('\n', 1)[0]
|
|
title += t[:20]
|
|
except:
|
|
title = 'Imported'
|
|
|
|
starttimeunix = arrow.get(rowdatetime).timestamp
|
|
|
|
res = make_cumvalues(0.1*strokedata['t'])
|
|
cum_time = res[0]
|
|
lapidx = res[1]
|
|
|
|
unixtime = cum_time+starttimeunix
|
|
# unixtime[0] = starttimeunix
|
|
seconds = 0.1*strokedata.ix[:,'t']
|
|
|
|
nr_rows = len(unixtime)
|
|
|
|
try:
|
|
latcoord = strokedata.ix[:,'lat']
|
|
loncoord = strokedata.ix[:,'lon']
|
|
except:
|
|
latcoord = np.zeros(nr_rows)
|
|
loncoord = np.zeros(nr_rows)
|
|
|
|
|
|
try:
|
|
strokelength = strokedata.ix[:,'strokelength']
|
|
except:
|
|
strokelength = np.zeros(nr_rows)
|
|
|
|
dist2 = 0.1*strokedata.ix[:,'d']
|
|
|
|
try:
|
|
spm = strokedata.ix[:,'spm']
|
|
except KeyError:
|
|
spm = 0*dist2
|
|
|
|
try:
|
|
hr = strokedata.ix[:,'hr']
|
|
except KeyError:
|
|
hr = 0*spm
|
|
pace = strokedata.ix[:,'p']/10.
|
|
pace = np.clip(pace,0,1e4)
|
|
pace = pace.replace(0,300)
|
|
|
|
velo = 500./pace
|
|
|
|
power = 2.8*velo**3
|
|
|
|
# save csv
|
|
# Create data frame with all necessary data to write to csv
|
|
df = pd.DataFrame({'TimeStamp (sec)':unixtime,
|
|
' Horizontal (meters)': dist2,
|
|
' Cadence (stokes/min)':spm,
|
|
' HRCur (bpm)':hr,
|
|
' longitude':loncoord,
|
|
' latitude':latcoord,
|
|
' Stroke500mPace (sec/500m)':pace,
|
|
' Power (watts)':power,
|
|
' DragFactor':np.zeros(nr_rows),
|
|
' DriveLength (meters)':np.zeros(nr_rows),
|
|
' StrokeDistance (meters)':strokelength,
|
|
' DriveTime (ms)':np.zeros(nr_rows),
|
|
' StrokeRecoveryTime (ms)':np.zeros(nr_rows),
|
|
' AverageDriveForce (lbs)':np.zeros(nr_rows),
|
|
' PeakDriveForce (lbs)':np.zeros(nr_rows),
|
|
' lapIdx':lapidx,
|
|
' ElapsedTime (sec)':seconds
|
|
})
|
|
|
|
|
|
df.sort_values(by='TimeStamp (sec)',ascending=True)
|
|
|
|
timestr = strftime("%Y%m%d-%H%M%S")
|
|
|
|
|
|
# Create CSV file name and save data to CSV file
|
|
csvfilename ='media/Import_'+str(importid)+'.csv'
|
|
|
|
res = df.to_csv(csvfilename+'.gz',index_label='index',
|
|
compression='gzip')
|
|
|
|
|
|
# with Concept2
|
|
if source=='c2':
|
|
try:
|
|
totaldist = data['distance']
|
|
totaltime = data['time']/10.
|
|
except KeyError:
|
|
totaldist = 0
|
|
totaltime = 0
|
|
else:
|
|
totaldist = 0
|
|
totaltime = 0
|
|
|
|
id,message = dataprep.save_workout_database(
|
|
csvfilename,r,
|
|
workouttype=workouttype,
|
|
title=title,notes=comments,
|
|
# totaldist=totaldist,
|
|
# totaltime=totaltime,
|
|
workoutsource=workoutsource,
|
|
dosummary=True
|
|
)
|
|
|
|
|
|
|
|
return id,message
|
|
|
|
# Create workout from RunKeeper Data
|
|
def add_workout_from_runkeeperdata(user,importid,data):
|
|
# To Do - add utcoffset to time
|
|
workouttype = data['type']
|
|
if workouttype not in [x[0] for x in Workout.workouttypes]:
|
|
workouttype = 'other'
|
|
try:
|
|
comments = data['notes']
|
|
except:
|
|
comments = ''
|
|
|
|
try:
|
|
utcoffset = tz(data['utcoffset'])
|
|
except:
|
|
utcoffset = 0
|
|
|
|
r = getrower(user)
|
|
|
|
try:
|
|
rowdatetime = iso8601.parse_date(data['start_time'])
|
|
except iso8601.ParseError:
|
|
try:
|
|
rowdatetime = datetime.datetime.strptime(data['start_time'],"%Y-%m-%d %H:%M:%S")
|
|
rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc)
|
|
except:
|
|
try:
|
|
rowdatetime = dateutil.parser.parse(data['start_time'])
|
|
#rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc)
|
|
except:
|
|
rowdatetime = datetime.datetime.strptime(data['date'],"%Y-%m-%d %H:%M:%S")
|
|
rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc)
|
|
starttimeunix = arrow.get(rowdatetime).timestamp
|
|
#starttimeunix = mktime(rowdatetime.utctimetuple())
|
|
starttimeunix += utcoffset*3600
|
|
|
|
|
|
try:
|
|
title = data['name']
|
|
except:
|
|
title = "Imported data"
|
|
|
|
|
|
|
|
res = splitrunkeeperdata(data['distance'],'timestamp','distance')
|
|
|
|
distance = res[1]
|
|
times_distance = res[0]
|
|
|
|
try:
|
|
l = data['path']
|
|
|
|
res = splitrunkeeperlatlongdata(l,'timestamp','latitude','longitude')
|
|
times_location = res[0]
|
|
latcoord = res[1]
|
|
loncoord = res[2]
|
|
|
|
except:
|
|
times_location = times_distance
|
|
latcoord = np.zeros(len(times_distance))
|
|
loncoord = np.zeros(len(times_distance))
|
|
if workouttype in ('water','coastal'):
|
|
workouttype = 'rower'
|
|
|
|
try:
|
|
res = splitrunkeeperdata(data['cadence'],'timestamp','cadence')
|
|
times_spm = res[0]
|
|
spm = res[1]
|
|
except KeyError:
|
|
times_spm = times_distance
|
|
spm = 0*times_distance
|
|
|
|
try:
|
|
res = splitrunkeeperdata(data['heart_rate'],'timestamp','heart_rate')
|
|
hr = res[1]
|
|
times_hr = res[0]
|
|
except KeyError:
|
|
times_hr = times_distance
|
|
hr = 0*times_distance
|
|
|
|
|
|
# create data series and remove duplicates
|
|
distseries = pd.Series(distance,index=times_distance)
|
|
distseries = distseries.groupby(distseries.index).first()
|
|
latseries = pd.Series(latcoord,index=times_location)
|
|
try:
|
|
latseries = latseries.groupby(latseries.index).first()
|
|
except TypeError:
|
|
latseries = 0.0*distseries
|
|
|
|
lonseries = pd.Series(loncoord,index=times_location)
|
|
try:
|
|
lonseries = lonseries.groupby(lonseries.index).first()
|
|
except TypeError:
|
|
lonseries = 0.0*distseries
|
|
|
|
spmseries = pd.Series(spm,index=times_spm)
|
|
spmseries = spmseries.groupby(spmseries.index).first()
|
|
hrseries = pd.Series(hr,index=times_hr)
|
|
try:
|
|
hrseries = hrseries.groupby(hrseries.index).first()
|
|
except TypeError:
|
|
hrseries = 0*distseries
|
|
|
|
|
|
# Create dicts and big dataframe
|
|
d = {
|
|
' Horizontal (meters)': distseries,
|
|
' latitude': latseries,
|
|
' longitude': lonseries,
|
|
' Cadence (stokes/min)': spmseries,
|
|
' HRCur (bpm)' : hrseries,
|
|
}
|
|
|
|
|
|
|
|
df = pd.DataFrame(d)
|
|
|
|
df = df.groupby(level=0).last()
|
|
|
|
cum_time = df.index.values
|
|
df[' ElapsedTime (sec)'] = cum_time
|
|
|
|
velo = df[' Horizontal (meters)'].diff()/df[' ElapsedTime (sec)'].diff()
|
|
|
|
df[' Power (watts)'] = 0.0*velo
|
|
|
|
nr_rows = len(velo.values)
|
|
|
|
df[' DriveLength (meters)'] = np.zeros(nr_rows)
|
|
df[' StrokeDistance (meters)'] = np.zeros(nr_rows)
|
|
df[' DriveTime (ms)'] = np.zeros(nr_rows)
|
|
df[' StrokeRecoveryTime (ms)'] = np.zeros(nr_rows)
|
|
df[' AverageDriveForce (lbs)'] = np.zeros(nr_rows)
|
|
df[' PeakDriveForce (lbs)'] = np.zeros(nr_rows)
|
|
df[' lapIdx'] = np.zeros(nr_rows)
|
|
|
|
|
|
|
|
unixtime = cum_time+starttimeunix
|
|
unixtime[0] = starttimeunix
|
|
|
|
df['TimeStamp (sec)'] = unixtime
|
|
|
|
|
|
dt = np.diff(cum_time).mean()
|
|
wsize = round(5./dt)
|
|
|
|
# velo2 = stravastuff.ewmovingaverage(velo,wsize)
|
|
|
|
# df[' Stroke500mPace (sec/500m)'] = 500./velo2
|
|
|
|
|
|
df = df.fillna(0)
|
|
|
|
df.sort_values(by='TimeStamp (sec)',ascending=True)
|
|
|
|
timestr = strftime("%Y%m%d-%H%M%S")
|
|
|
|
csvfilename ='media/Import_'+str(importid)+'.csv'
|
|
|
|
res = df.to_csv(csvfilename+'.gz',index_label='index',
|
|
compression='gzip')
|
|
|
|
id,message = dataprep.save_workout_database(csvfilename,r,
|
|
workouttype=workouttype,
|
|
workoutsource='runkeeper',
|
|
title=title,
|
|
notes=comments)
|
|
|
|
return (id,message)
|
|
|
|
|
|
|
|
# Create workout from SportTracks Data, which are slightly different
|
|
# than Strava or Concept2 data
|
|
def add_workout_from_stdata(user,importid,data):
|
|
workouttype = data['type']
|
|
if workouttype not in [x[0] for x in Workout.workouttypes]:
|
|
workouttype = 'other'
|
|
try:
|
|
comments = data['comments']
|
|
except:
|
|
comments = ''
|
|
|
|
|
|
r = getrower(user)
|
|
try:
|
|
rowdatetime = iso8601.parse_date(data['start_time'])
|
|
except iso8601.ParseError:
|
|
try:
|
|
rowdatetime = datetime.datetime.strptime(data['start_time'],"%Y-%m-%d %H:%M:%S")
|
|
rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc)
|
|
except:
|
|
try:
|
|
rowdatetime = dateutil.parser.parse(data['start_time'])
|
|
rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc)
|
|
except:
|
|
rowdatetime = datetime.datetime.strptime(data['date'],"%Y-%m-%d %H:%M:%S")
|
|
rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc)
|
|
starttimeunix = arrow.get(rowdatetime).timestamp
|
|
|
|
try:
|
|
title = data['name']
|
|
except:
|
|
title = "Imported data"
|
|
|
|
try:
|
|
res = splitstdata(data['distance'])
|
|
distance = res[1]
|
|
times_distance = res[0]
|
|
except KeyError:
|
|
try:
|
|
res = splitstdata(data['heartrate'])
|
|
times_distance = res[0]
|
|
distance = 0*times_distance
|
|
except KeyError:
|
|
return (0,"No distance or heart rate data in the workout")
|
|
|
|
|
|
try:
|
|
l = data['location']
|
|
|
|
res = splitstdata(l)
|
|
times_location = res[0]
|
|
latlong = res[1]
|
|
latcoord = []
|
|
loncoord = []
|
|
|
|
for coord in latlong:
|
|
lat = coord[0]
|
|
lon = coord[1]
|
|
latcoord.append(lat)
|
|
loncoord.append(lon)
|
|
except:
|
|
times_location = times_distance
|
|
latcoord = np.zeros(len(times_distance))
|
|
loncoord = np.zeros(len(times_distance))
|
|
if workouttype in ('water','coastal'):
|
|
workouttype = 'rower'
|
|
|
|
try:
|
|
res = splitstdata(data['cadence'])
|
|
times_spm = res[0]
|
|
spm = res[1]
|
|
except KeyError:
|
|
times_spm = times_distance
|
|
spm = 0*times_distance
|
|
|
|
try:
|
|
res = splitstdata(data['heartrate'])
|
|
hr = res[1]
|
|
times_hr = res[0]
|
|
except KeyError:
|
|
times_hr = times_distance
|
|
hr = 0*times_distance
|
|
|
|
|
|
# create data series and remove duplicates
|
|
distseries = pd.Series(distance,index=times_distance)
|
|
distseries = distseries.groupby(distseries.index).first()
|
|
latseries = pd.Series(latcoord,index=times_location)
|
|
latseries = latseries.groupby(latseries.index).first()
|
|
lonseries = pd.Series(loncoord,index=times_location)
|
|
lonseries = lonseries.groupby(lonseries.index).first()
|
|
spmseries = pd.Series(spm,index=times_spm)
|
|
spmseries = spmseries.groupby(spmseries.index).first()
|
|
hrseries = pd.Series(hr,index=times_hr)
|
|
hrseries = hrseries.groupby(hrseries.index).first()
|
|
|
|
|
|
# Create dicts and big dataframe
|
|
d = {
|
|
' Horizontal (meters)': distseries,
|
|
' latitude': latseries,
|
|
' longitude': lonseries,
|
|
' Cadence (stokes/min)': spmseries,
|
|
' HRCur (bpm)' : hrseries,
|
|
}
|
|
|
|
|
|
|
|
df = pd.DataFrame(d)
|
|
|
|
df = df.groupby(level=0).last()
|
|
|
|
cum_time = df.index.values
|
|
df[' ElapsedTime (sec)'] = cum_time
|
|
|
|
velo = df[' Horizontal (meters)'].diff()/df[' ElapsedTime (sec)'].diff()
|
|
|
|
df[' Power (watts)'] = 0.0*velo
|
|
|
|
nr_rows = len(velo.values)
|
|
|
|
df[' DriveLength (meters)'] = np.zeros(nr_rows)
|
|
df[' StrokeDistance (meters)'] = np.zeros(nr_rows)
|
|
df[' DriveTime (ms)'] = np.zeros(nr_rows)
|
|
df[' StrokeRecoveryTime (ms)'] = np.zeros(nr_rows)
|
|
df[' AverageDriveForce (lbs)'] = np.zeros(nr_rows)
|
|
df[' PeakDriveForce (lbs)'] = np.zeros(nr_rows)
|
|
df[' lapIdx'] = np.zeros(nr_rows)
|
|
|
|
|
|
|
|
unixtime = cum_time+starttimeunix
|
|
unixtime[0] = starttimeunix
|
|
|
|
df['TimeStamp (sec)'] = unixtime
|
|
|
|
|
|
dt = np.diff(cum_time).mean()
|
|
wsize = round(5./dt)
|
|
|
|
velo2 = stravastuff.ewmovingaverage(velo,wsize)
|
|
|
|
df[' Stroke500mPace (sec/500m)'] = 500./velo2
|
|
|
|
|
|
df = df.fillna(0)
|
|
|
|
df.sort_values(by='TimeStamp (sec)',ascending=True)
|
|
|
|
timestr = strftime("%Y%m%d-%H%M%S")
|
|
|
|
csvfilename ='media/Import_'+str(importid)+'.csv'
|
|
|
|
res = df.to_csv(csvfilename+'.gz',index_label='index',
|
|
compression='gzip')
|
|
|
|
id,message = dataprep.save_workout_database(csvfilename,r,
|
|
workouttype=workouttype,
|
|
title=title,
|
|
notes=comments,
|
|
workoutsource='sporttracks')
|
|
|
|
return (id,message)
|
|
|
|
# Create workout from SportTracks Data, which are slightly different
|
|
# than Strava or Concept2 data
|
|
def add_workout_from_underarmourdata(user,importid,data):
|
|
workouttype = 'water'
|
|
|
|
try:
|
|
comments = data['notes']
|
|
except:
|
|
comments = ''
|
|
|
|
try:
|
|
thetimezone = tz(data['start_locale_timezone'])
|
|
except:
|
|
thetimezone = 'UTC'
|
|
|
|
r = getrower(user)
|
|
try:
|
|
rowdatetime = iso8601.parse_date(data['start_datetime'])
|
|
except iso8601.ParseError:
|
|
try:
|
|
rowdatetime = datetime.datetime.strptime(data['start_datetime'],"%Y-%m-%d %H:%M:%S")
|
|
rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc)
|
|
except:
|
|
try:
|
|
rowdatetime = dateutil.parser.parse(data['start_datetime'])
|
|
rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc)
|
|
except:
|
|
rowdatetime = datetime.datetime.strptime(data['date'],"%Y-%m-%d %H:%M:%S")
|
|
rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc)
|
|
starttimeunix = arrow.get(rowdatetime).timestamp
|
|
#starttimeunix = mktime(rowdatetime.utctimetuple())
|
|
|
|
|
|
try:
|
|
title = data['name']
|
|
except:
|
|
title = "Imported data"
|
|
|
|
timeseries = data['time_series']
|
|
|
|
# position, distance, speed, cadence, power,
|
|
|
|
res = splituadata(timeseries['distance'])
|
|
|
|
distance = res[1]
|
|
|
|
times_distance = res[0]
|
|
|
|
|
|
try:
|
|
l = timeseries['position']
|
|
|
|
res = splituadata(l)
|
|
times_location = res[0]
|
|
latlong = res[1]
|
|
latcoord = []
|
|
loncoord = []
|
|
|
|
for coord in latlong:
|
|
lat = coord['lat']
|
|
lon = coord['lng']
|
|
latcoord.append(lat)
|
|
loncoord.append(lon)
|
|
except:
|
|
times_location = times_distance
|
|
latcoord = np.zeros(len(times_distance))
|
|
loncoord = np.zeros(len(times_distance))
|
|
if workouttype in ('water','coastal'):
|
|
workouttype = 'rower'
|
|
|
|
try:
|
|
res = splituadata(timeseries['cadence'])
|
|
times_spm = res[0]
|
|
spm = res[1]
|
|
except KeyError:
|
|
times_spm = times_distance
|
|
spm = 0*times_distance
|
|
|
|
try:
|
|
res = splituadata(timeseries['heartrate'])
|
|
hr = res[1]
|
|
times_hr = res[0]
|
|
except KeyError:
|
|
times_hr = times_distance
|
|
hr = 0*times_distance
|
|
|
|
|
|
# create data series and remove duplicates
|
|
distseries = pd.Series(distance,index=times_distance)
|
|
distseries = distseries.groupby(distseries.index).first()
|
|
latseries = pd.Series(latcoord,index=times_location)
|
|
latseries = latseries.groupby(latseries.index).first()
|
|
lonseries = pd.Series(loncoord,index=times_location)
|
|
lonseries = lonseries.groupby(lonseries.index).first()
|
|
spmseries = pd.Series(spm,index=times_spm)
|
|
spmseries = spmseries.groupby(spmseries.index).first()
|
|
hrseries = pd.Series(hr,index=times_hr)
|
|
hrseries = hrseries.groupby(hrseries.index).first()
|
|
|
|
|
|
# Create dicts and big dataframe
|
|
d = {
|
|
' Horizontal (meters)': distseries,
|
|
' latitude': latseries,
|
|
' longitude': lonseries,
|
|
' Cadence (stokes/min)': spmseries,
|
|
' HRCur (bpm)' : hrseries,
|
|
}
|
|
|
|
|
|
|
|
df = pd.DataFrame(d)
|
|
|
|
df = df.groupby(level=0).last()
|
|
|
|
cum_time = df.index.values
|
|
df[' ElapsedTime (sec)'] = cum_time
|
|
|
|
velo = df[' Horizontal (meters)'].diff()/df[' ElapsedTime (sec)'].diff()
|
|
|
|
df[' Power (watts)'] = 0.0*velo
|
|
|
|
nr_rows = len(velo.values)
|
|
|
|
df[' DriveLength (meters)'] = np.zeros(nr_rows)
|
|
df[' StrokeDistance (meters)'] = np.zeros(nr_rows)
|
|
df[' DriveTime (ms)'] = np.zeros(nr_rows)
|
|
df[' StrokeRecoveryTime (ms)'] = np.zeros(nr_rows)
|
|
df[' AverageDriveForce (lbs)'] = np.zeros(nr_rows)
|
|
df[' PeakDriveForce (lbs)'] = np.zeros(nr_rows)
|
|
df[' lapIdx'] = np.zeros(nr_rows)
|
|
|
|
|
|
|
|
unixtime = cum_time+starttimeunix
|
|
unixtime[0] = starttimeunix
|
|
|
|
df['TimeStamp (sec)'] = unixtime
|
|
|
|
|
|
dt = np.diff(cum_time).mean()
|
|
wsize = round(5./dt)
|
|
|
|
# velo2 = stravastuff.ewmovingaverage(velo,wsize)
|
|
|
|
# df[' Stroke500mPace (sec/500m)'] = 500./velo2
|
|
|
|
|
|
df = df.fillna(0)
|
|
|
|
df.sort_values(by='TimeStamp (sec)',ascending=True)
|
|
|
|
timestr = strftime("%Y%m%d-%H%M%S")
|
|
|
|
csvfilename ='media/Import_'+str(importid)+'.csv'
|
|
|
|
res = df.to_csv(csvfilename+'.gz',index_label='index',
|
|
compression='gzip')
|
|
|
|
id,message = dataprep.save_workout_database(csvfilename,r,
|
|
workouttype=workouttype,
|
|
workoutsource='mapmyfitness',
|
|
title=title,
|
|
notes=comments)
|
|
|
|
return (id,message)
|
|
|
|
|
|
|
|
|
|
|
|
# Export workout to TCX and send to user's email address
|
|
@login_required()
|
|
def workout_tcxemail_view(request,id=0):
|
|
message = ""
|
|
successmessage = ""
|
|
r = getrower(request.user)
|
|
w = get_workout(id)
|
|
if (checkworkoutuser(request.user,w)):
|
|
tcxfile,tcxmessg = stravastuff.createstravaworkoutdata(w,dozip=False)
|
|
if tcxfile == 0:
|
|
message = "Something went wrong (TCX export) "+tcxmessg
|
|
messages.error(request,message)
|
|
url = reverse(workout_export_view,
|
|
kwargs = {
|
|
'id':str(w.id),
|
|
})
|
|
return HttpResponseRedirect(url)
|
|
if tcxfile:
|
|
res = myqueue(queuehigh,handle_sendemailtcx,
|
|
r.user.first_name,
|
|
r.user.last_name,
|
|
r.user.email,
|
|
tcxfile)
|
|
|
|
successmessage = "The TCX file was sent to you per email"
|
|
messages.info(request,successmessage)
|
|
url = reverse(workout_export_view,
|
|
kwargs = {
|
|
'id':str(w.id),
|
|
})
|
|
|
|
response = HttpResponseRedirect(url)
|
|
|
|
else:
|
|
message = "You are not allowed to export this workout"
|
|
messages.error(request,message)
|
|
url = reverse(workout_export_view,
|
|
kwargs = {
|
|
'id':str(w.id),
|
|
})
|
|
response = HttpResponseRedirect(url)
|
|
|
|
return response
|
|
|
|
# Export workout to GPX and send to user's email address
|
|
@login_required()
|
|
def workout_gpxemail_view(request,id=0):
|
|
message = ""
|
|
successmessage = ""
|
|
r = Rower.objects.get(user=request.user)
|
|
w = get_workout(id)
|
|
|
|
if (checkworkoutuser(request.user,w)):
|
|
filename = w.csvfilename
|
|
row = rdata(filename)
|
|
gpxfilename = filename[:-4]+'.gpx'
|
|
row.exporttogpx(gpxfilename)
|
|
res = myqueue(queuehigh,handle_sendemailtcx,
|
|
r.user.first_name,
|
|
r.user.last_name,
|
|
r.user.email,gpxfilename)
|
|
|
|
|
|
successmessage = "The GPX file was sent to you per email"
|
|
messages.info(request,successmessage)
|
|
url = reverse(workout_export_view,
|
|
kwargs = {
|
|
'id':str(w.id),
|
|
})
|
|
|
|
response = HttpResponseRedirect(url)
|
|
|
|
else:
|
|
message = "You are not allowed to export this workout"
|
|
messages.error(request,message)
|
|
url = reverse(workout_export_view,
|
|
kwargs = {
|
|
'id':str(w.id),
|
|
})
|
|
response = HttpResponseRedirect(url)
|
|
|
|
return response
|
|
|
|
# Get Workout summary CSV file
|
|
@login_required()
|
|
def workouts_summaries_email_view(request):
|
|
r = getrower(request.user)
|
|
|
|
if request.method == 'POST':
|
|
form = DateRangeForm(request.POST)
|
|
if form.is_valid():
|
|
startdate = form.cleaned_data['startdate']
|
|
enddate = form.cleaned_data['enddate']
|
|
filename = 'rowsandall_workouts_{first}_{last}.csv'.format(
|
|
first=startdate,
|
|
last=enddate
|
|
)
|
|
df = dataprep.workout_summary_to_df(r,startdate=startdate,enddate=enddate)
|
|
df.to_csv(filename,encoding='utf-8')
|
|
res = myqueue(queuehigh,handle_sendemailsummary,
|
|
r.user.first_name,
|
|
r.user.last_name,
|
|
r.user.email,
|
|
filename)
|
|
messages.info(request,'The summary CSV file was sent to you per email')
|
|
else:
|
|
form = DateRangeForm()
|
|
|
|
return render(request,"export_workouts.html",
|
|
{
|
|
'form':form
|
|
})
|
|
|
|
|
|
# Get Workout CSV file and send it to user's email address
|
|
@login_required()
|
|
def workout_csvemail_view(request,id=0):
|
|
message = ""
|
|
r = getrower(request.user)
|
|
w = get_workout(id)
|
|
|
|
if (checkworkoutuser(request.user,w)):
|
|
csvfile = w.csvfilename
|
|
res = myqueue(queuehigh,handle_sendemailcsv,r.user.first_name,
|
|
r.user.last_name,
|
|
r.user.email,csvfile)
|
|
|
|
successmessage = "The CSV file was sent to you per email"
|
|
messages.info(request,successmessage)
|
|
url = reverse(workout_export_view,
|
|
kwargs = {
|
|
'id':str(w.id),
|
|
})
|
|
response = HttpResponseRedirect(url)
|
|
|
|
else:
|
|
message = "You are not allowed to export this workout"
|
|
messages.error(request,message)
|
|
url = reverse(workout_export_view,
|
|
kwargs = {
|
|
'id':str(w.id),
|
|
})
|
|
response = HttpResponseRedirect(url)
|
|
|
|
return response
|
|
|
|
# Get Workout CSV file and send it to user's email address
|
|
@login_required()
|
|
def workout_csvtoadmin_view(request,id=0):
|
|
message = ""
|
|
r = getrower(request.user)
|
|
w = get_workout(id)
|
|
|
|
|
|
csvfile = w.csvfilename
|
|
res = myqueue(queuehigh,
|
|
handle_sendemailcsv,
|
|
'Sander',
|
|
'Roosendaal',
|
|
'roosendaalsander@gmail.com',
|
|
csvfile)
|
|
|
|
successmessage = "The CSV file was sent to the site admin per email"
|
|
messages.info(request,successmessage)
|
|
url = reverse(workout_view,
|
|
kwargs = {
|
|
'id':str(w.id),
|
|
})
|
|
response = HttpResponseRedirect(url)
|
|
|
|
return response
|
|
|
|
# Send workout to TP
|
|
@login_required()
|
|
def workout_tp_upload_view(request,id=0):
|
|
|
|
message = ""
|
|
r = getrower(request.user)
|
|
res = -1
|
|
try:
|
|
thetoken = tp_open(r.user)
|
|
except TPNoTokenError:
|
|
return HttpResponseRedirect("/rowers/me/tpauthorize/")
|
|
|
|
# ready to upload. Hurray
|
|
w = get_workout_permitted(request.user,id)
|
|
r = w.user
|
|
|
|
if (checkworkoutuser(request.user,w)):
|
|
tcxfile = tpstuff.createtpworkoutdata(w)
|
|
if tcxfile:
|
|
res,reason,status_code,headers = tpstuff.uploadactivity(
|
|
r.tptoken,tcxfile,
|
|
name=w.name
|
|
)
|
|
if res == 0:
|
|
message = "Upload to TrainingPeaks failed with status code "+str(status_code)+": "+reason
|
|
try:
|
|
os.remove(tcxfile)
|
|
except WindowsError:
|
|
pass
|
|
|
|
messages.error(request,message)
|
|
|
|
else: # res != 0
|
|
w.uploadedtotp = res
|
|
w.save()
|
|
os.remove(tcxfile)
|
|
messages.info(request,'Uploaded to TrainingPeaks')
|
|
|
|
else: # no tcxfile
|
|
message = "Upload to TrainingPeaks failed"
|
|
w.uploadedtotp = -1
|
|
w.save()
|
|
messages.error(request,message)
|
|
|
|
else: # not allowed to upload
|
|
message = "You are not allowed to export this workout to TP"
|
|
messages.error(request,message)
|
|
|
|
url = reverse(workout_export_view,
|
|
kwargs = {
|
|
'id':str(w.id),
|
|
})
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
|
|
# Send workout to Strava
|
|
# abundance of error logging here because there were/are some bugs
|
|
@login_required()
|
|
def workout_strava_upload_view(request,id=0):
|
|
message = ""
|
|
r = getrower(request.user)
|
|
res = -1
|
|
if (r.stravatoken == '') or (r.stravatoken is None):
|
|
s = "Token doesn't exist. Need to authorize"
|
|
return HttpResponseRedirect("/rowers/me/stravaauthorize/")
|
|
else:
|
|
# ready to upload. Hurray
|
|
w = get_workout_permitted(request.user,id)
|
|
r = w.user
|
|
if (checkworkoutuser(request.user,w)):
|
|
try:
|
|
tcxfile,tcxmessg = stravastuff.createstravaworkoutdata(w)
|
|
if tcxfile:
|
|
with open(tcxfile,'rb') as f:
|
|
try:
|
|
newnotes = w.notes+'\n from '+w.workoutsource+' via rowsandall.com'
|
|
except TypeError:
|
|
newnotes = 'from '+w.workoutsource+' via rowsandall.com'
|
|
activity_type = r.stravaexportas
|
|
res,mes = stravastuff.handle_stravaexport(f,w.name,
|
|
r.stravatoken,
|
|
description=newnotes,
|
|
activity_type=activity_type)
|
|
if res==0:
|
|
messages.error(request,mes)
|
|
w.uploadedtostrava = -1
|
|
w.save()
|
|
try:
|
|
os.remove(tcxfile)
|
|
except WindowsError:
|
|
pass
|
|
url = reverse(workout_export_view,
|
|
kwargs = {
|
|
'id':str(w.id),
|
|
})
|
|
response = HttpResponseRedirect(url)
|
|
return response
|
|
|
|
try:
|
|
w.uploadedtostrava = res
|
|
w.save()
|
|
try:
|
|
os.remove(tcxfile)
|
|
except WindowsError:
|
|
pass
|
|
url = "/rowers/workout/"+str(w.id)+"/edit"
|
|
|
|
messages.info(request,mes)
|
|
except:
|
|
with open("media/stravaerrors.log","a") as errorlog:
|
|
errorstring = str(sys.exc_info()[0])
|
|
timestr = strftime("%Y%m%d-%H%M%S")
|
|
errorlog.write(timestr+errorstring+"\r\n")
|
|
errorlog.write("views.py line 826\r\n")
|
|
message = 'Error: '+errorstring
|
|
messages.error(request,message)
|
|
else: # No tcxfile
|
|
message = "Strava Data error "+tcxmessg
|
|
messages.error(request,message)
|
|
w.uploadedtostrava = -1
|
|
w.save()
|
|
url = reverse(workout_export_view,
|
|
kwargs = {
|
|
'id':str(w.id),
|
|
})
|
|
response = HttpResponseRedirect(url)
|
|
|
|
|
|
url = reverse(workout_export_view,
|
|
kwargs = {
|
|
'id':str(w.id),
|
|
}
|
|
)
|
|
response = HttpResponseRedirect(url)
|
|
except ActivityUploadFailed as e:
|
|
message = "Strava Upload error: %s" % e
|
|
messages.error(request,message)
|
|
w.uploadedtostrava = -1
|
|
w.save()
|
|
os.remove(tcxfile)
|
|
url = reverse(workout_export_view,
|
|
kwargs = {
|
|
'id':str(w.id),
|
|
})
|
|
response = HttpResponseRedirect(url)
|
|
|
|
return response
|
|
|
|
# Upload workout to Concept2 logbook
|
|
@login_required()
|
|
def workout_c2_upload_view(request,id=0):
|
|
message = ""
|
|
# ready to upload. Hurray
|
|
w = get_workout(id)
|
|
r = w.user
|
|
|
|
|
|
try:
|
|
message,c2id = c2stuff.workout_c2_upload(request.user,w)
|
|
except C2NoTokenError:
|
|
return HttpResponseRedirect("/rowers/me/c2authorize/")
|
|
|
|
if message and c2id <=0:
|
|
messages.error(request,message)
|
|
elif message:
|
|
messages.info(request,message)
|
|
|
|
|
|
url = reverse(workout_export_view,
|
|
kwargs = {
|
|
'id':str(w.id),
|
|
})
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
# Upload workout to RunKeeper
|
|
@login_required()
|
|
def workout_runkeeper_upload_view(request,id=0):
|
|
message = ""
|
|
w = get_workout(id)
|
|
r = w.user
|
|
|
|
try:
|
|
thetoken = runkeeper_open(r.user)
|
|
except RunKeeperNoTokenError:
|
|
return HttpResponseRedirect("/rowers/me/runkeeperauthorize/")
|
|
|
|
# ready to upload. Hurray
|
|
|
|
if (checkworkoutuser(request.user,w)):
|
|
data = runkeeperstuff.createrunkeeperworkoutdata(w)
|
|
if not data:
|
|
message = "Data error"
|
|
messages.error(request,message)
|
|
url = reverse(workout_export_view,
|
|
kwargs = {
|
|
'id':str(w.id),
|
|
})
|
|
return HttpResponseRedirect(url)
|
|
|
|
authorizationstring = str('Bearer ' + thetoken)
|
|
headers = {'Authorization': authorizationstring,
|
|
'user-agent': 'sanderroosendaal',
|
|
'Content-Type': 'application/vnd.com.runkeeper.NewFitnessActivity+json',
|
|
'Content-Length':'nnn'}
|
|
|
|
url = "https://api.runkeeper.com/fitnessActivities"
|
|
response = requests.post(url,headers=headers,data=json.dumps(data))
|
|
|
|
# check for duplicate error first
|
|
if (response.status_code == 409 ):
|
|
message = "Duplicate error"
|
|
messages.error(request,message)
|
|
w.uploadedtorunkeeper = -1
|
|
w.save()
|
|
elif (response.status_code == 201 or response.status_code==200):
|
|
runkeeperid = runkeeperstuff.getidfromresponse(response)
|
|
w.uploadedtorunkeeper = runkeeperid
|
|
w.save()
|
|
url = "/rowers/workout/"+str(w.id)+"/export"
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
s = response
|
|
message = "Something went wrong in workout_runkeeper_upload_view: %s - %s" % (s.reason,s.text)
|
|
messages.error(request,message)
|
|
|
|
else:
|
|
message = "You are not authorized to upload this workout"
|
|
messages.error(request,message)
|
|
|
|
url = reverse(workout_export_view,
|
|
kwargs = {
|
|
'id':str(w.id),
|
|
})
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
# Upload workout to Underarmour
|
|
@login_required()
|
|
def workout_underarmour_upload_view(request,id=0):
|
|
message = ""
|
|
w = get_workout(id)
|
|
r = w.user
|
|
|
|
try:
|
|
thetoken = underarmour_open(r.user)
|
|
except UnderArmourNoTokenError:
|
|
return HttpResponseRedirect("/rowers/me/underarmourauthorize/")
|
|
|
|
# ready to upload. Hurray
|
|
|
|
if (checkworkoutuser(request.user,w)):
|
|
data = underarmourstuff.createunderarmourworkoutdata(w)
|
|
# return HttpResponse(json.dumps(data))
|
|
if not data:
|
|
message = "Data error"
|
|
messages.error(request,message)
|
|
url = reverse(workout_export_view,
|
|
kwargs = {
|
|
'id':str(w.id),
|
|
})
|
|
return HttpResponseRedirect(url)
|
|
|
|
authorizationstring = str('Bearer ' + thetoken)
|
|
headers = {'Authorization': authorizationstring,
|
|
'Api-Key': UNDERARMOUR_CLIENT_KEY,
|
|
'user-agent': 'sanderroosendaal',
|
|
'Content-Type': 'application/json',
|
|
}
|
|
|
|
url = "https://api.ua.com/v7.1/workout/"
|
|
response = requests.post(url,headers=headers,data=json.dumps(data))
|
|
|
|
# check for duplicate error first
|
|
if (response.status_code == 409 ):
|
|
message = "Duplicate error"
|
|
messages.error(request,message)
|
|
w.uploadedtounderarmour = -1
|
|
w.save()
|
|
elif (response.status_code == 201 or response.status_code==200):
|
|
underarmourid = underarmourstuff.getidfromresponse(response)
|
|
w.uploadedtounderarmour = underarmourid
|
|
w.save()
|
|
url = "/rowers/workout/"+str(w.id)+"/export"
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
s = response
|
|
message = "Something went wrong in workout_underarmour_upload_view: %s - %s" % (s.reason,s.text)
|
|
messages.error(request,message)
|
|
else:
|
|
message = "You are not authorized to upload this workout"
|
|
messages.error(request,message)
|
|
|
|
url = reverse(workout_export_view,
|
|
kwargs = {
|
|
'id':str(w.id),
|
|
})
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
# Upload workout to SportTracks
|
|
@login_required()
|
|
def workout_sporttracks_upload_view(request,id=0):
|
|
message = ""
|
|
# ready to upload. Hurray
|
|
w = get_workout(id)
|
|
r = w.user
|
|
|
|
try:
|
|
thetoken = sporttracks_open(r.user)
|
|
except SportTracksNoTokenError:
|
|
return HttpResponseRedirect("/rowers/me/sporttracksauthorize/")
|
|
|
|
|
|
if (checkworkoutuser(request.user,w)):
|
|
data = sporttracksstuff.createsporttracksworkoutdata(w)
|
|
if not data:
|
|
message = "Data error"
|
|
messages.error(request,message)
|
|
url = reverse(workout_export_view,
|
|
kwargs = {
|
|
'id':str(w.id),
|
|
})
|
|
return HttpResponseRedirect(url)
|
|
|
|
authorizationstring = str('Bearer ' + thetoken)
|
|
headers = {'Authorization': authorizationstring,
|
|
'user-agent': 'sanderroosendaal',
|
|
'Content-Type': 'application/json'}
|
|
|
|
url = "https://api.sporttracks.mobi/api/v2/fitnessActivities.json"
|
|
response = requests.post(url,headers=headers,data=json.dumps(data))
|
|
|
|
# check for duplicate error first
|
|
if (response.status_code == 409 ):
|
|
message = "Duplicate error"
|
|
messages.error(request,message)
|
|
w.uploadedtosporttracks = -1
|
|
w.save()
|
|
elif (response.status_code == 201 or response.status_code==200):
|
|
s= json.loads(response.text)
|
|
sporttracksid = sporttracksstuff.getidfromresponse(response)
|
|
w.uploadedtosporttracks = sporttracksid
|
|
w.save()
|
|
message = "Upload to SportTracks was successful"
|
|
messages.info(request,message)
|
|
url = "/rowers/workout/"+str(w.id)+"/export"
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
s = response
|
|
message = "Something went wrong in workout_sporttracks_upload_view: %s" % s.reason
|
|
messages.error(request,message)
|
|
else:
|
|
message = "You are not authorized to upload this workout"
|
|
messages.error(request,message)
|
|
|
|
url = reverse(workout_export_view,
|
|
kwargs = {
|
|
'id':str(w.id),
|
|
})
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
# Concept2 authorization
|
|
@login_required()
|
|
def rower_c2_authorize(request):
|
|
# Generate a random string for the state parameter
|
|
# Save it for use later to prevent xsrf attacks
|
|
from uuid import uuid4
|
|
state = str(uuid4())
|
|
scope = "user:read,results:write"
|
|
params = {"client_id": C2_CLIENT_ID,
|
|
"response_type": "code",
|
|
"redirect_uri": C2_REDIRECT_URI}
|
|
url = "http://log.concept2.com/oauth/authorize?"+ urllib.urlencode(params)
|
|
url += "&scope="+scope
|
|
return HttpResponseRedirect(url)
|
|
|
|
# Strava Authorization
|
|
@login_required()
|
|
def rower_strava_authorize(request):
|
|
# Generate a random string for the state parameter
|
|
# Save it for use later to prevent xsrf attacks
|
|
from uuid import uuid4
|
|
state = str(uuid4())
|
|
|
|
params = {"client_id": STRAVA_CLIENT_ID,
|
|
"response_type": "code",
|
|
"redirect_uri": STRAVA_REDIRECT_URI,
|
|
"scope": "write"}
|
|
|
|
url = "https://www.strava.com/oauth/authorize?"+ urllib.urlencode(params)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
# Runkeeper authorization
|
|
@login_required()
|
|
def rower_runkeeper_authorize(request):
|
|
# Generate a random string for the state parameter
|
|
# Save it for use later to prevent xsrf attacks
|
|
from uuid import uuid4
|
|
state = str(uuid4())
|
|
|
|
params = {"client_id": RUNKEEPER_CLIENT_ID,
|
|
"response_type": "code",
|
|
"state": state,
|
|
"redirect_uri": RUNKEEPER_REDIRECT_URI}
|
|
|
|
url = "https://runkeeper.com/apps/authorize?"+ urllib.urlencode(params)
|
|
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
# SportTracks Authorization
|
|
@login_required()
|
|
def rower_sporttracks_authorize(request):
|
|
# Generate a random string for the state parameter
|
|
# Save it for use later to prevent xsrf attacks
|
|
from uuid import uuid4
|
|
state = str(uuid4())
|
|
|
|
params = {"client_id": SPORTTRACKS_CLIENT_ID,
|
|
"response_type": "code",
|
|
"state": state,
|
|
"redirect_uri": SPORTTRACKS_REDIRECT_URI}
|
|
|
|
url = "https://api.sporttracks.mobi/oauth2/authorize?"+ urllib.urlencode(params)
|
|
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
# Underarmour Authorization
|
|
@login_required()
|
|
def rower_underarmour_authorize(request):
|
|
# Generate a random string for the state parameter
|
|
# Save it for use later to prevent xsrf attacks
|
|
from uuid import uuid4
|
|
state = str(uuid4())
|
|
|
|
redirect_uri = UNDERARMOUR_REDIRECT_URI
|
|
|
|
url = 'https://www.mapmyfitness.com/v7.1/oauth2/authorize/?' \
|
|
'client_id={0}&response_type=code&redirect_uri={1}'.format(
|
|
UNDERARMOUR_CLIENT_KEY, redirect_uri
|
|
)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
# Underarmour Authorization
|
|
@login_required()
|
|
def rower_tp_authorize(request):
|
|
# Generate a random string for the state parameter
|
|
# Save it for use later to prevent xsrf attacks
|
|
from uuid import uuid4
|
|
state = str(uuid4())
|
|
params = {"client_id": TP_CLIENT_KEY,
|
|
"response_type": "code",
|
|
"redirect_uri": TP_REDIRECT_URI,
|
|
"scope": "file:write",
|
|
}
|
|
url = "https://oauth.trainingpeaks.com/oauth/authorize/?" +urllib.urlencode(params)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
|
|
# Concept2 token refresh. URL for manual refresh. Not visible to users
|
|
@login_required()
|
|
def rower_c2_token_refresh(request):
|
|
r = getrower(request.user)
|
|
res = c2stuff.do_refresh_token(r.c2refreshtoken)
|
|
if res[0] != None:
|
|
access_token = res[0]
|
|
expires_in = res[1]
|
|
refresh_token = res[2]
|
|
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
|
|
r = getrower(request.user)
|
|
r.c2token = access_token
|
|
r.tokenexpirydate = expirydatetime
|
|
r.c2refreshtoken = refresh_token
|
|
|
|
r.save()
|
|
|
|
successmessage = "Tokens refreshed. Good to go"
|
|
messages.info(request,successmessage)
|
|
else:
|
|
message = "Something went wrong (refreshing tokens). Please reauthorize:"
|
|
messages.error(request,successmessage)
|
|
|
|
return imports_view(request)
|
|
|
|
# Underarmour token refresh. URL for manual refresh. Not visible to users
|
|
@login_required()
|
|
def rower_underarmour_token_refresh(request):
|
|
r = getrower(request.user)
|
|
res = underarmourstuff.do_refresh_token(
|
|
r.underarmourrefreshtoken,
|
|
r.underarmourtoken
|
|
)
|
|
access_token = res[0]
|
|
expires_in = res[1]
|
|
refresh_token = res[2]
|
|
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
|
|
|
|
r = getrower(request.user)
|
|
r.underarmourtoken = access_token
|
|
r.underarmourtokenexpirydate = expirydatetime
|
|
r.underarmourrefreshtoken = refresh_token
|
|
|
|
r.save()
|
|
|
|
successmessage = "Tokens refreshed. Good to go"
|
|
messages.info(request,successmessage)
|
|
return imports_view(request)
|
|
|
|
|
|
# TrainingPeaks token refresh. URL for manual refresh. Not visible to users
|
|
@login_required()
|
|
def rower_tp_token_refresh(request):
|
|
r = getrower(request.user)
|
|
res = tpstuff.do_refresh_token(
|
|
r.tprefreshtoken,
|
|
)
|
|
access_token = res[0]
|
|
expires_in = res[1]
|
|
refresh_token = res[2]
|
|
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
|
|
|
|
r = getrower(request.user)
|
|
r.tptoken = access_token
|
|
r.tptokenexpirydate = expirydatetime
|
|
r.tprefreshtoken = refresh_token
|
|
|
|
r.save()
|
|
|
|
successmessage = "Tokens refreshed. Good to go"
|
|
messages.info(request,successmessage)
|
|
return imports_view(request)
|
|
|
|
|
|
# SportTracks token refresh. URL for manual refresh. Not visible to users
|
|
@login_required()
|
|
def rower_sporttracks_token_refresh(request):
|
|
r = getrower(request.user)
|
|
res = sporttracksstuff.do_refresh_token(
|
|
r.sporttracksrefreshtoken,
|
|
)
|
|
access_token = res[0]
|
|
expires_in = res[1]
|
|
refresh_token = res[2]
|
|
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
|
|
|
|
r = getrower(request.user)
|
|
r.sporttrackstoken = access_token
|
|
r.sporttrackstokenexpirydate = expirydatetime
|
|
r.sporttracksrefreshtoken = refresh_token
|
|
|
|
r.save()
|
|
|
|
successmessage = "Tokens refreshed. Good to go"
|
|
messages.info(request,successmessage)
|
|
return imports_view(request)
|
|
|
|
|
|
# Concept2 Callback
|
|
@login_required()
|
|
def rower_process_callback(request):
|
|
try:
|
|
code = request.GET['code']
|
|
res = c2stuff.get_token(code)
|
|
except MultiValueDictKeyError:
|
|
message = "The resource owner or authorization server denied the request"
|
|
messages.error(request,message)
|
|
return imports_view(request)
|
|
|
|
access_token = res[0]
|
|
if access_token == 0:
|
|
message = res[1]
|
|
message += ' Contact info@rowsandall.com if this behavior persists.'
|
|
messages.error(request,message)
|
|
return imports_view(request)
|
|
|
|
expires_in = res[1]
|
|
refresh_token = res[2]
|
|
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
|
|
|
|
r = getrower(request.user)
|
|
r.c2token = access_token
|
|
r.tokenexpirydate = expirydatetime
|
|
r.c2refreshtoken = refresh_token
|
|
|
|
r.save()
|
|
|
|
successmessage = "Tokens stored. Good to go"
|
|
messages.info(request,successmessage)
|
|
return imports_view(request)
|
|
|
|
# The imports page
|
|
@login_required()
|
|
def imports_view(request,successmessage="",message=""):
|
|
messages.info(request,successmessage)
|
|
messages.error(request,message)
|
|
return render(request,"imports.html",
|
|
{
|
|
'teams':get_my_teams(request.user),
|
|
})
|
|
|
|
# Just for testing purposes
|
|
@login_required()
|
|
def test_reverse_view(request):
|
|
successmessage = "Tokens stored. Good to go"
|
|
messages.info(request,successmessage)
|
|
return imports_view(request)
|
|
|
|
# dummy
|
|
@login_required()
|
|
def rower_process_twittercallback(request):
|
|
return "dummy"
|
|
|
|
# Process Strava Callback
|
|
@login_required()
|
|
def rower_process_stravacallback(request):
|
|
try:
|
|
code = request.GET['code']
|
|
except MultiValueDictKeyError:
|
|
try:
|
|
message = request.GET['error']
|
|
except MultiValueDictKeyError:
|
|
message = "access error"
|
|
|
|
messages.error(request,message)
|
|
return imports_view(request)
|
|
|
|
res = stravastuff.get_token(code)
|
|
|
|
if res[0]:
|
|
access_token = res[0]
|
|
|
|
r = getrower(request.user)
|
|
r.stravatoken = access_token
|
|
|
|
r.save()
|
|
|
|
successmessage = "Tokens stored. Good to go"
|
|
messages.info(request,successmessage)
|
|
return imports_view(request)
|
|
else:
|
|
message = "Something went wrong with the Strava authorization"
|
|
messages.error(request,message)
|
|
return imports_view(request)
|
|
|
|
# Process Runkeeper callback
|
|
@login_required()
|
|
def rower_process_runkeepercallback(request):
|
|
code = request.GET['code']
|
|
access_token = runkeeperstuff.get_token(code)
|
|
|
|
r = getrower(request.user)
|
|
r.runkeepertoken = access_token
|
|
|
|
r.save()
|
|
|
|
successmessage = "Tokens stored. Good to go"
|
|
messages.info(request,successmessage)
|
|
return imports_view(request)
|
|
|
|
# Process SportTracks callback
|
|
@login_required()
|
|
def rower_process_sporttrackscallback(request):
|
|
code = request.GET['code']
|
|
res = sporttracksstuff.get_token(code)
|
|
|
|
|
|
access_token = res[0]
|
|
expires_in = res[1]
|
|
refresh_token = res[2]
|
|
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
|
|
|
|
r = getrower(request.user)
|
|
r.sporttrackstoken = access_token
|
|
r.sporttrackstokenexpirydate = expirydatetime
|
|
r.sporttracksrefreshtoken = refresh_token
|
|
|
|
r.save()
|
|
|
|
successmessage = "Tokens stored. Good to go"
|
|
messages.info(request,successmessage)
|
|
return imports_view(request)
|
|
|
|
# Process Underarmour callback
|
|
@login_required()
|
|
def rower_process_underarmourcallback(request):
|
|
code = request.GET['code']
|
|
res = underarmourstuff.get_token(code)
|
|
|
|
|
|
access_token = res[0]
|
|
expires_in = res[1]
|
|
refresh_token = res[2]
|
|
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
|
|
|
|
r = getrower(request.user)
|
|
r.underarmourtoken = access_token
|
|
r.underarmourtokenexpirydate = expirydatetime
|
|
r.underarmourrefreshtoken = refresh_token
|
|
|
|
r.save()
|
|
|
|
successmessage = "Tokens stored. Good to go"
|
|
messages.info(request,successmessage)
|
|
return imports_view(request)
|
|
|
|
# Process TrainingPeaks callback
|
|
@login_required()
|
|
def rower_process_tpcallback(request):
|
|
code = request.GET['code']
|
|
res = tpstuff.get_token(code)
|
|
|
|
|
|
access_token = res[0]
|
|
expires_in = res[1]
|
|
refresh_token = res[2]
|
|
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
|
|
|
|
r = getrower(request.user)
|
|
r.tptoken = access_token
|
|
r.tptokenexpirydate = expirydatetime
|
|
r.tprefreshtoken = refresh_token
|
|
|
|
r.save()
|
|
|
|
successmessage = "Tokens stored. Good to go"
|
|
messages.info(request,successmessage)
|
|
return imports_view(request)
|
|
|
|
# Process Own API callback - for API testing purposes
|
|
@login_required()
|
|
def rower_process_testcallback(request):
|
|
code = request.GET['code']
|
|
res = ownapistuff.get_token(code)
|
|
|
|
|
|
access_token = res[0]
|
|
expires_in = res[1]
|
|
refresh_token = res[2]
|
|
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
|
|
|
|
text = "Access Token:\n"
|
|
text += access_token
|
|
|
|
text += "\n\nRefresh Token:\n"
|
|
text += refresh_token
|
|
|
|
return HttpResponse(text)
|
|
|
|
# View around the Histogram of all strokes. Not implemented in UI
|
|
@login_required()
|
|
def histo_all(request,theuser=0,
|
|
startdate=timezone.now()-datetime.timedelta(days=10),
|
|
enddate=timezone.now()+datetime.timedelta(days=1),
|
|
deltadays=-1,
|
|
startdatestring="",
|
|
enddatestring="",
|
|
options={
|
|
'includereststrokes':False,
|
|
'workouttypes':['rower','dynamic','slides'],
|
|
'waterboattype':['1x','2x','2-','4x','4-','8+']
|
|
}):
|
|
|
|
if 'options' in request.session:
|
|
options = request.session['options']
|
|
|
|
workouttypes = options['workouttypes']
|
|
includereststrokes = options['includereststrokes']
|
|
waterboattype = options['waterboattype']
|
|
workstrokesonly = not includereststrokes
|
|
|
|
|
|
checktypes = ['water','rower','dynamic','slides','skierg',
|
|
'paddle','snow','coastal','other']
|
|
|
|
|
|
if deltadays>0:
|
|
startdate = enddate-datetime.timedelta(days=int(deltadays))
|
|
|
|
if startdatestring != "":
|
|
startdate = iso8601.parse_date(startdatestring)
|
|
|
|
if enddatestring != "":
|
|
enddate = iso8601.parse_date(enddatestring)
|
|
|
|
if enddate < startdate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
|
|
promember=0
|
|
if theuser == 0:
|
|
theuser = request.user.id
|
|
|
|
if not request.user.is_anonymous():
|
|
r = getrower(request.user)
|
|
result = request.user.is_authenticated() and ispromember(request.user)
|
|
if result:
|
|
promember=1
|
|
|
|
if not promember:
|
|
return HttpResponseRedirect("/rowers/about/")
|
|
|
|
# get all indoor rows of past 12 months
|
|
ayearago = timezone.now()-datetime.timedelta(days=365)
|
|
try:
|
|
r2 = getrower(theuser)
|
|
allergworkouts = Workout.objects.filter(user=r2,
|
|
workouttype__in=['rower','dynamic','slides'],
|
|
startdatetime__gte=ayearago)
|
|
except Rower.DoesNotExist:
|
|
allergworkouts = []
|
|
r2=0
|
|
|
|
try:
|
|
u = User.objects.get(id=theuser)
|
|
except User.DoesNotExist:
|
|
u = ''
|
|
|
|
if allergworkouts:
|
|
res = interactive_histoall(allergworkouts)
|
|
script = res[0]
|
|
div = res[1]
|
|
else:
|
|
script = ''
|
|
div = '<p>No erg pieces uploaded yet.</p>'
|
|
|
|
return render(request, 'histoall.html',
|
|
{'interactiveplot':script,
|
|
'the_div':div,
|
|
'id':theuser,
|
|
'theuser':u,
|
|
'teams':get_my_teams(request.user),
|
|
})
|
|
|
|
# The Flex plot for a large selection of workouts
|
|
@login_required()
|
|
def cum_flex_data(
|
|
request,
|
|
options={
|
|
'includereststrokes':False,
|
|
'workouttypes':['rower','dynamic','slides'],
|
|
'waterboattype':['1x','2x','2-','4x','4-','8+'],
|
|
'theuser':0,
|
|
'xparam':'spm',
|
|
'yparam1':'power',
|
|
'yparam2':'None',
|
|
'enddatestring':'',
|
|
'startdatestring':'',
|
|
'deltadays':-1,
|
|
}):
|
|
|
|
if 'options' in request.session:
|
|
options = request.session['options']
|
|
|
|
workouttypes = options['workouttypes']
|
|
includereststrokes = options['includereststrokes']
|
|
waterboattype = options['waterboattype']
|
|
workstrokesonly = not includereststrokes
|
|
theuser = options['theuser']
|
|
xparam = options['xparam']
|
|
yparam1 = options['yparam1']
|
|
yparam2 = options['yparam2']
|
|
startdatestring = options['startdatestring']
|
|
enddatestring = options['enddatestring']
|
|
deltadays = options['deltadays']
|
|
|
|
|
|
try:
|
|
startdate = iso8601.parse_date(startdatestring)
|
|
except ParseError:
|
|
startdate = timezone.now()-datetime.timedelta(days=7)
|
|
|
|
try:
|
|
enddate = iso8601.parse_date(enddatestring)
|
|
except ParseError:
|
|
enddate = timezone.now()
|
|
|
|
if deltadays>0:
|
|
startdate = enddate-datetime.timedelta(days=int(deltadays))
|
|
|
|
|
|
if enddate < startdate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
|
|
promember=0
|
|
if theuser == 0:
|
|
theuser = request.user.id
|
|
|
|
if not request.user.is_anonymous():
|
|
r = getrower(request.user)
|
|
result = request.user.is_authenticated() and ispromember(request.user)
|
|
if result:
|
|
promember=1
|
|
|
|
r2 = getrower(theuser)
|
|
allworkouts = Workout.objects.filter(user=r2,
|
|
workouttype__in=workouttypes,
|
|
boattype__in=waterboattype,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate)
|
|
|
|
if allworkouts:
|
|
res = interactive_cum_flex_chart2(allworkouts,xparam=xparam,
|
|
yparam1=yparam1,
|
|
yparam2=yparam2,
|
|
promember=promember,
|
|
workstrokesonly=workstrokesonly)
|
|
script = res[0]
|
|
div = res[1]
|
|
else:
|
|
script = ''
|
|
div = '<p>No erg pieces uploaded for this date range.</p>'
|
|
|
|
scripta = script.split('\n')[2:-1]
|
|
script = ''.join(scripta)
|
|
|
|
data = {
|
|
"script":script,
|
|
"div":div,
|
|
}
|
|
|
|
return JSONResponse(data)
|
|
|
|
|
|
|
|
|
|
@login_required()
|
|
def cum_flex(request,theuser=0,
|
|
xparam='spm',
|
|
yparam1='power',
|
|
yparam2='None',
|
|
startdate=timezone.now()-datetime.timedelta(days=10),
|
|
enddate=timezone.now()+datetime.timedelta(days=1),
|
|
deltadays=-1,
|
|
startdatestring="",
|
|
enddatestring="",
|
|
options={
|
|
'includereststrokes':False,
|
|
'workouttypes':['rower','dynamic','slides'],
|
|
'waterboattype':['1x','2x','2-','4x','4-','8+']
|
|
}):
|
|
|
|
if 'options' in request.session:
|
|
options = request.session['options']
|
|
|
|
try:
|
|
workouttypes = options['workouttypes']
|
|
except KeyError:
|
|
workouttypes = ['rower','dynamic','slides']
|
|
|
|
try:
|
|
includereststrokes = options['includereststrokes']
|
|
except KeyError:
|
|
includereststrokes = False
|
|
|
|
try:
|
|
waterboattype = options['waterboattype']
|
|
except KeyError:
|
|
waterboattype = ['1x','2x','2-','4x','4-','8+']
|
|
|
|
workstrokesonly = not includereststrokes
|
|
|
|
|
|
checktypes = ['water','rower','dynamic','slides','skierg',
|
|
'paddle','snow','coastal','other']
|
|
|
|
|
|
if deltadays>0:
|
|
startdate = enddate-datetime.timedelta(days=int(deltadays))
|
|
|
|
if startdatestring != "":
|
|
startdate = iso8601.parse_date(startdatestring)
|
|
|
|
if enddatestring != "":
|
|
enddate = iso8601.parse_date(enddatestring)
|
|
|
|
if enddate < startdate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
|
|
promember=0
|
|
if theuser == 0:
|
|
theuser = request.user.id
|
|
|
|
if not request.user.is_anonymous():
|
|
r = getrower(request.user)
|
|
result = request.user.is_authenticated() and ispromember(request.user)
|
|
if result:
|
|
promember=1
|
|
|
|
#if not promember:
|
|
#return HttpResponseRedirect("/rowers/about/")
|
|
|
|
# get all indoor rows of in date range
|
|
|
|
# process form
|
|
if request.method == 'POST' and "daterange" in request.POST:
|
|
form = DateRangeForm(request.POST)
|
|
deltaform = DeltaDaysForm(request.POST)
|
|
optionsform = StatsOptionsForm()
|
|
if form.is_valid():
|
|
startdate = form.cleaned_data['startdate']
|
|
enddate = form.cleaned_data['enddate']
|
|
if startdate > enddate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
elif request.method == 'POST' and "datedelta" in request.POST:
|
|
deltaform = DeltaDaysForm(request.POST)
|
|
optionsform = StatsOptionsForm()
|
|
if deltaform.is_valid():
|
|
deltadays = deltaform.cleaned_data['deltadays']
|
|
if deltadays != 0 and isinstance(deltadays, Number):
|
|
enddate = timezone.now()
|
|
startdate = enddate-datetime.timedelta(days=deltadays)
|
|
if startdate > enddate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
form = DateRangeForm(initial={
|
|
'startdate': startdate,
|
|
'enddate': enddate,
|
|
})
|
|
else:
|
|
form = DateRangeForm()
|
|
optionsform = StatsOptionsForm()
|
|
elif request.method == 'POST' and 'options' in request.POST:
|
|
optionsform = StatsOptionsForm(request.POST)
|
|
if optionsform.is_valid():
|
|
includereststrokes = optionsform.cleaned_data['includereststrokes']
|
|
workstrokesonly = not includereststrokes
|
|
waterboattype = optionsform.cleaned_data['waterboattype']
|
|
workouttypes = []
|
|
for type in checktypes:
|
|
if optionsform.cleaned_data[type]:
|
|
workouttypes.append(type)
|
|
|
|
|
|
options = {
|
|
'includereststrokes':includereststrokes,
|
|
'workouttypes':workouttypes,
|
|
'waterboattype':waterboattype,
|
|
}
|
|
request.session['options'] = options
|
|
form = DateRangeForm(initial={
|
|
'startdate': startdate,
|
|
'enddate': enddate,
|
|
})
|
|
deltaform = DeltaDaysForm()
|
|
else:
|
|
form = DateRangeForm(initial={
|
|
'startdate': startdate,
|
|
'enddate': enddate,
|
|
})
|
|
deltaform = DeltaDaysForm()
|
|
else:
|
|
form = DateRangeForm(initial={
|
|
'startdate': startdate,
|
|
'enddate': enddate,
|
|
})
|
|
deltaform = DeltaDaysForm()
|
|
optionsform = StatsOptionsForm()
|
|
|
|
script = ''
|
|
div = get_call()
|
|
js_resources = ''
|
|
css_resources = ''
|
|
|
|
axchoicesbasic = {ax[0]:ax[1] for ax in axes if ax[4]=='basic'}
|
|
axchoicespro = {ax[0]:ax[1] for ax in axes if ax[4]=='pro'}
|
|
noylist = ["time","distance"]
|
|
axchoicesbasic.pop("cumdist")
|
|
|
|
# set options form correctly
|
|
initial = {}
|
|
initial['includereststrokes'] = includereststrokes
|
|
|
|
initial['waterboattype'] = waterboattype
|
|
|
|
for wtype in checktypes:
|
|
if wtype in workouttypes:
|
|
initial[wtype] = True
|
|
else:
|
|
initial[wtype] = False
|
|
|
|
|
|
try:
|
|
u = User.objects.get(id=theuser)
|
|
except User.DoesNotExist:
|
|
u = ''
|
|
|
|
optionsform = StatsOptionsForm(initial=initial)
|
|
|
|
|
|
options['includereststrokes'] = includereststrokes
|
|
options['workouttypes'] =workouttypes
|
|
options['waterboattype'] =waterboattype
|
|
options['theuser'] =theuser
|
|
options['xparam'] =xparam
|
|
options['yparam1'] =yparam1
|
|
options['yparam2'] =yparam2
|
|
options['startdatestring'] = startdatestring
|
|
options['enddatestring'] =enddatestring
|
|
options['deltadays'] =deltadays
|
|
|
|
|
|
request.session['options'] = options
|
|
|
|
return render(request, 'cum_flex.html',
|
|
{'interactiveplot':script,
|
|
'the_div':div,
|
|
'js_res': js_resources,
|
|
'css_res':css_resources,
|
|
'id':theuser,
|
|
'theuser':u,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
'form':form,
|
|
'optionsform':optionsform,
|
|
'deltaform':deltaform,
|
|
'xparam':xparam,
|
|
'yparam1':yparam1,
|
|
'yparam2':yparam2,
|
|
'promember':promember,
|
|
'teams':get_my_teams(request.user),
|
|
'axchoicesbasic':axchoicesbasic,
|
|
'axchoicespro':axchoicespro,
|
|
'noylist':noylist,
|
|
})
|
|
|
|
|
|
# Show the EMpower Oarlock generated Stroke Profile
|
|
@user_passes_test(ispromember,login_url="/",redirect_field_name=None)
|
|
def workout_forcecurve_view(request,id=0,workstrokesonly=False):
|
|
row = get_workout(id)
|
|
|
|
promember=0
|
|
mayedit=0
|
|
if not request.user.is_anonymous():
|
|
r = getrower(request.user)
|
|
result = request.user.is_authenticated() and ispromember(request.user)
|
|
if result:
|
|
promember=1
|
|
if request.user == row.user.user:
|
|
mayedit=1
|
|
|
|
if not promember:
|
|
return HttpResponseRedirect("/rowers/about/")
|
|
|
|
if request.method == 'POST' and 'workstrokesonly' in request.POST:
|
|
workstrokesonly = request.POST['workstrokesonly']
|
|
if workstrokesonly == 'True':
|
|
workstrokesonly = True
|
|
else:
|
|
workstrokesonly = False
|
|
|
|
script,div,js_resources,css_resources = interactive_forcecurve([row],
|
|
workstrokesonly=workstrokesonly)
|
|
|
|
return render(request,
|
|
'forcecurve_single.html',
|
|
{
|
|
'the_script':script,
|
|
'the_div':div,
|
|
'js_res': js_resources,
|
|
'css_res':css_resources,
|
|
'id':int(id),
|
|
'mayedit':mayedit,
|
|
'workstrokesonly': not workstrokesonly,
|
|
'teams':get_my_teams(request.user),
|
|
})
|
|
|
|
# Test asynchronous tasking and messaging
|
|
@login_required()
|
|
def workout_test_task_view(request,id=0):
|
|
row = Workout.objects.get(id=id)
|
|
res = myqueue(queuehigh,addcomment2,request.user.id,row.id)
|
|
|
|
|
|
url = reverse(workout_edit_view,
|
|
kwargs = {
|
|
'id':int(id),
|
|
})
|
|
return HttpResponseRedirect(url)
|
|
|
|
# Show Stroke power histogram for a workout
|
|
@login_required()
|
|
def workout_histo_view(request,id=0):
|
|
row = get_workout(id)
|
|
|
|
promember=0
|
|
mayedit=0
|
|
if not request.user.is_anonymous():
|
|
r = getrower(request.user)
|
|
result = request.user.is_authenticated() and ispromember(request.user)
|
|
if result:
|
|
promember=1
|
|
if request.user == row.user.user:
|
|
mayedit=1
|
|
|
|
if not promember:
|
|
return HttpResponseRedirect("/rowers/about/")
|
|
|
|
res = interactive_histoall([row])
|
|
script = res[0]
|
|
div = res[1]
|
|
|
|
return render(request,
|
|
'histo_single.html',
|
|
{'interactiveplot':script,
|
|
'the_div':div,
|
|
'id':int(id),
|
|
'mayedit':mayedit,
|
|
'teams':get_my_teams(request.user),
|
|
})
|
|
|
|
|
|
|
|
# Histogram for a date/time range
|
|
@login_required()
|
|
def histo(request,theuser=0,
|
|
startdate=timezone.now()-datetime.timedelta(days=365),
|
|
enddate=timezone.now(),
|
|
deltadays=-1,
|
|
startdatestring="",
|
|
enddatestring="",
|
|
options={
|
|
'includereststrokes':False,
|
|
'workouttypes':['water','rower','dynamic','slides'],
|
|
'waterboattype':['1x','2x','2-','4x','4-','8+']
|
|
}):
|
|
|
|
if 'options' in request.session:
|
|
options = request.session['options']
|
|
|
|
try:
|
|
workouttypes = options['workouttypes']
|
|
includereststrokes = options['includereststrokes']
|
|
waterboattype = options['waterboattype']
|
|
except KeyError:
|
|
workouttypes = ['water','rower','dynamic','slides']
|
|
waterboattype = ['1x','2x','2-','4x','4-','8+']
|
|
includereststrokes = False
|
|
|
|
workstrokesonly = not includereststrokes
|
|
|
|
|
|
checktypes = ['water','rower','dynamic','slides','skierg',
|
|
'paddle','snow','coastal','other']
|
|
|
|
if deltadays>0:
|
|
startdate = enddate-datetime.timedelta(days=int(deltadays))
|
|
|
|
if startdatestring != "":
|
|
startdate = iso8601.parse_date(startdatestring)
|
|
|
|
if enddatestring != "":
|
|
enddate = iso8601.parse_date(enddatestring)
|
|
|
|
if enddate < startdate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
|
|
promember=0
|
|
if theuser == 0:
|
|
theuser = request.user.id
|
|
|
|
if not request.user.is_anonymous():
|
|
r = getrower(request.user)
|
|
result = request.user.is_authenticated() and ispromember(request.user)
|
|
if result:
|
|
promember=1
|
|
|
|
if not promember:
|
|
return HttpResponseRedirect("/rowers/promembership/")
|
|
|
|
# get all indoor rows of in date range
|
|
|
|
# process form
|
|
if request.method == 'POST' and "daterange" in request.POST:
|
|
form = DateRangeForm(request.POST)
|
|
deltaform = DeltaDaysForm(request.POST)
|
|
optionsform = StatsOptionsForm()
|
|
if form.is_valid():
|
|
startdate = form.cleaned_data['startdate']
|
|
enddate = form.cleaned_data['enddate']
|
|
if startdate > enddate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
elif request.method == 'POST' and "datedelta" in request.POST:
|
|
deltaform = DeltaDaysForm(request.POST)
|
|
optionsform = StatsOptionsForm()
|
|
if deltaform.is_valid():
|
|
deltadays = deltaform.cleaned_data['deltadays']
|
|
if deltadays != 0 and deltadays != None:
|
|
enddate = timezone.now()
|
|
startdate = enddate-datetime.timedelta(days=deltadays)
|
|
if startdate > enddate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
form = DateRangeForm(initial={
|
|
'startdate': startdate,
|
|
'enddate': enddate,
|
|
})
|
|
else:
|
|
form = DateRangeForm()
|
|
optionsform = StatsOptionsForm()
|
|
elif request.method == 'POST' and 'options' in request.POST:
|
|
optionsform = StatsOptionsForm(request.POST)
|
|
if optionsform.is_valid():
|
|
includereststrokes = optionsform.cleaned_data['includereststrokes']
|
|
workstrokesonly = not includereststrokes
|
|
waterboattype = optionsform.cleaned_data['waterboattype']
|
|
workouttypes = []
|
|
for type in checktypes:
|
|
if optionsform.cleaned_data[type]:
|
|
workouttypes.append(type)
|
|
|
|
|
|
options = {
|
|
'includereststrokes':includereststrokes,
|
|
'workouttypes':workouttypes,
|
|
'waterboattype':waterboattype,
|
|
}
|
|
request.session['options'] = options
|
|
form = DateRangeForm(initial={
|
|
'startdate': startdate,
|
|
'enddate': enddate,
|
|
})
|
|
deltaform = DeltaDaysForm()
|
|
else:
|
|
form = DateRangeForm(initial={
|
|
'startdate': startdate,
|
|
'enddate': enddate,
|
|
})
|
|
deltaform = DeltaDaysForm()
|
|
|
|
else:
|
|
form = DateRangeForm(initial={
|
|
'startdate': startdate,
|
|
'enddate': enddate,
|
|
})
|
|
deltaform = DeltaDaysForm()
|
|
optionsform = StatsOptionsForm()
|
|
try:
|
|
r2 = getrower(theuser)
|
|
allergworkouts = Workout.objects.filter(user=r2,
|
|
workouttype__in=workouttypes,
|
|
boattype__in=waterboattype,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate)
|
|
|
|
except Rower.DoesNotExist:
|
|
allergworkouts = []
|
|
r2=0
|
|
|
|
try:
|
|
u = User.objects.get(id=theuser)
|
|
except User.DoesNotExist:
|
|
u = ''
|
|
|
|
if allergworkouts:
|
|
res = interactive_histoall(allergworkouts)
|
|
script = res[0]
|
|
div = res[1]
|
|
else:
|
|
script = ''
|
|
typesstring = ', '.join('{}'.format(item) for i,item in enumerate(workouttypes))
|
|
if 'water' in workouttypes:
|
|
boatsstring = 'water ('+', '.join('{}'.format(item) for i,item in enumerate(waterboattype))+')'
|
|
typesstring = typesstring.replace('water',boatsstring)
|
|
div = '<p>No pieces found for '+typesstring+' for this date range.</p>'
|
|
|
|
# set options form correctly
|
|
initial = {}
|
|
initial['includereststrokes'] = includereststrokes
|
|
|
|
initial['waterboattype'] = waterboattype
|
|
|
|
for wtype in checktypes:
|
|
if wtype in workouttypes:
|
|
initial[wtype] = True
|
|
else:
|
|
initial[wtype] = False
|
|
|
|
|
|
optionsform = StatsOptionsForm(initial=initial)
|
|
|
|
request.session['options'] = options
|
|
|
|
return render(request, 'histo.html',
|
|
{'interactiveplot':script,
|
|
'the_div':div,
|
|
'id':theuser,
|
|
'theuser':u,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
'form':form,
|
|
'optionsform':optionsform,
|
|
'deltaform':deltaform,
|
|
'teams':get_my_teams(request.user),
|
|
})
|
|
|
|
# add a workout manually
|
|
@login_required()
|
|
def addmanual_view(request):
|
|
r = Rower.objects.get(user=request.user)
|
|
|
|
if request.method == 'POST':
|
|
# Form was submitted
|
|
form = WorkoutForm(request.POST)
|
|
if form.is_valid():
|
|
# Get values from form
|
|
name = form.cleaned_data['name']
|
|
date = form.cleaned_data['date']
|
|
starttime = form.cleaned_data['starttime']
|
|
workouttype = form.cleaned_data['workouttype']
|
|
duration = form.cleaned_data['duration']
|
|
distance = form.cleaned_data['distance']
|
|
notes = form.cleaned_data['notes']
|
|
thetimezone = form.cleaned_data['timezone']
|
|
try:
|
|
boattype = request.POST['boattype']
|
|
except KeyError:
|
|
boattype = '1x'
|
|
try:
|
|
privacy = request.POST['privacy']
|
|
except KeyError:
|
|
privacy = 'visible'
|
|
try:
|
|
rankingpiece = form.cleaned_data['rankingpiece']
|
|
except KeyError:
|
|
rankingpiece =- Workout.objects.get(id=id).rankingpiece
|
|
|
|
startdatetime = (str(date) + ' ' + str(starttime))
|
|
startdatetime = datetime.datetime.strptime(startdatetime,
|
|
"%Y-%m-%d %H:%M:%S")
|
|
startdatetime = timezone.make_aware(startdatetime)
|
|
startdatetime = startdatetime.astimezone(
|
|
pytz.timezone(thetimezone)
|
|
)
|
|
|
|
|
|
id,message = dataprep.create_row_df(r,
|
|
distance,
|
|
duration,startdatetime,
|
|
title = name,
|
|
notes=notes,
|
|
workouttype=workouttype)
|
|
|
|
|
|
|
|
if message:
|
|
messages.error(request,message)
|
|
|
|
if id:
|
|
w = Workout.objects.get(id=id)
|
|
w.rankingpiece = rankingpiece or is_ranking_piece(w)
|
|
w.notes = notes
|
|
w.save()
|
|
messages.info(request,'New workout created')
|
|
|
|
|
|
initial = {
|
|
'workouttype':'rower',
|
|
'date':datetime.date.today(),
|
|
'starttime':timezone.now(),
|
|
'timezone':r.defaulttimezone,
|
|
'duration':datetime.timedelta(minutes=2),
|
|
'distance':500,
|
|
|
|
}
|
|
form = WorkoutForm(initial=initial)
|
|
|
|
return render(request,'manualadd.html',
|
|
{'form':form,
|
|
})
|
|
|
|
@login_required()
|
|
def fitness_metric_view(request,mode='rower',days=42):
|
|
r = getrower(request.user)
|
|
startdate = timezone.now()-datetime.timedelta(days=days)
|
|
|
|
# test if not something already done
|
|
ms = PowerTimeFitnessMetric.objects.filter(user=request.user)
|
|
max_workout_id = max([m.last_workout for m in ms])
|
|
last_update_date = max([m.date.strftime('%Y-%m-%d') for m in ms])
|
|
|
|
|
|
now_date = timezone.now().strftime('%Y-%m-%d')
|
|
|
|
|
|
if mode == 'rower':
|
|
workouts = Workout.objects.filter(
|
|
user=r,
|
|
workouttype__in=['rower','dynamic','slides'],
|
|
startdatetime__gte=startdate)
|
|
else:
|
|
workouts = Workout.objects.filter(
|
|
user=r,
|
|
workouttype__in=['water','coastal'],
|
|
startdatetime__gte=startdate)
|
|
|
|
theids = [int(w.id) for w in workouts]
|
|
max_id = max(theids)
|
|
|
|
if last_update_date >= now_date or max_workout_id >= max_id:
|
|
return HttpResponse("already done today or no new workouts")
|
|
|
|
|
|
job = myqueue(queue,
|
|
handle_updatefitnessmetric,
|
|
request.user.id,mode,theids,
|
|
)
|
|
|
|
return HttpResponse("job queued")
|
|
|
|
|
|
# Show ranking distances including predicted paces
|
|
@login_required()
|
|
def rankings_view(request,theuser=0,
|
|
startdate=timezone.now()-datetime.timedelta(days=365),
|
|
enddate=timezone.now(),
|
|
deltadays=-1,
|
|
startdatestring="",
|
|
enddatestring=""):
|
|
|
|
if deltadays>0:
|
|
startdate = enddate-datetime.timedelta(days=int(deltadays))
|
|
|
|
if startdatestring != "":
|
|
startdate = iso8601.parse_date(startdatestring)
|
|
|
|
if enddatestring != "":
|
|
enddate = iso8601.parse_date(enddatestring)
|
|
|
|
if enddate < startdate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
|
|
if theuser == 0:
|
|
theuser = request.user.id
|
|
|
|
promember=0
|
|
if not request.user.is_anonymous():
|
|
r = getrower(request.user)
|
|
if r.birthdate:
|
|
age = calculate_age(r.birthdate)
|
|
worldclasspower = int(metrics.getagegrouprecord(
|
|
age,
|
|
sex=r.sex,
|
|
weightcategory=r.weightcategory
|
|
))
|
|
else:
|
|
worldclasspower = None
|
|
|
|
result = request.user.is_authenticated() and ispromember(request.user)
|
|
if result:
|
|
promember=1
|
|
|
|
# get all indoor rows in date range
|
|
|
|
# process form
|
|
if request.method == 'POST' and "daterange" in request.POST:
|
|
dateform = DateRangeForm(request.POST)
|
|
deltaform = DeltaDaysForm(request.POST)
|
|
if dateform.is_valid():
|
|
startdate = dateform.cleaned_data['startdate']
|
|
enddate = dateform.cleaned_data['enddate']
|
|
if startdate > enddate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
elif request.method == 'POST' and "datedelta" in request.POST:
|
|
deltaform = DeltaDaysForm(request.POST)
|
|
if deltaform.is_valid():
|
|
deltadays = deltaform.cleaned_data['deltadays']
|
|
if deltadays:
|
|
enddate = timezone.now()
|
|
startdate = enddate-datetime.timedelta(days=deltadays)
|
|
if startdate > enddate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
dateform = DateRangeForm(initial={
|
|
'startdate': startdate,
|
|
'enddate': enddate,
|
|
})
|
|
else:
|
|
dateform = DateRangeForm()
|
|
deltaform = DeltaDaysForm()
|
|
|
|
else:
|
|
dateform = DateRangeForm(initial={
|
|
'startdate': startdate,
|
|
'enddate': enddate,
|
|
})
|
|
deltaform = DeltaDaysForm()
|
|
|
|
# get all 2k (if any) - this rower, in date range
|
|
try:
|
|
r = getrower(theuser)
|
|
except Rower.DoesNotExist:
|
|
allergworkouts = []
|
|
r=0
|
|
|
|
|
|
try:
|
|
uu = User.objects.get(id=theuser)
|
|
except User.DoesNotExist:
|
|
uu = ''
|
|
|
|
|
|
# test to fix bug
|
|
startdate = datetime.datetime.combine(startdate,datetime.time())
|
|
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
|
enddate = enddate+datetime.timedelta(days=1)
|
|
|
|
thedistances = []
|
|
theworkouts = []
|
|
thesecs = []
|
|
|
|
rankingdistances.sort()
|
|
rankingdurations.sort()
|
|
|
|
for rankingdistance in rankingdistances:
|
|
|
|
workouts = Workout.objects.filter(
|
|
user=r,distance=rankingdistance,
|
|
workouttype__in=['rower','dynamic','slides'],
|
|
rankingpiece=True,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate
|
|
).order_by('duration')
|
|
if workouts:
|
|
thedistances.append(rankingdistance)
|
|
theworkouts.append(workouts[0])
|
|
|
|
timesecs = 3600*workouts[0].duration.hour
|
|
timesecs += 60*workouts[0].duration.minute
|
|
timesecs += workouts[0].duration.second
|
|
timesecs += 1.e-6*workouts[0].duration.microsecond
|
|
|
|
thesecs.append(timesecs)
|
|
|
|
for rankingduration in rankingdurations:
|
|
|
|
workouts = Workout.objects.filter(
|
|
user=r,duration=rankingduration,
|
|
workouttype='rower',
|
|
rankingpiece=True,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate
|
|
).order_by('-distance')
|
|
if workouts:
|
|
thedistances.append(workouts[0].distance)
|
|
theworkouts.append(workouts[0])
|
|
|
|
timesecs = 3600*workouts[0].duration.hour
|
|
timesecs += 60*workouts[0].duration.minute
|
|
timesecs += workouts[0].duration.second
|
|
timesecs += 1.e-5*workouts[0].duration.microsecond
|
|
|
|
thesecs.append(timesecs)
|
|
|
|
thedistances = np.array(thedistances)
|
|
thesecs = np.array(thesecs)
|
|
|
|
thevelos = thedistances/thesecs
|
|
theavpower = 2.8*(thevelos**3)
|
|
|
|
|
|
# create interactive plot
|
|
if len(thedistances) !=0 :
|
|
res = interactive_cpchart(
|
|
r,thedistances,thesecs,theavpower,
|
|
theworkouts,promember=promember
|
|
)
|
|
script = res[0]
|
|
div = res[1]
|
|
paulslope = res[2]
|
|
paulintercept = res[3]
|
|
p1 = res[4]
|
|
message = res[5]
|
|
else:
|
|
script = ''
|
|
div = '<p>No ranking pieces found.</p>'
|
|
paulslope = 1
|
|
paulintercept = 1
|
|
p1 = [1,1,1,1]
|
|
message = ""
|
|
|
|
|
|
if request.method == 'POST' and "piece" in request.POST:
|
|
form = PredictedPieceForm(request.POST)
|
|
if form.is_valid():
|
|
value = form.cleaned_data['value']
|
|
hourvalue,value = divmod(value,60)
|
|
if hourvalue >= 24:
|
|
hourvalue = 23
|
|
pieceunit = form.cleaned_data['pieceunit']
|
|
if pieceunit == 'd':
|
|
rankingdistances.append(value)
|
|
else:
|
|
rankingdurations.append(datetime.time(minute=int(value),hour=int(hourvalue)))
|
|
else:
|
|
form = PredictedPieceForm()
|
|
|
|
rankingdistances.sort()
|
|
rankingdurations.sort()
|
|
|
|
|
|
predictions = []
|
|
cpredictions = []
|
|
|
|
|
|
for rankingdistance in rankingdistances:
|
|
# Paul's model
|
|
p = paulslope*np.log10(rankingdistance)+paulintercept
|
|
velo = 500./p
|
|
t = rankingdistance/velo
|
|
pwr = 2.8*(velo**3)
|
|
a = {'distance':rankingdistance,
|
|
'duration':timedeltaconv(t),
|
|
'pace':timedeltaconv(p),
|
|
'power':int(pwr)}
|
|
predictions.append(a)
|
|
|
|
# CP model -
|
|
pwr2 = p1[0]/(1+t/p1[2])
|
|
pwr2 += p1[1]/(1+t/p1[3])
|
|
|
|
if pwr2 <= 0:
|
|
pwr2 = 50.
|
|
|
|
velo2 = (pwr2/2.8)**(1./3.)
|
|
|
|
if np.isnan(velo2) or velo2 <= 0:
|
|
velo2 = 1.0
|
|
|
|
t2 = rankingdistance/velo2
|
|
|
|
pwr3 = p1[0]/(1+t2/p1[2])
|
|
pwr3 += p1[1]/(1+t2/p1[3])
|
|
|
|
if pwr3 <= 0:
|
|
pwr3 = 50.
|
|
|
|
velo3 = (pwr3/2.8)**(1./3.)
|
|
if np.isnan(velo3) or velo3 <= 0:
|
|
velo3 = 1.0
|
|
|
|
t3 = rankingdistance/velo3
|
|
p3 = 500./velo3
|
|
|
|
a = {'distance':rankingdistance,
|
|
'duration':timedeltaconv(t3),
|
|
'pace':timedeltaconv(p3),
|
|
'power':int(pwr3)}
|
|
cpredictions.append(a)
|
|
|
|
|
|
|
|
|
|
for rankingduration in rankingdurations:
|
|
t = 3600.*rankingduration.hour
|
|
t += 60.*rankingduration.minute
|
|
t += rankingduration.second
|
|
t += rankingduration.microsecond/1.e6
|
|
|
|
# Paul's model
|
|
ratio = paulintercept/paulslope
|
|
|
|
u = ((2**(2+ratio))*(5.**(3+ratio))*t*np.log(10))/paulslope
|
|
|
|
d = 500*t*np.log(10.)
|
|
d = d/(paulslope*lambertw(u))
|
|
d = d.real
|
|
|
|
velo = d/t
|
|
p = 500./velo
|
|
pwr = 2.8*(velo**3)
|
|
a = {'distance':int(d),
|
|
'duration':timedeltaconv(t),
|
|
'pace':timedeltaconv(p),
|
|
'power':int(pwr)}
|
|
predictions.append(a)
|
|
|
|
# CP model
|
|
pwr = p1[0]/(1+t/p1[2])
|
|
pwr += p1[1]/(1+t/p1[3])
|
|
|
|
if pwr <= 0:
|
|
pwr = 50.
|
|
|
|
velo = (pwr/2.8)**(1./3.)
|
|
|
|
if np.isnan(velo) or velo <=0:
|
|
velo = 1.0
|
|
|
|
d = t*velo
|
|
p = 500./velo
|
|
a = {'distance':int(d),
|
|
'duration':timedeltaconv(t),
|
|
'pace':timedeltaconv(p),
|
|
'power':int(pwr)}
|
|
cpredictions.append(a)
|
|
|
|
|
|
messages.error(request,message)
|
|
return render(request, 'rankings.html',
|
|
{'rankingworkouts':theworkouts,
|
|
'interactiveplot':script,
|
|
'the_div':div,
|
|
'predictions':predictions,
|
|
'cpredictions':cpredictions,
|
|
'nrdata':len(thedistances),
|
|
'form':form,
|
|
'dateform':dateform,
|
|
'deltaform':deltaform,
|
|
'worldclasspower':worldclasspower,
|
|
'id': theuser,
|
|
'theuser':uu,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
'teams':get_my_teams(request.user),
|
|
})
|
|
|
|
@login_required()
|
|
def ajax_agegrouprecords(request,
|
|
age=25,
|
|
sex='female',
|
|
weightcategory='hwt',
|
|
userid=0):
|
|
|
|
wcdurations = []
|
|
wcpower = []
|
|
durations = [1,4,30,60]
|
|
distances = [100,500,1000,2000,5000,6000,10000,21097,42195]
|
|
|
|
df = pd.DataFrame(
|
|
list(
|
|
C2WorldClassAgePerformance.objects.filter(
|
|
sex=sex,
|
|
weightcategory=weightcategory
|
|
).values()
|
|
)
|
|
)
|
|
|
|
jsondf = df.to_json()
|
|
|
|
job = myqueue(queue,
|
|
handle_getagegrouprecords,
|
|
jsondf,distances,durations,age,sex,weightcategory,
|
|
)
|
|
|
|
|
|
|
|
return JSONResponse(
|
|
{
|
|
'job':job.id
|
|
}
|
|
)
|
|
|
|
|
|
# Show ranking distances including predicted paces
|
|
@login_required()
|
|
def rankings_view2(request,theuser=0,
|
|
startdate=timezone.now()-datetime.timedelta(days=365),
|
|
enddate=timezone.now(),
|
|
deltadays=-1,
|
|
startdatestring="",
|
|
enddatestring=""):
|
|
|
|
if deltadays>0:
|
|
startdate = enddate-datetime.timedelta(days=int(deltadays))
|
|
|
|
if startdatestring != "":
|
|
startdate = iso8601.parse_date(startdatestring)
|
|
|
|
if enddatestring != "":
|
|
enddate = iso8601.parse_date(enddatestring)
|
|
|
|
if enddate < startdate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
|
|
if theuser == 0:
|
|
theuser = request.user.id
|
|
else:
|
|
lastupdated = "01-01-1900"
|
|
|
|
|
|
promember=0
|
|
if not request.user.is_anonymous():
|
|
r = getrower(request.user)
|
|
wcdurations = []
|
|
wcpower = []
|
|
|
|
lastupdated = "01-01-1900"
|
|
userid = 0
|
|
if 'options' in request.session:
|
|
options = request.session['options']
|
|
try:
|
|
wcdurations = options['wcdurations']
|
|
wcpower = options['wcpower']
|
|
lastupdated = options['lastupdated']
|
|
except KeyError:
|
|
pass
|
|
try:
|
|
userid = options['userid']
|
|
except KeyError:
|
|
userid = 0
|
|
else:
|
|
options = {}
|
|
|
|
|
|
|
|
lastupdatedtime = arrow.get(lastupdated).timestamp
|
|
current_time = arrow.utcnow().timestamp
|
|
|
|
deltatime_seconds = current_time - lastupdatedtime
|
|
recalc = False
|
|
if str(userid) != str(theuser) or deltatime_seconds > 3600:
|
|
recalc = True
|
|
options['lastupdated'] = arrow.utcnow().isoformat()
|
|
else:
|
|
recalc = False
|
|
|
|
options['userid'] = theuser
|
|
|
|
if r.birthdate:
|
|
age = calculate_age(r.birthdate)
|
|
else:
|
|
worldclasspower = None
|
|
age = 0
|
|
|
|
agerecords = CalcAgePerformance.objects.filter(
|
|
age = age,
|
|
sex = r.sex,
|
|
weightcategory = r.weightcategory)
|
|
|
|
if len(agerecords) == 0:
|
|
recalc = True
|
|
wcpower = []
|
|
wcduration = []
|
|
else:
|
|
wcdurations = []
|
|
wcpower = []
|
|
for record in agerecords:
|
|
wcdurations.append(record.duration)
|
|
wcpower.append(record.power)
|
|
|
|
options['wcpower'] = wcpower
|
|
options['wcdurations'] = wcdurations
|
|
if theuser:
|
|
options['userid'] = theuser
|
|
|
|
request.session['options'] = options
|
|
|
|
|
|
result = request.user.is_authenticated() and ispromember(request.user)
|
|
if result:
|
|
promember=1
|
|
|
|
# get all indoor rows in date range
|
|
|
|
# process form
|
|
if request.method == 'POST' and "daterange" in request.POST:
|
|
dateform = DateRangeForm(request.POST)
|
|
deltaform = DeltaDaysForm(request.POST)
|
|
if dateform.is_valid():
|
|
startdate = dateform.cleaned_data['startdate']
|
|
enddate = dateform.cleaned_data['enddate']
|
|
if startdate > enddate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
elif request.method == 'POST' and "datedelta" in request.POST:
|
|
deltaform = DeltaDaysForm(request.POST)
|
|
if deltaform.is_valid():
|
|
deltadays = deltaform.cleaned_data['deltadays']
|
|
if deltadays:
|
|
enddate = timezone.now()
|
|
startdate = enddate-datetime.timedelta(days=deltadays)
|
|
if startdate > enddate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
dateform = DateRangeForm(initial={
|
|
'startdate': startdate,
|
|
'enddate': enddate,
|
|
})
|
|
else:
|
|
dateform = DateRangeForm()
|
|
deltaform = DeltaDaysForm()
|
|
|
|
else:
|
|
dateform = DateRangeForm(initial={
|
|
'startdate': startdate,
|
|
'enddate': enddate,
|
|
})
|
|
deltaform = DeltaDaysForm()
|
|
|
|
# get all 2k (if any) - this rower, in date range
|
|
try:
|
|
r = getrower(theuser)
|
|
except Rower.DoesNotExist:
|
|
allergworkouts = []
|
|
r=0
|
|
|
|
|
|
try:
|
|
uu = User.objects.get(id=theuser)
|
|
except User.DoesNotExist:
|
|
uu = ''
|
|
|
|
|
|
# test to fix bug
|
|
startdate = datetime.datetime.combine(startdate,datetime.time())
|
|
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
|
enddate = enddate+datetime.timedelta(days=1)
|
|
|
|
|
|
thedistances = []
|
|
theworkouts = []
|
|
thesecs = []
|
|
|
|
|
|
|
|
rankingdistances.sort()
|
|
rankingdurations.sort()
|
|
|
|
for rankingdistance in rankingdistances:
|
|
|
|
workouts = Workout.objects.filter(
|
|
user=r,distance=rankingdistance,
|
|
workouttype__in=['rower','dynamic','slides'],
|
|
rankingpiece=True,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate).order_by('duration')
|
|
if workouts:
|
|
thedistances.append(rankingdistance)
|
|
theworkouts.append(workouts[0])
|
|
|
|
timesecs = 3600*workouts[0].duration.hour
|
|
timesecs += 60*workouts[0].duration.minute
|
|
timesecs += workouts[0].duration.second
|
|
timesecs += 1.e-6*workouts[0].duration.microsecond
|
|
|
|
thesecs.append(timesecs)
|
|
|
|
for rankingduration in rankingdurations:
|
|
|
|
workouts = Workout.objects.filter(
|
|
user=r,duration=rankingduration,
|
|
workouttype='rower',
|
|
rankingpiece=True,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate).order_by('-distance')
|
|
if workouts:
|
|
thedistances.append(workouts[0].distance)
|
|
theworkouts.append(workouts[0])
|
|
|
|
timesecs = 3600*workouts[0].duration.hour
|
|
timesecs += 60*workouts[0].duration.minute
|
|
timesecs += workouts[0].duration.second
|
|
timesecs += 1.e-5*workouts[0].duration.microsecond
|
|
|
|
thesecs.append(timesecs)
|
|
|
|
thedistances = np.array(thedistances)
|
|
thesecs = np.array(thesecs)
|
|
|
|
thevelos = thedistances/thesecs
|
|
theavpower = 2.8*(thevelos**3)
|
|
|
|
|
|
# create interactive plot
|
|
if len(thedistances) !=0 :
|
|
res = interactive_cpchart(
|
|
r,thedistances,thesecs,theavpower,
|
|
theworkouts,promember=promember,
|
|
wcdurations=wcdurations,wcpower=wcpower
|
|
)
|
|
script = res[0]
|
|
div = res[1]
|
|
paulslope = res[2]
|
|
paulintercept = res[3]
|
|
p1 = res[4]
|
|
message = res[5]
|
|
try:
|
|
testcalc = pd.Series(res[6])*3
|
|
except TypeError:
|
|
age = 0
|
|
|
|
else:
|
|
script = ''
|
|
div = '<p>No ranking pieces found.</p>'
|
|
paulslope = 1
|
|
paulintercept = 1
|
|
p1 = [1,1,1,1]
|
|
message = ""
|
|
|
|
|
|
if request.method == 'POST' and "piece" in request.POST:
|
|
form = PredictedPieceForm(request.POST)
|
|
if form.is_valid():
|
|
value = form.cleaned_data['value']
|
|
hourvalue,value = divmod(value,60)
|
|
if hourvalue >= 24:
|
|
hourvalue = 23
|
|
pieceunit = form.cleaned_data['pieceunit']
|
|
if pieceunit == 'd':
|
|
rankingdistances.append(value)
|
|
else:
|
|
rankingdurations.append(datetime.time(minute=int(value),hour=int(hourvalue)))
|
|
else:
|
|
form = PredictedPieceForm()
|
|
|
|
rankingdistances.sort()
|
|
rankingdurations.sort()
|
|
|
|
|
|
predictions = []
|
|
cpredictions = []
|
|
|
|
|
|
for rankingdistance in rankingdistances:
|
|
# Paul's model
|
|
p = paulslope*np.log10(rankingdistance)+paulintercept
|
|
velo = 500./p
|
|
t = rankingdistance/velo
|
|
pwr = 2.8*(velo**3)
|
|
a = {'distance':rankingdistance,
|
|
'duration':timedeltaconv(t),
|
|
'pace':timedeltaconv(p),
|
|
'power':int(pwr)}
|
|
predictions.append(a)
|
|
|
|
# CP model -
|
|
pwr2 = p1[0]/(1+t/p1[2])
|
|
pwr2 += p1[1]/(1+t/p1[3])
|
|
|
|
if pwr2 <= 0:
|
|
pwr2 = 50.
|
|
|
|
velo2 = (pwr2/2.8)**(1./3.)
|
|
|
|
if np.isnan(velo2) or velo2 <= 0:
|
|
velo2 = 1.0
|
|
|
|
t2 = rankingdistance/velo2
|
|
|
|
pwr3 = p1[0]/(1+t2/p1[2])
|
|
pwr3 += p1[1]/(1+t2/p1[3])
|
|
|
|
if pwr3 <= 0:
|
|
pwr3 = 50.
|
|
|
|
velo3 = (pwr3/2.8)**(1./3.)
|
|
if np.isnan(velo3) or velo3 <= 0:
|
|
velo3 = 1.0
|
|
|
|
t3 = rankingdistance/velo3
|
|
p3 = 500./velo3
|
|
|
|
a = {'distance':rankingdistance,
|
|
'duration':timedeltaconv(t3),
|
|
'pace':timedeltaconv(p3),
|
|
'power':int(pwr3)}
|
|
cpredictions.append(a)
|
|
|
|
|
|
|
|
|
|
for rankingduration in rankingdurations:
|
|
t = 3600.*rankingduration.hour
|
|
t += 60.*rankingduration.minute
|
|
t += rankingduration.second
|
|
t += rankingduration.microsecond/1.e6
|
|
|
|
# Paul's model
|
|
ratio = paulintercept/paulslope
|
|
|
|
u = ((2**(2+ratio))*(5.**(3+ratio))*t*np.log(10))/paulslope
|
|
|
|
d = 500*t*np.log(10.)
|
|
d = d/(paulslope*lambertw(u))
|
|
d = d.real
|
|
|
|
velo = d/t
|
|
p = 500./velo
|
|
pwr = 2.8*(velo**3)
|
|
a = {'distance':int(d),
|
|
'duration':timedeltaconv(t),
|
|
'pace':timedeltaconv(p),
|
|
'power':int(pwr)}
|
|
predictions.append(a)
|
|
|
|
# CP model
|
|
pwr = p1[0]/(1+t/p1[2])
|
|
pwr += p1[1]/(1+t/p1[3])
|
|
|
|
if pwr <= 0:
|
|
pwr = 50.
|
|
|
|
velo = (pwr/2.8)**(1./3.)
|
|
|
|
if np.isnan(velo) or velo <=0:
|
|
velo = 1.0
|
|
|
|
d = t*velo
|
|
p = 500./velo
|
|
a = {'distance':int(d),
|
|
'duration':timedeltaconv(t),
|
|
'pace':timedeltaconv(p),
|
|
'power':int(pwr)}
|
|
cpredictions.append(a)
|
|
|
|
if recalc:
|
|
wcdurations = []
|
|
wcpower = []
|
|
durations = [1,4,30,60]
|
|
distances = [100,500,1000,2000,5000,6000,10000,21097,42195]
|
|
|
|
df = pd.DataFrame(
|
|
list(
|
|
C2WorldClassAgePerformance.objects.filter(
|
|
sex=r.sex,
|
|
weightcategory=r.weightcategory
|
|
).values()
|
|
)
|
|
)
|
|
|
|
jsondf = df.to_json()
|
|
|
|
job = myqueue(queue,
|
|
handle_getagegrouprecords,
|
|
jsondf,distances,durations,age,r.sex,r.weightcategory)
|
|
try:
|
|
request.session['async_tasks'] += [(job.id,'agegrouprecords')]
|
|
except KeyError:
|
|
request.session['async_tasks'] = [(job.id,'agegrouprecords')]
|
|
|
|
|
|
|
|
messages.error(request,message)
|
|
return render(request, 'rankings.html',
|
|
{'rankingworkouts':theworkouts,
|
|
'interactiveplot':script,
|
|
'the_div':div,
|
|
'predictions':predictions,
|
|
'cpredictions':cpredictions,
|
|
'nrdata':len(thedistances),
|
|
'form':form,
|
|
'dateform':dateform,
|
|
'deltaform':deltaform,
|
|
'id': theuser,
|
|
'theuser':uu,
|
|
'age':age,
|
|
'sex':r.sex,
|
|
'recalc':recalc,
|
|
'weightcategory':r.weightcategory,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
'teams':get_my_teams(request.user),
|
|
})
|
|
|
|
@user_passes_test(ispromember,login_url="/",redirect_field_name=None)
|
|
def workout_update_cp_view(request,id=0):
|
|
row = get_workout(id)
|
|
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
message = "You are not allowed to edit this workout"
|
|
messages.error(request,message)
|
|
url = reverse(workouts_view)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
row.rankingpiece = True
|
|
row.save()
|
|
|
|
r = getrower(request.user)
|
|
|
|
dataprep.runcpupdate(r)
|
|
|
|
if row.workouttype in ('water','coastal'):
|
|
url = reverse(otwrankings_view)
|
|
else:
|
|
url = reverse(oterankings_view)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
# Show ranking distances including predicted paces
|
|
@user_passes_test(ispromember,login_url="/",redirect_field_name=None)
|
|
def otwrankings_view(request,theuser=0,
|
|
startdate=timezone.now()-datetime.timedelta(days=365),
|
|
enddate=timezone.now(),
|
|
deltadays=-1,
|
|
startdatestring="",
|
|
enddatestring=""):
|
|
|
|
if deltadays>0:
|
|
startdate = enddate-datetime.timedelta(days=int(deltadays))
|
|
|
|
if startdatestring != "":
|
|
try:
|
|
startdate = iso8601.parse_date(startdatestring)
|
|
except ParseError:
|
|
pass
|
|
|
|
if enddatestring != "":
|
|
try:
|
|
enddate = iso8601.parse_date(enddatestring)
|
|
except ParseError:
|
|
pass
|
|
|
|
if 'startdate' in request.session:
|
|
startdate = iso8601.parse_date(request.session['startdate'])
|
|
|
|
|
|
if 'enddate' in request.session:
|
|
enddate = iso8601.parse_date(request.session['enddate'])
|
|
|
|
if enddate < startdate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
|
|
if theuser == 0:
|
|
theuser = request.user.id
|
|
|
|
promember=0
|
|
if not request.user.is_anonymous():
|
|
r = Rower.objects.get(user=request.user)
|
|
result = request.user.is_authenticated() and ispromember(request.user)
|
|
if result:
|
|
promember=1
|
|
|
|
# get all OTW rows in date range
|
|
|
|
# process form
|
|
if request.method == 'POST' and "daterange" in request.POST:
|
|
dateform = DateRangeForm(request.POST)
|
|
deltaform = DeltaDaysForm(request.POST)
|
|
if dateform.is_valid():
|
|
startdate = dateform.cleaned_data['startdate']
|
|
enddate = dateform.cleaned_data['enddate']
|
|
if startdate > enddate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
elif request.method == 'POST' and "datedelta" in request.POST:
|
|
deltaform = DeltaDaysForm(request.POST)
|
|
if deltaform.is_valid():
|
|
deltadays = deltaform.cleaned_data['deltadays']
|
|
if deltadays:
|
|
enddate = timezone.now()
|
|
startdate = enddate-datetime.timedelta(days=deltadays)
|
|
if startdate > enddate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
dateform = DateRangeForm(initial={
|
|
'startdate': startdate,
|
|
'enddate': enddate,
|
|
})
|
|
else:
|
|
dateform = DateRangeForm(initial={
|
|
'startdate': startdate,
|
|
'enddate': enddate,
|
|
})
|
|
deltaform = DeltaDaysForm()
|
|
|
|
else:
|
|
dateform = DateRangeForm(initial={
|
|
'startdate': startdate,
|
|
'enddate': enddate,
|
|
})
|
|
deltaform = DeltaDaysForm()
|
|
|
|
# get all 2k (if any) - this rower, in date range
|
|
try:
|
|
r = Rower.objects.get(user=theuser)
|
|
except Rower.DoesNotExist:
|
|
raise Http404("Rower doesn't exist")
|
|
|
|
|
|
|
|
try:
|
|
uu = User.objects.get(id=theuser)
|
|
except User.DoesNotExist:
|
|
uu = ''
|
|
|
|
|
|
# test to fix bug
|
|
startdate = datetime.datetime.combine(startdate,datetime.time())
|
|
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
|
enddate = enddate+datetime.timedelta(days=1)
|
|
|
|
|
|
|
|
thedistances = []
|
|
theworkouts = []
|
|
thesecs = []
|
|
|
|
theworkouts = Workout.objects.filter(
|
|
user=r,rankingpiece=True,
|
|
workouttype='water',
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate
|
|
).order_by(
|
|
"-startdatetime"
|
|
)
|
|
|
|
|
|
delta,cpvalue,avgpower = dataprep.fetchcp(r,theworkouts)
|
|
|
|
runningjob = 0
|
|
|
|
taskstatus = get_stored_tasks_status(request)
|
|
for task in taskstatus:
|
|
if task['func_name'] == 'updatecpwater':
|
|
if 'success' in task['status'].lower() or 'finished' in task['status'].lower():
|
|
runningjob = 1
|
|
messages.info(request,'CP chart data have been updated')
|
|
remove_asynctask(request,task['id'])
|
|
elif 'fail' in task['status'].lower():
|
|
runningjob = 0
|
|
try:
|
|
remove_asynctask(request,task[id])
|
|
messages.error(request,'Oh, your task failed')
|
|
except KeyError:
|
|
pass
|
|
elif 'started' in task['status'].lower():
|
|
messages.info(request,'Busy updating CP chart data')
|
|
runningjob = 1
|
|
elif 'queued' in task['status'].lower() or 'pending' in task['status'].lower():
|
|
messages.info(request,'Getting ready to update CP chart data')
|
|
runningjob = 1
|
|
|
|
|
|
|
|
if not runningjob:
|
|
job = dataprep.runcpupdate(
|
|
r,type='water',
|
|
startdate=startdate,
|
|
enddate=enddate
|
|
)
|
|
request.session['job_id'] = job.id
|
|
try:
|
|
request.session['async_tasks'] += [(job.id,'updatecpwater')]
|
|
except KeyError:
|
|
request.session['async_tasks'] = [(job.id,'updatecpwater')]
|
|
messages.info(request,'New calculation queued. Page will reload automatically. You can check the status of your calculations <a href="/rowers/jobs-status/" target="_blank">here</a>')
|
|
|
|
powerdf = pd.DataFrame({
|
|
'Delta':delta,
|
|
'CP':cpvalue,
|
|
})
|
|
|
|
if powerdf.empty:
|
|
messages.info(request,'Your calculations are running in the background. Page will reload automatically. You can check the status of your calculations <a href="/rowers/jobs-status/" target="_blank">here</a>')
|
|
|
|
powerdf = powerdf[powerdf['CP']>0]
|
|
powerdf.dropna(axis=0,inplace=True)
|
|
powerdf.sort_values(['Delta','CP'],ascending=[1,0],inplace=True)
|
|
powerdf.drop_duplicates(subset='Delta',keep='first',inplace=True)
|
|
|
|
|
|
rowername = r.user.first_name+" "+r.user.last_name
|
|
# create interactive plot
|
|
if len(powerdf) !=0 :
|
|
res = interactive_otwcpchart(powerdf,promember=promember,rowername=rowername)
|
|
script = res[0]
|
|
div = res[1]
|
|
p1 = res[2]
|
|
ratio = res[3]
|
|
r.p0 = p1[0]
|
|
r.p1 = p1[1]
|
|
r.p2 = p1[2]
|
|
r.p3 = p1[3]
|
|
r.cpratio = ratio
|
|
r.save()
|
|
paulslope = 1
|
|
paulintercept = 1
|
|
message = res[4]
|
|
else:
|
|
script = ''
|
|
div = '<p>No ranking pieces found.</p>'
|
|
paulslope = 1
|
|
paulintercept = 1
|
|
p1 = [1,1,1,1]
|
|
message = ""
|
|
|
|
|
|
if request.method == 'POST' and "piece" in request.POST:
|
|
form = PredictedPieceForm(request.POST)
|
|
clean = form.is_valid()
|
|
value = form.cleaned_data['value']
|
|
hourvalue,value = divmod(value,60)
|
|
hourvalue = int(hourvalue)
|
|
minutevalue = int(value)
|
|
value = int(60*(value-minutevalue))
|
|
if hourvalue >= 24:
|
|
hourvalue = 23
|
|
rankingdurations.append(datetime.time(minute=minutevalue,
|
|
hour=hourvalue,
|
|
second=value))
|
|
else:
|
|
form = PredictedPieceForm()
|
|
|
|
|
|
cpredictions = []
|
|
|
|
for rankingduration in rankingdurations:
|
|
t = 3600.*rankingduration.hour
|
|
t += 60.*rankingduration.minute
|
|
t += rankingduration.second
|
|
t += rankingduration.microsecond/1.e6
|
|
|
|
|
|
# CP model
|
|
pwr = p1[0]/(1+t/p1[2])
|
|
pwr += p1[1]/(1+t/p1[3])
|
|
|
|
|
|
if pwr <= 0:
|
|
pwr = 50.
|
|
|
|
|
|
if not np.isnan(pwr):
|
|
try:
|
|
pwr2 = pwr*ratio
|
|
except:
|
|
pwr2 = pwr
|
|
|
|
a = {
|
|
'duration':timedeltaconv(t),
|
|
'power':int(pwr),
|
|
'upper':int(pwr2)}
|
|
cpredictions.append(a)
|
|
|
|
|
|
del form.fields["pieceunit"]
|
|
|
|
startdatestring = startdate.strftime('%Y-%m-%d')
|
|
enddatestring = enddate.strftime('%Y-%m-%d')
|
|
request.session['startdate'] = startdatestring
|
|
request.session['enddate'] = enddatestring
|
|
|
|
messages.error(request,message)
|
|
return render(request, 'otwrankings.html',
|
|
{'rankingworkouts':theworkouts,
|
|
'interactiveplot':script,
|
|
'the_div':div,
|
|
'cpredictions':cpredictions,
|
|
'avgpower':avgpower,
|
|
'form':form,
|
|
'dateform':dateform,
|
|
'deltaform':deltaform,
|
|
'id': theuser,
|
|
'theuser':uu,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
'teams':get_my_teams(request.user),
|
|
'workouttype':'water',
|
|
})
|
|
|
|
# Show ranking distances including predicted paces
|
|
@user_passes_test(ispromember,login_url="/",redirect_field_name=None)
|
|
def oterankings_view(request,theuser=0,
|
|
startdate=timezone.now()-datetime.timedelta(days=365),
|
|
enddate=timezone.now(),
|
|
deltadays=-1,
|
|
startdatestring="",
|
|
enddatestring=""):
|
|
|
|
if deltadays>0:
|
|
startdate = enddate-datetime.timedelta(days=int(deltadays))
|
|
|
|
if startdatestring != "":
|
|
try:
|
|
startdate = iso8601.parse_date(startdatestring)
|
|
except ParseError:
|
|
pass
|
|
|
|
if enddatestring != "":
|
|
try:
|
|
enddate = iso8601.parse_date(enddatestring)
|
|
except ParseError:
|
|
pass
|
|
|
|
if 'startdate' in request.session:
|
|
startdate = iso8601.parse_date(request.session['startdate'])
|
|
|
|
|
|
if 'enddate' in request.session:
|
|
enddate = iso8601.parse_date(request.session['enddate'])
|
|
|
|
if enddate < startdate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
|
|
if theuser == 0:
|
|
theuser = request.user.id
|
|
|
|
promember=0
|
|
if not request.user.is_anonymous():
|
|
r = Rower.objects.get(user=request.user)
|
|
result = request.user.is_authenticated() and ispromember(request.user)
|
|
if result:
|
|
promember=1
|
|
|
|
# get all OTW rows in date range
|
|
|
|
# process form
|
|
if request.method == 'POST' and "daterange" in request.POST:
|
|
dateform = DateRangeForm(request.POST)
|
|
deltaform = DeltaDaysForm(request.POST)
|
|
if dateform.is_valid():
|
|
startdate = dateform.cleaned_data['startdate']
|
|
enddate = dateform.cleaned_data['enddate']
|
|
if startdate > enddate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
elif request.method == 'POST' and "datedelta" in request.POST:
|
|
deltaform = DeltaDaysForm(request.POST)
|
|
if deltaform.is_valid():
|
|
deltadays = deltaform.cleaned_data['deltadays']
|
|
if deltadays:
|
|
enddate = timezone.now()
|
|
startdate = enddate-datetime.timedelta(days=deltadays)
|
|
if startdate > enddate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
dateform = DateRangeForm(initial={
|
|
'startdate': startdate,
|
|
'enddate': enddate,
|
|
})
|
|
else:
|
|
dateform = DateRangeForm(initial={
|
|
'startdate': startdate,
|
|
'enddate': enddate,
|
|
})
|
|
deltaform = DeltaDaysForm()
|
|
|
|
else:
|
|
dateform = DateRangeForm(initial={
|
|
'startdate': startdate,
|
|
'enddate': enddate,
|
|
})
|
|
deltaform = DeltaDaysForm()
|
|
|
|
# get all 2k (if any) - this rower, in date range
|
|
try:
|
|
r = Rower.objects.get(user=theuser)
|
|
except Rower.DoesNotExist:
|
|
allergworkouts = []
|
|
raise Http404("Rower doesn't exist")
|
|
|
|
|
|
try:
|
|
uu = User.objects.get(id=theuser)
|
|
except User.DoesNotExist:
|
|
uu = ''
|
|
|
|
|
|
# test to fix bug
|
|
startdate = datetime.datetime.combine(startdate,datetime.time())
|
|
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
|
enddate = enddate+datetime.timedelta(days=1)
|
|
|
|
|
|
|
|
thedistances = []
|
|
theworkouts = []
|
|
thesecs = []
|
|
|
|
theworkouts = Workout.objects.filter(
|
|
user=r,rankingpiece=True,
|
|
workouttype__in=[
|
|
'rower',
|
|
'dynamic',
|
|
'slides'
|
|
],
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate
|
|
).order_by("-startdatetime")
|
|
|
|
|
|
delta,cpvalue,avgpower = dataprep.fetchcp(
|
|
r,theworkouts,table='cpergdata'
|
|
)
|
|
|
|
runningjob = 0
|
|
|
|
taskstatus = get_stored_tasks_status(request)
|
|
for task in taskstatus:
|
|
if task['func_name'] == 'updatecp':
|
|
if 'success' in task['status'].lower() or 'finished' in task['status'].lower():
|
|
runningjob = 1
|
|
messages.info(request,'CP chart data have been updated')
|
|
remove_asynctask(request,task['id'])
|
|
elif 'fail' in task['status'].lower():
|
|
runningjob = 0
|
|
try:
|
|
remove_asynctask(request,task[id])
|
|
messages.error(request,'Oh, your task failed')
|
|
except KeyError:
|
|
pass
|
|
elif 'started' in task['status'].lower():
|
|
messages.info(request,'Busy updating CP chart data')
|
|
runningjob = 1
|
|
elif 'queued' in task['status'].lower():
|
|
messages.info(request,'Getting ready to update CP chart data')
|
|
runningjob = 1
|
|
|
|
|
|
if not runningjob:
|
|
job = dataprep.runcpupdate(
|
|
r,type='rower',
|
|
startdate=startdate,
|
|
enddate=enddate
|
|
)
|
|
request.session['job_id'] = job.id
|
|
try:
|
|
request.session['async_tasks'] += [(job.id,'updatecp')]
|
|
except KeyError:
|
|
request.session['async_tasks'] = [(job.id,'updatecp')]
|
|
messages.info(request,'New calculation queued. Page will reload automatically. You can check the status of your calculations <a href="/rowers/jobs-status/" target="_blank">here</a>')
|
|
|
|
powerdf = pd.DataFrame({
|
|
'Delta':delta,
|
|
'CP':cpvalue,
|
|
})
|
|
|
|
if powerdf.empty:
|
|
messages.info(request,'Your calculations are running in the background. Page will reload automatically. You can check the status of your calculations <a href="/rowers/jobs-status/" target="_blank">here</a>')
|
|
|
|
powerdf = powerdf[powerdf['CP']>0]
|
|
powerdf.dropna(axis=0,inplace=True)
|
|
powerdf.sort_values(['Delta','CP'],ascending=[1,0],inplace=True)
|
|
powerdf.drop_duplicates(subset='Delta',keep='first',inplace=True)
|
|
|
|
rowername = r.user.first_name+" "+r.user.last_name
|
|
# create interactive plot
|
|
if len(powerdf) !=0 :
|
|
res = interactive_otwcpchart(powerdf,promember=promember,rowername=rowername)
|
|
script = res[0]
|
|
div = res[1]
|
|
p1 = res[2]
|
|
ratio = res[3]
|
|
r.ep0 = p1[0]
|
|
r.ep1 = p1[1]
|
|
r.ep2 = p1[2]
|
|
r.ep3 = p1[3]
|
|
r.ecpratio = ratio
|
|
r.save()
|
|
paulslope = 1
|
|
paulintercept = 1
|
|
message = res[4]
|
|
else:
|
|
ratio = 1
|
|
script = ''
|
|
div = '<p>No ranking pieces found.</p>'
|
|
paulslope = 1
|
|
paulintercept = 1
|
|
p1 = [1,1,1,1]
|
|
message = ""
|
|
|
|
|
|
if request.method == 'POST' and "piece" in request.POST:
|
|
form = PredictedPieceForm(request.POST)
|
|
clean = form.is_valid()
|
|
value = form.cleaned_data['value']
|
|
hourvalue,tvalue = divmod(value,60)
|
|
hourvalue = int(hourvalue)
|
|
minutevalue = int(tvalue)
|
|
tvalue = int(60*(tvalue-minutevalue))
|
|
if hourvalue >= 24:
|
|
hourvalue = 23
|
|
pieceunit = form.cleaned_data['pieceunit']
|
|
if pieceunit == 'd':
|
|
rankingdistances.append(value)
|
|
else:
|
|
rankingdurations.append(datetime.time(
|
|
minute=minutevalue,
|
|
hour=hourvalue,
|
|
second=tvalue
|
|
))
|
|
else:
|
|
form = PredictedPieceForm()
|
|
|
|
|
|
cpredictions = []
|
|
|
|
|
|
|
|
for rankingduration in rankingdurations:
|
|
t = 3600.*rankingduration.hour
|
|
t += 60.*rankingduration.minute
|
|
t += rankingduration.second
|
|
t += rankingduration.microsecond/1.e6
|
|
|
|
|
|
# CP model
|
|
pwr = p1[0]/(1+t/p1[2])
|
|
pwr += p1[1]/(1+t/p1[3])
|
|
|
|
velo = (pwr/2.8)**(1./3.)
|
|
p = 500./velo
|
|
d = t*velo
|
|
|
|
if pwr <= 0:
|
|
pwr = 50.
|
|
|
|
|
|
if not np.isnan(pwr):
|
|
try:
|
|
pwr2 = pwr*ratio
|
|
except:
|
|
pwr2 = pwr
|
|
|
|
a = {
|
|
'distance':int(d),
|
|
'duration':timedeltaconv(t),
|
|
'power':int(pwr),
|
|
'upper':int(pwr2),
|
|
'pace':timedeltaconv(p)}
|
|
|
|
cpredictions.append(a)
|
|
|
|
|
|
# initiation - get 10 min power, then use Paul's law
|
|
|
|
t_10 = 600.
|
|
power_10 = p1[0]/(1+t_10/p1[2])
|
|
power_10 += p1[1]/(1+t_10/p1[3])
|
|
|
|
velo_10 = (power_10/2.8)**(1./3.)
|
|
pace_10 = 500./velo_10
|
|
distance_10 = t_10*velo_10
|
|
|
|
paulslope = 5.
|
|
|
|
for rankingdistance in rankingdistances:
|
|
|
|
delta = paulslope * np.log(rankingdistance/distance_10)/np.log(2)
|
|
|
|
|
|
p = pace_10+delta
|
|
velo = 500./p
|
|
t = rankingdistance/velo
|
|
|
|
pwr2 = p1[0]/(1+t/p1[2])
|
|
pwr2 += p1[1]/(1+t/p1[3])
|
|
try:
|
|
pwr2 *= ratio
|
|
except UnboundLocalError:
|
|
pass
|
|
|
|
if pwr2 <= 0:
|
|
pwr2 = 50.
|
|
|
|
velo2 = (pwr2/2.8)**(1./3.)
|
|
|
|
if np.isnan(velo2) or velo2 <= 0:
|
|
velo2 = 1.0
|
|
|
|
t2 = rankingdistance/velo2
|
|
|
|
pwr3 = p1[0]/(1+t2/p1[2])
|
|
pwr3 += p1[1]/(1+t2/p1[3])
|
|
pwr3 *= ratio
|
|
|
|
|
|
if pwr3 <= 0:
|
|
pwr3 = 50.
|
|
|
|
velo3 = (pwr3/2.8)**(1./3.)
|
|
if np.isnan(velo3) or velo3 <= 0:
|
|
velo3 = 1.0
|
|
|
|
t3 = rankingdistance/velo3
|
|
p3 = 500./velo3
|
|
|
|
a = {
|
|
'distance':rankingdistance,
|
|
'duration':timedeltaconv(t3),
|
|
'power':'--',
|
|
'upper':int(pwr3),
|
|
'pace':timedeltaconv(p3)}
|
|
|
|
cpredictions.append(a)
|
|
|
|
# del form.fields["pieceunit"]
|
|
|
|
startdatestring = startdate.strftime('%Y-%m-%d')
|
|
enddatestring = enddate.strftime('%Y-%m-%d')
|
|
request.session['startdate'] = startdatestring
|
|
request.session['enddate'] = enddatestring
|
|
|
|
|
|
|
|
messages.error(request,message)
|
|
return render(request, 'oterankings.html',
|
|
{'rankingworkouts':theworkouts,
|
|
'interactiveplot':script,
|
|
'the_div':div,
|
|
'cpredictions':cpredictions,
|
|
'avgpower':avgpower,
|
|
'form':form,
|
|
'dateform':dateform,
|
|
'deltaform':deltaform,
|
|
'id': theuser,
|
|
'theuser':uu,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
'teams':get_my_teams(request.user),
|
|
'workouttype':'rower',
|
|
})
|
|
|
|
# Reload the workout and calculate the summary from the stroke data (lapIDx)
|
|
@login_required()
|
|
def workout_recalcsummary_view(request,id=0):
|
|
row = get_workout(id)
|
|
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
message = "You are not allowed to edit this workout"
|
|
messages.error(request,message)
|
|
url = reverse(workouts_view)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
filename = row.csvfilename
|
|
rowdata = rdata(filename)
|
|
if rowdata:
|
|
row.summary = rowdata.allstats()
|
|
row.save()
|
|
successmessage = "Summary Updated"
|
|
messages.info(request,successmessage)
|
|
url = reverse(workout_edit_view,
|
|
kwargs = {
|
|
'id':int(id),
|
|
})
|
|
else:
|
|
message = "Something went wrong. Could not update summary"
|
|
messages.error(request,message)
|
|
url = reverse(workout_edit_view,
|
|
kwargs = {
|
|
'id':int(id),
|
|
})
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
@login_required()
|
|
def workout_makepublic_view(request,id,
|
|
message='',
|
|
successmessage=''):
|
|
|
|
row = get_workout(id)
|
|
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
message = "You are not allowed to edit this workout"
|
|
messages.error(request,message)
|
|
url = reverse(workouts_view)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
row.privacy = 'visible'
|
|
row.save()
|
|
rr = getrower(request.user)
|
|
|
|
teams = rr.team.all()
|
|
for team in teams:
|
|
row.team.add(team)
|
|
|
|
|
|
message = "Workout set to public. Your followers and team members will see it"
|
|
messages.info(request,message)
|
|
|
|
url = reverse(workout_edit_view,
|
|
kwargs = {
|
|
'id':int(id),
|
|
})
|
|
return HttpResponseRedirect(url)
|
|
|
|
@login_required()
|
|
def workout_setprivate_view(request,id,
|
|
message='',
|
|
successmessage=''):
|
|
row = get_workout(id)
|
|
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
message = "You are not allowed to edit this workout"
|
|
messages.error(request,message)
|
|
url = reverse(workouts_view)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
row.privacy = 'private'
|
|
row.save()
|
|
|
|
for team in row.team.all():
|
|
row.team.remove(team)
|
|
|
|
message = "Workout set to private. Only you will see it"
|
|
messages.info(request,message)
|
|
|
|
url = reverse(workout_edit_view,
|
|
kwargs = {
|
|
'id':int(id),
|
|
})
|
|
return HttpResponseRedirect(url)
|
|
|
|
# Joining workout
|
|
@user_passes_test(ispromember,login_url="/",redirect_field_name=None)
|
|
def workouts_join_view(request):
|
|
promember=0
|
|
if not request.user.is_anonymous():
|
|
r = getrower(request.user)
|
|
result = request.user.is_authenticated() and ispromember(request.user)
|
|
if result:
|
|
promember=1
|
|
|
|
|
|
if request.method == 'POST' and 'workouts' in request.POST:
|
|
form = WorkoutMultipleCompareForm(request.POST)
|
|
paramform = WorkoutJoinParamForm(request.POST)
|
|
if form.is_valid() and paramform.is_valid():
|
|
workout_name = paramform.cleaned_data['workout_name']
|
|
set_private = paramform.cleaned_data['set_private']
|
|
|
|
cd = form.cleaned_data
|
|
workouts = cd['workouts']
|
|
ids = [int(w.id) for w in workouts]
|
|
request.session['ids'] = ids
|
|
|
|
|
|
id,message = dataprep.join_workouts(r,ids,
|
|
title=workout_name,
|
|
setprivate=set_private)
|
|
|
|
if message:
|
|
messages.error(request,message)
|
|
|
|
url = reverse(r.defaultlandingpage,
|
|
kwargs = {
|
|
'id':int(id),
|
|
})
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
else:
|
|
return HttpResponse("form is not valid")
|
|
|
|
else:
|
|
url = reverse(workouts_join_select)
|
|
return HttpResponseRedirect(url)
|
|
|
|
@user_passes_test(ispromember,login_url="/",redirect_field_name=None)
|
|
def workouts_join_select(request,
|
|
startdatestring="",
|
|
enddatestring="",
|
|
message='',
|
|
successmessage='',
|
|
startdate=timezone.now()-datetime.timedelta(days=30),
|
|
enddate=timezone.now()+datetime.timedelta(days=1),
|
|
teamid=0):
|
|
|
|
try:
|
|
r = getrower(request.user)
|
|
except Rower.DoesNotExist:
|
|
raise Http404("Rower doesn't exist")
|
|
|
|
if 'startdate' in request.session:
|
|
startdate = iso8601.parse_date(request.session['startdate'])
|
|
|
|
|
|
if 'enddate' in request.session:
|
|
enddate = iso8601.parse_date(request.session['enddate'])
|
|
|
|
|
|
if 'waterboattype' in request.session:
|
|
waterboattype = request.session['waterboattype']
|
|
else:
|
|
waterboattype = ['1x','2x','2-','4x','4-','8+']
|
|
|
|
|
|
if 'modalities' in request.session:
|
|
modalities = request.session['modalities']
|
|
if len(modalities) > 1:
|
|
modality = 'all'
|
|
else:
|
|
modality = modalities[0]
|
|
else:
|
|
modalities = [m[0] for m in types.workouttypes]
|
|
modality = 'all'
|
|
|
|
if request.method == 'POST' and 'daterange' in request.POST:
|
|
dateform = DateRangeForm(request.POST)
|
|
if dateform.is_valid():
|
|
startdate = dateform.cleaned_data['startdate']
|
|
enddate = dateform.cleaned_data['enddate']
|
|
startdatestring = startdate.strftime('%Y-%m-%d')
|
|
enddatestring = enddate.strftime('%Y-%m-%d')
|
|
request.session['startdate'] = startdatestring
|
|
request.session['enddate'] = enddatestring
|
|
else:
|
|
dateform = DateRangeForm(initial={
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
})
|
|
|
|
|
|
if request.method == 'POST' and 'modality' in request.POST:
|
|
modalityform = TrendFlexModalForm(request.POST)
|
|
if modalityform.is_valid():
|
|
modality = modalityform.cleaned_data['modality']
|
|
waterboattype = modalityform.cleaned_data['waterboattype']
|
|
if modality == 'all':
|
|
modalities = [m[0] for m in types.workouttypes]
|
|
else:
|
|
modalities = [modality]
|
|
|
|
if modality != 'water':
|
|
waterboattype = [b[0] for b in types.boattypes]
|
|
|
|
|
|
request.session['modalities'] = modalities
|
|
request.session['waterboattype'] = waterboattype
|
|
|
|
negtypes = []
|
|
for b in types.boattypes:
|
|
if b[0] not in waterboattype:
|
|
negtypes.append(b[0])
|
|
|
|
startdate = datetime.datetime.combine(startdate,datetime.time())
|
|
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
|
enddate = enddate+datetime.timedelta(days=1)
|
|
|
|
if startdatestring:
|
|
startdate = iso8601.parse_date(startdatestring)
|
|
if enddatestring:
|
|
enddate = iso8601.parse_date(enddatestring)
|
|
|
|
if enddate < startdate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
|
|
try:
|
|
theteam = Team.objects.get(id=teamid)
|
|
except Team.DoesNotExist:
|
|
theteam = 0
|
|
|
|
if r.rowerplan == 'basic' and theteam==0:
|
|
raise PermissionDenied("Access denied")
|
|
|
|
if theteam and (theteam.viewing == 'allmembers' or theteam.manager == request.user):
|
|
workouts = Workout.objects.filter(team=theteam,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate,
|
|
workouttype__in=modalities).order_by("-date", "-starttime").exclude(boattype__in=negtypes)
|
|
elif theteam and theteam.viewing == 'coachonly':
|
|
workouts = Workout.objects.filter(team=theteam,user=r,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate,
|
|
workouttype__in=modalities).order_by("-date","-starttime").exclude(boattype__in=negtypes)
|
|
|
|
|
|
else:
|
|
theteam = None
|
|
workouts = Workout.objects.filter(user=r,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate,
|
|
workouttype__in=modalities).order_by("-date", "-starttime").exclude(boattype__in=negtypes)
|
|
|
|
query = request.GET.get('q')
|
|
if query:
|
|
query_list = query.split()
|
|
workouts = workouts.filter(
|
|
reduce(operator.and_,
|
|
(Q(name__icontains=q) for q in query_list)) |
|
|
reduce(operator.and_,
|
|
(Q(notes__icontains=q) for q in query_list))
|
|
)
|
|
|
|
form = WorkoutMultipleCompareForm()
|
|
form.fields["workouts"].queryset = workouts
|
|
|
|
if theteam:
|
|
theid = theteam.id
|
|
else:
|
|
theid = 0
|
|
|
|
joinparamform = WorkoutJoinParamForm()
|
|
modalityform = TrendFlexModalForm(initial={
|
|
'modality':modality,
|
|
'waterboattype':waterboattype
|
|
})
|
|
|
|
|
|
messages.info(request,successmessage)
|
|
messages.error(request,message)
|
|
|
|
return render(request, 'workout_join_select.html',
|
|
{'workouts': workouts,
|
|
'dateform':dateform,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
'team':theteam,
|
|
'form':form,
|
|
'joinparamform':joinparamform,
|
|
'modalityform':modalityform,
|
|
'teams':get_my_teams(request.user),
|
|
})
|
|
|
|
# Team comparison
|
|
@login_required()
|
|
def team_comparison_select(request,
|
|
startdatestring="",
|
|
enddatestring="",
|
|
message='',
|
|
successmessage='',
|
|
startdate=timezone.now()-datetime.timedelta(days=30),
|
|
enddate=timezone.now()+datetime.timedelta(days=1),
|
|
teamid=0):
|
|
|
|
try:
|
|
r = getrower(request.user)
|
|
except Rower.DoesNotExist:
|
|
raise Http404("Rower doesn't exist")
|
|
|
|
if 'startdate' in request.session:
|
|
startdate = iso8601.parse_date(request.session['startdate'])
|
|
|
|
|
|
if 'enddate' in request.session:
|
|
enddate = iso8601.parse_date(request.session['enddate'])
|
|
|
|
|
|
if 'waterboattype' in request.session:
|
|
waterboattype = request.session['waterboattype']
|
|
else:
|
|
waterboattype = ['1x','2x','2-','4x','4-','8+']
|
|
|
|
|
|
if 'modalities' in request.session:
|
|
modalities = request.session['modalities']
|
|
if len(modalities) > 1:
|
|
modality = 'all'
|
|
else:
|
|
modality = modalities[0]
|
|
else:
|
|
modalities = [m[0] for m in types.workouttypes]
|
|
modality = 'all'
|
|
|
|
if request.method == 'POST' and 'daterange' in request.POST:
|
|
dateform = DateRangeForm(request.POST)
|
|
if dateform.is_valid():
|
|
startdate = dateform.cleaned_data['startdate']
|
|
enddate = dateform.cleaned_data['enddate']
|
|
startdatestring = startdate.strftime('%Y-%m-%d')
|
|
enddatestring = enddate.strftime('%Y-%m-%d')
|
|
request.session['startdate'] = startdatestring
|
|
request.session['enddate'] = enddatestring
|
|
else:
|
|
dateform = DateRangeForm(initial={
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
})
|
|
|
|
|
|
if request.method == 'POST' and 'modality' in request.POST:
|
|
modalityform = TrendFlexModalForm(request.POST)
|
|
if modalityform.is_valid():
|
|
modality = modalityform.cleaned_data['modality']
|
|
waterboattype = modalityform.cleaned_data['waterboattype']
|
|
if modality == 'all':
|
|
modalities = [m[0] for m in types.workouttypes]
|
|
else:
|
|
modalities = [modality]
|
|
|
|
if modality != 'water':
|
|
waterboattype = [b[0] for b in types.boattypes]
|
|
|
|
|
|
request.session['modalities'] = modalities
|
|
request.session['waterboattype'] = waterboattype
|
|
|
|
negtypes = []
|
|
for b in types.boattypes:
|
|
if b[0] not in waterboattype:
|
|
negtypes.append(b[0])
|
|
|
|
startdate = datetime.datetime.combine(startdate,datetime.time())
|
|
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
|
enddate = enddate+datetime.timedelta(days=1)
|
|
|
|
if startdatestring:
|
|
startdate = iso8601.parse_date(startdatestring)
|
|
if enddatestring:
|
|
enddate = iso8601.parse_date(enddatestring)
|
|
|
|
if enddate < startdate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
|
|
try:
|
|
theteam = Team.objects.get(id=teamid)
|
|
except Team.DoesNotExist:
|
|
theteam = 0
|
|
|
|
if r.rowerplan == 'basic' and theteam==0:
|
|
if r.protrialexpires is None or r.protrialexpires<datetime.date.today():
|
|
raise PermissionDenied("Access denied")
|
|
|
|
if theteam and (theteam.viewing == 'allmembers' or theteam.manager == request.user):
|
|
workouts = Workout.objects.filter(team=theteam,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate,
|
|
workouttype__in=modalities).order_by("-date", "-starttime").exclude(boattype__in=negtypes)
|
|
elif theteam and theteam.viewing == 'coachonly':
|
|
workouts = Workout.objects.filter(team=theteam,user=r,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate,
|
|
workouttype__in=modalities).order_by("-date","-starttime").exclude(boattype__in=negtypes)
|
|
|
|
|
|
else:
|
|
theteam = None
|
|
workouts = Workout.objects.filter(user=r,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate,
|
|
workouttype__in=modalities).order_by("-date", "-starttime").exclude(boattype__in=negtypes)
|
|
|
|
query = request.GET.get('q')
|
|
if query:
|
|
query_list = query.split()
|
|
workouts = workouts.filter(
|
|
reduce(operator.and_,
|
|
(Q(name__icontains=q) for q in query_list)) |
|
|
reduce(operator.and_,
|
|
(Q(notes__icontains=q) for q in query_list))
|
|
)
|
|
|
|
form = WorkoutMultipleCompareForm()
|
|
form.fields["workouts"].queryset = workouts
|
|
|
|
if theteam:
|
|
theid = theteam.id
|
|
else:
|
|
theid = 0
|
|
|
|
chartform = ChartParamChoiceForm(initial={'teamid':0})
|
|
modalityform = TrendFlexModalForm(initial={
|
|
'modality':modality,
|
|
'waterboattype':waterboattype
|
|
})
|
|
|
|
|
|
messages.info(request,successmessage)
|
|
messages.error(request,message)
|
|
|
|
return render(request, 'team_compare_select.html',
|
|
{'workouts': workouts,
|
|
'dateform':dateform,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
'team':theteam,
|
|
'form':form,
|
|
'chartform':chartform,
|
|
'modalityform':modalityform,
|
|
'teams':get_my_teams(request.user),
|
|
})
|
|
|
|
|
|
# Team comparison
|
|
@login_required()
|
|
def multi_compare_view(request):
|
|
promember=0
|
|
if not request.user.is_anonymous():
|
|
r = getrower(request.user)
|
|
result = request.user.is_authenticated() and ispromember(request.user)
|
|
if result:
|
|
promember=1
|
|
|
|
|
|
if request.method == 'POST' and 'workouts' in request.POST:
|
|
form = WorkoutMultipleCompareForm(request.POST)
|
|
chartform = ChartParamChoiceForm(request.POST)
|
|
if form.is_valid() and chartform.is_valid():
|
|
cd = form.cleaned_data
|
|
workouts = cd['workouts']
|
|
xparam = chartform.cleaned_data['xparam']
|
|
yparam = chartform.cleaned_data['yparam']
|
|
plottype = chartform.cleaned_data['plottype']
|
|
teamid = chartform.cleaned_data['teamid']
|
|
ids = [int(w.id) for w in workouts]
|
|
request.session['ids'] = ids
|
|
|
|
labeldict = {
|
|
int(w.id): w.__unicode__() for w in workouts
|
|
}
|
|
|
|
res = interactive_multiple_compare_chart(ids,xparam,yparam,
|
|
promember=promember,
|
|
plottype=plottype,
|
|
labeldict=labeldict)
|
|
script = res[0]
|
|
div = res[1]
|
|
errormessage = res[3]
|
|
if errormessage != '':
|
|
messages.error(request,errormessage)
|
|
|
|
return render(request,'multicompare.html',
|
|
{'interactiveplot':script,
|
|
'the_div':div,
|
|
'promember':promember,
|
|
'teamid':teamid,
|
|
'chartform':chartform,
|
|
'teams':get_my_teams(request.user),
|
|
})
|
|
else:
|
|
return HttpResponse("Form is not valid")
|
|
if request.method == 'POST' and 'ids' in request.session:
|
|
chartform = ChartParamChoiceForm(request.POST)
|
|
if chartform.is_valid():
|
|
xparam = chartform.cleaned_data['xparam']
|
|
yparam = chartform.cleaned_data['yparam']
|
|
plottype = chartform.cleaned_data['plottype']
|
|
teamid = chartform.cleaned_data['teamid']
|
|
ids = request.session['ids']
|
|
request.session['ids'] = ids
|
|
workouts = [Workout.objects.get(id=id) for id in ids]
|
|
|
|
labeldict = {
|
|
int(w.id): w.__unicode__() for w in workouts
|
|
}
|
|
|
|
res = interactive_multiple_compare_chart(ids,xparam,yparam,
|
|
promember=promember,
|
|
plottype=plottype,
|
|
labeldict=labeldict)
|
|
script = res[0]
|
|
div = res[1]
|
|
|
|
return render(request,'multicompare.html',
|
|
{'interactiveplot':script,
|
|
'the_div':div,
|
|
'promember':promember,
|
|
'teamid':teamid,
|
|
'chartform':chartform,
|
|
'teams':get_my_teams(request.user),
|
|
})
|
|
|
|
else:
|
|
url = reverse(workouts_view)
|
|
return HttpResponseRedirect(url)
|
|
|
|
# Multi Flex Chart with Grouping
|
|
@user_passes_test(ispromember,login_url="/",redirect_field_name=None)
|
|
def user_multiflex_select(request,
|
|
startdatestring="",
|
|
enddatestring="",
|
|
message='',
|
|
successmessage='',
|
|
startdate=timezone.now()-datetime.timedelta(days=30),
|
|
enddate=timezone.now()+datetime.timedelta(days=1),
|
|
userid=0):
|
|
|
|
if userid == 0:
|
|
user = request.user
|
|
else:
|
|
user = User.objects.get(id=userid)
|
|
|
|
|
|
r = getrower(user)
|
|
|
|
if 'options' in request.session:
|
|
options = request.session['options']
|
|
else:
|
|
options = {}
|
|
|
|
try:
|
|
palette = request.session['palette']
|
|
except KeyError:
|
|
palette = 'monochrome_blue'
|
|
|
|
try:
|
|
includereststrokes = request.session['includereststrokes']
|
|
except KeyError:
|
|
includereststrokes = False
|
|
|
|
try:
|
|
ploterrorbars = request.session['ploterrorbars']
|
|
except:
|
|
ploterrorbars = False
|
|
|
|
|
|
|
|
if 'startdate' in request.session:
|
|
startdate = iso8601.parse_date(request.session['startdate'])
|
|
|
|
|
|
if 'enddate' in request.session:
|
|
enddate = iso8601.parse_date(request.session['enddate'])
|
|
|
|
|
|
if 'waterboattype' in request.session:
|
|
waterboattype = request.session['waterboattype']
|
|
else:
|
|
waterboattype = ['1x','2x','2-','4x','4-','8+']
|
|
|
|
|
|
if 'modalities' in request.session:
|
|
modalities = request.session['modalities']
|
|
if len(modalities) > 1:
|
|
modality = 'all'
|
|
else:
|
|
modality = modalities[0]
|
|
else:
|
|
modalities = [m[0] for m in types.workouttypes]
|
|
modality = 'all'
|
|
|
|
if request.method == 'POST' and 'daterange' in request.POST:
|
|
dateform = DateRangeForm(request.POST)
|
|
if dateform.is_valid():
|
|
startdate = dateform.cleaned_data['startdate']
|
|
enddate = dateform.cleaned_data['enddate']
|
|
startdatestring = startdate.strftime('%Y-%m-%d')
|
|
enddatestring = enddate.strftime('%Y-%m-%d')
|
|
request.session['startdate'] = startdatestring
|
|
request.session['enddate'] = enddatestring
|
|
else:
|
|
dateform = DateRangeForm(initial={
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
})
|
|
|
|
if request.method == 'POST' and 'modality' in request.POST:
|
|
modalityform = TrendFlexModalForm(request.POST)
|
|
if modalityform.is_valid():
|
|
modality = modalityform.cleaned_data['modality']
|
|
waterboattype = modalityform.cleaned_data['waterboattype']
|
|
if modality == 'all':
|
|
modalities = [m[0] for m in types.workouttypes]
|
|
else:
|
|
modalities = [modality]
|
|
|
|
if modality != 'water':
|
|
waterboattype = [b[0] for b in types.boattypes]
|
|
|
|
|
|
request.session['modalities'] = modalities
|
|
request.session['waterboattype'] = waterboattype
|
|
|
|
|
|
startdate = datetime.datetime.combine(startdate,datetime.time())
|
|
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
|
enddate = enddate+datetime.timedelta(days=1)
|
|
|
|
if startdatestring:
|
|
startdate = iso8601.parse_date(startdatestring)
|
|
if enddatestring:
|
|
enddate = iso8601.parse_date(enddatestring)
|
|
|
|
if enddate < startdate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
|
|
|
|
negtypes = []
|
|
for b in types.boattypes:
|
|
if b[0] not in waterboattype:
|
|
negtypes.append(b[0])
|
|
|
|
workouts = Workout.objects.filter(user=r,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate,
|
|
workouttype__in=modalities).order_by("-date", "-starttime").exclude(boattype__in=negtypes)
|
|
|
|
query = request.GET.get('q')
|
|
if query:
|
|
query_list = query.split()
|
|
workouts = workouts.filter(
|
|
reduce(operator.and_,
|
|
(Q(name__icontains=q) for q in query_list)) |
|
|
reduce(operator.and_,
|
|
(Q(notes__icontains=q) for q in query_list))
|
|
)
|
|
|
|
form = WorkoutMultipleCompareForm()
|
|
form.fields["workouts"].queryset = workouts
|
|
|
|
chartform = MultiFlexChoiceForm(initial={
|
|
'palette':palette,
|
|
'ploterrorbars':ploterrorbars,
|
|
'includereststrokes':includereststrokes,
|
|
})
|
|
|
|
modalityform = TrendFlexModalForm(initial={
|
|
'modality':modality,
|
|
'waterboattype':waterboattype
|
|
})
|
|
|
|
messages.info(request,successmessage)
|
|
messages.error(request,message)
|
|
|
|
startdatestring = startdate.strftime('%Y-%m-%d')
|
|
enddatestring = enddate.strftime('%Y-%m-%d')
|
|
request.session['startdate'] = startdatestring
|
|
request.session['enddate'] = enddatestring
|
|
|
|
return render(request, 'user_multiflex_select.html',
|
|
{'workouts': workouts,
|
|
'dateform':dateform,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
'theuser':user,
|
|
'form':form,
|
|
'chartform':chartform,
|
|
'modalityform':modalityform,
|
|
'teams':get_my_teams(request.user),
|
|
})
|
|
|
|
@user_passes_test(ispromember,login_url="/",redirect_field_name=None)
|
|
def multiflex_data(request,userid=0,
|
|
options={
|
|
'includereststrokes':False,
|
|
'ploterrorbars':False,
|
|
'userid':0,
|
|
'palette': 'monochrome_blue',
|
|
'groupby': 'spm',
|
|
'binsize': 1,
|
|
'xparam': 'hr',
|
|
'yparam': 'pace',
|
|
'spmmin': 15,
|
|
'spmmax': 55,
|
|
'workmin': 400,
|
|
'workmax': 1500,
|
|
'ids': [],
|
|
'ploterrorbars':False,
|
|
}):
|
|
|
|
if 'options' in request.session:
|
|
options = request.session['options']
|
|
|
|
try:
|
|
includereststrokes = options['includereststrokes']
|
|
except KeyError:
|
|
includereststrokes = False
|
|
|
|
try:
|
|
ploterrorbars = options['ploterrorbars']
|
|
except KeyError:
|
|
ploterrorbars = False
|
|
|
|
try:
|
|
palette = request.session['palette']
|
|
except KeyError:
|
|
palette = 'monochrome_blue'
|
|
|
|
workstrokesonly = not includereststrokes
|
|
|
|
if userid==0:
|
|
userid = request.user.id
|
|
|
|
palette = options['palette']
|
|
groupby = options['groupby']
|
|
binsize = options['binsize']
|
|
xparam = options['xparam']
|
|
yparam = options['yparam']
|
|
spmmin = options['spmmin']
|
|
spmmax = options['spmmax']
|
|
workmin = options['workmin']
|
|
workmax = options['workmax']
|
|
ids = options['ids']
|
|
|
|
workouts = [Workout.objects.get(id=id) for id in ids]
|
|
labeldict = {
|
|
int(w.id): w.__unicode__() for w in workouts
|
|
}
|
|
|
|
fieldlist,fielddict = dataprep.getstatsfields()
|
|
fieldlist = [xparam,yparam,groupby,
|
|
'workoutid','spm','driveenergy',
|
|
'workoutstate']
|
|
|
|
# prepare data frame
|
|
datadf,extracols = dataprep.read_cols_df_sql(ids,fieldlist)
|
|
|
|
|
|
datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly)
|
|
|
|
|
|
datadf = dataprep.filter_df(datadf,'spm',spmmin,
|
|
largerthan=True)
|
|
datadf = dataprep.filter_df(datadf,'spm',spmmax,
|
|
largerthan=False)
|
|
|
|
datadf = dataprep.filter_df(datadf,'driveenergy',workmin,
|
|
largerthan=True)
|
|
datadf = dataprep.filter_df(datadf,'driveneergy',workmax,
|
|
largerthan=False)
|
|
|
|
|
|
datadf.dropna(axis=0,how='any',inplace=True)
|
|
|
|
|
|
datemapping = {
|
|
w.id:w.date for w in workouts
|
|
}
|
|
|
|
datadf['date'] = datadf['workoutid']
|
|
datadf['date'].replace(datemapping,inplace=True)
|
|
|
|
today = datetime.date.today()
|
|
datadf['days ago'] = map(lambda x : x.days, datadf.date - today)
|
|
|
|
if groupby != 'date':
|
|
try:
|
|
bins = np.arange(datadf[groupby].min()-binsize,
|
|
datadf[groupby].max()+binsize,
|
|
binsize)
|
|
groups = datadf.groupby(pd.cut(datadf[groupby],bins,labels=False))
|
|
except ValueError:
|
|
messages.error(
|
|
request,
|
|
"Unable to compete. Probably not enough data selected"
|
|
)
|
|
url = reverse(user_multiflex_select)
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
bins = np.arange(datadf['days ago'].min()-binsize,
|
|
datadf['days ago'].max()+binsize,
|
|
binsize,
|
|
)
|
|
groups = datadf.groupby(pd.cut(datadf['days ago'], bins,
|
|
labels=False))
|
|
|
|
|
|
xvalues = groups.mean()[xparam]
|
|
yvalues = groups.mean()[yparam]
|
|
xerror = groups.std()[xparam]
|
|
yerror = groups.std()[yparam]
|
|
groupsize = groups.count()[xparam]
|
|
|
|
mask = groupsize <= min([0.01*groupsize.sum(),0.2*groupsize.mean()])
|
|
xvalues.loc[mask] = np.nan
|
|
yvalues.loc[mask] = np.nan
|
|
xerror.loc[mask] = np.nan
|
|
yerror.loc[mask] = np.nan
|
|
groupsize.loc[mask] = np.nan
|
|
|
|
xvalues.dropna(inplace=True)
|
|
yvalues.dropna(inplace=True)
|
|
xerror.dropna(inplace=True)
|
|
yerror.dropna(inplace=True)
|
|
groupsize.dropna(inplace=True)
|
|
|
|
|
|
if len(groupsize) == 0:
|
|
messages.error(request,'No data in selection')
|
|
url = reverse(user_multiflex_select)
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
groupsize = 30.*np.sqrt(groupsize/float(groupsize.max()))
|
|
|
|
df = pd.DataFrame({
|
|
xparam:xvalues,
|
|
yparam:yvalues,
|
|
'x':xvalues,
|
|
'y':yvalues,
|
|
'xerror':xerror,
|
|
'yerror':yerror,
|
|
'groupsize':groupsize,
|
|
})
|
|
|
|
if yparam == 'pace':
|
|
df['y'] = dataprep.paceformatsecs(df['y']/1.0e3)
|
|
|
|
aantal = len(df)
|
|
|
|
if groupby != 'date':
|
|
try:
|
|
df['groupval'] = groups.mean()[groupby]
|
|
df['groupval'].loc[mask] = np.nan
|
|
|
|
groupcols = df['groupval']
|
|
except ValueError:
|
|
df['groupval'] = groups.mean()[groupby].fillna(value=0)
|
|
df['groupval'].loc[mask] = np.nan
|
|
groupcols = df['groupval']
|
|
except KeyError:
|
|
messages.error(request,'Data selection error')
|
|
url = reverse(user_multiflex_select)
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
try:
|
|
dates = groups.min()[groupby]
|
|
dates.loc[mask] = np.nan
|
|
dates.dropna(inplace=True)
|
|
df['groupval'] = [x.strftime("%Y-%m-%d") for x in dates]
|
|
df['groupval'].loc[mask] = np.nan
|
|
groupcols = 100.*np.arange(aantal)/float(aantal)
|
|
except AttributeError:
|
|
df['groupval'] = groups.mean()['days ago'].fillna(value=0)
|
|
groupcols = 100.*np.arange(aantal)/float(aantal)
|
|
|
|
|
|
groupcols = (groupcols-groupcols.min())/(groupcols.max()-groupcols.min())
|
|
if aantal == 1:
|
|
groupcols = np.array([1.])
|
|
|
|
|
|
colors = range_to_color_hex(groupcols,palette=palette)
|
|
|
|
df['color'] = colors
|
|
|
|
clegendx = np.arange(0,1.2,.2)
|
|
legcolors = range_to_color_hex(clegendx,palette=palette)
|
|
if groupby != 'date':
|
|
clegendy = df['groupval'].min()+clegendx*(df['groupval'].max()-df['groupval'].min())
|
|
else:
|
|
clegendy = df.index.min()+clegendx*(df.index.max()-df.index.min())
|
|
|
|
|
|
|
|
colorlegend = zip(range(6),clegendy,legcolors)
|
|
|
|
|
|
if userid == 0:
|
|
extratitle = ''
|
|
else:
|
|
u = User.objects.get(id=userid)
|
|
extratitle = ' '+u.first_name+' '+u.last_name
|
|
|
|
|
|
|
|
script,div = interactive_multiflex(df,xparam,yparam,
|
|
groupby,
|
|
extratitle=extratitle,
|
|
ploterrorbars=ploterrorbars,
|
|
binsize=binsize,
|
|
colorlegend=colorlegend)
|
|
|
|
scripta= script.split('\n')[2:-1]
|
|
script = ''.join(scripta)
|
|
|
|
|
|
return JSONResponse({
|
|
"script":script,
|
|
"div":div,
|
|
})
|
|
|
|
|
|
@user_passes_test(ispromember,login_url="/",redirect_field_name=None)
|
|
def multiflex_view(request,userid=0,
|
|
options={
|
|
'includereststrokes':False,
|
|
'ploterrorbars':False,
|
|
}):
|
|
|
|
if 'options' in request.session:
|
|
options = request.session['options']
|
|
|
|
try:
|
|
includereststrokes = options['includereststrokes']
|
|
except KeyError:
|
|
includereststrokes = False
|
|
|
|
try:
|
|
ploterrorbars = options['ploterrorbars']
|
|
except KeyError:
|
|
ploterrorbars = False
|
|
|
|
try:
|
|
palette = request.session['palette']
|
|
except KeyError:
|
|
palette = 'monochrome_blue'
|
|
|
|
workstrokesonly = not includereststrokes
|
|
|
|
if userid==0:
|
|
userid = request.user.id
|
|
|
|
if request.method == 'POST' and 'workouts' in request.POST:
|
|
form = WorkoutMultipleCompareForm(request.POST)
|
|
chartform = MultiFlexChoiceForm(request.POST)
|
|
if form.is_valid() and chartform.is_valid():
|
|
cd = form.cleaned_data
|
|
workouts = cd['workouts']
|
|
xparam = chartform.cleaned_data['xparam']
|
|
yparam = chartform.cleaned_data['yparam']
|
|
includereststrokes = chartform.cleaned_data['includereststrokes']
|
|
ploterrorbars = chartform.cleaned_data['ploterrorbars']
|
|
|
|
workstrokesonly = not includereststrokes
|
|
palette = chartform.cleaned_data['palette']
|
|
|
|
groupby = chartform.cleaned_data['groupby']
|
|
binsize = chartform.cleaned_data['binsize']
|
|
if binsize <= 0:
|
|
binsize = 1
|
|
if groupby == 'pace':
|
|
binsize *= 1000
|
|
|
|
spmmin = chartform.cleaned_data['spmmin']
|
|
spmmax = chartform.cleaned_data['spmmax']
|
|
workmin = chartform.cleaned_data['workmin']
|
|
workmax = chartform.cleaned_data['workmax']
|
|
|
|
ids = [int(w.id) for w in workouts]
|
|
request.session['ids'] = ids
|
|
|
|
else:
|
|
return HttpResponse("Form is not valid")
|
|
elif request.method == 'POST' and 'ids' in request.session:
|
|
chartform = MultiFlexChoiceForm(request.POST)
|
|
if chartform.is_valid():
|
|
xparam = chartform.cleaned_data['xparam']
|
|
yparam = chartform.cleaned_data['yparam']
|
|
includereststrokes = chartform.cleaned_data['includereststrokes']
|
|
ploterrorbars = chartform.cleaned_data['ploterrorbars']
|
|
request.session['ploterrorbars'] = ploterrorbars
|
|
request.session['includereststrokes'] = includereststrokes
|
|
workstrokesonly = not includereststrokes
|
|
palette = chartform.cleaned_data['palette']
|
|
|
|
groupby = chartform.cleaned_data['groupby']
|
|
binsize = chartform.cleaned_data['binsize']
|
|
if binsize <= 0:
|
|
binsize = 1
|
|
if groupby == 'pace':
|
|
binsize *= 1000.
|
|
|
|
spmmin = chartform.cleaned_data['spmmin']
|
|
spmmax = chartform.cleaned_data['spmmax']
|
|
workmin = chartform.cleaned_data['workmin']
|
|
workmax = chartform.cleaned_data['workmax']
|
|
|
|
ids = request.session['ids']
|
|
request.session['ids'] = ids
|
|
workouts = dataprep.get_workouts(ids,userid)
|
|
if not workouts:
|
|
message = 'Error: Workouts in session storage do not belong to this user.'
|
|
messages.error(request,message)
|
|
url = reverse(user_multiflex_select,
|
|
kwargs={
|
|
'userid':userid,
|
|
}
|
|
)
|
|
return HttpResponseRedirect(url)
|
|
|
|
# workouts = [Workout.objects.get(id=id) for id in ids]
|
|
|
|
|
|
else:
|
|
return HttpResponse("invalid form")
|
|
else:
|
|
url = reverse(user_multiflex_select)
|
|
return HttpResponseRedirect(url)
|
|
|
|
div = get_call()
|
|
|
|
options['includereststrokes'] = includereststrokes
|
|
options['ploterrorbars'] = ploterrorbars
|
|
options['userid'] = userid
|
|
options['palette'] = palette
|
|
options['groupby'] = groupby
|
|
options['binsize'] = binsize
|
|
options['xparam'] = xparam
|
|
options['yparam'] = yparam
|
|
options['spmmin'] = spmmin
|
|
options['spmmax'] = spmmax
|
|
options['workmin'] = workmin
|
|
options['workmax'] = workmax
|
|
options['ids'] = ids
|
|
|
|
|
|
request.session['options'] = options
|
|
|
|
|
|
return render(request,'multiflex.html',
|
|
{'interactiveplot':'',
|
|
'the_div':div,
|
|
'chartform':chartform,
|
|
'userid':userid,
|
|
'teams':get_my_teams(request.user),
|
|
})
|
|
|
|
|
|
# Box plots
|
|
@user_passes_test(ispromember,login_url="/",redirect_field_name=None)
|
|
def user_boxplot_select(request,
|
|
startdatestring="",
|
|
enddatestring="",
|
|
message='',
|
|
successmessage='',
|
|
startdate=timezone.now()-datetime.timedelta(days=30),
|
|
enddate=timezone.now()+datetime.timedelta(days=1),
|
|
userid=0):
|
|
|
|
if userid == 0:
|
|
user = request.user
|
|
else:
|
|
user = User.objects.get(id=userid)
|
|
|
|
|
|
r = getrower(user)
|
|
|
|
if 'startdate' in request.session:
|
|
startdate = iso8601.parse_date(request.session['startdate'])
|
|
|
|
|
|
if 'enddate' in request.session:
|
|
enddate = iso8601.parse_date(request.session['enddate'])
|
|
|
|
|
|
if 'waterboattype' in request.session:
|
|
waterboattype = request.session['waterboattype']
|
|
else:
|
|
waterboattype = ['1x','2x','2-','4x','4-','8+']
|
|
|
|
|
|
if 'modalities' in request.session:
|
|
modalities = request.session['modalities']
|
|
if len(modalities) > 1:
|
|
modality = 'all'
|
|
else:
|
|
modality = modalities[0]
|
|
else:
|
|
modalities = [m[0] for m in types.workouttypes]
|
|
modality = 'all'
|
|
|
|
if request.method == 'POST' and 'daterange' in request.POST:
|
|
dateform = DateRangeForm(request.POST)
|
|
if dateform.is_valid():
|
|
startdate = dateform.cleaned_data['startdate']
|
|
enddate = dateform.cleaned_data['enddate']
|
|
startdatestring = startdate.strftime('%Y-%m-%d')
|
|
enddatestring = enddate.strftime('%Y-%m-%d')
|
|
request.session['startdate'] = startdatestring
|
|
request.session['enddate'] = enddatestring
|
|
else:
|
|
dateform = DateRangeForm(initial={
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
})
|
|
|
|
if request.method == 'POST' and 'modality' in request.POST:
|
|
modalityform = TrendFlexModalForm(request.POST)
|
|
if modalityform.is_valid():
|
|
modality = modalityform.cleaned_data['modality']
|
|
waterboattype = modalityform.cleaned_data['waterboattype']
|
|
if modality == 'all':
|
|
modalities = [m[0] for m in types.workouttypes]
|
|
else:
|
|
modalities = [modality]
|
|
|
|
if modality != 'water':
|
|
waterboattype = [b[0] for b in types.boattypes]
|
|
|
|
|
|
request.session['modalities'] = modalities
|
|
request.session['waterboattype'] = waterboattype
|
|
|
|
startdate = datetime.datetime.combine(startdate,datetime.time())
|
|
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
|
enddate = enddate+datetime.timedelta(days=1)
|
|
|
|
if startdatestring:
|
|
startdate = iso8601.parse_date(startdatestring)
|
|
if enddatestring:
|
|
enddate = iso8601.parse_date(enddatestring)
|
|
|
|
if enddate < startdate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
|
|
negtypes = []
|
|
for b in types.boattypes:
|
|
if b[0] not in waterboattype:
|
|
negtypes.append(b[0])
|
|
|
|
workouts = Workout.objects.filter(user=r,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate,
|
|
workouttype__in=modalities).order_by("-date", "-starttime").exclude(boattype__in=negtypes)
|
|
|
|
query = request.GET.get('q')
|
|
if query:
|
|
query_list = query.split()
|
|
workouts = workouts.filter(
|
|
reduce(operator.and_,
|
|
(Q(name__icontains=q) for q in query_list)) |
|
|
reduce(operator.and_,
|
|
(Q(notes__icontains=q) for q in query_list))
|
|
)
|
|
|
|
form = WorkoutMultipleCompareForm()
|
|
form.fields["workouts"].queryset = workouts
|
|
|
|
chartform = BoxPlotChoiceForm()
|
|
modalityform = TrendFlexModalForm(initial={
|
|
'modality':modality,
|
|
'waterboattype':waterboattype
|
|
})
|
|
|
|
messages.info(request,successmessage)
|
|
messages.error(request,message)
|
|
|
|
startdatestring = startdate.strftime('%Y-%m-%d')
|
|
enddatestring = enddate.strftime('%Y-%m-%d')
|
|
request.session['startdate'] = startdatestring
|
|
request.session['enddate'] = enddatestring
|
|
|
|
return render(request, 'user_boxplot_select.html',
|
|
{'workouts': workouts,
|
|
'dateform':dateform,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
'theuser':user,
|
|
'form':form,
|
|
'chartform':chartform,
|
|
'modalityform':modalityform,
|
|
'teams':get_my_teams(request.user),
|
|
})
|
|
|
|
@user_passes_test(ispromember,login_url="/",redirect_field_name=None)
|
|
def boxplot_view_data(request,userid=0,
|
|
options={
|
|
'includereststrokes':False,
|
|
'spmmin':15,
|
|
'spmmax':55,
|
|
'workmin':0,
|
|
'workmax':1500,
|
|
'ids':[],
|
|
'userid':0,
|
|
'plotfield':'spm',
|
|
}):
|
|
|
|
if 'options' in request.session:
|
|
options = request.session['options']
|
|
|
|
try:
|
|
includereststrokes = options['includereststrokes']
|
|
spmmin = options['spmmin']
|
|
spmmax = options['spmmax']
|
|
workmin = options['workmin']
|
|
workmax = options['workmax']
|
|
ids = options['ids']
|
|
userid = options['userid']
|
|
plotfield = options['plotfield']
|
|
except KeyError:
|
|
includereststrokes = False
|
|
spmmin = 15
|
|
spmmax = 55
|
|
workmin = 0
|
|
workmax = 55
|
|
ids = []
|
|
userid = 0
|
|
plotfield = 'spm'
|
|
|
|
|
|
workstrokesonly = not includereststrokes
|
|
|
|
if userid==0:
|
|
userid = request.user.id
|
|
|
|
workouts = [Workout.objects.get(id=id) for id in ids]
|
|
labeldict = {
|
|
int(w.id): w.__unicode__() for w in workouts
|
|
}
|
|
|
|
|
|
datemapping = {
|
|
w.id:w.date for w in workouts
|
|
}
|
|
|
|
|
|
|
|
fieldlist,fielddict = dataprep.getstatsfields()
|
|
fieldlist = [plotfield,'workoutid','spm','driveenergy',
|
|
'workoutstate']
|
|
|
|
# prepare data frame
|
|
datadf,extracols = dataprep.read_cols_df_sql(ids,fieldlist)
|
|
|
|
datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly)
|
|
|
|
datadf = dataprep.filter_df(datadf,'spm',spmmin,
|
|
largerthan=True)
|
|
datadf = dataprep.filter_df(datadf,'spm',spmmax,
|
|
largerthan=False)
|
|
datadf = dataprep.filter_df(datadf,'driveenergy',workmin,
|
|
largerthan=True)
|
|
datadf = dataprep.filter_df(datadf,'driveneergy',workmax,
|
|
largerthan=False)
|
|
|
|
datadf.dropna(axis=0,how='any',inplace=True)
|
|
|
|
datadf['workoutid'].replace(datemapping,inplace=True)
|
|
datadf.rename(columns={"workoutid":"date"},inplace=True)
|
|
datadf = datadf.sort_values(['date'])
|
|
|
|
if userid == 0:
|
|
extratitle = ''
|
|
else:
|
|
u = User.objects.get(id=userid)
|
|
extratitle = ' '+u.first_name+' '+u.last_name
|
|
|
|
|
|
|
|
script,div = interactive_boxchart(datadf,plotfield,
|
|
extratitle=extratitle)
|
|
|
|
scripta = script.split('\n')[2:-1]
|
|
script = ''.join(scripta)
|
|
|
|
|
|
return JSONResponse({
|
|
"script":script,
|
|
"div":div,
|
|
})
|
|
|
|
@user_passes_test(ispromember,login_url="/",redirect_field_name=None)
|
|
def boxplot_view(request,userid=0,
|
|
options={
|
|
'includereststrokes':False,
|
|
}):
|
|
|
|
if 'options' in request.session:
|
|
options = request.session['options']
|
|
|
|
try:
|
|
includereststrokes = options['includereststrokes']
|
|
except KeyError:
|
|
includereststrokes = False
|
|
options['includereststrokes'] = False
|
|
|
|
workstrokesonly = not includereststrokes
|
|
|
|
if userid==0:
|
|
userid = request.user.id
|
|
|
|
if request.method == 'POST' and 'workouts' in request.POST:
|
|
form = WorkoutMultipleCompareForm(request.POST)
|
|
chartform = BoxPlotChoiceForm(request.POST)
|
|
if form.is_valid() and chartform.is_valid():
|
|
cd = form.cleaned_data
|
|
workouts = cd['workouts']
|
|
plotfield = chartform.cleaned_data['yparam']
|
|
includereststrokes = chartform.cleaned_data['includereststrokes']
|
|
request.session['includereststrokes'] = includereststrokes
|
|
workstrokesonly = not includereststrokes
|
|
|
|
spmmin = chartform.cleaned_data['spmmin']
|
|
spmmax = chartform.cleaned_data['spmmax']
|
|
workmin = chartform.cleaned_data['workmin']
|
|
workmax = chartform.cleaned_data['workmax']
|
|
|
|
ids = [int(w.id) for w in workouts]
|
|
request.session['ids'] = ids
|
|
|
|
else:
|
|
return HttpResponse("Form is not valid")
|
|
elif request.method == 'POST' and 'ids' in request.session:
|
|
chartform = BoxPlotChoiceForm(request.POST)
|
|
if chartform.is_valid():
|
|
plotfield = chartform.cleaned_data['yparam']
|
|
includereststrokes = chartform.cleaned_data['includereststrokes']
|
|
spmmin = chartform.cleaned_data['spmmin']
|
|
spmmax = chartform.cleaned_data['spmmax']
|
|
workmin = chartform.cleaned_data['workmin']
|
|
workmax = chartform.cleaned_data['workmax']
|
|
request.session['includereststrokes'] = includereststrokes
|
|
workstrokesonly = not includereststrokes
|
|
ids = request.session['ids']
|
|
request.session['ids'] = ids
|
|
|
|
|
|
else:
|
|
return HttpResponse("invalid form")
|
|
else:
|
|
url = reverse(user_boxplot_select)
|
|
return HttpResponseRedirect(url)
|
|
|
|
div = get_call()
|
|
|
|
options['spmmin'] = spmmin
|
|
options['spmmax'] = spmmax
|
|
options['workmin'] = workmin
|
|
options['workmax'] = workmax
|
|
options['ids'] = ids
|
|
options['userid'] = userid
|
|
options['plotfield'] = plotfield
|
|
|
|
request.session['options'] = options
|
|
|
|
return render(request,'boxplot.html',
|
|
{'interactiveplot':'',
|
|
'the_div':div,
|
|
'chartform':chartform,
|
|
'userid':userid,
|
|
'teams':get_my_teams(request.user),
|
|
})
|
|
|
|
|
|
# List Courses
|
|
@login_required()
|
|
def courses_view(request):
|
|
r = getrower(request.user)
|
|
|
|
courses = GeoCourse.objects.all().order_by("country","name")
|
|
|
|
# add search processing
|
|
query = request.GET.get('q')
|
|
if query:
|
|
query_list = query.split()
|
|
courses = GeoCourse.objects.filter(
|
|
reduce(operator.and_,
|
|
(Q(name__icontains=q) for q in query_list)) |
|
|
reduce(operator.and_,
|
|
(Q(country__icontains=q) for q in query_list)) |
|
|
reduce(operator.and_,
|
|
(Q(notes__icontains=q) for q in query_list))
|
|
)
|
|
|
|
return render(request,'list_courses.html',
|
|
{'courses':courses,
|
|
'rower':r,
|
|
})
|
|
|
|
|
|
# List Workouts
|
|
@login_required()
|
|
def workouts_view(request,message='',successmessage='',
|
|
startdatestring="",enddatestring="",
|
|
startdate=timezone.now()-datetime.timedelta(days=365),
|
|
enddate=timezone.now()+datetime.timedelta(days=1),
|
|
teamid=0,rankingonly=False,rowerid=0,userid=0):
|
|
|
|
request.session['referer'] = absolute(request)['PATH']
|
|
try:
|
|
if rowerid != 0:
|
|
r = Rower.objects.get(id=rowerid)
|
|
elif userid != 0:
|
|
u = User.objects.get(id=userid)
|
|
r = getrower(u)
|
|
else:
|
|
r = getrower(request.user)
|
|
|
|
except Rower.DoesNotExist:
|
|
raise Http404("Rower doesn't exist")
|
|
|
|
# check if access is allowed
|
|
if not checkaccessuser(request.user,r):
|
|
raise PermissionDenied("Access denied")
|
|
|
|
if request.method == 'POST':
|
|
dateform = DateRangeForm(request.POST)
|
|
if dateform.is_valid():
|
|
startdate = dateform.cleaned_data['startdate']
|
|
enddate = dateform.cleaned_data['enddate']
|
|
startdatestring = None
|
|
enddatestring = None
|
|
else:
|
|
dateform = DateRangeForm(initial={
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
})
|
|
|
|
startdate = datetime.datetime.combine(startdate,datetime.time())
|
|
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
|
enddate = enddate+datetime.timedelta(days=1)
|
|
|
|
if startdatestring:
|
|
startdate = iso8601.parse_date(startdatestring)
|
|
if enddatestring:
|
|
enddate = iso8601.parse_date(enddatestring)
|
|
|
|
if enddate < startdate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
|
|
# start date for the small graph
|
|
activity_startdate = enddate-datetime.timedelta(days=15)
|
|
|
|
try:
|
|
if utc.localize(enddate) > timezone.now():
|
|
activity_enddate = timezone.now()
|
|
activity_startdate = activity_enddate-datetime.timedelta(days=15)
|
|
else:
|
|
activity_enddate = enddate
|
|
except ValueError:
|
|
activity_enddate = enddate
|
|
|
|
if teamid:
|
|
try:
|
|
theteam = Team.objects.get(id=teamid)
|
|
except Team.DoesNotExist:
|
|
raise Http404("Team doesn't exist")
|
|
|
|
if theteam.viewing == 'allmembers' or theteam.manager == request.user:
|
|
workouts = Workout.objects.filter(team=theteam,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate).order_by("-date","-starttime")
|
|
g_workouts = Workout.objects.filter(team=theteam,
|
|
startdatetime__gte=activity_startdate,
|
|
startdatetime__lte=activity_enddate).order_by("-date", "-starttime")
|
|
elif theteam.viewing == 'coachonly':
|
|
workouts = Workout.objects.filter(team=theteam,user=r,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate).order_by("-startdatetime")
|
|
g_workouts = Workout.objects.filter(team=theteam,user=r,
|
|
startdatetime__gte=activity_startdate,
|
|
enddatetime__lte=activity_enddate).order_by("-startdatetime")
|
|
|
|
|
|
else:
|
|
theteam = None
|
|
workouts = Workout.objects.filter(user=r,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate).order_by("-date", "-starttime")
|
|
g_workouts = Workout.objects.filter(user=r,
|
|
startdatetime__gte=activity_startdate,
|
|
startdatetime__lte=activity_enddate).order_by("-startdatetime")
|
|
|
|
if rankingonly:
|
|
workouts = [w for w in workouts if w.rankingpiece]
|
|
|
|
query = request.GET.get('q')
|
|
if query:
|
|
query_list = query.split()
|
|
workouts = workouts.filter(
|
|
reduce(operator.and_,
|
|
(Q(name__icontains=q) for q in query_list)) |
|
|
reduce(operator.and_,
|
|
(Q(notes__icontains=q) for q in query_list))
|
|
)
|
|
|
|
paginator = Paginator(workouts,20) # show 25 workouts per page
|
|
page = request.GET.get('page')
|
|
|
|
try:
|
|
workouts = paginator.page(page)
|
|
except PageNotAnInteger:
|
|
workouts = paginator.page(1)
|
|
except EmptyPage:
|
|
workouts = paginator.page(paginator.num_pages)
|
|
|
|
today = timezone.now()
|
|
announcements = SiteAnnouncement.objects.filter(
|
|
expires__gte=today
|
|
).order_by(
|
|
"-created",
|
|
"-id"
|
|
)
|
|
|
|
if theteam:
|
|
stack='rower'
|
|
else:
|
|
stack='type'
|
|
|
|
script,div = interactive_activitychart(g_workouts,
|
|
activity_startdate,
|
|
activity_enddate,
|
|
stack=stack)
|
|
|
|
messages.info(request,successmessage)
|
|
messages.error(request,message)
|
|
|
|
return render(request, 'list_workouts.html',
|
|
{'workouts': workouts,
|
|
'rower':r,
|
|
'dateform':dateform,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
'announcements':announcements[0:4],
|
|
'team':theteam,
|
|
'rankingonly':rankingonly,
|
|
'teams':get_my_teams(request.user),
|
|
'interactiveplot':script,
|
|
'the_div':div,
|
|
})
|
|
|
|
|
|
# List of workouts to compare a selected workout to
|
|
@user_passes_test(ispromember,login_url="/",redirect_field_name=None)
|
|
def workout_comparison_list(request,id=0,message='',successmessage='',
|
|
startdatestring="",enddatestring="",
|
|
startdate=timezone.now()-datetime.timedelta(days=365),
|
|
enddate=timezone.now()):
|
|
|
|
try:
|
|
r = getrower(request.user)
|
|
u = User.objects.get(id=r.user.id)
|
|
if request.method == 'POST':
|
|
dateform = DateRangeForm(request.POST)
|
|
if dateform.is_valid():
|
|
startdate = dateform.cleaned_data['startdate']
|
|
enddate = dateform.cleaned_data['enddate']
|
|
else:
|
|
dateform = DateRangeForm(initial={
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
})
|
|
|
|
if startdatestring:
|
|
startdate = iso8601.parse_date(startdatestring)
|
|
if enddatestring:
|
|
enddate = iso8601.parse_date(enddatestring)
|
|
|
|
startdate = datetime.datetime.combine(startdate,datetime.time())
|
|
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
|
enddate = enddate+datetime.timedelta(days=1)
|
|
|
|
if enddate < startdate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
|
|
workouts = Workout.objects.filter(user=r,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate).order_by("-date", "-starttime").exclude(id=id)
|
|
|
|
query = request.GET.get('q')
|
|
if query:
|
|
query_list = query.split()
|
|
workouts = workouts.filter(
|
|
reduce(operator.and_,
|
|
(Q(name__icontains=q) for q in query_list)) |
|
|
reduce(operator.and_,
|
|
(Q(notes__icontains=q) for q in query_list))
|
|
)
|
|
|
|
paginator = Paginator(workouts,15) # show 25 workouts per page
|
|
page = request.GET.get('page')
|
|
|
|
try:
|
|
workouts = paginator.page(page)
|
|
except PageNotAnInteger:
|
|
workouts = paginator.page(1)
|
|
except EmptyPage:
|
|
workouts = paginator.page(paginator.num_pages)
|
|
|
|
row = get_workout(id)
|
|
|
|
messages.error(request,message)
|
|
messages.info(request,successmessage)
|
|
|
|
return render(request, 'comparison_list.html',
|
|
{'id':int(id),
|
|
'workout':row,
|
|
'workouts': workouts,
|
|
'last_name':u.last_name,
|
|
'first_name':u.first_name,
|
|
'dateform':dateform,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
'teams':get_my_teams(request.user),
|
|
})
|
|
except Rower.DoesNotExist:
|
|
raise Http404("User has no rower instance")
|
|
|
|
# List of workouts to compare a selected workout to
|
|
@user_passes_test(ispromember,login_url="/",redirect_field_name=None)
|
|
def workout_fusion_list(request,id=0,message='',successmessage='',
|
|
startdatestring="",enddatestring="",
|
|
startdate=timezone.now()-datetime.timedelta(days=365),
|
|
enddate=timezone.now()):
|
|
|
|
try:
|
|
r = getrower(request.user)
|
|
u = User.objects.get(id=r.user.id)
|
|
if request.method == 'POST':
|
|
dateform = DateRangeForm(request.POST)
|
|
if dateform.is_valid():
|
|
startdate = dateform.cleaned_data['startdate']
|
|
enddate = dateform.cleaned_data['enddate']
|
|
else:
|
|
dateform = DateRangeForm(initial={
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
})
|
|
|
|
if startdatestring:
|
|
startdate = iso8601.parse_date(startdatestring)
|
|
if enddatestring:
|
|
enddate = iso8601.parse_date(enddatestring)
|
|
|
|
startdate = datetime.datetime.combine(startdate,datetime.time())
|
|
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
|
enddate = enddate+datetime.timedelta(days=1)
|
|
|
|
if enddate < startdate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
|
|
workouts = Workout.objects.filter(user=r,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate).order_by("-date", "-starttime").exclude(id=id)
|
|
|
|
query = request.GET.get('q')
|
|
if query:
|
|
query_list = query.split()
|
|
workouts = workouts.filter(
|
|
reduce(operator.and_,
|
|
(Q(name__icontains=q) for q in query_list)) |
|
|
reduce(operator.and_,
|
|
(Q(notes__icontains=q) for q in query_list))
|
|
)
|
|
|
|
paginator = Paginator(workouts,15) # show 25 workouts per page
|
|
page = request.GET.get('page')
|
|
|
|
try:
|
|
workouts = paginator.page(page)
|
|
except PageNotAnInteger:
|
|
workouts = paginator.page(1)
|
|
except EmptyPage:
|
|
workouts = paginator.page(paginator.num_pages)
|
|
row = get_workout(id)
|
|
|
|
messages.info(request,successmessage)
|
|
messages.error(request,message)
|
|
|
|
return render(request, 'fusion_list.html',
|
|
{'id':int(id),
|
|
'workout':row,
|
|
'workouts': workouts,
|
|
'last_name':u.last_name,
|
|
'first_name':u.first_name,
|
|
'dateform':dateform,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
'teams':get_my_teams(request.user),
|
|
})
|
|
except Rower.DoesNotExist:
|
|
raise Http404("User has no rower instance")
|
|
|
|
# Basic 'EDIT' view of workout
|
|
def workout_view(request,id=0):
|
|
request.session['referer'] = absolute(request)['PATH']
|
|
|
|
try:
|
|
# check if valid ID exists (workout exists)
|
|
row = Workout.objects.get(id=id)
|
|
comments = WorkoutComment.objects.filter(workout=row)
|
|
|
|
aantalcomments = len(comments)
|
|
|
|
|
|
if row.privacy == 'private':
|
|
raise PermissionDenied("Access denied")
|
|
|
|
g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime")
|
|
for i in g:
|
|
try:
|
|
width,height = Image.open(i.filename).size
|
|
i.width = width
|
|
i.height = height
|
|
i.save()
|
|
except:
|
|
pass
|
|
|
|
r = Rower.objects.get(id=row.user.id)
|
|
u = User.objects.get(id=r.user.id)
|
|
|
|
|
|
# create interactive plot
|
|
res = interactive_chart(id)
|
|
script = res[0]
|
|
div = res[1]
|
|
|
|
# create map
|
|
f1 = row.csvfilename
|
|
u = row.user.user
|
|
r = getrower(u)
|
|
rowdata = rdata(f1)
|
|
hascoordinates = 1
|
|
if rowdata != 0:
|
|
try:
|
|
latitude = rowdata.df[' latitude']
|
|
if not latitude.std():
|
|
hascoordinates = 0
|
|
except KeyError,AttributeError:
|
|
hascoordinates = 0
|
|
|
|
else:
|
|
hascoordinates = 0
|
|
|
|
|
|
if hascoordinates:
|
|
mapscript,mapdiv = leaflet_chart(rowdata.df[' latitude'],
|
|
rowdata.df[' longitude'],
|
|
row.name)
|
|
|
|
|
|
else:
|
|
mapscript = ""
|
|
mapdiv = ""
|
|
|
|
|
|
# render page
|
|
if (len(g)<=3):
|
|
return render(request, 'workout_view.html',
|
|
{'workout':row,
|
|
'graphs1':g[0:3],
|
|
'last_name':u.last_name,
|
|
'first_name':u.first_name,
|
|
'interactiveplot':script,
|
|
'aantalcomments':aantalcomments,
|
|
'mapscript':mapscript,
|
|
'mapdiv':mapdiv,
|
|
'teams':get_my_teams(request.user),
|
|
'the_div':div})
|
|
else:
|
|
return render(request, 'workout_view.html',
|
|
{'workout':row,
|
|
'graphs1':g[0:3],
|
|
'graphs2':g[3:6],
|
|
'last_name':u.last_name,
|
|
'first_name':u.first_name,
|
|
'teams':get_my_teams(request.user),
|
|
'aantalcomments':aantalcomments,
|
|
'mapscript':mapscript,
|
|
'mapdiv':mapdiv,
|
|
'interactiveplot':script,
|
|
'the_div':div})
|
|
|
|
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
# Resets stroke data to raw data (pace)
|
|
@user_passes_test(ispromember,login_url="/",redirect_field_name=None)
|
|
def workout_undo_smoothenpace_view(
|
|
request,id=0,message="",successmessage=""
|
|
):
|
|
row = get_workout(id)
|
|
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
message = "You are not allowed to edit this workout"
|
|
messages.error(request,message)
|
|
url = reverse(workouts_view)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
filename = row.csvfilename
|
|
row = rdata(filename)
|
|
if row == 0:
|
|
return HttpResponse("Error: CSV Data File Not Found")
|
|
|
|
if 'originalvelo' in row.df:
|
|
velo = row.df['originalvelo'].values
|
|
row.df[' Stroke500mPace (sec/500m)'] = 500./velo
|
|
|
|
row.write_csv(filename,gzip=True)
|
|
dataprep.update_strokedata(id,row.df)
|
|
|
|
url = "/rowers/workout/"+str(id)+"/advanced"
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
|
|
# Data smoothing of pace data
|
|
@user_passes_test(ispromember,login_url="/",redirect_field_name=None)
|
|
def workout_smoothenpace_view(request,id=0,message="",successmessage=""):
|
|
row = get_workout(id)
|
|
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
message = "You are not allowed to edit this workout"
|
|
messages.error(request,message)
|
|
url = reverse(workouts_view)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
filename = row.csvfilename
|
|
row = rdata(filename)
|
|
if row == 0:
|
|
return HttpResponse("Error: CSV Data File Not Found")
|
|
|
|
pace = row.df[' Stroke500mPace (sec/500m)'].values
|
|
velo = 500./pace
|
|
|
|
if not 'originalvelo' in row.df:
|
|
row.df['originalvelo'] = velo
|
|
|
|
velo2 = stravastuff.ewmovingaverage(velo,5)
|
|
|
|
pace2 = 500./abs(velo2)
|
|
|
|
row.df[' Stroke500mPace (sec/500m)'] = pace2
|
|
|
|
row.df = row.df.fillna(0)
|
|
|
|
row.write_csv(filename,gzip=True)
|
|
dataprep.update_strokedata(id,row.df)
|
|
|
|
url = "/rowers/workout/"+str(id)+"/advanced"
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
# Process CrewNerd Summary CSV and update summary
|
|
@user_passes_test(ispromember,login_url="/",redirect_field_name=None)
|
|
def workout_crewnerd_summary_view(request,id=0,message="",successmessage=""):
|
|
row = get_workout(id)
|
|
|
|
if request.method == 'POST':
|
|
form = CNsummaryForm(request.POST,request.FILES)
|
|
if form.is_valid():
|
|
f = request.FILES['file']
|
|
res = handle_uploaded_file(f)
|
|
fname = res[1]
|
|
try:
|
|
sumd = summarydata(fname)
|
|
row.summary = sumd.allstats()
|
|
row.save()
|
|
os.remove(fname)
|
|
successmessage = "CrewNerd summary added"
|
|
messages.info(request,successmessage)
|
|
url = reverse(workout_edit_view,
|
|
kwargs = {
|
|
'id':int(id),
|
|
})
|
|
|
|
return HttpResponseRedirect(url)
|
|
except:
|
|
try:
|
|
os.remove(fname)
|
|
except:
|
|
pass
|
|
message = "Something went wrong (workout_crewnerd_summary_view)"
|
|
messages.error(request,message)
|
|
url = reverse(workout_edit_view,
|
|
kwargs = {
|
|
'id':int(id),
|
|
})
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
return render(request,
|
|
"cn_form.html",
|
|
{'form':form,
|
|
'teams':get_my_teams(request.user),
|
|
'id':row.id})
|
|
else:
|
|
form = CNsummaryForm()
|
|
|
|
return render(request,
|
|
"cn_form.html",
|
|
{'form':form,
|
|
'teams':get_my_teams(request.user),
|
|
'id':row.id})
|
|
|
|
# Get weather for given location and date/time
|
|
@user_passes_test(ispromember,login_url="/",redirect_field_name=None)
|
|
def workout_downloadwind_view(request,id=0,
|
|
airportcode=None,
|
|
message="",successmessage=""):
|
|
row = get_workout(id)
|
|
|
|
f1 = row.csvfilename
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
message = "You are not allowed to edit this workout"
|
|
messages.error(request,message)
|
|
url = reverse(workouts_view)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
# create bearing
|
|
rowdata = rdata(f1)
|
|
if rowdata == 0:
|
|
return HttpResponse("Error: CSV Data File Not Found")
|
|
|
|
try:
|
|
bearing = rowdata.df.ix[:,'bearing'].values
|
|
except KeyError:
|
|
rowdata.add_bearing()
|
|
rowdata.write_csv(f1,gzip=True)
|
|
|
|
# get wind
|
|
try:
|
|
avglat = rowdata.df[' latitude'].mean()
|
|
avglon = rowdata.df[' longitude'].mean()
|
|
avgtime = int(rowdata.df['TimeStamp (sec)'].mean()-rowdata.df.ix[0,'TimeStamp (sec)'])
|
|
startdatetime = dateutil.parser.parse("{}, {}".format(row.date,
|
|
row.starttime))
|
|
|
|
starttimeunix = int(arrow.get(row.startdatetime).timestamp)
|
|
#starttimeunix = int(mktime(startdatetime.utctimetuple()))
|
|
avgtime = starttimeunix+avgtime
|
|
winddata = get_wind_data(avglat,avglon,avgtime)
|
|
windspeed = winddata[0]
|
|
windbearing = winddata[1]
|
|
message = winddata[2]
|
|
row.notes += "\n"+message
|
|
row.save()
|
|
rowdata.add_wind(windspeed,windbearing)
|
|
rowdata.write_csv(f1,gzip=True)
|
|
|
|
messages.info(request,message)
|
|
|
|
kwargs = {
|
|
'id':int(id)}
|
|
|
|
url = reverse(workout_wind_view,kwargs=kwargs)
|
|
response = HttpResponseRedirect(url)
|
|
except KeyError:
|
|
message = "No latitude/longitude data"
|
|
messages.error(request,message)
|
|
kwargs = {
|
|
'id':int(id)
|
|
}
|
|
url = reverse(workout_wind_view,kwargs=kwargs)
|
|
response = HttpResponseRedirect(url)
|
|
|
|
|
|
|
|
return response
|
|
|
|
# Get weather for given location and date/time
|
|
@user_passes_test(ispromember,login_url="/",redirect_field_name=None)
|
|
def workout_downloadmetar_view(request,id=0,
|
|
airportcode=None,
|
|
message="",successmessage=""):
|
|
row = get_workout(id)
|
|
|
|
f1 = row.csvfilename
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
message = "You are not allowed to edit this workout"
|
|
messages.error(request,message)
|
|
url = reverse(workouts_view)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
# create bearing
|
|
rowdata = rdata(f1)
|
|
if rowdata == 0:
|
|
return HttpResponse("Error: CSV Data File Not Found")
|
|
|
|
try:
|
|
bearing = rowdata.df.ix[:,'bearing'].values
|
|
except KeyError:
|
|
rowdata.add_bearing()
|
|
rowdata.write_csv(f1,gzip=True)
|
|
|
|
# get wind
|
|
try:
|
|
avglat = rowdata.df[' latitude'].mean()
|
|
avglon = rowdata.df[' longitude'].mean()
|
|
airportcode = get_airport_code(avglat,avglon)[0]
|
|
avgtime = int(rowdata.df['TimeStamp (sec)'].mean()-rowdata.df.ix[0,'TimeStamp (sec)'])
|
|
startdatetime = dateutil.parser.parse("{}, {}".format(row.date,
|
|
row.starttime))
|
|
|
|
starttimeunix = arrow.get(row.startdatetime).timestamp
|
|
#starttimeunix = int(mktime(startdatetime.utctimetuple()))
|
|
avgtime = starttimeunix+avgtime
|
|
winddata = get_metar_data(airportcode,avgtime)
|
|
windspeed = winddata[0]
|
|
windbearing = winddata[1]
|
|
message = winddata[2]
|
|
row.notes += "\n"+message
|
|
row.save()
|
|
rowdata.add_wind(windspeed,windbearing)
|
|
rowdata.write_csv(f1,gzip=True)
|
|
messages.info(request,message)
|
|
|
|
kwargs = {
|
|
'id':int(id)}
|
|
|
|
url = reverse(workout_wind_view,kwargs=kwargs)
|
|
response = HttpResponseRedirect(url)
|
|
except KeyError:
|
|
message = "No latitude/longitude data"
|
|
messages.error(request,message)
|
|
kwargs = {
|
|
'id':int(id)
|
|
}
|
|
url = reverse(workout_wind_view,kwargs=kwargs)
|
|
response = HttpResponseRedirect(url)
|
|
|
|
|
|
|
|
return response
|
|
|
|
|
|
# Show form to update wind data
|
|
@user_passes_test(ispromember,login_url="/",redirect_field_name=None)
|
|
def workout_wind_view(request,id=0,message="",successmessage=""):
|
|
row = get_workout(id)
|
|
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
message = "You are not allowed to edit this workout"
|
|
messages.error(request,message)
|
|
url = reverse(workouts_view)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
|
|
# get data
|
|
f1 = row.csvfilename
|
|
u = row.user.user
|
|
r = getrower(u)
|
|
|
|
# create bearing
|
|
rowdata = rdata(f1)
|
|
if row == 0:
|
|
return HttpResponse("Error: CSV Data File Not Found")
|
|
|
|
|
|
hascoordinates = 1
|
|
try:
|
|
latitude = rowdata.df.ix[:,' latitude']
|
|
except KeyError:
|
|
hascoordinates = 0
|
|
|
|
if hascoordinates and not latitude.std():
|
|
hascoordinates = 0
|
|
|
|
try:
|
|
bearing = rowdata.df.ix[:,'bearing'].values
|
|
except KeyError:
|
|
rowdata.add_bearing()
|
|
rowdata.write_csv(f1,gzip=True)
|
|
|
|
|
|
if hascoordinates:
|
|
avglat = rowdata.df[' latitude'].mean()
|
|
avglon = rowdata.df[' longitude'].mean()
|
|
airportcode,newlat,newlon,airportdistance = get_airport_code(avglat,avglon)
|
|
airportcode = airportcode.upper()
|
|
airportdistance = airportdistance[0]
|
|
else:
|
|
airportcode = 'UNKNOWN'
|
|
airportdistance = 0
|
|
|
|
|
|
if request.method == 'POST':
|
|
# process form
|
|
form = UpdateWindForm(request.POST)
|
|
|
|
if form.is_valid():
|
|
|
|
vwind1 = form.cleaned_data['vwind1']
|
|
vwind2 = form.cleaned_data['vwind2']
|
|
dist1 = form.cleaned_data['dist1']
|
|
dist2 = form.cleaned_data['dist2']
|
|
winddirection1 = form.cleaned_data['winddirection1']
|
|
winddirection2 = form.cleaned_data['winddirection2']
|
|
windunit = form.cleaned_data['windunit']
|
|
|
|
rowdata.update_wind(vwind1,vwind2,
|
|
winddirection1,
|
|
winddirection2,
|
|
dist1,dist2,
|
|
units=windunit)
|
|
|
|
rowdata.write_csv(f1,gzip=True)
|
|
|
|
|
|
else:
|
|
message = "Invalid Form"
|
|
messages.error(request,message)
|
|
kwargs = {
|
|
'id':int(id)
|
|
}
|
|
url = reverse(workout_wind_view,kwargs=kwargs)
|
|
response = HttpResponseRedirect(url)
|
|
|
|
else:
|
|
form = UpdateWindForm()
|
|
|
|
# create interactive plot
|
|
res = interactive_windchart(id,promember=1)
|
|
script = res[0]
|
|
div = res[1]
|
|
|
|
if hascoordinates:
|
|
gmscript,gmdiv = leaflet_chart(
|
|
rowdata.df[' latitude'],
|
|
rowdata.df[' longitude'],
|
|
row.name)
|
|
else:
|
|
gmscript = ""
|
|
gmdiv = "No GPS data available"
|
|
|
|
|
|
messages.info(request,successmessage)
|
|
messages.error(request,message)
|
|
|
|
return render(request,
|
|
'windedit.html',
|
|
{'workout':row,
|
|
'teams':get_my_teams(request.user),
|
|
'interactiveplot':script,
|
|
'form':form,
|
|
'airport':airportcode,
|
|
'airportdistance':airportdistance,
|
|
'the_div':div,
|
|
'gmap':gmscript,
|
|
'gmapdiv':gmdiv})
|
|
|
|
|
|
# Show form to update River stream data (for river dwellers)
|
|
@user_passes_test(ispromember,login_url="/",redirect_field_name=None)
|
|
def workout_stream_view(request,id=0,message="",successmessage=""):
|
|
row = get_workout(id)
|
|
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
message = "You are not allowed to edit this workout"
|
|
messages.error(request,message)
|
|
url = reverse(workouts_view)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
|
|
# create interactive plot
|
|
f1 = row.csvfilename
|
|
u = row.user.user
|
|
r = getrower(u)
|
|
|
|
rowdata = rdata(f1)
|
|
if rowdata == 0:
|
|
return HttpResponse("Error: CSV Data File Not Found")
|
|
|
|
if request.method == 'POST':
|
|
# process form
|
|
form = UpdateStreamForm(request.POST)
|
|
|
|
if form.is_valid():
|
|
|
|
dist1 = form.cleaned_data['dist1']
|
|
dist2 = form.cleaned_data['dist2']
|
|
stream1 = form.cleaned_data['stream1']
|
|
stream2 = form.cleaned_data['stream2']
|
|
streamunit = form.cleaned_data['streamunit']
|
|
|
|
rowdata.update_stream(stream1,stream2,dist1,dist2,
|
|
units=streamunit)
|
|
|
|
rowdata.write_csv(f1,gzip=True)
|
|
|
|
|
|
else:
|
|
message = "Invalid Form"
|
|
messages.error(request,message)
|
|
kwargs = {
|
|
'id':int(id)}
|
|
url = reverse(workout_wind_view,kwargs=kwargs)
|
|
response = HttpResponseRedirect(url)
|
|
|
|
else:
|
|
form = UpdateStreamForm()
|
|
|
|
# create interactive plot
|
|
res = interactive_streamchart(id,promember=1)
|
|
script = res[0]
|
|
div = res[1]
|
|
|
|
messages.info(request,successmessage)
|
|
messages.error(request,message)
|
|
return render(request,
|
|
'streamedit.html',
|
|
{'workout':row,
|
|
'teams':get_my_teams(request.user),
|
|
'interactiveplot':script,
|
|
'form':form,
|
|
'the_div':div})
|
|
|
|
# Form to set average crew weight and boat type, then run power calcs
|
|
@user_passes_test(ispromember, login_url="/",redirect_field_name=None)
|
|
def workout_otwsetpower_view(request,id=0,message="",successmessage=""):
|
|
row = get_workout(id)
|
|
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
message = "You are not allowed to edit this workout"
|
|
messages.error(request,message)
|
|
url = reverse(workouts_view)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
|
|
if request.method == 'POST':
|
|
# process form
|
|
form = AdvancedWorkoutForm(request.POST)
|
|
|
|
if form.is_valid():
|
|
quick_calc = form.cleaned_data['quick_calc']
|
|
boattype = form.cleaned_data['boattype']
|
|
weightvalue = form.cleaned_data['weightvalue']
|
|
row.boattype = boattype
|
|
row.weightvalue = weightvalue
|
|
row.save()
|
|
|
|
|
|
# load row data & create power/wind/bearing columns if not set
|
|
f1 = row.csvfilename
|
|
rowdata = rdata(f1)
|
|
if rowdata == 0:
|
|
return HttpResponse("Error: CSV Data File Not Found")
|
|
try:
|
|
vstream = rowdata.df['vstream']
|
|
except KeyError:
|
|
rowdata.add_stream(0)
|
|
rowdata.write_csv(f1,gzip=True)
|
|
|
|
try:
|
|
bearing = rowdata.df['bearing']
|
|
except KeyError:
|
|
rowdata.add_bearing()
|
|
rowdata.write_csv(f1,gzip=True)
|
|
|
|
try:
|
|
vwind = rowdata.df['vwind']
|
|
except KeyError:
|
|
rowdata.add_wind(0,0)
|
|
rowdata.write_csv(f1,gzip=True)
|
|
|
|
# do power calculation (asynchronous)
|
|
r = row.user
|
|
u = r.user
|
|
|
|
first_name = u.first_name
|
|
last_name = u.last_name
|
|
emailaddress = u.email
|
|
|
|
job = myqueue(queuelow,
|
|
handle_otwsetpower,f1,boattype,
|
|
weightvalue,
|
|
first_name,last_name,emailaddress,id,
|
|
ps=[r.p0,r.p1,r.p2,r.p3],
|
|
ratio=r.cpratio,
|
|
quick_calc = quick_calc,
|
|
)
|
|
|
|
try:
|
|
request.session['async_tasks'] += [(job.id,'otwsetpower')]
|
|
except KeyError:
|
|
request.session['async_tasks'] = [(job.id,'otwsetpower')]
|
|
|
|
successmessage = 'Your calculations have been submitted. You will receive an email when they are done. You can check the status of your calculations <a href="/rowers/jobs-status/" target="_blank">here</a>'
|
|
messages.info(request,successmessage)
|
|
kwargs = {
|
|
'id':int(id)}
|
|
|
|
try:
|
|
url = request.session['referer']
|
|
except KeyError:
|
|
url = reverse(workout_advanced_view,kwargs=kwargs)
|
|
|
|
response = HttpResponseRedirect(url)
|
|
return response
|
|
|
|
else:
|
|
message = "Invalid Form"
|
|
messages.error(request,message)
|
|
kwargs = {
|
|
'id':int(id)}
|
|
url = reverse(workout_otwsetpower_view,kwargs=kwargs)
|
|
response = HttpResponseRedirect(url)
|
|
|
|
else:
|
|
form = AdvancedWorkoutForm(instance=row)
|
|
|
|
messages.error(request,message)
|
|
messages.info(request,successmessage)
|
|
return render(request,
|
|
'otwsetpower.html',
|
|
{'workout':row,
|
|
'teams':get_my_teams(request.user),
|
|
'form':form,
|
|
})
|
|
|
|
@login_required()
|
|
def instroke_view(request,id=0):
|
|
row = get_workout(id)
|
|
|
|
form = WorkoutForm(instance=row)
|
|
g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime")
|
|
# check if user is owner of this workout
|
|
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
message = "You are not allowed to edit this workout"
|
|
messages.error(request,message)
|
|
url = reverse(workouts_view)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
rowdata = rrdata(csvfile=row.csvfilename)
|
|
try:
|
|
instrokemetrics = rowdata.get_instroke_columns()
|
|
except AttributeError:
|
|
instrokemetrics = []
|
|
|
|
|
|
return render(request,
|
|
'instroke.html',
|
|
{'workout':row,
|
|
'teams':get_my_teams(request.user),
|
|
'instrokemetrics':instrokemetrics,
|
|
})
|
|
|
|
# A special Edit page with all the Geeky functionality for the workout
|
|
@login_required()
|
|
def workout_geeky_view(request,id=0,message="",successmessage=""):
|
|
row = get_workout(id)
|
|
|
|
form = WorkoutForm(instance=row)
|
|
g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime")
|
|
rowdata = rrdata(csvfile=row.csvfilename)
|
|
instrokemetrics = rowdata.get_instroke_columns()
|
|
|
|
|
|
for i in g:
|
|
try:
|
|
width,height = Image.open(i.filename).size
|
|
i.width = width
|
|
i.height = height
|
|
i.save()
|
|
except:
|
|
pass
|
|
|
|
# check if user is owner of this workout
|
|
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
message = "You are not allowed to edit this workout"
|
|
messages.error(request,message)
|
|
url = reverse(workouts_view)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
|
|
# create interactive plot
|
|
f1 = row.csvfilename
|
|
u = row.user.user
|
|
r = getrower(u)
|
|
|
|
# create interactive plot
|
|
try:
|
|
res = interactive_chart(id,promember=1)
|
|
script = res[0]
|
|
div = res[1]
|
|
except ValueError:
|
|
pass
|
|
|
|
messages.error(request,message)
|
|
messages.info(request,successmessage)
|
|
|
|
if row.workouttype in ('water','coastal'):
|
|
return render(request,
|
|
'otwgeeky.html',
|
|
{'workout':row,
|
|
'teams':get_my_teams(request.user),
|
|
'interactiveplot':script,
|
|
'instrokemetrics':instrokemetrics,
|
|
'the_div':div})
|
|
else:
|
|
return render(request,
|
|
'advancededit.html',
|
|
{'workout':row,
|
|
'teams':get_my_teams(request.user),
|
|
'interactiveplot':script,
|
|
'the_div':div})
|
|
|
|
# generate instroke chart
|
|
@login_required()
|
|
def instroke_chart(request,id=0,metric=''):
|
|
w = get_workout(id)
|
|
|
|
if (checkworkoutuser(request.user,w)==False):
|
|
message = "You are not allowed to edit this workout"
|
|
messages.error(request,message)
|
|
url = reverse(workouts_view)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
rowdata = rrdata(csvfile=w.csvfilename)
|
|
instrokemetrics = rowdata.get_instroke_columns()
|
|
|
|
if metric in instrokemetrics:
|
|
f1 = w.csvfilename[6:-4]
|
|
timestr = strftime("%Y%m%d-%H%M%S")
|
|
imagename = f1+timestr+'.png'
|
|
fullpathimagename = 'static/plots/'+imagename
|
|
u = w.user.user
|
|
r = getrower(u)
|
|
title = w.name
|
|
fig1 = rowdata.get_plot_instroke(metric)
|
|
canvas = FigureCanvas(fig1)
|
|
canvas.print_figure('static/plots/'+imagename)
|
|
plt.close(fig1)
|
|
fig1.clf()
|
|
gc.collect()
|
|
|
|
try:
|
|
width,height = Image.open(fullpathimagename).size
|
|
except:
|
|
width = 1200
|
|
height = 600
|
|
|
|
imgs = GraphImage.objects.filter(workout=w)
|
|
if len(imgs) < 7:
|
|
i = GraphImage(workout=w,
|
|
creationdatetime=timezone.now(),
|
|
filename=fullpathimagename,
|
|
width=width,height=height)
|
|
|
|
i.save()
|
|
else:
|
|
messages.error(request,'You have reached the maximum number of static images for this workout. Delete an image first')
|
|
|
|
|
|
r = getrower(request.user)
|
|
url = reverse(r.defaultlandingpage,
|
|
kwargs = {
|
|
'id':id,
|
|
})
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
# Cumulative stats page
|
|
@login_required()
|
|
def cumstats(request,theuser=0,
|
|
startdate=timezone.now()-datetime.timedelta(days=30),
|
|
enddate=timezone.now(),
|
|
deltadays=-1,
|
|
startdatestring="",
|
|
enddatestring="",
|
|
plotfield='spm',
|
|
options={
|
|
'includereststrokes':False,
|
|
'workouttypes':['rower','dynamic','slides'],
|
|
'waterboattype':['1x','2x','2-','4x','4-','8+']
|
|
}):
|
|
|
|
if 'options' in request.session:
|
|
options = request.session['options']
|
|
|
|
try:
|
|
workouttypes = options['workouttypes']
|
|
except KeyError:
|
|
workouttypes = ['rower','dynamic','slides']
|
|
|
|
try:
|
|
includereststrokes = options['includereststrokes']
|
|
except KeyError:
|
|
includereststrokes = False
|
|
|
|
|
|
workstrokesonly = not includereststrokes
|
|
|
|
checktypes = ['water','rower','dynamic','slides','skierg',
|
|
'paddle','snow','other','coastal']
|
|
waterboattype = ['1x','2x','2-','4x','4-','8+']
|
|
|
|
if deltadays>0:
|
|
startdate = enddate-datetime.timedelta(days=int(deltadays))
|
|
|
|
if startdatestring != "":
|
|
startdate = iso8601.parse_date(startdatestring)
|
|
|
|
if enddatestring != "":
|
|
enddate = iso8601.parse_date(enddatestring)
|
|
|
|
if enddate < startdate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
|
|
promember=0
|
|
if theuser == 0:
|
|
theuser = request.user.id
|
|
|
|
if not request.user.is_anonymous():
|
|
r = getrower(request.user)
|
|
result = request.user.is_authenticated() and ispromember(request.user)
|
|
if result:
|
|
promember=1
|
|
|
|
if not promember:
|
|
return HttpResponseRedirect("/rowers/promembership/")
|
|
|
|
# get all indoor rows of in date range
|
|
# process form
|
|
if request.method == 'POST' and "daterange" in request.POST:
|
|
form = DateRangeForm(request.POST)
|
|
deltaform = DeltaDaysForm(request.POST)
|
|
optionsform = StatsOptionsForm()
|
|
if form.is_valid():
|
|
startdate = form.cleaned_data['startdate']
|
|
enddate = form.cleaned_data['enddate']
|
|
if startdate > enddate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
elif request.method == 'POST' and "datedelta" in request.POST:
|
|
deltaform = DeltaDaysForm(request.POST)
|
|
if deltaform.is_valid():
|
|
deltadays = deltaform.cleaned_data['deltadays']
|
|
if deltadays != 0 and isinstance(deltadays, Number):
|
|
enddate = timezone.now()
|
|
startdate = enddate-datetime.timedelta(days=deltadays)
|
|
if startdate > enddate:
|
|
s = enddate
|
|
enddate = startdate
|
|
startdate = s
|
|
form = DateRangeForm(initial={
|
|
'startdate': startdate,
|
|
'enddate': enddate,
|
|
})
|
|
optionsform = StatsOptionsForm()
|
|
else:
|
|
form = DateRangeForm()
|
|
optionsform = StatsOptionsForm()
|
|
elif request.method == 'POST' and 'options' in request.POST:
|
|
optionsform = StatsOptionsForm(request.POST)
|
|
if optionsform.is_valid():
|
|
includereststrokes = optionsform.cleaned_data['includereststrokes']
|
|
workstrokesonly = not includereststrokes
|
|
workouttypes = []
|
|
waterboattype = optionsform.cleaned_data['waterboattype']
|
|
for type in checktypes:
|
|
if optionsform.cleaned_data[type]:
|
|
workouttypes.append(type)
|
|
|
|
options = {
|
|
'includereststrokes':includereststrokes,
|
|
'workouttypes':workouttypes,
|
|
'waterboattype':waterboattype,
|
|
}
|
|
request.session['options'] = options
|
|
form = DateRangeForm()
|
|
deltaform = DeltaDaysForm()
|
|
else:
|
|
form = DateRangeForm()
|
|
deltaform = DeltaDaysForm()
|
|
else:
|
|
form = DateRangeForm(initial={
|
|
'startdate': startdate,
|
|
'enddate': enddate,
|
|
})
|
|
deltaform = DeltaDaysForm()
|
|
optionsform = StatsOptionsForm()
|
|
|
|
try:
|
|
r2 = getrower(theuser)
|
|
allergworkouts = Workout.objects.filter(user=r2,
|
|
workouttype__in=workouttypes,
|
|
boattype__in=waterboattype,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate)
|
|
|
|
except Rower.DoesNotExist:
|
|
allergworkouts = []
|
|
r2=0
|
|
|
|
try:
|
|
u = User.objects.get(id=theuser)
|
|
except User.DoesNotExist:
|
|
u = ''
|
|
|
|
ids = [int(workout.id) for workout in allergworkouts]
|
|
|
|
datemapping = {
|
|
w.id:w.date for w in allergworkouts
|
|
}
|
|
|
|
|
|
|
|
fieldlist,fielddict = dataprep.getstatsfields()
|
|
|
|
# prepare data frame
|
|
datadf,extracols = dataprep.read_cols_df_sql(ids,fieldlist)
|
|
|
|
datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly)
|
|
|
|
|
|
if datadf.empty:
|
|
stats = {}
|
|
plotfield = 'spm'
|
|
cordict = {}
|
|
div = "No Data found"
|
|
script = ''
|
|
|
|
response = render(request,
|
|
'cumstats.html',
|
|
{
|
|
'stats':stats,
|
|
'teams':get_my_teams(request.user),
|
|
'options':options,
|
|
'id':theuser,
|
|
'theuser':u,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
'form':form,
|
|
'deltaform':deltaform,
|
|
'optionsform':optionsform,
|
|
'cordict':cordict,
|
|
'plotscript':script,
|
|
'plotdiv':div,
|
|
'plotfield':plotfield,
|
|
})
|
|
|
|
request.session['options'] = options
|
|
|
|
return response
|
|
|
|
|
|
|
|
# Create stats
|
|
stats = {}
|
|
fielddict.pop('workoutstate')
|
|
fielddict.pop('workoutid')
|
|
|
|
for field,verbosename in fielddict.iteritems():
|
|
thedict = {
|
|
'mean':datadf[field].mean(),
|
|
'min': datadf[field].min(),
|
|
'std': datadf[field].std(),
|
|
'max': datadf[field].max(),
|
|
'median': datadf[field].median(),
|
|
'firstq':datadf[field].quantile(q=0.25),
|
|
'thirdq':datadf[field].quantile(q=0.75),
|
|
'verbosename':verbosename,
|
|
}
|
|
stats[field] = thedict
|
|
|
|
# Create a dict with correlation values
|
|
cor = datadf.corr(method='spearman')
|
|
cor.fillna(value=0,inplace=True)
|
|
cordict = {}
|
|
for field1,verbosename in fielddict.iteritems():
|
|
thedict = {}
|
|
for field2,verbosename in fielddict.iteritems():
|
|
try:
|
|
thedict[field2] = cor.ix[field1,field2]
|
|
except KeyError:
|
|
thedict[field2] = 0
|
|
|
|
cordict[field1] = thedict
|
|
|
|
# interactive box plot
|
|
datadf['workoutid'].replace(datemapping,inplace=True)
|
|
datadf.rename(columns={"workoutid":"date"},inplace=True)
|
|
datadf = datadf.sort_values(['date'])
|
|
script,div = interactive_boxchart(datadf,plotfield)
|
|
|
|
# set options form correctly
|
|
initial = {}
|
|
initial['includereststrokes'] = includereststrokes
|
|
initial['waterboattype'] = waterboattype
|
|
|
|
for wtype in checktypes:
|
|
if wtype in workouttypes:
|
|
initial[wtype] = True
|
|
else:
|
|
initial[wtype] = False
|
|
|
|
|
|
optionsform = StatsOptionsForm(initial=initial)
|
|
|
|
response = render(request,
|
|
'cumstats.html',
|
|
{
|
|
'stats':stats,
|
|
'teams':get_my_teams(request.user),
|
|
'options':options,
|
|
'id':theuser,
|
|
'theuser':u,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
'form':form,
|
|
'deltaform':deltaform,
|
|
'optionsform':optionsform,
|
|
'cordict':cordict,
|
|
'plotscript':script,
|
|
'plotdiv':div,
|
|
'plotfield':plotfield,
|
|
})
|
|
|
|
request.session['options'] = options
|
|
|
|
return response
|
|
|
|
# Stats page
|
|
@login_required()
|
|
def workout_stats_view(request,id=0,message="",successmessage=""):
|
|
|
|
r = getrower(request.user)
|
|
w = get_workout(id)
|
|
|
|
workstrokesonly = True
|
|
if request.method == 'POST' and 'workstrokesonly' in request.POST:
|
|
workstrokesonly = str2bool(request.POST['workstrokesonly'])
|
|
|
|
|
|
# prepare data frame
|
|
datadf,row = dataprep.getrowdata_db(id=id)
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
message = "You are not allowed to see the stats of this workout"
|
|
messages.error(request,message)
|
|
url = reverse(workouts_view)
|
|
return HttpResponseRedirect(url)
|
|
|
|
datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly)
|
|
|
|
if datadf.empty:
|
|
datadf,row = dataprep.getrowdata_db(id=id)
|
|
datadf = dataprep.clean_df_stats(datadf,workstrokesonly=False)
|
|
workstrokesonly=False
|
|
if datadf.empty:
|
|
return HttpResponse("CSV data file not found")
|
|
|
|
#datadf['deltat'] = datadf['time'].diff()
|
|
|
|
|
|
workoutstateswork = [1,4,5,8,9,6,7]
|
|
workoutstatesrest = [3]
|
|
workoutstatetransition = [0,2,10,11,12,13]
|
|
|
|
|
|
# Create stats
|
|
stats = {}
|
|
|
|
fieldlist,fielddict = dataprep.getstatsfields()
|
|
fielddict.pop('workoutstate')
|
|
fielddict.pop('workoutid')
|
|
|
|
for field,verbosename in fielddict.iteritems():
|
|
thedict = {
|
|
'mean':datadf[field].mean(),
|
|
'wmean': wavg(datadf, field, 'deltat'),
|
|
'min': datadf[field].min(),
|
|
'std': datadf[field].std(),
|
|
'max': datadf[field].max(),
|
|
'median': datadf[field].median(),
|
|
'firstq':datadf[field].quantile(q=0.25),
|
|
'thirdq':datadf[field].quantile(q=0.75),
|
|
'verbosename':verbosename,
|
|
}
|
|
stats[field] = thedict
|
|
|
|
# Create a dict with correlation values
|
|
cor = datadf.corr(method='spearman')
|
|
cor.fillna(value=0,inplace=True)
|
|
cordict = {}
|
|
for field1,verbosename in fielddict.iteritems():
|
|
thedict = {}
|
|
for field2,verbosename in fielddict.iteritems():
|
|
try:
|
|
thedict[field2] = cor.ix[field1,field2]
|
|
except KeyError:
|
|
thedict[field2] = 0
|
|
|
|
cordict[field1] = thedict
|
|
|
|
# additional non-automated stats
|
|
otherstats = {}
|
|
|
|
# Normalized power & TSS
|
|
tss,normp = dataprep.workout_rscore(w)
|
|
|
|
|
|
if not np.isnan(tss) and tss != 0:
|
|
otherstats['tss'] = {
|
|
'verbose_name':'rScore',
|
|
'value':int(tss),
|
|
'unit':''
|
|
}
|
|
|
|
if not np.isnan(normp):
|
|
otherstats['np'] = {
|
|
'verbose_name':'rPower',
|
|
'value':int(10*normp)/10.,
|
|
'unit':'Watt'
|
|
}
|
|
|
|
# HR Drift
|
|
tmax = datadf['time'].max()
|
|
tmin = datadf['time'].min()
|
|
thalf = tmin+0.5*(tmax-tmin)
|
|
mask1 = datadf['time'] < thalf
|
|
mask2 = datadf['time'] > thalf
|
|
|
|
hr1 = datadf.loc[mask1,'hr'].mean()
|
|
hr2 = datadf.loc[mask2,'hr'].mean()
|
|
|
|
pwr1 = datadf.loc[mask1,'power'].mean()
|
|
pwr2 = datadf.loc[mask2,'power'].mean()
|
|
|
|
try:
|
|
hrdrift = ((pwr1/hr1)-(pwr2/hr2))/(pwr1/hr1)
|
|
hrdrift *= 100.
|
|
if not np.isnan(hrdrift):
|
|
hrdrift = int(100*hrdrift)/100.
|
|
otherstats['hrdrift'] = {
|
|
'verbose_name': 'Heart Rate Drift',
|
|
'value': hrdrift,
|
|
'unit': '%',
|
|
}
|
|
except ZeroDivisionError,ValueError:
|
|
pass
|
|
|
|
# TRIMP
|
|
trimp = dataprep.workout_trimp(w)
|
|
|
|
otherstats['trimp'] = {
|
|
'verbose_name': 'TRIMP',
|
|
'value': trimp,
|
|
'unit': ''
|
|
}
|
|
|
|
return render(request,
|
|
'workoutstats.html',
|
|
{
|
|
'stats':stats,
|
|
'teams':get_my_teams(request.user),
|
|
'workout':row,
|
|
'workstrokesonly':workstrokesonly,
|
|
'cordict':cordict,
|
|
'otherstats':otherstats,
|
|
})
|
|
|
|
# The Advanced edit page
|
|
@login_required()
|
|
def workout_advanced_view(request,id=0,message="",successmessage=""):
|
|
row = get_workout(id)
|
|
|
|
form = WorkoutForm(instance=row)
|
|
g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime")
|
|
for i in g:
|
|
try:
|
|
width,height = Image.open(i.filename).size
|
|
i.width = width
|
|
i.height = height
|
|
i.save()
|
|
except:
|
|
pass
|
|
|
|
# check if user is owner of this workout
|
|
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
message = "You are not allowed to edit this workout"
|
|
messages.error(request,message)
|
|
url = reverse(workouts_view)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
|
|
# create interactive plot
|
|
f1 = row.csvfilename
|
|
u = row.user.user
|
|
r = getrower(u)
|
|
|
|
# create interactive plot
|
|
try:
|
|
res = interactive_chart(id,promember=1)
|
|
script = res[0]
|
|
div = res[1]
|
|
except ValueError:
|
|
pass
|
|
|
|
messages.error(request,message)
|
|
messages.info(request,successmessage)
|
|
|
|
|
|
if row.workouttype in ('water','coastal'):
|
|
return render(request,
|
|
'advancedotw.html',
|
|
{'workout':row,
|
|
'teams':get_my_teams(request.user),
|
|
'interactiveplot':script,
|
|
'the_div':div})
|
|
else:
|
|
return render(request,
|
|
'advancededit.html',
|
|
{'workout':row,
|
|
'teams':get_my_teams(request.user),
|
|
'interactiveplot':script,
|
|
'the_div':div})
|
|
|
|
# The interactive plot comparing two workouts (obsolete version)
|
|
def workout_comparison_view(request,id1=0,id2=0,xparam='distance',yparam='spm'):
|
|
promember=0
|
|
if not request.user.is_anonymous():
|
|
r = getrower(request.user)
|
|
result = request.user.is_authenticated() and ispromember(request.user)
|
|
if result:
|
|
promember=1
|
|
|
|
# create interactive plot
|
|
res = interactive_comparison_chart(id1,id2,xparam=xparam,yparam=yparam,
|
|
promember=promember)
|
|
script = res[0]
|
|
div = res[1]
|
|
|
|
axchoicesbasic = {ax[0]:ax[1] for ax in axes if ax[4]=='basic'}
|
|
axchoicespro = {ax[0]:ax[1] for ax in axes if ax[4]=='pro'}
|
|
noylist = ["time","distance"]
|
|
axchoicesbasic.pop("cumdist")
|
|
|
|
return render(request,
|
|
'comparisonchart.html',
|
|
{'interactiveplot':script,
|
|
'the_div':div,
|
|
'teams':get_my_teams(request.user),
|
|
'id1':id1,
|
|
'id2':id2,
|
|
'axchoicesbasic':axchoicesbasic,
|
|
'axchoicespro':axchoicespro,
|
|
'noylist':noylist,
|
|
'xparam':xparam,
|
|
'yparam':yparam,
|
|
})
|
|
|
|
# Updated version of comparison plot
|
|
def workout_comparison_view2(request,id1=0,id2=0,xparam='distance',
|
|
yparam='spm',plottype='line'):
|
|
promember=0
|
|
if not request.user.is_anonymous():
|
|
r = getrower(request.user)
|
|
result = request.user.is_authenticated() and ispromember(request.user)
|
|
if result:
|
|
promember=1
|
|
|
|
if not Workout.objects.filter(id=id1).exists() or not Workout.objects.filter(id=id2).exists():
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
# create interactive plot
|
|
res = interactive_comparison_chart(id1,id2,xparam=xparam,yparam=yparam,
|
|
promember=promember,plottype=plottype)
|
|
script = res[0]
|
|
div = res[1]
|
|
|
|
axchoicesbasic = {ax[0]:ax[1] for ax in axes if ax[4]=='basic'}
|
|
axchoicespro = {ax[0]:ax[1] for ax in axes if ax[4]=='pro'}
|
|
noylist = ["time","distance"]
|
|
axchoicesbasic.pop("cumdist")
|
|
|
|
row1 = Workout.objects.get(id=id1)
|
|
row2 = Workout.objects.get(id=id2)
|
|
|
|
mayedit = 0
|
|
if request.user == row1.user.user:
|
|
mayedit=1
|
|
if checkworkoutuser(request.user,row1):
|
|
mayedit=1
|
|
|
|
if row1.workouttype != 'water' or row2.workouttype != 'water':
|
|
axchoicespro.pop('slip')
|
|
axchoicespro.pop('wash')
|
|
axchoicespro.pop('catch')
|
|
axchoicespro.pop('finish')
|
|
axchoicespro.pop('totalangle')
|
|
axchoicespro.pop('effectiveangle')
|
|
axchoicespro.pop('peakforceangle')
|
|
|
|
|
|
return render(request,
|
|
'comparisonchart2.html',
|
|
{'interactiveplot':script,
|
|
'the_div':div,
|
|
'teams':get_my_teams(request.user),
|
|
'id1':id1,
|
|
'id2':id2,
|
|
'mayedit':mayedit,
|
|
'axchoicesbasic':axchoicesbasic,
|
|
'axchoicespro':axchoicespro,
|
|
'noylist':noylist,
|
|
'xparam':xparam,
|
|
'yparam':yparam,
|
|
'plottype':plottype,
|
|
'promember':promember,
|
|
})
|
|
|
|
# Change default landing page
|
|
@login_required()
|
|
def workflow_default_view(request):
|
|
r = getrower(request.user)
|
|
if r.defaultlandingpage == 'workout_edit_view':
|
|
r.defaultlandingpage = 'workout_workflow_view'
|
|
else:
|
|
r.defaultlandingpage = 'workout_edit_view'
|
|
|
|
r.save()
|
|
|
|
url = reverse(workout_workflow_config2_view)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
|
|
# Workflow Configuration
|
|
@login_required()
|
|
def workout_workflow_config_view(request):
|
|
request.session['referer'] = absolute(request)['PATH']
|
|
request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE
|
|
try:
|
|
workoutid = request.session['lastworkout']
|
|
except KeyError:
|
|
workoutid = 0
|
|
|
|
|
|
r = getrower(request.user)
|
|
|
|
if request.method == 'POST' and 'leftpanel' in request.POST:
|
|
formleft = WorkFlowLeftPanelForm(request.POST)
|
|
if formleft.is_valid():
|
|
leftpanel = formleft.cleaned_data['leftpanel']
|
|
r.workflowleftpanel = leftpanel
|
|
r.save()
|
|
if request.method == 'POST' and 'middlepanel' in request.POST:
|
|
formmiddle = WorkFlowMiddlePanelForm(request.POST)
|
|
if formmiddle.is_valid():
|
|
middlepanel = formmiddle.cleaned_data['middlepanel']
|
|
r.workflowmiddlepanel = middlepanel
|
|
r.save()
|
|
|
|
|
|
formleft = WorkFlowLeftPanelForm(instance=r)
|
|
formmiddle = WorkFlowMiddlePanelForm(instance=r)
|
|
|
|
tmplt = 'workflowconfig.html'
|
|
|
|
|
|
return render(request,tmplt,
|
|
{
|
|
'rower':r,
|
|
'formleft':formleft,
|
|
'formmiddle':formmiddle,
|
|
'workoutid': workoutid,
|
|
})
|
|
|
|
@login_required()
|
|
def workout_workflow_config2_view(request):
|
|
request.session['referer'] = absolute(request)['PATH']
|
|
request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE
|
|
try:
|
|
workoutid = request.session['lastworkout']
|
|
except KeyError:
|
|
workoutid = 0
|
|
|
|
|
|
r = getrower(request.user)
|
|
|
|
MiddlePanelFormSet = formset_factory(WorkFlowMiddlePanelElement,extra=1)
|
|
LeftPanelFormSet = formset_factory(WorkFlowLeftPanelElement,extra=1)
|
|
|
|
|
|
if request.method == 'POST':
|
|
wasmiddle = [1 for key,value in request.POST.items() if 'middlepanel' in key.lower()]
|
|
wasleft = [1 for key,valye in request.POST.items() if 'leftpanel' in key.lower()]
|
|
if wasmiddle:
|
|
middlepanel_formset = MiddlePanelFormSet(request.POST,
|
|
prefix='middlepanel')
|
|
newmiddlepanel = []
|
|
if middlepanel_formset.is_valid():
|
|
for form in middlepanel_formset:
|
|
value = form.cleaned_data.get('panel')
|
|
if value != 'None':
|
|
newmiddlepanel.append(value)
|
|
|
|
|
|
newmiddlepanel = [i for i in newmiddlepanel if i != None]
|
|
r.workflowmiddlepanel = newmiddlepanel
|
|
try:
|
|
r.save()
|
|
except IntegrityError:
|
|
messages.error(request,'Something went wrong')
|
|
if wasleft:
|
|
leftpanel_formset = LeftPanelFormSet(request.POST,
|
|
prefix='leftpanel')
|
|
newleftpanel = []
|
|
if leftpanel_formset.is_valid():
|
|
for form in leftpanel_formset:
|
|
value = form.cleaned_data.get('panel')
|
|
if value != 'None':
|
|
newleftpanel.append(value)
|
|
|
|
|
|
newleftpanel = [i for i in newleftpanel if i != None]
|
|
r.workflowleftpanel = newleftpanel
|
|
try:
|
|
r.save()
|
|
except IntegrityError:
|
|
messages.error(request,'Something went wrong')
|
|
|
|
leftpanelform_data = [{'panel':panel}
|
|
for panel in r.workflowleftpanel]
|
|
|
|
|
|
middlepanelform_data = [{'panel':panel}
|
|
for panel in r.workflowmiddlepanel]
|
|
|
|
leftpanel_formset = LeftPanelFormSet(initial=leftpanelform_data,
|
|
prefix='leftpanel')
|
|
middlepanel_formset = MiddlePanelFormSet(initial=middlepanelform_data,
|
|
prefix='middlepanel')
|
|
|
|
|
|
tmplt = 'workflowconfig2.html'
|
|
|
|
return render(request,tmplt,
|
|
{
|
|
'rower':getrower(request.user),
|
|
'leftpanel_formset':leftpanel_formset,
|
|
'middlepanel_formset':middlepanel_formset,
|
|
'workoutid': workoutid,
|
|
})
|
|
|
|
|
|
# Workflow View
|
|
@login_required()
|
|
def workout_workflow_view(request,id):
|
|
request.session['referer'] = absolute(request)['PATH']
|
|
request.session['lastworkout'] = id
|
|
request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE
|
|
row = get_workout_permitted(request.user,id)
|
|
|
|
r = getrower(request.user)
|
|
result = request.user.is_authenticated() and ispromember(request.user)
|
|
if result:
|
|
promember=1
|
|
if request.user == row.user.user:
|
|
mayedit=1
|
|
|
|
comments = WorkoutComment.objects.filter(workout=row)
|
|
|
|
aantalcomments = len(comments)
|
|
|
|
workouttype = 'ote'
|
|
if row.workouttype in ('water','coastal'):
|
|
workouttype = 'otw'
|
|
|
|
try:
|
|
favorites = FavoriteChart.objects.filter(user=r,
|
|
workouttype__in=[workouttype,'both']).order_by("id")
|
|
maxfav = len(favorites)-1
|
|
except:
|
|
favorites = None
|
|
maxfav = 0
|
|
|
|
charts = get_call()
|
|
|
|
|
|
if 'panel_map.html' in r.workflowmiddlepanel and rowhascoordinates(row):
|
|
rowdata = rdata(row.csvfilename)
|
|
mapscript,mapdiv = leaflet_chart2(rowdata.df[' latitude'],
|
|
rowdata.df[' longitude'],
|
|
row.name)
|
|
else:
|
|
mapscript = ''
|
|
mapdiv = ''
|
|
|
|
|
|
|
|
statcharts = GraphImage.objects.filter(workout=row)
|
|
|
|
|
|
middleTemplates = []
|
|
for t in r.workflowmiddlepanel:
|
|
try:
|
|
template.loader.get_template(t)
|
|
middleTemplates.append(t)
|
|
except template.TemplateDoesNotExist:
|
|
pass
|
|
|
|
leftTemplates = []
|
|
for t in r.workflowleftpanel:
|
|
try:
|
|
template.loader.get_template(t)
|
|
leftTemplates.append(t)
|
|
except template.TemplateDoesNotExist:
|
|
pass
|
|
|
|
|
|
return render(request,
|
|
'workflow.html',
|
|
{
|
|
'middleTemplates':middleTemplates,
|
|
'leftTemplates':leftTemplates,
|
|
'charts':charts,
|
|
'workout':row,
|
|
'mapscript':mapscript,
|
|
'mapdiv':mapdiv,
|
|
'statcharts':statcharts,
|
|
'rower':r,
|
|
'aantalcomments':aantalcomments,
|
|
})
|
|
|
|
# The famous flex chart
|
|
def workout_flexchart3_view(request,*args,**kwargs):
|
|
|
|
try:
|
|
id = kwargs['id']
|
|
except KeyError:
|
|
return HttpResponse("Invalid workout number")
|
|
|
|
if 'promember' in kwargs:
|
|
promember = kwargs['promember']
|
|
else:
|
|
promember = 0
|
|
|
|
try:
|
|
favoritenr = int(request.GET['favoritechart'])
|
|
except:
|
|
favoritenr = -1
|
|
|
|
row = get_workout(id)
|
|
|
|
promember=0
|
|
mayedit=0
|
|
if not request.user.is_anonymous():
|
|
r = getrower(request.user)
|
|
result = request.user.is_authenticated() and ispromember(request.user)
|
|
if result:
|
|
promember=1
|
|
if request.user == row.user.user:
|
|
mayedit=1
|
|
if checkworkoutuser(request.user,row):
|
|
mayedit=1
|
|
|
|
workouttype = 'ote'
|
|
if row.workouttype in ('water','coastal'):
|
|
workouttype = 'otw'
|
|
|
|
try:
|
|
favorites = FavoriteChart.objects.filter(user=r,
|
|
workouttype__in=[workouttype,'both']).order_by("id")
|
|
maxfav = len(favorites)-1
|
|
except:
|
|
favorites = None
|
|
maxfav = 0
|
|
|
|
# check if favoritenr is not out of range
|
|
if favorites:
|
|
try:
|
|
t = favorites[favoritenr].xparam
|
|
except IndexError:
|
|
favoritenr=0
|
|
except AssertionError:
|
|
favoritenr=0
|
|
|
|
if 'xparam' in kwargs:
|
|
xparam = kwargs['xparam']
|
|
else:
|
|
if favorites:
|
|
xparam = favorites[favoritenr].xparam
|
|
else:
|
|
xparam = 'distance'
|
|
|
|
if 'yparam1' in kwargs:
|
|
yparam1 = kwargs['yparam1']
|
|
else:
|
|
if favorites:
|
|
yparam1 = favorites[favoritenr].yparam1
|
|
else:
|
|
yparam1 = 'pace'
|
|
|
|
if 'yparam2' in kwargs:
|
|
yparam2 = kwargs['yparam2']
|
|
if yparam2 == '':
|
|
yparam2 = 'None'
|
|
else:
|
|
if favorites:
|
|
yparam2 = favorites[favoritenr].yparam2
|
|
if yparam2 == '':
|
|
yparam2 = 'None'
|
|
else:
|
|
yparam2 = 'hr'
|
|
|
|
if not request.user.is_anonymous():
|
|
r = getrower(request.user)
|
|
if favoritenr>=0 and r.showfavoritechartnotes:
|
|
try:
|
|
favoritechartnotes = favorites[favoritenr].notes
|
|
except IndexError:
|
|
favoritechartnotes = ''
|
|
else:
|
|
favoritechartnotes = ''
|
|
else:
|
|
favoritechartnotes = ''
|
|
favoritenr = 0
|
|
|
|
if 'plottype' in kwargs:
|
|
plottype = kwargs['plottype']
|
|
else:
|
|
if favorites:
|
|
plottype = favorites[favoritenr].plottype
|
|
else:
|
|
plottype = 'line'
|
|
|
|
if 'workstrokesonly' in kwargs:
|
|
workstrokesonly = kwargs['workstrokesonly']
|
|
else:
|
|
if favorites:
|
|
workstrokesonly = not favorites[favoritenr].reststrokes
|
|
else:
|
|
workstrokesonly = False
|
|
|
|
if request.method == 'POST' and 'savefavorite' in request.POST:
|
|
if not request.user.is_anonymous():
|
|
workstrokesonly = request.POST['workstrokesonlysave']
|
|
reststrokes = not workstrokesonly
|
|
r = getrower(request.user)
|
|
f = FavoriteChart(user=r,xparam=xparam,
|
|
yparam1=yparam1,yparam2=yparam2,
|
|
plottype=plottype,workouttype=workouttype,
|
|
reststrokes=reststrokes)
|
|
f.save()
|
|
|
|
if request.method == 'POST' and 'workstrokesonly' in request.POST:
|
|
workstrokesonly = request.POST['workstrokesonly']
|
|
if workstrokesonly == 'True':
|
|
workstrokesonly = True
|
|
else:
|
|
workstrokesonly = False
|
|
|
|
if not promember:
|
|
for name,d in rowingmetrics:
|
|
if d['type'] != 'basic':
|
|
if xparam == name:
|
|
xparam = 'time'
|
|
messages.info(request,'To use '+d['verbose_name']+', you have to be Pro member')
|
|
if yparam1 == name:
|
|
yparam1 = 'pace'
|
|
messages.info(request,'To use '+d['verbose_name']+', you have to be Pro member')
|
|
if yparam2 == name:
|
|
yparam2 = 'spm'
|
|
messages.info(request,'To use '+d['verbose_name']+', you have to be Pro member')
|
|
|
|
# bring back slashes
|
|
yparam1 = yparam1.replace('_slsh_','/')
|
|
yparam2 = yparam2.replace('_slsh_','/')
|
|
xparam = xparam.replace('_slsh_','/')
|
|
|
|
# create interactive plot
|
|
try:
|
|
(
|
|
script,
|
|
div,
|
|
js_resources,
|
|
css_resources,
|
|
workstrokesonly
|
|
) = interactive_flex_chart2(
|
|
id,xparam=xparam,yparam1=yparam1,
|
|
yparam2=yparam2,
|
|
promember=promember,plottype=plottype,
|
|
workstrokesonly=workstrokesonly
|
|
)
|
|
except ValueError:
|
|
(
|
|
script,
|
|
div,
|
|
js_resources,
|
|
css_resources,
|
|
workstrokesonly
|
|
) = interactive_flex_chart2(
|
|
id,xparam=xparam,yparam1=yparam1,
|
|
yparam2=yparam2,
|
|
promember=promember,plottype=plottype,
|
|
workstrokesonly=workstrokesonly
|
|
)
|
|
js_resources = ""
|
|
css_resources = ""
|
|
|
|
|
|
axchoicesbasic = {ax[0]:ax[1] for ax in axes if ax[4]=='basic'}
|
|
axchoicespro = {ax[0]:ax[1] for ax in axes if ax[4]=='pro'}
|
|
noylist = ["time","distance"]
|
|
axchoicesbasic.pop("cumdist")
|
|
|
|
if row.workouttype in ('water','coastal'):
|
|
for name,d in rowingmetrics:
|
|
if d['mode'] == 'erg':
|
|
axchoicespro.pop(name)
|
|
|
|
else:
|
|
for name,d in rowingmetrics:
|
|
if d['mode'] == 'water':
|
|
axchoicespro.pop(name)
|
|
|
|
|
|
rowdata = rdata(row.csvfilename)
|
|
try:
|
|
additionalmetrics = rowdata.get_additional_metrics()
|
|
except AttributeError:
|
|
additionalmetrics = []
|
|
|
|
|
|
extrametrics = {m.replace('/','_slsh_'):m for m in additionalmetrics}
|
|
|
|
xparam = xparam.replace('/','_slsh_')
|
|
yparam1 = yparam1.replace('/','_slsh_')
|
|
yparam2 = yparam2.replace('/','_slsh_')
|
|
|
|
from rowers.metrics import nometrics
|
|
|
|
for metric in nometrics:
|
|
try:
|
|
extrametrics.pop(metric)
|
|
except KeyError:
|
|
pass
|
|
|
|
return render(request,
|
|
'flexchart3otw.html',
|
|
{'the_script':script,
|
|
'the_div':div,
|
|
'js_res': js_resources,
|
|
'css_res':css_resources,
|
|
'teams':get_my_teams(request.user),
|
|
'id':int(id),
|
|
'xparam':xparam,
|
|
'yparam1':yparam1,
|
|
'yparam2':yparam2,
|
|
'plottype':plottype,
|
|
'axchoicesbasic':axchoicesbasic,
|
|
'axchoicespro':axchoicespro,
|
|
'extrametrics':extrametrics,
|
|
'favoritechartnotes':favoritechartnotes,
|
|
'noylist':noylist,
|
|
'mayedit':mayedit,
|
|
'promember':promember,
|
|
'workstrokesonly': not workstrokesonly,
|
|
'favoritenr':favoritenr,
|
|
'maxfav':maxfav,
|
|
})
|
|
|
|
|
|
# The interactive plot with the colored Heart rate zones
|
|
def workout_biginteractive_view(request,id=0,message="",successmessage=""):
|
|
row = get_workout(id)
|
|
|
|
# check if user is owner of this workout
|
|
|
|
|
|
# create interactive plot
|
|
f1 = row.csvfilename
|
|
u = row.user.user
|
|
# r = getrower(u)
|
|
|
|
promember=0
|
|
mayedit=0
|
|
if not request.user.is_anonymous():
|
|
r = getrower(request.user)
|
|
result = request.user.is_authenticated() and ispromember(request.user)
|
|
if result:
|
|
promember=1
|
|
if request.user == row.user.user:
|
|
mayedit=1
|
|
|
|
|
|
# create interactive plot
|
|
res = interactive_bar_chart(id,promember=promember)
|
|
script = res[0]
|
|
div = res[1]
|
|
|
|
messages.error(request,message)
|
|
messages.info(request,successmessage)
|
|
|
|
|
|
return render(request,
|
|
'biginteractive1.html',
|
|
{'workout':row,
|
|
'teams':get_my_teams(request.user),
|
|
'interactiveplot':script,
|
|
'the_div':div,
|
|
'promember':promember,
|
|
'mayedit':mayedit})
|
|
|
|
# The interactive plot with wind corrected pace for OTW outings
|
|
def workout_otwpowerplot_view(request,id=0,message="",successmessage=""):
|
|
row = get_workout(id)
|
|
|
|
# check if user is owner of this workout
|
|
|
|
|
|
# create interactive plot
|
|
f1 = row.csvfilename
|
|
u = row.user.user
|
|
# r = getrower(u)
|
|
|
|
promember=0
|
|
mayedit=0
|
|
if not request.user.is_anonymous():
|
|
r = getrower(request.user)
|
|
result = request.user.is_authenticated() and ispromember(request.user)
|
|
if result:
|
|
promember=1
|
|
if request.user == row.user.user:
|
|
mayedit=1
|
|
|
|
# create interactive plot
|
|
res = interactive_otw_advanced_pace_chart(id,promember=promember)
|
|
script = res[0]
|
|
div = res[1]
|
|
|
|
messages.error(request,message)
|
|
messages.info(request,successmessage)
|
|
|
|
return render(request,
|
|
'otwinteractive.html',
|
|
{'workout':row,
|
|
'teams':get_my_teams(request.user),
|
|
'interactiveplot':script,
|
|
'the_div':div,
|
|
'mayedit':mayedit})
|
|
|
|
# the page where you can choose where to export this workout
|
|
@login_required()
|
|
def workout_export_view(request,id=0, message="", successmessage=""):
|
|
request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE
|
|
row = get_workout(id)
|
|
|
|
try:
|
|
thetoken = c2_open(request.user)
|
|
except C2NoTokenError:
|
|
thetoken = 0
|
|
|
|
if (checkworkoutuser(request.user,row)) and thetoken:
|
|
c2userid = c2stuff.get_userid(thetoken)
|
|
else:
|
|
c2userid = 0
|
|
|
|
try:
|
|
rktoken = runkeeper_open(request.user)
|
|
except RunKeeperNoTokenError:
|
|
rktoken = 0
|
|
|
|
if (checkworkoutuser(request.user,row)) and rktoken:
|
|
rkuserid = runkeeperstuff.get_userid(rktoken)
|
|
else:
|
|
rkuserid = 0
|
|
|
|
|
|
form = WorkoutForm(instance=row)
|
|
g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime")
|
|
for i in g:
|
|
try:
|
|
width,height = Image.open(i.filename).size
|
|
i.width = width
|
|
i.height = height
|
|
i.save()
|
|
except:
|
|
pass
|
|
|
|
# check if user is owner of this workout
|
|
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
message = "You are not allowed to edit this workout"
|
|
messages.error(request,message)
|
|
url = reverse(workouts_view)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
messages.error(request,message)
|
|
messages.info(request,successmessage)
|
|
|
|
return render(request,
|
|
'export.html',
|
|
{'workout':row,
|
|
'teams':get_my_teams(request.user),
|
|
'c2userid':c2userid,
|
|
'rkuserid':rkuserid,
|
|
})
|
|
|
|
#
|
|
@login_required()
|
|
def workout_unsubscribe_view(request,id=0):
|
|
w = get_workout(id)
|
|
|
|
if w.privacy == 'private' and w.user.user != request.user:
|
|
return HttpResponseForbidden("Permission error")
|
|
|
|
comments = WorkoutComment.objects.filter(workout=w,
|
|
user=request.user).order_by("created")
|
|
|
|
for c in comments:
|
|
c.notification = False
|
|
c.save()
|
|
|
|
form = WorkoutCommentForm()
|
|
|
|
successmessage = 'You have been unsubscribed from new comment notifications for this workout'
|
|
|
|
messages.info(request,successmessage)
|
|
|
|
return render(request,
|
|
'workout_comments.html',
|
|
{'workout':w,
|
|
'teams':get_my_teams(request.user),
|
|
'comments':comments,
|
|
'form':form,
|
|
})
|
|
|
|
|
|
# list of comments to a workout
|
|
@login_required()
|
|
def workout_comment_view(request,id=0):
|
|
w = get_workout(id)
|
|
|
|
if w.privacy == 'private' and w.user.user != request.user:
|
|
return HttpResponseForbidden("Permission error")
|
|
|
|
comments = WorkoutComment.objects.filter(workout=w).order_by("created")
|
|
|
|
# ok we're permitted
|
|
if request.method == 'POST':
|
|
r = w.user
|
|
form = WorkoutCommentForm(request.POST)
|
|
if form.is_valid():
|
|
cd = form.cleaned_data
|
|
comment = cd['comment']
|
|
comment = bleach.clean(comment)
|
|
notification = cd['notification']
|
|
c = WorkoutComment(workout=w,user=request.user,comment=comment,
|
|
notification=notification)
|
|
c.save()
|
|
url = reverse(workout_comment_view,
|
|
kwargs={
|
|
'id':id
|
|
})
|
|
message = '{name} says: <a href="{url}">{comment}</a>'.format(
|
|
name = request.user.first_name,
|
|
comment = comment,
|
|
url = url,
|
|
)
|
|
if request.user != r.user:
|
|
a_messages.info(r.user,message.encode('ascii','ignore'))
|
|
|
|
res = myqueue(queuehigh,
|
|
handle_sendemailnewcomment,r.user.first_name,
|
|
r.user.last_name,
|
|
r.user.email,
|
|
request.user.first_name,
|
|
request.user.last_name,
|
|
comment,w.name,w.id)
|
|
|
|
commenters = {oc.user for oc in comments if oc.notification}
|
|
for u in commenters:
|
|
a_messages.info(u,message)
|
|
if u != request.user and u != r.user:
|
|
res = myqueue(queuelow,
|
|
handle_sendemailnewresponse,
|
|
u.first_name,
|
|
u.last_name,
|
|
u.email,
|
|
request.user.first_name,
|
|
request.user.last_name,
|
|
comment,
|
|
w.name,
|
|
w.id,
|
|
c.id)
|
|
|
|
url = reverse(workout_comment_view,kwargs = {
|
|
'id':id})
|
|
return HttpResponseRedirect(url)
|
|
|
|
form = WorkoutCommentForm()
|
|
|
|
g = GraphImage.objects.filter(workout=w).order_by("-creationdatetime")
|
|
for i in g:
|
|
try:
|
|
width,height = Image.open(i.filename).size
|
|
i.width = width
|
|
i.height = height
|
|
i.save()
|
|
except:
|
|
pass
|
|
|
|
|
|
if (len(g)<=3):
|
|
return render(request,
|
|
'workout_comments.html',
|
|
{'workout':w,
|
|
'teams':get_my_teams(request.user),
|
|
'graphs1':g[0:3],
|
|
'comments':comments,
|
|
'form':form,
|
|
})
|
|
else:
|
|
return render(request,
|
|
'workout_comments.html',
|
|
{'workout':w,
|
|
'teams':get_my_teams(request.user),
|
|
'graphs1':g[0:3],
|
|
'graphs1':g[3:6],
|
|
'comments':comments,
|
|
'form':form,
|
|
})
|
|
|
|
@login_required()
|
|
def course_delete_view(request,id=0):
|
|
try:
|
|
course = GeoCourse.objects.get(id=id)
|
|
except GeoCourse.DoesNotExist:
|
|
return Http404("Course doesn't exist")
|
|
|
|
r = getrower(request.user)
|
|
|
|
if course.manager != r:
|
|
raise PermissionDenied("Access denied")
|
|
|
|
ps = PlannedSession.objects.filter(course=course)
|
|
nosessions = len(ps) == 0
|
|
|
|
if nosessions:
|
|
course.delete()
|
|
|
|
url = reverse(courses_view)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
@login_required()
|
|
def course_edit_view(request,id=0):
|
|
try:
|
|
course = GeoCourse.objects.get(id=id)
|
|
except GeoCourse.DoesNotExist:
|
|
return Http404("Course doesn't exist")
|
|
|
|
r = getrower(request.user)
|
|
|
|
if course.manager != r:
|
|
raise PermissionDenied("Access denied")
|
|
|
|
ps = PlannedSession.objects.filter(course=course)
|
|
nosessions = len(ps) == 0
|
|
|
|
script,div = course_map(course)
|
|
|
|
if request.method == 'POST':
|
|
form = GeoCourseEditForm(request.POST)
|
|
if form.is_valid():
|
|
name = form.cleaned_data['name']
|
|
country = form.cleaned_data['country']
|
|
notes = form.cleaned_data['notes']
|
|
if isinstance(name,unicode):
|
|
name = name.encode('utf8')
|
|
elif isinstance(name, str):
|
|
name = name.decode('utf8')
|
|
|
|
course.name = name
|
|
course.country = country
|
|
course.notes = notes
|
|
course.save()
|
|
|
|
form = GeoCourseEditForm(instance=course)
|
|
|
|
return render(request, 'course_edit_view.html',
|
|
{
|
|
'course':course,
|
|
'mapscript':script,
|
|
'mapdiv':div,
|
|
'nosessions':nosessions,
|
|
'rower':r,
|
|
'form':form,
|
|
}
|
|
)
|
|
|
|
@login_required()
|
|
def course_view(request,id=0):
|
|
try:
|
|
course = GeoCourse.objects.get(id=id)
|
|
except GeoCourse.DoesNotExist:
|
|
return Http404("Course doesn't exist")
|
|
|
|
r = getrower(request.user)
|
|
|
|
script,div = course_map(course)
|
|
|
|
return render(request, 'course_view.html',
|
|
{
|
|
'course':course,
|
|
'mapscript':script,
|
|
'mapdiv':div,
|
|
'nosessions':False,
|
|
'rower':r,
|
|
}
|
|
)
|
|
|
|
|
|
# The basic edit page
|
|
@login_required()
|
|
def workout_edit_view(request,id=0,message="",successmessage=""):
|
|
request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE
|
|
request.session['referer'] = absolute(request)['PATH']
|
|
|
|
|
|
row = get_workout(id)
|
|
|
|
form = WorkoutForm(instance=row)
|
|
|
|
if request.method == 'POST':
|
|
# Form was submitted
|
|
form = WorkoutForm(request.POST)
|
|
if form.is_valid():
|
|
# Get values from form
|
|
name = form.cleaned_data['name']
|
|
date = form.cleaned_data['date']
|
|
starttime = form.cleaned_data['starttime']
|
|
workouttype = form.cleaned_data['workouttype']
|
|
duration = form.cleaned_data['duration']
|
|
distance = form.cleaned_data['distance']
|
|
notes = form.cleaned_data['notes']
|
|
thetimezone = form.cleaned_data['timezone']
|
|
try:
|
|
boattype = request.POST['boattype']
|
|
except KeyError:
|
|
boattype = Workout.objects.get(id=id).boattype
|
|
try:
|
|
privacy = request.POST['privacy']
|
|
except KeyError:
|
|
privacy = Workout.objects.get(id=id).privacy
|
|
try:
|
|
rankingpiece = form.cleaned_data['rankingpiece']
|
|
except KeyError:
|
|
rankingpiece =- Workout.objects.get(id=id).rankingpiece
|
|
|
|
startdatetime = (str(date) + ' ' + str(starttime))
|
|
startdatetime = datetime.datetime.strptime(startdatetime,
|
|
"%Y-%m-%d %H:%M:%S")
|
|
|
|
startdatetime = startdatetime.replace(tzinfo=pytz.timezone(thetimezone))
|
|
try:
|
|
# aware object can be in any timezone
|
|
out = startdatetime.astimezone(pytz.utc)
|
|
except (ValueError, TypeError):
|
|
startdatetime = timezone.make_aware(startdatetime)
|
|
|
|
startdatetime = startdatetime.astimezone(pytz.timezone(thetimezone))
|
|
|
|
|
|
# check if user is owner of this workout
|
|
if checkworkoutuser(request.user,row):
|
|
row.name = name
|
|
row.date = date
|
|
row.starttime = starttime
|
|
row.startdatetime = startdatetime
|
|
row.workouttype = workouttype
|
|
row.notes = notes
|
|
row.duration = duration
|
|
row.distance = distance
|
|
row.boattype = boattype
|
|
row.privacy = privacy
|
|
row.rankingpiece = rankingpiece
|
|
row.timezone = thetimezone
|
|
try:
|
|
row.save()
|
|
except IntegrityError:
|
|
pass
|
|
# change data in csv file
|
|
|
|
r = rdata(row.csvfilename)
|
|
if r == 0:
|
|
return HttpResponse("Error: CSV Data File Not Found")
|
|
r.rowdatetime = startdatetime
|
|
r.write_csv(row.csvfilename,gzip=True)
|
|
dataprep.update_strokedata(id,r.df)
|
|
successmessage = "Changes saved"
|
|
|
|
if rankingpiece:
|
|
dataprep.runcpupdate(row.user,type=row.workouttype)
|
|
|
|
messages.info(request,successmessage)
|
|
url = reverse(workout_edit_view,
|
|
kwargs = {
|
|
'id':str(row.id),
|
|
})
|
|
response = HttpResponseRedirect(url)
|
|
else:
|
|
message = "You are not allowed to change this workout"
|
|
messages.error(request,message)
|
|
url = reverse(workouts_view)
|
|
|
|
response = HttpResponseRedirect(url)
|
|
|
|
#else: # form not POSTed
|
|
form = WorkoutForm(instance=row)
|
|
|
|
row = get_workout(id)
|
|
|
|
g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime")
|
|
for i in g:
|
|
try:
|
|
width,height = Image.open(i.filename).size
|
|
i.width = width
|
|
i.height = height
|
|
i.save()
|
|
except:
|
|
pass
|
|
|
|
|
|
# check if user is owner of this workout
|
|
|
|
comments = WorkoutComment.objects.filter(workout=row)
|
|
|
|
aantalcomments = len(comments)
|
|
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
raise PermissionDenied("Access denied")
|
|
|
|
# create interactive plot
|
|
f1 = row.csvfilename
|
|
u = row.user.user
|
|
r = getrower(u)
|
|
rowdata = rdata(f1)
|
|
hascoordinates = 1
|
|
if rowdata != 0:
|
|
try:
|
|
latitude = rowdata.df[' latitude']
|
|
if not latitude.std():
|
|
hascoordinates = 0
|
|
except KeyError,AttributeError:
|
|
hascoordinates = 0
|
|
|
|
else:
|
|
hascoordinates = 0
|
|
|
|
|
|
if hascoordinates:
|
|
mapscript,mapdiv = leaflet_chart(rowdata.df[' latitude'],
|
|
rowdata.df[' longitude'],
|
|
row.name)
|
|
|
|
#res = googlemap_chart(rowdata.df[' latitude'],
|
|
# rowdata.df[' longitude'],
|
|
# row.name)
|
|
#gmscript = res[0]
|
|
#gmdiv = res[1]
|
|
|
|
else:
|
|
mapscript = ""
|
|
mapdiv = ""
|
|
|
|
|
|
# render page
|
|
if (len(g)<=3):
|
|
return render(request, 'workout_form.html',
|
|
{'form':form,
|
|
'workout':row,
|
|
'teams':get_my_teams(request.user),
|
|
'graphs1':g[0:3],
|
|
'mapscript':mapscript,
|
|
'aantalcomments':aantalcomments,
|
|
'mapdiv':mapdiv,
|
|
'rower':r,
|
|
})
|
|
|
|
else:
|
|
return render(request, 'workout_form.html',
|
|
{'form':form,
|
|
'teams':get_my_teams(request.user),
|
|
'workout':row,
|
|
'graphs1':g[0:3],
|
|
'graphs2':g[3:6],
|
|
'mapscript':mapscript,
|
|
'aantalcomments':aantalcomments,
|
|
'mapdiv':mapdiv,
|
|
'rower':r,
|
|
})
|
|
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
@login_required()
|
|
def workout_map_view(request,id=0):
|
|
request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE
|
|
request.session['referer'] = absolute(request)['PATH']
|
|
|
|
row = get_workout(id)
|
|
|
|
# create interactive plot
|
|
f1 = row.csvfilename
|
|
u = row.user.user
|
|
r = getrower(u)
|
|
rowdata = rdata(f1)
|
|
hascoordinates = 1
|
|
if rowdata != 0:
|
|
try:
|
|
latitude = rowdata.df[' latitude']
|
|
if not latitude.std():
|
|
hascoordinates = 0
|
|
except KeyError,AttributeError:
|
|
hascoordinates = 0
|
|
|
|
else:
|
|
hascoordinates = 0
|
|
|
|
|
|
if hascoordinates:
|
|
mapscript,mapdiv = leaflet_chart2(rowdata.df[' latitude'],
|
|
rowdata.df[' longitude'],
|
|
row.name)
|
|
else:
|
|
mapscript = ""
|
|
mapdiv = ""
|
|
|
|
mayedit=0
|
|
if not request.user.is_anonymous():
|
|
r = getrower(request.user)
|
|
result = request.user.is_authenticated() and ispromember(request.user)
|
|
if result:
|
|
promember=1
|
|
if request.user == row.user.user:
|
|
mayedit=1
|
|
|
|
return render(request, 'map_view.html',
|
|
{'mapscript':mapscript,
|
|
'workout':row,
|
|
'mapdiv':mapdiv,
|
|
'mayedit':mayedit,
|
|
})
|
|
|
|
# The basic edit page
|
|
@login_required()
|
|
def workout_edit_view_navionics(request,id=0,message="",successmessage=""):
|
|
request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE
|
|
request.session['referer'] = absolute(request)['PATH']
|
|
|
|
row = get_workout(id)
|
|
form = WorkoutForm(instance=row)
|
|
|
|
if request.method == 'POST':
|
|
# Form was submitted
|
|
form = WorkoutForm(request.POST)
|
|
if form.is_valid():
|
|
# Get values from form
|
|
name = form.cleaned_data['name']
|
|
date = form.cleaned_data['date']
|
|
starttime = form.cleaned_data['starttime']
|
|
workouttype = form.cleaned_data['workouttype']
|
|
duration = form.cleaned_data['duration']
|
|
distance = form.cleaned_data['distance']
|
|
notes = form.cleaned_data['notes']
|
|
thetimezone = form.cleaned_data['timezone']
|
|
try:
|
|
boattype = request.POST['boattype']
|
|
except KeyError:
|
|
boattype = Workout.objects.get(id=id).boattype
|
|
try:
|
|
privacy = request.POST['privacy']
|
|
except KeyError:
|
|
privacy = Workout.objects.get(id=id).privacy
|
|
try:
|
|
rankingpiece = form.cleaned_data['rankingpiece']
|
|
except KeyError:
|
|
rankingpiece =- Workout.objects.get(id=id).rankingpiece
|
|
|
|
startdatetime = (str(date) + ' ' + str(starttime))
|
|
startdatetime = datetime.datetime.strptime(startdatetime,
|
|
"%Y-%m-%d %H:%M:%S")
|
|
startdatetime = timezone.make_aware(startdatetime)
|
|
startdatetime = startdatetime.astimezone(pytz.timezone(thetimezone))
|
|
|
|
|
|
# check if user is owner of this workout
|
|
if checkworkoutuser(request.user,row):
|
|
row.name = name
|
|
row.date = date
|
|
row.starttime = starttime
|
|
row.startdatetime = startdatetime
|
|
row.workouttype = workouttype
|
|
row.notes = notes
|
|
row.duration = duration
|
|
row.distance = distance
|
|
row.boattype = boattype
|
|
row.privacy = privacy
|
|
row.rankingpiece = rankingpiece
|
|
row.timezone = thetimezone
|
|
try:
|
|
row.save()
|
|
except IntegrityError:
|
|
pass
|
|
# change data in csv file
|
|
|
|
r = rdata(row.csvfilename)
|
|
if r == 0:
|
|
return HttpResponse("Error: CSV Data File Not Found")
|
|
r.rowdatetime = startdatetime
|
|
r.write_csv(row.csvfilename,gzip=True)
|
|
dataprep.update_strokedata(id,r.df)
|
|
successmessage = "Changes saved"
|
|
messages.info(request,successmessage)
|
|
url = reverse(workout_edit_view,
|
|
kwargs = {
|
|
'id':str(row.id),
|
|
})
|
|
response = HttpResponseRedirect(url)
|
|
else:
|
|
message = "You are not allowed to change this workout"
|
|
messages.error(request,message)
|
|
url = reverse(workouts_view)
|
|
|
|
response = HttpResponseRedirect(url)
|
|
|
|
#else: # form not POSTed
|
|
form = WorkoutForm(instance=row)
|
|
|
|
row = get_workout(id)
|
|
|
|
g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime")
|
|
for i in g:
|
|
try:
|
|
width,height = Image.open(i.filename).size
|
|
i.width = width
|
|
i.height = height
|
|
i.save()
|
|
except:
|
|
pass
|
|
|
|
# check if user is owner of this workout
|
|
|
|
comments = WorkoutComment.objects.filter(workout=row)
|
|
|
|
aantalcomments = len(comments)
|
|
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
raise PermissionDenied("Access denied")
|
|
|
|
# create interactive plot
|
|
f1 = row.csvfilename
|
|
u = row.user.user
|
|
r = getrower(u)
|
|
rowdata = rdata(f1)
|
|
hascoordinates = 1
|
|
if rowdata != 0:
|
|
try:
|
|
latitude = rowdata.df[' latitude']
|
|
if not latitude.std():
|
|
hascoordinates = 0
|
|
except KeyError,AttributeError:
|
|
hascoordinates = 0
|
|
|
|
else:
|
|
hascoordinates = 0
|
|
|
|
|
|
if hascoordinates:
|
|
mapscript,mapdiv = leaflet_chart2(rowdata.df[' latitude'],
|
|
rowdata.df[' longitude'],
|
|
row.name)
|
|
|
|
else:
|
|
mapscript = ""
|
|
mapdiv = ""
|
|
|
|
|
|
# render page
|
|
if (len(g)<=3):
|
|
return render(request, 'workout_form.html',
|
|
{'form':form,
|
|
'workout':row,
|
|
'teams':get_my_teams(request.user),
|
|
'graphs1':g[0:3],
|
|
'mapscript':mapscript,
|
|
'aantalcomments':aantalcomments,
|
|
'mapdiv':mapdiv,
|
|
})
|
|
|
|
else:
|
|
return render(request, 'workout_form.html',
|
|
{'form':form,
|
|
'teams':get_my_teams(request.user),
|
|
'workout':row,
|
|
'graphs1':g[0:3],
|
|
'graphs2':g[3:6],
|
|
'mapscript':mapscript,
|
|
'aantalcomments':aantalcomments,
|
|
'mapdiv':mapdiv,
|
|
})
|
|
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
# Image upload
|
|
@login_required()
|
|
def workout_uploadimage_view(request,id):
|
|
is_ajax = False
|
|
if request.is_ajax():
|
|
is_ajax = True
|
|
|
|
r = getrower(request.user)
|
|
|
|
w = get_workout(id)
|
|
|
|
if not checkworkoutuser(request.user,w):
|
|
raise PermissionDenied("You are not allowed to edit this workout")
|
|
|
|
images = GraphImage.objects.filter(workout=w)
|
|
|
|
|
|
if len(images) >= 6:
|
|
message = "You have reached the maximum number of static images for this workout"
|
|
messages.error(request,message)
|
|
url = reverse(r.defaultlandingpage,
|
|
kwargs = {
|
|
'id':int(id),
|
|
})
|
|
return HttpResponseRedirect(url)
|
|
|
|
|
|
if request.method == 'POST':
|
|
form = ImageForm(request.POST,request.FILES)
|
|
|
|
if form.is_valid():
|
|
f = form.cleaned_data['file']
|
|
|
|
if f is not None:
|
|
filename,path_and_filename = handle_uploaded_image(f)
|
|
try:
|
|
width,height = Image.open(path_and_filename).size
|
|
except:
|
|
message = "Not a valid image"
|
|
messages.error(request,message)
|
|
os.remove(path_and_filename)
|
|
url = reverse(workout_uploadimage_view,
|
|
kwargs = {'id':id})
|
|
|
|
if is_ajax:
|
|
return JSONResponse({'result':0,'url':0})
|
|
else:
|
|
return HttpResponseRedirect(url)
|
|
|
|
i = GraphImage(workout=w,
|
|
creationdatetime=timezone.now(),
|
|
filename = path_and_filename,
|
|
width=width,height=height)
|
|
i.save()
|
|
|
|
url = reverse(r.defaultlandingpage,
|
|
kwargs = {'id':id})
|
|
if is_ajax:
|
|
return JSONResponse({'result':1,'url':url})
|
|
else:
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
messages.error(request,'Something went wrong - no file attached')
|
|
url = reverse(workout_uploadimage_view,
|
|
kwargs = {'id':id})
|
|
|
|
if is_ajax:
|
|
return JSONResponse({'result':0,'url':0})
|
|
else:
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
return HttpResponse("Form is not valid")
|
|
|
|
else:
|
|
if not is_ajax:
|
|
form = ImageForm()
|
|
return render(request,'image_form.html',
|
|
{'form':form,
|
|
'teams':get_my_teams(request.user),
|
|
'workout': w,
|
|
})
|
|
else:
|
|
return {'result':0}
|
|
|
|
# Image upload
|
|
@login_required()
|
|
def course_upload_view(request):
|
|
is_ajax = False
|
|
if request.is_ajax():
|
|
is_ajax = True
|
|
|
|
r = getrower(request.user)
|
|
|
|
if request.method == 'POST':
|
|
form = CourseForm(request.POST,request.FILES)
|
|
|
|
if form.is_valid():
|
|
f = form.cleaned_data['file']
|
|
name = form.cleaned_data['name']
|
|
notes = form.cleaned_data['notes']
|
|
if f is not None:
|
|
filename,path_and_filename = handle_uploaded_file(f)
|
|
|
|
cs = courses.kmltocourse(path_and_filename)
|
|
|
|
for course in cs:
|
|
cname = name+' - '+course['name']
|
|
cnotes = notes+'\n\n'+course['description']
|
|
polygons = course['polygons']
|
|
|
|
course = courses.createcourse(r,cname,polygons,notes=cnotes)
|
|
|
|
os.remove(path_and_filename)
|
|
|
|
url = reverse(courses_view)
|
|
if is_ajax:
|
|
return JSONResponse({'result':1,'url':url})
|
|
else:
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
messages.error(request,'Something went wrong - no file attached')
|
|
url = reverse(course_upload_view)
|
|
|
|
if is_ajax:
|
|
return JSONResponse({'result':0,'url':0})
|
|
else:
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
messages.error(request,'Form is not valid')
|
|
return render(request,'course_form.html',
|
|
{'form':form,
|
|
})
|
|
|
|
else:
|
|
if not is_ajax:
|
|
form = CourseForm()
|
|
return render(request,'course_form.html',
|
|
{'form':form,
|
|
})
|
|
else:
|
|
return {'result':0}
|
|
|
|
|
|
|
|
# Generic chart creation
|
|
@login_required()
|
|
def workout_add_chart_view(request,id,plotnr=1):
|
|
|
|
w = get_workout(id)
|
|
|
|
plotnr = int(plotnr)
|
|
|
|
if (checkworkoutuser(request.user,w)==False):
|
|
raise PermissionDenied("You are not allowed add plots to this workout")
|
|
else:
|
|
f1 = w.csvfilename[6:-4]
|
|
timestr = strftime("%Y%m%d-%H%M%S")
|
|
imagename = f1+timestr+'.png'
|
|
u = w.user.user
|
|
r = getrower(u)
|
|
title = w.name
|
|
res,jobid = uploads.make_plot(
|
|
r,w,f1,w.csvfilename,'timeplot',title,plotnr=plotnr,
|
|
imagename=imagename
|
|
)
|
|
if res == 0:
|
|
messages.error(request,jobid)
|
|
else:
|
|
try:
|
|
request.session['async_tasks'] += [(jobid,'make_plot')]
|
|
except KeyError:
|
|
request.session['async_tasks'] = [(jobid,'make_plot')]
|
|
|
|
try:
|
|
url = request.session['referer']
|
|
except KeyError:
|
|
url = "/rowers/workout/"+str(w.id)+"/edit"
|
|
return HttpResponseRedirect(url)
|
|
|
|
|
|
# The page where you select which Strava workout to import
|
|
@login_required()
|
|
def workout_stravaimport_view(request,message=""):
|
|
res = stravastuff.get_strava_workout_list(request.user)
|
|
|
|
if (res.status_code != 200):
|
|
if (res.status_code == 401):
|
|
r = getrower(request.user)
|
|
if (r.stravatoken == '') or (r.stravatoken is None):
|
|
s = "Token doesn't exist. Need to authorize"
|
|
return HttpResponseRedirect("/rowers/me/stravaauthorize/")
|
|
message = "Something went wrong in workout_stravaimport_view"
|
|
messages.error(request,message)
|
|
if settings.DEBUG:
|
|
return HttpResponse(res)
|
|
else:
|
|
url = reverse(workouts_view)
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
workouts = []
|
|
r = getrower(request.user)
|
|
stravaids = [int(item['id']) for item in res.json()]
|
|
knownstravaids = uniqify([
|
|
w.uploadedtostrava for w in Workout.objects.filter(user=r)
|
|
])
|
|
newids = [stravaid for stravaid in stravaids if not stravaid in knownstravaids]
|
|
|
|
for item in res.json():
|
|
d = int(float(item['distance']))
|
|
i = item['id']
|
|
if i in knownstravaids:
|
|
nnn = ''
|
|
else:
|
|
nnn = 'NEW'
|
|
n = item['name']
|
|
ttot = str(datetime.timedelta(seconds=int(float(item['elapsed_time']))))
|
|
s = item['start_date']
|
|
r = item['type']
|
|
keys = ['id','distance','duration','starttime','type','name','new']
|
|
values = [i,d,ttot,s,r,n,nnn]
|
|
res = dict(zip(keys,values))
|
|
workouts.append(res)
|
|
|
|
|
|
return render(request,'strava_list_import.html',
|
|
{'workouts':workouts,
|
|
'teams':get_my_teams(request.user),
|
|
})
|
|
|
|
return HttpResponse(res)
|
|
|
|
# The page where you select which RunKeeper workout to import
|
|
@login_required()
|
|
def workout_runkeeperimport_view(request,message=""):
|
|
res = runkeeperstuff.get_runkeeper_workout_list(request.user)
|
|
if (res.status_code != 200):
|
|
if (res.status_code == 401):
|
|
r = getrower(request.user)
|
|
if (r.runkeepertoken == '') or (r.runkeepertoken is None):
|
|
s = "Token doesn't exist. Need to authorize"
|
|
return HttpResponseRedirect("/rowers/me/runkeeperauthorize/")
|
|
message = "Something went wrong in workout_runkeeperimport_view"
|
|
messages.error(request,message)
|
|
|
|
if settings.DEBUG:
|
|
return HttpResponse(res)
|
|
else:
|
|
url = reverse(workouts_view)
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
workouts = []
|
|
for item in res.json()['items']:
|
|
d = int(float(item['total_distance']))
|
|
i = getidfromsturi(item['uri'],length=9)
|
|
ttot = str(datetime.timedelta(seconds=int(float(item['duration']))))
|
|
s = item['start_time']
|
|
r = item['type']
|
|
keys = ['id','distance','duration','starttime','type']
|
|
values = [i,d,ttot,s,r]
|
|
res = dict(zip(keys,values))
|
|
workouts.append(res)
|
|
return render(request,'runkeeper_list_import.html',
|
|
{'workouts':workouts,
|
|
'teams':get_my_teams(request.user),
|
|
})
|
|
|
|
return HttpResponse(res)
|
|
|
|
# The page where you select which RunKeeper workout to import
|
|
@login_required()
|
|
def workout_underarmourimport_view(request,message=""):
|
|
res = underarmourstuff.get_underarmour_workout_list(request.user)
|
|
if (res.status_code != 200):
|
|
if (res.status_code == 401):
|
|
r = getrower(request.user)
|
|
if (r.underarmourtoken == '') or (r.underarmourtoken is None):
|
|
s = "Token doesn't exist. Need to authorize"
|
|
return HttpResponseRedirect("/rowers/me/underarmourauthorize/")
|
|
message = "Something went wrong in workout_underarmourimport_view"
|
|
messages.error(request,message)
|
|
if settings.DEBUG:
|
|
return HttpResponse(res)
|
|
else:
|
|
url = reverse(workouts_view)
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
workouts = []
|
|
items = res.json()['_embedded']['workouts']
|
|
for item in items:
|
|
if 'has_time_series' in item:
|
|
if item['has_time_series']:
|
|
s = item['start_datetime']
|
|
i,r = underarmourstuff.get_idfromuri(request.user,item['_links'])
|
|
n = item['name']
|
|
try:
|
|
d = item['aggregates']['distance_total']
|
|
except KeyError:
|
|
d = 0
|
|
try:
|
|
ttot = item['aggregates']['active_time_total']
|
|
except KeyError:
|
|
ttot = 0
|
|
|
|
keys = ['id','distance','duration','starttime','type']
|
|
values = [i,d,ttot,s,r]
|
|
thedict = dict(zip(keys,values))
|
|
|
|
workouts.append(thedict)
|
|
|
|
return render(request,'underarmour_list_import.html',
|
|
{'workouts':workouts,
|
|
'teams':get_my_teams(request.user),
|
|
})
|
|
|
|
return HttpResponse(res)
|
|
|
|
# The page where you select which SportTracks workout to import
|
|
@login_required()
|
|
def workout_sporttracksimport_view(request,message=""):
|
|
res = sporttracksstuff.get_sporttracks_workout_list(request.user)
|
|
if (res.status_code != 200):
|
|
if (res.status_code == 401):
|
|
r = getrower(request.user)
|
|
if (r.sporttrackstoken == '') or (r.sporttrackstoken is None):
|
|
s = "Token doesn't exist. Need to authorize"
|
|
return HttpResponseRedirect("/rowers/me/sporttracksauthorize/")
|
|
message = "Something went wrong in workout_sporttracksimport_view"
|
|
messages.error(request,message)
|
|
if settings.DEBUG:
|
|
return HttpResponse(res)
|
|
else:
|
|
url = reverse(workouts_view)
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
workouts = []
|
|
r = getrower(request.user)
|
|
stids = [int(getidfromsturi(item['uri'])) for item in res.json()['items']]
|
|
knownstids = uniqify([
|
|
w.uploadedtosporttracks for w in Workout.objects.filter(user=r)
|
|
])
|
|
newids = [stid for stid in stids if not stid in knownstids]
|
|
for item in res.json()['items']:
|
|
d = int(float(item['total_distance']))
|
|
i = int(getidfromsturi(item['uri']))
|
|
if i in knownstids:
|
|
nnn = ''
|
|
else:
|
|
nnn = 'NEW'
|
|
n = item['name']
|
|
ttot = str(datetime.timedelta(seconds=int(float(item['duration']))))
|
|
s = item['start_time']
|
|
r = item['type']
|
|
keys = ['id','distance','duration','starttime','type','name','new']
|
|
values = [i,d,ttot,s,r,n,nnn]
|
|
res = dict(zip(keys,values))
|
|
workouts.append(res)
|
|
return render(request,'sporttracks_list_import.html',
|
|
{'workouts':workouts,
|
|
'teams':get_my_teams(request.user),
|
|
})
|
|
|
|
return HttpResponse(res)
|
|
|
|
# List of workouts on Concept2 logbook. This view only used for debugging
|
|
@login_required()
|
|
def c2listdebug_view(request,page=1,message=""):
|
|
try:
|
|
thetoken = c2_open(request.user)
|
|
except C2NoTokenError:
|
|
return HttpResponseRedirect("/rowers/me/c2authorize/")
|
|
|
|
r = getrower(request.user)
|
|
|
|
res = c2stuff.get_c2_workout_list(request.user,page=page)
|
|
|
|
if (res.status_code != 200):
|
|
message = "Something went wrong in workout_c2import_view (C2 token renewal)"
|
|
messages.error(request,message)
|
|
if settings.DEBUG:
|
|
return HttpResponse(res)
|
|
else:
|
|
url = reverse(workouts_view)
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
workouts = []
|
|
|
|
for item in res.json()['data']:
|
|
d = item['distance']
|
|
i = item['id']
|
|
ttot = item['time_formatted']
|
|
s = item['date']
|
|
r = item['type']
|
|
s2 = item['source']
|
|
c = item['comments']
|
|
keys = ['id','distance','duration','starttime','rowtype','source','comment']
|
|
values = [i,d,ttot,s,r,s2,c]
|
|
res = dict(zip(keys,values))
|
|
workouts.append(res)
|
|
|
|
|
|
return render(request,
|
|
'c2_list_import2.html',
|
|
{'workouts':workouts,
|
|
'teams':get_my_teams(request.user),
|
|
})
|
|
|
|
# Import all unknown workouts available on Concept2 logbook
|
|
@login_required()
|
|
def workout_getc2workout_all(request,page=1,message=""):
|
|
try:
|
|
thetoken = c2_open(request.user)
|
|
except C2NoTokenError:
|
|
return HttpResponseRedirect("/rowers/me/c2authorize/")
|
|
|
|
res = c2stuff.get_c2_workout_list(request.user,page=page)
|
|
|
|
if (res.status_code != 200):
|
|
message = "Something went wrong in workout_c2import_view (C2 token refresh)"
|
|
messages.error(request,message)
|
|
else:
|
|
r = getrower(request.user)
|
|
c2ids = [item['id'] for item in res.json()['data']]
|
|
alldata = {}
|
|
for item in res.json()['data']:
|
|
alldata[item['id']] = item
|
|
|
|
knownc2ids = uniqify([
|
|
w.uploadedtoc2 for w in Workout.objects.filter(user=r)
|
|
])
|
|
newids = [c2id for c2id in c2ids if not c2id in knownc2ids]
|
|
|
|
for c2id in newids:
|
|
workoutid = c2stuff.create_async_workout(alldata,
|
|
request.user,c2id)
|
|
|
|
url = reverse(workouts_view)
|
|
return HttpResponseRedirect(url)
|
|
|
|
|
|
# List of workouts available on Concept2 logbook - for import
|
|
@login_required()
|
|
def workout_c2import_view(request,page=1,message=""):
|
|
try:
|
|
thetoken = c2_open(request.user)
|
|
except C2NoTokenError:
|
|
return HttpResponseRedirect("/rowers/me/c2authorize/")
|
|
|
|
res = c2stuff.get_c2_workout_list(request.user,page=page)
|
|
|
|
if (res.status_code != 200):
|
|
message = "Something went wrong in workout_c2import_view (C2 token refresh)"
|
|
messages.error(request,message)
|
|
if settings.DEBUG:
|
|
return HttpResponse(res)
|
|
else:
|
|
url = reverse(workouts_view)
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
workouts = []
|
|
r = getrower(request.user)
|
|
c2ids = [item['id'] for item in res.json()['data']]
|
|
knownc2ids = uniqify([
|
|
w.uploadedtoc2 for w in Workout.objects.filter(user=r)
|
|
])
|
|
newids = [c2id for c2id in c2ids if not c2id in knownc2ids]
|
|
for item in res.json()['data']:
|
|
d = item['distance']
|
|
i = item['id']
|
|
ttot = item['time_formatted']
|
|
s = item['date']
|
|
r = item['type']
|
|
s2 = item['source']
|
|
c = item['comments']
|
|
if i in knownc2ids:
|
|
nnn = ''
|
|
else:
|
|
nnn = 'NEW'
|
|
keys = ['id','distance','duration','starttime','rowtype','source','comment','new']
|
|
values = [i,d,ttot,s,r,s2,c,nnn]
|
|
res = dict(zip(keys,values))
|
|
workouts.append(res)
|
|
|
|
|
|
return render(request,
|
|
'c2_list_import2.html',
|
|
{'workouts':workouts,
|
|
'teams':get_my_teams(request.user),
|
|
'page':page,
|
|
})
|
|
|
|
# Import a workout from Strava
|
|
@login_required()
|
|
def workout_getstravaworkout_view(request,stravaid):
|
|
res = stravastuff.get_strava_workout(request.user,stravaid)
|
|
if not res[0]:
|
|
messages.error(request,res[1])
|
|
return imports_view(request)
|
|
|
|
strokedata = res[1]
|
|
data = res[0]
|
|
id,message = add_workout_from_strokedata(request.user,stravaid,data,strokedata,
|
|
source='strava',
|
|
workoutsource='strava')
|
|
w = get_workout(id)
|
|
|
|
w.uploadedtostrava=stravaid
|
|
w.save()
|
|
if message:
|
|
messages.error(request,message)
|
|
|
|
|
|
r = getrower(request.user)
|
|
|
|
url = reverse(r.defaultlandingpage,
|
|
kwargs = {
|
|
'id':int(id),
|
|
})
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
# Imports a workout from Runkeeper
|
|
@login_required()
|
|
def workout_getrunkeeperworkout_view(request,runkeeperid):
|
|
res = runkeeperstuff.get_runkeeper_workout(request.user,runkeeperid)
|
|
try:
|
|
data = res.json()
|
|
except ValueError:
|
|
messages.error(request,'Error getting workout from Runkeeper')
|
|
url = reverse(workout_runkeeperimport_view)
|
|
return HttpResponseRedirect(url)
|
|
|
|
id,message = add_workout_from_runkeeperdata(request.user,runkeeperid,data)
|
|
w = Workout.objects.get(id=id)
|
|
w.uploadedtorunkeeper=runkeeperid
|
|
thetoken = runkeeper_open(request.user)
|
|
w.save()
|
|
if message:
|
|
messages.error(request,message)
|
|
|
|
r = getrower(request.user)
|
|
|
|
url = reverse(r.defaultlandingpage,
|
|
kwargs = {
|
|
'id':int(id),
|
|
})
|
|
return HttpResponseRedirect(url)
|
|
|
|
# Imports a workout from Underarmour
|
|
@login_required()
|
|
def workout_getunderarmourworkout_view(request,underarmourid):
|
|
res = underarmourstuff.get_underarmour_workout(request.user,underarmourid)
|
|
data = res.json()
|
|
|
|
id,message = add_workout_from_underarmourdata(request.user,underarmourid,data)
|
|
w = Workout.objects.get(id=id)
|
|
w.uploadedtounderarmour=underarmourid
|
|
w.save()
|
|
if message:
|
|
messages.error(request,message)
|
|
|
|
r = getrower(request.user)
|
|
|
|
url = reverse(r.defaultlandingpage,
|
|
kwargs = {
|
|
'id':int(id),
|
|
})
|
|
return HttpResponseRedirect(url)
|
|
|
|
|
|
|
|
# Imports a workout from SportTracks
|
|
@login_required()
|
|
def workout_getsporttracksworkout_view(request,sporttracksid):
|
|
res = sporttracksstuff.get_sporttracks_workout(request.user,sporttracksid)
|
|
data = res.json()
|
|
|
|
id,message = add_workout_from_stdata(request.user,sporttracksid,data)
|
|
if id==0:
|
|
messages.error(request,message)
|
|
url = reverse(workouts_view)
|
|
return HttpResponseRedirect(url)
|
|
w = Workout.objects.get(id=id)
|
|
w.uploadedtosporttracks=sporttracksid
|
|
w.save()
|
|
if message:
|
|
messages.error(request,message)
|
|
|
|
r = getrower(request.user)
|
|
|
|
url = reverse(r.defaultlandingpage,
|
|
kwargs = {
|
|
'id':int(id),
|
|
})
|
|
return HttpResponseRedirect(url)
|
|
|
|
# Imports all new workouts from SportTracks
|
|
@login_required()
|
|
def workout_getsporttracksworkout_all(request):
|
|
res = sporttracksstuff.get_sporttracks_workout_list(request.user)
|
|
if (res.status_code == 200):
|
|
r = getrower(request.user)
|
|
stids = [int(getidfromsturi(item['uri'])) for item in res.json()['items']]
|
|
knownstids = uniqify([
|
|
w.uploadedtosporttracks for w in Workout.objects.filter(user=r)
|
|
])
|
|
newids = [stid for stid in stids if not stid in knownstids]
|
|
for sporttracksid in newids:
|
|
res = sporttracksstuff.get_sporttracks_workout(
|
|
request.user,sporttracksid)
|
|
data = res.json()
|
|
|
|
id,message = add_workout_from_stdata(request.user,sporttracksid,data)
|
|
if id==0:
|
|
messages.error(request,message)
|
|
|
|
else:
|
|
w = Workout.objects.get(id=id)
|
|
w.uploadedtosporttracks=sporttracksid
|
|
w.save()
|
|
|
|
url = reverse(workouts_view)
|
|
return HttpResponseRedirect(url)
|
|
|
|
|
|
# Imports all new workouts from SportTracks
|
|
@login_required()
|
|
def workout_getstravaworkout_all(request):
|
|
res = stravastuff.get_strava_workout_list(request.user)
|
|
if (res.status_code == 200):
|
|
r = getrower(request.user)
|
|
stravaids = [int(item['id']) for item in res.json()]
|
|
knownstravaids = uniqify([
|
|
w.uploadedtostrava for w in Workout.objects.filter(user=r)
|
|
])
|
|
newids = [stravaid for stravaid in stravaids if not stravaid in knownstravaids]
|
|
|
|
for stravaid in newids:
|
|
res = stravastuff.get_strava_workout(request.user,stravaid)
|
|
strokedata = res[1]
|
|
data = res[0]
|
|
|
|
if data:
|
|
id,message = add_workout_from_strokedata(
|
|
request.user,stravaid,data,strokedata,
|
|
source='strava',
|
|
workoutsource='strava')
|
|
|
|
if id==0:
|
|
messages.error(request,message)
|
|
|
|
else:
|
|
messages.info(request,"imported Strava workout "+str(stravaid))
|
|
w = Workout.objects.get(id=id)
|
|
w.uploadedtostrava=stravaid
|
|
w.save()
|
|
|
|
else:
|
|
messages.error(request,"Couldn't import Strava workout "+str(stravaid))
|
|
|
|
url = reverse(workouts_view)
|
|
return HttpResponseRedirect(url)
|
|
|
|
|
|
|
|
# Imports a workout from Concept2
|
|
@login_required()
|
|
def workout_getc2workout_view(request,c2id):
|
|
try:
|
|
thetoken = c2_open(request.user)
|
|
except C2NoTokenError:
|
|
return HttpResponseRedirect("/rowers/me/c2authorize/")
|
|
|
|
res = c2stuff.get_c2_workout(request.user,c2id)
|
|
if (res.status_code == 200):
|
|
data = res.json()['data']
|
|
splitdata = None
|
|
if 'workout' in data:
|
|
if 'splits' in data['workout']:
|
|
splitdata = data['workout']['splits']
|
|
if 'intervals' in data['workout']:
|
|
splitdata = data['workout']['intervals']
|
|
|
|
# Check if workout has stroke data, and get the stroke data
|
|
if data['stroke_data']:
|
|
res2 = c2stuff.get_c2_workout_strokes(request.user,c2id)
|
|
else:
|
|
distance = data['distance']
|
|
c2id = data['id']
|
|
workouttype = data['type']
|
|
verified = data['verified']
|
|
startdatetime = iso8601.parse_date(data['date'])
|
|
weightclass = data['weight_class']
|
|
weightcategory = 'hwt'
|
|
if weightclass == "L":
|
|
weightcategory = 'lwt'
|
|
totaltime = data['time']/10.
|
|
duration = dataprep.totaltime_sec_to_string(totaltime)
|
|
duration = datetime.datetime.strptime(duration,'%H:%M:%S.%f').time()
|
|
|
|
try:
|
|
timezone_str = data['timezone']
|
|
except:
|
|
timezone_str = 'UTC'
|
|
|
|
if timezone_str is None:
|
|
timezone_str = 'UTC'
|
|
|
|
workoutdate = startdatetime.astimezone(
|
|
pytz.timezone(timezone_str)
|
|
).strftime('%Y-%m-%d')
|
|
starttime = startdatetime.astimezone(
|
|
pytz.timezone(timezone_str)
|
|
).strftime('%H:%M:%S')
|
|
|
|
r = getrower(request.user)
|
|
|
|
id, message = dataprep.create_row_df(r,
|
|
distance,
|
|
duration,
|
|
startdatetime,
|
|
title = 'Imported from C2',
|
|
workouttype=workouttype)
|
|
|
|
w = Workout.objects.get(id=id)
|
|
w.uploadedtoc2 = c2id
|
|
w.save()
|
|
|
|
message = "This workout does not have any stroke data associated with it. We created synthetic stroke data."
|
|
messages.info(request,message)
|
|
url = reverse(r.defaultlandingpage,
|
|
kwargs = {
|
|
'id':int(id),
|
|
})
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
# We have stroke data
|
|
if res2.status_code == 200:
|
|
strokedata = pd.DataFrame.from_dict(res2.json()['data'])
|
|
# create the workout
|
|
id,message = add_workout_from_strokedata(request.user,c2id,data,strokedata,
|
|
source='c2')
|
|
w = Workout.objects.get(id=id)
|
|
w.uploadedtoc2=c2id
|
|
# If we have split data, update the stroke data so they
|
|
# match exactly (some users are anal about this)
|
|
if splitdata:
|
|
try:
|
|
w.summary,sa,results = c2stuff.summaryfromsplitdata(splitdata,data,w.csvfilename)
|
|
except:
|
|
sa = []
|
|
results = []
|
|
with open("media/c2splitdata.log","a") as errorlog:
|
|
errorstring = str(sys.exc_info()[0])
|
|
timestr = strftime("%Y%m%d-%H%M%S")
|
|
errorlog.write(timestr+errorstring+"\r\n")
|
|
errorlog.write("views.py line 952\r\n")
|
|
|
|
w.save()
|
|
|
|
from rowingdata.trainingparser import getlist
|
|
# set stroke data in CSV file
|
|
if sa:
|
|
values = getlist(sa)
|
|
units = getlist(sa,sel='unit')
|
|
types = getlist(sa,sel='type')
|
|
|
|
rowdata = rdata(w.csvfilename)
|
|
if rowdata:
|
|
rowdata.updateintervaldata(values,
|
|
units,types,results)
|
|
|
|
rowdata.write_csv(w.csvfilename,gzip=True)
|
|
dataprep.update_strokedata(w.id,rowdata.df)
|
|
|
|
if message:
|
|
messages.error(request,message)
|
|
|
|
r = getrower(request.user)
|
|
|
|
url = reverse(r.defaultlandingpage,
|
|
kwargs = {
|
|
'id':int(id),
|
|
})
|
|
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
# message = json.loads(s.text)['message']
|
|
message = json.loads(res2.text)['message']
|
|
messages.error(request,message)
|
|
|
|
url = reverse(workout_c2import_view)
|
|
return HttpResponseRedirect(url)
|
|
|
|
else:
|
|
message = "Received error code from Concept2"
|
|
messages.error(request,message)
|
|
if settings.DEBUG:
|
|
return HttpResponse(res)
|
|
else:
|
|
url = reverse(workout_c2import_view)
|
|
return HttpResponseRedirect(url)
|
|
|
|
@login_required
|
|
def workout_toggle_ranking(request,id=0):
|
|
is_ajax = False
|
|
if request.is_ajax():
|
|
is_ajax = True
|
|
|
|
row = get_workout_permitted(request.user,id)
|
|
|
|
row.rankingpiece = not row.rankingpiece
|
|
row.save()
|
|
|
|
if is_ajax:
|
|
return JSONResponse({'result':row.rankingpiece})
|
|
else:
|
|
url = reverse(workouts_view)
|
|
response = HttpResponseRedirect(url)
|
|
|
|
return response
|
|
|
|
|
|
# This is the main view for processing uploaded files
|
|
@login_required()
|
|
def workout_upload_view(request,
|
|
uploadoptions={
|
|
'makeprivate':False,
|
|
'make_plot':False,
|
|
'upload_to_C2':False,
|
|
'plottype':'timeplot',
|
|
'landingpage':'workout_edit_view',
|
|
},
|
|
docformoptions={
|
|
'workouttype':'rower',
|
|
}):
|
|
|
|
is_ajax = False
|
|
if request.is_ajax():
|
|
is_ajax = True
|
|
|
|
r = getrower(request.user)
|
|
|
|
if 'uploadoptions' in request.session:
|
|
uploadoptions = request.session['uploadoptions']
|
|
try:
|
|
defaultlandingpage = uploadoptions['landingpage']
|
|
except KeyError:
|
|
uploadoptions['landingpage'] = r.defaultlandingpage
|
|
defaultlandingpage = r.defaultlandingpage
|
|
else:
|
|
request.session['uploadoptions'] = uploadoptions
|
|
|
|
if 'docformoptions' in request.session:
|
|
docformoptions = request.session['docformoptions']
|
|
else:
|
|
request.session['docformoptions'] = docformoptions
|
|
|
|
try:
|
|
makeprivate = uploadoptions['makeprivate']
|
|
except KeyError:
|
|
makeprivate = False
|
|
try:
|
|
make_plot = uploadoptions['make_plot']
|
|
except KeyError:
|
|
make_plot = False
|
|
|
|
try:
|
|
workouttype = docformoptions['workouttype']
|
|
except KeyError:
|
|
workouttype = 'rower'
|
|
|
|
try:
|
|
plottype = uploadoptions['plottype']
|
|
except KeyError:
|
|
plottype = 'timeplot'
|
|
|
|
try:
|
|
landingpage = uploadoptions['landingpage']
|
|
except KeyError:
|
|
landingpage = r.defaultlandingpage
|
|
uploadoptions['landingpage'] = landingpage
|
|
|
|
try:
|
|
upload_to_c2 = uploadoptions['upload_to_C2']
|
|
except KeyError:
|
|
upload_to_c2 = False
|
|
|
|
try:
|
|
upload_to_strava = uploadoptions['upload_to_Strava']
|
|
except KeyError:
|
|
upload_to_strava = False
|
|
|
|
try:
|
|
upload_to_st = uploadoptions['upload_to_SportTracks']
|
|
except KeyError:
|
|
upload_to_st = False
|
|
|
|
try:
|
|
upload_to_rk = uploadoptions['upload_to_RunKeeper']
|
|
except KeyError:
|
|
upload_to_rk = False
|
|
|
|
try:
|
|
upload_to_ua = uploadoptions['upload_to_MapMyFitness']
|
|
except KeyError:
|
|
upload_to_ua = False
|
|
|
|
try:
|
|
upload_to_tp = uploadoptions['upload_to_TrainingPeaks']
|
|
except KeyError:
|
|
upload_to_tp = False
|
|
|
|
response = {}
|
|
if request.method == 'POST':
|
|
form = DocumentsForm(request.POST,request.FILES)
|
|
optionsform = UploadOptionsForm(request.POST)
|
|
|
|
if form.is_valid():
|
|
# f = request.FILES['file']
|
|
f = form.cleaned_data['file']
|
|
|
|
if f is not None:
|
|
res = handle_uploaded_file(f)
|
|
else:
|
|
messages.error(request,
|
|
"Something went wrong - no file attached")
|
|
url = reverse(workout_upload_view)
|
|
if is_ajax:
|
|
return JSONResponse({'result':0,'url':0})
|
|
else:
|
|
return HttpResponseRedirect(url)
|
|
|
|
t = form.cleaned_data['title']
|
|
workouttype = form.cleaned_data['workouttype']
|
|
request.session['docformoptions'] = {
|
|
'workouttype':workouttype,
|
|
}
|
|
|
|
notes = form.cleaned_data['notes']
|
|
offline = form.cleaned_data['offline']
|
|
|
|
if optionsform.is_valid():
|
|
make_plot = optionsform.cleaned_data['make_plot']
|
|
plottype = optionsform.cleaned_data['plottype']
|
|
upload_to_c2 = optionsform.cleaned_data['upload_to_C2']
|
|
upload_to_strava = optionsform.cleaned_data['upload_to_Strava']
|
|
upload_to_st = optionsform.cleaned_data['upload_to_SportTracks']
|
|
upload_to_rk = optionsform.cleaned_data['upload_to_RunKeeper']
|
|
upload_to_ua = optionsform.cleaned_data['upload_to_MapMyFitness']
|
|
upload_to_tp = optionsform.cleaned_data['upload_to_TrainingPeaks']
|
|
makeprivate = optionsform.cleaned_data['makeprivate']
|
|
landingpage = optionsform.cleaned_data['landingpage']
|
|
|
|
uploadoptions = {
|
|
'makeprivate':makeprivate,
|
|
'make_plot':make_plot,
|
|
'plottype':plottype,
|
|
'upload_to_C2':upload_to_c2,
|
|
'upload_to_Strava':upload_to_strava,
|
|
'upload_to_SportTracks':upload_to_st,
|
|
'upload_to_RunKeeper':upload_to_rk,
|
|
'upload_to_MapMyFitness':upload_to_ua,
|
|
'upload_to_TrainingPeaks':upload_to_tp,
|
|
'landingpage':landingpage,
|
|
}
|
|
|
|
|
|
request.session['uploadoptions'] = uploadoptions
|
|
|
|
f1 = res[0] # file name
|
|
f2 = res[1] # file name incl media directory
|
|
|
|
if not offline:
|
|
id,message,f2 = dataprep.new_workout_from_file(
|
|
r,f2,
|
|
workouttype=workouttype,
|
|
makeprivate=makeprivate,
|
|
title = t,
|
|
notes=''
|
|
)
|
|
else:
|
|
workoutsbox = Mailbox.objects.filter(name='workouts')[0]
|
|
uploadoptions['fromuploadform'] = True
|
|
bodyyaml = yaml.safe_dump(
|
|
uploadoptions,
|
|
default_flow_style=False
|
|
)
|
|
msg = Message(mailbox=workoutsbox,
|
|
from_header=r.user.email,
|
|
subject = t,body=bodyyaml)
|
|
msg.save()
|
|
f3 = 'media/mailbox_attachments/'+f2[6:]
|
|
copyfile(f2,f3)
|
|
f3 = f3[6:]
|
|
a = MessageAttachment(message=msg,document=f3)
|
|
a.save()
|
|
os.remove(f2)
|
|
|
|
messages.info(
|
|
request,
|
|
"The file was too large to process in real time. It will be processed in a background process. You will receive an email when it is ready")
|
|
url = reverse(workout_upload_view)
|
|
if is_ajax:
|
|
return JSONResponse({'result':1,'url':url})
|
|
else:
|
|
response = HttpResponseRedirect(url)
|
|
return response
|
|
|
|
if not id:
|
|
messages.error(request,message)
|
|
url = reverse(workout_upload_view)
|
|
if is_ajax:
|
|
return JSONResponse({'result':0,'url':url})
|
|
else:
|
|
response = HttpResponseRedirect(url)
|
|
return response
|
|
elif id == -1:
|
|
message = 'The zip archive will be processed in the background. The files in the archive will only be uploaded without the extra actions. You will receive email when the workouts are ready.'
|
|
messages.info(request,message)
|
|
url = reverse(workout_upload_view)
|
|
if is_ajax:
|
|
return JSONResponse({'result':1,'url':url})
|
|
else:
|
|
response = HttpResponseRedirect(url)
|
|
return response
|
|
else:
|
|
if message:
|
|
messages.error(request,message)
|
|
|
|
url = reverse(workout_edit_view,
|
|
kwargs = {
|
|
'id':int(id),
|
|
})
|
|
|
|
if is_ajax:
|
|
response = {'result': 1,'url':url}
|
|
else:
|
|
response = HttpResponseRedirect(url)
|
|
|
|
w = Workout.objects.get(id=id)
|
|
|
|
r = getrower(request.user)
|
|
if (make_plot):
|
|
res,jobid = uploads.make_plot(r,w,f1,f2,plottype,t)
|
|
if res == 0:
|
|
messages.error(request,jobid)
|
|
else:
|
|
try:
|
|
request.session['async_tasks'] += [(jobid,'make_plot')]
|
|
except KeyError:
|
|
request.session['async_tasks'] = [(jobid,'make_plot')]
|
|
|
|
# upload to C2
|
|
if (upload_to_c2):
|
|
try:
|
|
message,id = c2stuff.workout_c2_upload(request.user,w)
|
|
except C2NoTokenError:
|
|
id = 0
|
|
message = "Something went wrong with the Concept2 sync"
|
|
if id>1:
|
|
messages.info(request,message)
|
|
else:
|
|
messages.error(request,message)
|
|
|
|
if (upload_to_strava):
|
|
try:
|
|
message,id = stravastuff.workout_strava_upload(
|
|
request.user,w
|
|
)
|
|
except StravaNoTokenError:
|
|
id = 0
|
|
message = "Please connect to Strava first"
|
|
if id>1:
|
|
messages.info(request,message)
|
|
else:
|
|
messages.error(request,message)
|
|
|
|
if (upload_to_st):
|
|
try:
|
|
message,id = sporttracksstuff.workout_sporttracks_upload(
|
|
request.user,w
|
|
)
|
|
except SportTracksNoTokenError:
|
|
message = "Please connect to SportTracks first"
|
|
id = 0
|
|
if id>1:
|
|
messages.info(request,message)
|
|
else:
|
|
messages.error(request,message)
|
|
|
|
if (upload_to_rk):
|
|
try:
|
|
message,id = runkeeperstuff.workout_runkeeper_upload(
|
|
request.user,w
|
|
)
|
|
except RunKeeperNoTokenError:
|
|
message = "Please connect to Runkeeper first"
|
|
id = 0
|
|
|
|
if id>1:
|
|
messages.info(request,message)
|
|
else:
|
|
messages.error(request,message)
|
|
|
|
|
|
if (upload_to_ua):
|
|
try:
|
|
message,id = underarmourstuff.workout_ua_upload(
|
|
request.user,w
|
|
)
|
|
except UnderArmourNoTokenError:
|
|
message = "Please connect to MapMyFitness first"
|
|
id = 0
|
|
|
|
if id>1:
|
|
messages.info(request,message)
|
|
else:
|
|
messages.error(request,message)
|
|
|
|
|
|
if (upload_to_tp):
|
|
try:
|
|
message,id = tpstuff.workout_tp_upload(
|
|
request.user,w
|
|
)
|
|
except TPNoTokenError:
|
|
message = "Please connect to TrainingPeaks first"
|
|
id = 0
|
|
|
|
if id>1:
|
|
messages.info(request,message)
|
|
else:
|
|
messages.error(request,message)
|
|
|
|
if landingpage != 'workout_upload_view':
|
|
url = reverse(landingpage,
|
|
kwargs = {
|
|
'id':w.id,
|
|
})
|
|
else:
|
|
url = reverse(landingpage)
|
|
|
|
if is_ajax:
|
|
response = {'result':1,'url':url}
|
|
else:
|
|
response = HttpResponseRedirect(url)
|
|
else:
|
|
if not is_ajax:
|
|
response = render(request,
|
|
'document_form.html',
|
|
{'form':form,
|
|
'teams':get_my_teams(request.user),
|
|
'optionsform': optionsform,
|
|
})
|
|
|
|
|
|
if is_ajax:
|
|
return JSONResponse(response)
|
|
else:
|
|
return response
|
|
else:
|
|
if not is_ajax:
|
|
form = DocumentsForm(initial=docformoptions)
|
|
optionsform = UploadOptionsForm(initial=uploadoptions)
|
|
return render(request, 'document_form.html',
|
|
{'form':form,
|
|
'teams':get_my_teams(request.user),
|
|
'optionsform': optionsform,
|
|
})
|
|
else:
|
|
return {'result':0}
|
|
|
|
|
|
# This is the main view for processing uploaded files
|
|
@user_passes_test(iscoachmember,login_url="/",redirect_field_name=None)
|
|
def team_workout_upload_view(request,message="",
|
|
successmessage="",
|
|
uploadoptions={
|
|
'make_plot':False,
|
|
'plottype':'timeplot',
|
|
}):
|
|
|
|
if 'uploadoptions' in request.session:
|
|
uploadoptions = request.session['uploadoptions']
|
|
else:
|
|
request.session['uploadoptions'] = uploadoptions
|
|
|
|
|
|
myteams = Team.objects.filter(manager=request.user)
|
|
|
|
make_plot = uploadoptions['make_plot']
|
|
plottype = uploadoptions['plottype']
|
|
|
|
r = getrower(request.user)
|
|
if request.method == 'POST':
|
|
form = DocumentsForm(request.POST,request.FILES)
|
|
optionsform = TeamUploadOptionsForm(request.POST)
|
|
|
|
rowerform = TeamInviteForm(request.POST)
|
|
rowerform.fields.pop('email')
|
|
rowerform.fields['user'].queryset = User.objects.filter(rower__isnull=False,rower__team__in=myteams).distinct()
|
|
if form.is_valid():
|
|
f = request.FILES['file']
|
|
res = handle_uploaded_file(f)
|
|
t = form.cleaned_data['title']
|
|
offline = form.cleaned_data['offline']
|
|
if rowerform.is_valid():
|
|
u = rowerform.cleaned_data['user']
|
|
if u:
|
|
r = getrower(u)
|
|
else:
|
|
message = 'Please select a rower'
|
|
messages.error(request,message)
|
|
messages.info(request,successmessage)
|
|
response = render(request,
|
|
'team_document_form.html',
|
|
{'form':form,
|
|
'teams':get_my_teams(request.user),
|
|
'optionsform': optionsform,
|
|
'rowerform': rowerform,
|
|
})
|
|
|
|
return response
|
|
|
|
workouttype = form.cleaned_data['workouttype']
|
|
|
|
notes = form.cleaned_data['notes']
|
|
|
|
if optionsform.is_valid():
|
|
make_plot = optionsform.cleaned_data['make_plot']
|
|
plottype = optionsform.cleaned_data['plottype']
|
|
|
|
uploadoptions = {
|
|
'makeprivate':False,
|
|
'make_plot':make_plot,
|
|
'plottype':plottype,
|
|
'upload_to_C2':False,
|
|
}
|
|
|
|
|
|
request.session['uploadoptions'] = uploadoptions
|
|
|
|
f1 = res[0] # file name
|
|
f2 = res[1] # file name incl media directory
|
|
|
|
|
|
if not offline:
|
|
id,message,f2 = dataprep.new_workout_from_file(
|
|
r,f2,
|
|
workouttype=workouttype,
|
|
makeprivate=False,
|
|
title = t,
|
|
notes=''
|
|
)
|
|
else:
|
|
job = myqueue(
|
|
queuehigh,
|
|
handle_zip_file,
|
|
r.user.email,
|
|
t,
|
|
f2)
|
|
|
|
messages.info(
|
|
request,
|
|
"The file was too large to process in real time. It will be processed in a background process. The user will receive an email when it is ready"
|
|
)
|
|
|
|
|
|
url = reverse(team_workout_upload_view)
|
|
response = HttpResponseRedirect(url)
|
|
return response
|
|
|
|
|
|
if not id:
|
|
messages.error(request,message)
|
|
url = reverse(team_workout_upload_view)
|
|
response = HttpResponseRedirect(url)
|
|
return response
|
|
elif id == -1:
|
|
message = 'The zip archive will be processed in the background. The files in the archive will only be uploaded without the extra actions. You will receive email when the workouts are ready.'
|
|
messages.info(request,message)
|
|
url = reverse(team_workout_upload_view)
|
|
response = HttpResponseRedirect(url)
|
|
return response
|
|
|
|
else:
|
|
successmessage = "The workout was added to the user's account"
|
|
messages.info(request,successmessage)
|
|
|
|
url = reverse(team_workout_upload_view)
|
|
|
|
response = HttpResponseRedirect(url)
|
|
w = Workout.objects.get(id=id)
|
|
|
|
r = getrower(request.user)
|
|
if (make_plot):
|
|
id,jobid = uploads.make_plot(r,w,f1,f2,plottype,t)
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
response = render(request,
|
|
'team_document_form.html',
|
|
{'form':form,
|
|
'teams':get_my_teams(request.user),
|
|
'optionsform': optionsform,
|
|
'rowerform': rowerform,
|
|
})
|
|
|
|
return response
|
|
else:
|
|
form = DocumentsForm()
|
|
optionsform = TeamUploadOptionsForm(initial=uploadoptions)
|
|
rowerform = TeamInviteForm()
|
|
rowerform.fields.pop('email')
|
|
rowerform.fields['user'].queryset = User.objects.filter(rower__isnull=False,rower__team__in=myteams).distinct()
|
|
return render(request, 'team_document_form.html',
|
|
{'form':form,
|
|
'teams':get_my_teams(request.user),
|
|
'optionsform': optionsform,
|
|
'rowerform':rowerform,
|
|
})
|
|
|
|
|
|
|
|
# Ask the user if he really wants to delete the workout
|
|
@login_required()
|
|
def workout_delete_confirm_view(request, id=0):
|
|
try:
|
|
row = Workout.objects.get(id=id)
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
raise PermissionDenied("You are not allowed to delete this workout")
|
|
else:
|
|
url = request.META.get('HTTP_REFERER','/')
|
|
return render(request,'workout_delete_confirm.html',
|
|
{'id':int(id),
|
|
'teams':get_my_teams(request.user),
|
|
'workout':row,
|
|
'url':url})
|
|
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
# Really deleting the workout
|
|
@login_required()
|
|
def workout_delete_view(request,id=0):
|
|
try:
|
|
row = Workout.objects.get(id=id)
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
raise PermissionDenied("You are not allowed to delete this workout")
|
|
else:
|
|
# files are removed by pre-delete in models.py
|
|
row.delete()
|
|
|
|
messages.info(request,'Workout deleted')
|
|
try:
|
|
url = request.session['referer']
|
|
if 'rowers/workout/' in url:
|
|
url = reverse(workouts_view)
|
|
except KeyError:
|
|
url = reverse(workouts_view)
|
|
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
# Ask the user to confirm that he wants to delete a chart
|
|
@login_required()
|
|
def graph_delete_confirm_view(request, id=0):
|
|
try:
|
|
img = GraphImage.objects.get(id=id)
|
|
row = Workout.objects.get(id=img.workout.id)
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
raise PermissionDenied("You are not allowed to delete this workout")
|
|
else:
|
|
try:
|
|
url = request.session['referer']
|
|
except KeyError:
|
|
url = '/rowers/list-graphs'
|
|
request.session['referer'] = url
|
|
return render(request,'graphimage_delete_confirm.html',
|
|
{'id':int(id),
|
|
'teams':get_my_teams(request.user),
|
|
'graph':img,
|
|
'url':url})
|
|
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
except GraphImage.DoesNotExist:
|
|
raise Http404("The image doesn't exist")
|
|
|
|
# Really deleting the chart
|
|
@login_required()
|
|
def graph_delete_view(request,id=0):
|
|
try:
|
|
img = GraphImage.objects.get(id=id)
|
|
row = Workout.objects.get(id=img.workout.id)
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
raise PermissionDenied("You are not allowed to delete this graph")
|
|
else:
|
|
img.delete()
|
|
messages.info(request,'Graph deleted')
|
|
try:
|
|
url = request.session['referer']
|
|
except KeyError:
|
|
url = reverse(graphs_view)
|
|
return HttpResponseRedirect(url)
|
|
|
|
except GraphImage.DoesNotExist:
|
|
raise Http404("Graph Image doesn't exist")
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
# A page with all the recent graphs (searchable on workout name)
|
|
@login_required()
|
|
def graphs_view(request):
|
|
request.session['referer'] = reverse(graphs_view)
|
|
try:
|
|
r = getrower(request.user)
|
|
workouts = Workout.objects.filter(user=r).order_by("-date", "-starttime")
|
|
query = request.GET.get('q')
|
|
if query:
|
|
query_list = query.split()
|
|
workouts = workouts.filter(
|
|
reduce(operator.and_,
|
|
(Q(name__icontains=q) for q in query_list)) |
|
|
reduce(operator.and_,
|
|
(Q(notes__icontains=q) for q in query_list))
|
|
)
|
|
|
|
g = GraphImage.objects.filter(workout__in=workouts).order_by("-creationdatetime")
|
|
if (len(g)<=5):
|
|
return render(request, 'list_graphs.html',
|
|
{'graphs1': g[0:4],
|
|
'teams':get_my_teams(request.user),
|
|
})
|
|
else:
|
|
return render(request, 'list_graphs.html',
|
|
{'graphs1': g[0:5],
|
|
'teams':get_my_teams(request.user),
|
|
'graphs2': g[5:10]})
|
|
except Rower.DoesNotExist:
|
|
raise Http404("User has no rower instance")
|
|
|
|
# Show the chart (png image)
|
|
def graph_show_view(request,id):
|
|
try:
|
|
g = GraphImage.objects.get(id=id)
|
|
try:
|
|
width,height = Image.open(g.filename).size
|
|
g.width = width
|
|
g.height = height
|
|
g.save()
|
|
except:
|
|
pass
|
|
|
|
w = Workout.objects.get(id=g.workout.id)
|
|
r = Rower.objects.get(id=w.user.id)
|
|
|
|
return render(request,'show_graph.html',
|
|
{'graph':g,
|
|
'teams':get_my_teams(request.user),
|
|
'workout':w,
|
|
'rower':r,})
|
|
|
|
except GraphImage.DoesNotExist:
|
|
raise Http404("This graph doesn't exist")
|
|
except Workout.DoesNotExist:
|
|
raise Http404("This workout doesn't exist")
|
|
|
|
# Restore original stroke data and summary
|
|
@login_required()
|
|
def workout_summary_restore_view(request,id,message="",successmessage=""):
|
|
try:
|
|
row = Workout.objects.get(id=id)
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
raise PermissionDenied("You are not allowed to edit this workout")
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
s = ""
|
|
# still here - this is a workout we may edit
|
|
f1 = row.csvfilename
|
|
u = row.user.user
|
|
r = getrower(u)
|
|
powerperc = 100*np.array([r.pw_ut2,
|
|
r.pw_ut1,
|
|
r.pw_at,
|
|
r.pw_tr,r.pw_an])/r.ftp
|
|
|
|
ftp = float(r.ftp)
|
|
if row.workouttype in ('water','coastal'):
|
|
ftp = ftp*(100.-r.otwslack)/100.
|
|
|
|
rr = rrower(hrmax=r.max,hrut2=r.ut2,
|
|
hrut1=r.ut1,hrat=r.at,
|
|
hrtr=r.tr,hran=r.an,ftp=ftp,
|
|
powerperc=powerperc,powerzones=r.powerzones)
|
|
rowdata = rdata(f1,rower=rr)
|
|
if rowdata == 0:
|
|
raise Http404("Error: CSV Data File Not Found")
|
|
rowdata.restoreintervaldata()
|
|
rowdata.write_csv(f1,gzip=True)
|
|
dataprep.update_strokedata(id,rowdata.df)
|
|
intervalstats = rowdata.allstats()
|
|
row.summary = intervalstats
|
|
row.save()
|
|
itime,idist,itype = rowdata.intervalstats_values()
|
|
nrintervals = len(idist)
|
|
|
|
# create interactive plot
|
|
try:
|
|
res = interactive_chart(id,promember=1)
|
|
script = res[0]
|
|
div = res[1]
|
|
except ValueError:
|
|
pass
|
|
|
|
|
|
messages.info(request,'Original Interval Data Restored')
|
|
url = reverse(workout_summary_edit_view,
|
|
kwargs={
|
|
'id':int(id),
|
|
}
|
|
)
|
|
return HttpResponseRedirect(url)
|
|
|
|
# Split a workout
|
|
@user_passes_test(ispromember,login_url="/",redirect_field_name=None)
|
|
def workout_split_view(request,id=id):
|
|
row = get_workout_permitted(request.user,id)
|
|
|
|
r = row.user
|
|
|
|
if request.method == 'POST':
|
|
form = WorkoutSplitForm(request.POST)
|
|
if form.is_valid():
|
|
splittime = form.cleaned_data['splittime']
|
|
splitsecond = splittime.hour*3600
|
|
splitsecond += splittime.minute*60
|
|
splitsecond += splittime.second
|
|
splitsecond += splittime.microsecond/1.e6
|
|
splitmode = form.cleaned_data['splitmode']
|
|
ids,mesgs = dataprep.split_workout(r,row,splitsecond,splitmode)
|
|
for message in mesgs:
|
|
messages.info(request,message)
|
|
|
|
|
|
if request.user == r:
|
|
url = reverse(workouts_view)
|
|
else:
|
|
mgrids = [team.id for team in Team.objects.filter(manager=request.user)]
|
|
rwrids = [team.id for team in r.team.all()]
|
|
teamids = list(set(mgrids) & set(rwrids))
|
|
if len(teamids) > 0:
|
|
teamid = teamids[0]
|
|
|
|
url = reverse(workouts_view,
|
|
kwargs={
|
|
'teamid':int(teamid),
|
|
}
|
|
)
|
|
else:
|
|
url = reverse(workouts_view)
|
|
|
|
rowname = row.name
|
|
if isinstance(rowname,unicode):
|
|
rowname = rowname.encode('utf8')
|
|
elif isinstance(rowname, str):
|
|
rowname = rowname.decode('utf8')
|
|
|
|
qdict = {'q':rowname}
|
|
url+='?'+urllib.urlencode(qdict)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
form = WorkoutSplitForm()
|
|
|
|
# create interactive plot
|
|
try:
|
|
res = interactive_chart(id,promember=1)
|
|
script = res[0]
|
|
div = res[1]
|
|
except ValueError:
|
|
pass
|
|
|
|
return render(request, 'splitworkout.html',
|
|
{'form':form,
|
|
'workout':row,
|
|
'thediv':script,
|
|
'thescript':div,
|
|
})
|
|
|
|
|
|
# Fuse two workouts
|
|
@user_passes_test(ispromember,login_url="/",redirect_field_name=None)
|
|
def workout_fusion_view(request,id1=0,id2=1):
|
|
try:
|
|
w1 = Workout.objects.get(id=id1)
|
|
w2 = Workout.objects.get(id=id2)
|
|
r = w1.user
|
|
if (checkworkoutuser(request.user,w1)==False) or \
|
|
(checkworkoutuser(request.user,w2)==False):
|
|
raise PermissionDenied("You are not allowed to use these workouts")
|
|
except Workout.DoesNotExist:
|
|
raise Http404("One of the workouts doesn't exist")
|
|
|
|
if request.method == 'POST':
|
|
form = FusionMetricChoiceForm(request.POST,instance=w2)
|
|
if form.is_valid():
|
|
cd = form.cleaned_data
|
|
columns = cd['columns']
|
|
timeoffset = cd['offset']
|
|
posneg = cd['posneg']
|
|
if posneg == 'neg':
|
|
timeoffset = -timeoffset
|
|
|
|
# Create DataFrame
|
|
df,forceunit = dataprep.datafusion(id1,id2,columns,timeoffset)
|
|
|
|
|
|
idnew,message = dataprep.new_workout_from_df(r,df,
|
|
title='Fused data',
|
|
parent=w1,
|
|
forceunit=forceunit)
|
|
if message != None:
|
|
messages.error(request,message)
|
|
else:
|
|
successmessage = 'Data fused'
|
|
messages.info(request,message)
|
|
|
|
url = reverse(workout_edit_view,
|
|
kwargs={
|
|
'id':idnew,
|
|
})
|
|
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
return render(request, 'fusion.html',
|
|
{'form':form,
|
|
'teams':get_my_teams(request.user),
|
|
'workout1':w1,
|
|
'workout2':w2,
|
|
})
|
|
|
|
|
|
form = FusionMetricChoiceForm(instance=w2)
|
|
|
|
return render(request, 'fusion.html',
|
|
{'form':form,
|
|
'teams':get_my_teams(request.user),
|
|
'workout1':w1,
|
|
'workout2':w2,
|
|
})
|
|
|
|
|
|
# Edit the splits/summary
|
|
@login_required()
|
|
def workout_summary_edit_view(request,id,message="",successmessage=""
|
|
):
|
|
row = get_workout_permitted(request.user,id)
|
|
|
|
s = ""
|
|
# still here - this is a workout we may edit
|
|
f1 = row.csvfilename
|
|
u = row.user.user
|
|
r = getrower(u)
|
|
powerperc = 100*np.array([r.pw_ut2,
|
|
r.pw_ut1,
|
|
r.pw_at,
|
|
r.pw_tr,r.pw_an])/r.ftp
|
|
|
|
ftp = float(r.ftp)
|
|
if row.workouttype in ('water','coastal'):
|
|
ftp = ftp*(100.-r.otwslack)/100.
|
|
|
|
rr = rrower(hrmax=r.max,hrut2=r.ut2,
|
|
hrut1=r.ut1,hrat=r.at,
|
|
hrtr=r.tr,hran=r.an,ftp=ftp,
|
|
powerperc=powerperc,powerzones=r.powerzones)
|
|
rowdata = rdata(f1,rower=rr)
|
|
if rowdata == 0:
|
|
return HttpResponse("Error: CSV Data File Not Found")
|
|
intervalstats = rowdata.allstats()
|
|
itime,idist,itype = rowdata.intervalstats_values()
|
|
nrintervals = len(idist)
|
|
|
|
# create interactive plot
|
|
try:
|
|
res = interactive_chart(id,promember=1)
|
|
script = res[0]
|
|
div = res[1]
|
|
except ValueError:
|
|
pass
|
|
|
|
savebutton = 'nosavebutton'
|
|
|
|
# We have submitted the mini language interpreter
|
|
if request.method == 'POST' and "intervalstring" in request.POST:
|
|
form = SummaryStringForm(request.POST)
|
|
if form.is_valid():
|
|
cd = form.cleaned_data
|
|
s = cd["intervalstring"]
|
|
try:
|
|
rowdata.updateinterval_string(s)
|
|
except IndexError:
|
|
messages.error(request,'Interval string parse error')
|
|
intervalstats = rowdata.allstats()
|
|
itime,idist,itype = rowdata.intervalstats_values()
|
|
nrintervals = len(idist)
|
|
savebutton = 'savestringform'
|
|
|
|
# we are saving the results obtained from the mini language interpreter
|
|
elif request.method == 'POST' and "savestringform" in request.POST:
|
|
s = request.POST["savestringform"]
|
|
rowdata.updateinterval_string(s)
|
|
intervalstats = rowdata.allstats()
|
|
itime,idist,itype = rowdata.intervalstats_values()
|
|
nrintervals = len(idist)
|
|
row.summary = intervalstats
|
|
#intervalstats = rowdata.allstats()
|
|
if s:
|
|
try:
|
|
row.notes = u'{n} \n {s}'.format(
|
|
n = row.notes,
|
|
s = s
|
|
)
|
|
except TypeError:
|
|
pass
|
|
|
|
row.save()
|
|
rowdata.write_csv(f1,gzip=True)
|
|
dataprep.update_strokedata(id,rowdata.df)
|
|
messages.info(request,"Updated interval data saved")
|
|
data = {'intervalstring':s}
|
|
form = SummaryStringForm(initial=data)
|
|
savebutton = 'savestringform'
|
|
|
|
# we are saving the results obtained from the detailed form
|
|
elif request.method == 'POST' and "savedetailform" in request.POST:
|
|
savebutton = 'savedetailform'
|
|
form = SummaryStringForm()
|
|
nrintervals = int(request.POST['nrintervals'])
|
|
detailform = IntervalUpdateForm(request.POST,aantal=nrintervals)
|
|
itime = []
|
|
idist = []
|
|
itype = []
|
|
ivalues = []
|
|
iunits = []
|
|
itypes = []
|
|
iresults = []
|
|
for i in xrange(nrintervals):
|
|
try:
|
|
t = datetime.datetime.strptime(request.POST['intervalt_%s' % i],"%H:%M:%S.%f")
|
|
except ValueError:
|
|
t = datetime.datetime.strptime(request.POST['intervalt_%s' % i],"%H:%M:%S")
|
|
|
|
timesecs = 3600*t.hour+60*t.minute+t.second+t.microsecond/1.e6
|
|
itime += [timesecs]
|
|
idist += [int(request.POST['intervald_%s' % i])]
|
|
itype += [int(request.POST['type_%s' % i])]
|
|
|
|
if itype[i] == 3: # rest
|
|
itypes += ['rest']
|
|
ivalues += [timesecs]
|
|
iresults += [idist[i]]
|
|
iunits += ['seconds']
|
|
if itype[i] == 5 or itype[i] == 2: # distance based work
|
|
itypes += ['work']
|
|
ivalues += [idist[i]]
|
|
iresults += [timesecs]
|
|
iunits += ['meters']
|
|
if itype[i] == 4 or itype[i] == 1: # time based work
|
|
itypes += ['work']
|
|
ivalues += [timesecs]
|
|
iresults += [idist[i]]
|
|
iunits += ['seconds']
|
|
|
|
|
|
rowdata.updateintervaldata(ivalues,iunits,itypes,iresults=iresults)
|
|
intervalstats = rowdata.allstats()
|
|
row.summary = intervalstats
|
|
row.notes += "\n"+s
|
|
row.save()
|
|
rowdata.write_csv(f1,gzip=True)
|
|
dataprep.update_strokedata(id,rowdata.df)
|
|
messages.info(request,"Updated interval data saved")
|
|
|
|
form = SummaryStringForm()
|
|
|
|
# we are processing the details form
|
|
elif request.method == 'POST' and "nrintervals" in request.POST:
|
|
savebutton = 'savedetailform'
|
|
nrintervals = int(request.POST['nrintervals'])
|
|
detailform = IntervalUpdateForm(request.POST,aantal=nrintervals)
|
|
if detailform.is_valid():
|
|
cd = detailform.cleaned_data
|
|
itime = []
|
|
idist = []
|
|
itype = []
|
|
ivalues = []
|
|
iunits = []
|
|
itypes = []
|
|
iresults = []
|
|
for i in xrange(nrintervals):
|
|
t = cd['intervalt_%s' % i]
|
|
timesecs = t.total_seconds()
|
|
itime += [timesecs]
|
|
idist += [cd['intervald_%s' % i]]
|
|
itype += [cd['type_%s' % i]]
|
|
|
|
if itype[i] == '3': # rest
|
|
itypes += ['rest']
|
|
ivalues += [timesecs]
|
|
iresults += [idist[i]]
|
|
iunits += ['seconds']
|
|
if itype[i] == '5' or itype[i] == '2': # distance based work
|
|
itypes += ['work']
|
|
ivalues += [idist[i]]
|
|
iresults += [timesecs]
|
|
iunits += ['meters']
|
|
if itype[i] == '4' or itype[i] == '1': # time based work
|
|
itypes += ['work']
|
|
ivalues += [timesecs]
|
|
iresults += [idist[i]]
|
|
iunits += ['seconds']
|
|
|
|
rowdata.updateintervaldata(ivalues,iunits,
|
|
itypes,iresults=iresults)
|
|
intervalstats = rowdata.allstats()
|
|
|
|
|
|
form = SummaryStringForm()
|
|
|
|
else:
|
|
form = SummaryStringForm()
|
|
|
|
initial = {}
|
|
for i in xrange(nrintervals):
|
|
initial['intervald_%s' % i] = idist[i]
|
|
initial['intervalt_%s' % i] = get_time(itime[i])
|
|
initial['type_%s' % i] = itype[i]
|
|
|
|
|
|
detailform = IntervalUpdateForm(aantal=nrintervals,initial=initial)
|
|
|
|
|
|
# render page
|
|
|
|
return render(request, 'summary_edit.html',
|
|
{'form':form,
|
|
'detailform':detailform,
|
|
'workout':row,
|
|
'teams':get_my_teams(request.user),
|
|
'intervalstats':intervalstats,
|
|
'nrintervals':nrintervals,
|
|
'interactiveplot':script,
|
|
'the_div':div,
|
|
'intervalstring':s,
|
|
'savebutton':savebutton,
|
|
})
|
|
|
|
# Page where user can manage his favorite charts
|
|
@login_required()
|
|
def rower_favoritecharts_view(request):
|
|
message = ''
|
|
successmessage = ''
|
|
r = getrower(request.user)
|
|
favorites = FavoriteChart.objects.filter(user=r).order_by('id')
|
|
aantal = len(favorites)
|
|
favorites_data = [{'yparam1':f.yparam1,
|
|
'yparam2':f.yparam2,
|
|
'xparam':f.xparam,
|
|
'plottype':f.plottype,
|
|
'workouttype':f.workouttype,
|
|
'reststrokes':f.reststrokes,
|
|
'notes':f.notes,}
|
|
for f in favorites]
|
|
FavoriteChartFormSet = formset_factory(FavoriteForm,formset=BaseFavoriteFormSet,extra=0)
|
|
if aantal==0:
|
|
FavoriteChartFormSet = formset_factory(FavoriteForm,formset=BaseFavoriteFormSet,extra=1)
|
|
|
|
|
|
if request.method == 'POST':
|
|
favorites_formset = FavoriteChartFormSet(request.POST)
|
|
if favorites_formset.is_valid():
|
|
new_instances = []
|
|
for favorites_form in favorites_formset:
|
|
yparam1 = favorites_form.cleaned_data.get('yparam1')
|
|
yparam2 = favorites_form.cleaned_data.get('yparam2')
|
|
xparam = favorites_form.cleaned_data.get('xparam')
|
|
plottype = favorites_form.cleaned_data.get('plottype')
|
|
workouttype = favorites_form.cleaned_data.get('workouttype')
|
|
reststrokes = favorites_form.cleaned_data.get('reststrokes')
|
|
notes = favorites_form.cleaned_data.get('notes')
|
|
new_instances.append(FavoriteChart(user=r,
|
|
yparam1=yparam1,
|
|
yparam2=yparam2,
|
|
xparam=xparam,
|
|
plottype=plottype,
|
|
notes=notes,
|
|
workouttype=workouttype,
|
|
reststrokes=reststrokes))
|
|
try:
|
|
with transaction.atomic():
|
|
FavoriteChart.objects.filter(user=r).delete()
|
|
FavoriteChart.objects.bulk_create(new_instances)
|
|
successmessage = "You have updated your favorites"
|
|
messages.info(request,message)
|
|
if len(new_instances)==0:
|
|
FavoriteChartFormSet=formset_factory(FavoriteForm,formset=BaseFavoriteFormSet,extra=1)
|
|
favorites_formset = FavoriteChartFormSet()
|
|
except IntegrityError:
|
|
message = "something went wrong"
|
|
messages.error(request,message)
|
|
else:
|
|
favorites_formset = FavoriteChartFormSet(initial=favorites_data)
|
|
|
|
|
|
context = {
|
|
'favorites_formset':favorites_formset,
|
|
'teams':get_my_teams(request.user),
|
|
}
|
|
|
|
|
|
|
|
return render(request,'favoritecharts.html',context)
|
|
|
|
# page where user sets his export settings
|
|
@login_required()
|
|
def rower_exportsettings_view(request):
|
|
r = getrower(request.user)
|
|
if request.method == 'POST':
|
|
form = RowerExportForm(request.POST)
|
|
if form.is_valid():
|
|
stravaexportas = form.cleaned_data['stravaexportas']
|
|
r.stravaexportas = stravaexportas
|
|
r.save()
|
|
else:
|
|
form = RowerExportForm(instance=r)
|
|
return render(request, 'rower_exportsettings.html',
|
|
{'form':form,
|
|
'rower':r,
|
|
})
|
|
|
|
|
|
# Page where user can set his details
|
|
# Add email address to form so user can change his email address
|
|
@login_required()
|
|
def rower_edit_view(request,rowerid=0,message=""):
|
|
if rowerid==0:
|
|
r = getrower(request.user)
|
|
else:
|
|
r = Rower.objects.get(id=rowerid)
|
|
if not checkaccessuser(request.user,r):
|
|
raise PermissionDenied("You have no access to these user settings")
|
|
|
|
rowerid = r.id
|
|
|
|
if request.method == 'POST' and "ut2" in request.POST:
|
|
form = RowerForm(request.POST)
|
|
if form.is_valid():
|
|
# something
|
|
cd = form.cleaned_data
|
|
hrmax = cd['max']
|
|
ut2 = cd['ut2']
|
|
ut1 = cd['ut1']
|
|
at = cd['at']
|
|
tr = cd['tr']
|
|
an = cd['an']
|
|
rest = cd['rest']
|
|
try:
|
|
r.max = max(min(hrmax,250),10)
|
|
r.ut2 = max(min(ut2,250),10)
|
|
r.ut1 = max(min(ut1,250),10)
|
|
r.at = max(min(at,250),10)
|
|
r.tr = max(min(tr,250),10)
|
|
r.an = max(min(an,250),10)
|
|
r.rest = max(min(rest,250),10)
|
|
r.save()
|
|
successmessage = "Your Heart Rate data were changed"
|
|
messages.info(request,successmessage)
|
|
form = RowerForm(instance=r)
|
|
powerform = RowerPowerForm(instance=r)
|
|
powerzonesform = RowerPowerZonesForm(instance=r)
|
|
accountform = AccountRowerForm(instance=r)
|
|
userform = UserForm(instance=r.user)
|
|
return render(request, 'rower_form.html',
|
|
{'form':form,
|
|
'powerzonesform':powerzonesform,
|
|
'teams':get_my_teams(request.user),
|
|
'powerform':powerform,
|
|
'rower':r,
|
|
'accountform':accountform,
|
|
'userform':userform,
|
|
})
|
|
except Rower.DoesNotExist:
|
|
message = "Funny. This user doesn't exist."
|
|
messages.error(request,message)
|
|
url = reverse(workouts_view)
|
|
response = HttpResponseRedirect(url)
|
|
else:
|
|
message = HttpResponse("invalid form")
|
|
#form = RowerForm(instance=r)
|
|
powerform = RowerPowerForm(instance=r)
|
|
powerzonesform = RowerPowerZonesForm(instance=r)
|
|
userform = UserForm(instance=r.user)
|
|
accountform = AccountRowerForm(instance=r)
|
|
return render(request, 'rower_form.html',
|
|
{'form':form,
|
|
'teams':get_my_teams(request.user),
|
|
'powerzonesform':powerzonesform,
|
|
'userform':userform,
|
|
'accountform':accountform,
|
|
'powerform':powerform,
|
|
'rower':r,
|
|
})
|
|
|
|
|
|
|
|
return response
|
|
elif request.method == 'POST' and "ftp" in request.POST:
|
|
powerform = RowerPowerForm(request.POST)
|
|
if powerform.is_valid():
|
|
cd = powerform.cleaned_data
|
|
ftp = cd['ftp']
|
|
otwslack = cd['otwslack']
|
|
try:
|
|
powerfrac = 100*np.array([r.pw_ut2,
|
|
r.pw_ut1,
|
|
r.pw_at,
|
|
r.pw_tr,r.pw_an])/r.ftp
|
|
r.ftp = max(min(ftp,650),50)
|
|
r.otwslack = max(min(otwslack,50),0)
|
|
ut2,ut1,at,tr,an = (r.ftp*powerfrac/100.).astype(int)
|
|
r.pw_ut2 = ut2
|
|
r.pw_ut1 = ut1
|
|
r.pw_at = at
|
|
r.pw_tr = tr
|
|
r.pw_an = an
|
|
r.save()
|
|
message = "FTP and/or OTW slack values changed."
|
|
messages.info(request,message)
|
|
url = reverse(rower_edit_view,
|
|
kwargs = {
|
|
'rowerid':r.id,
|
|
})
|
|
response = HttpResponseRedirect(url)
|
|
except Rower.DoesNotExist:
|
|
message = "Funny. This user doesn't exist."
|
|
messages.error(request,message)
|
|
url = reverse(rower_edit_view)
|
|
response = HttpResponseRedirect(url)
|
|
else:
|
|
message = HttpResponse("invalid form")
|
|
form = RowerForm(instance=r)
|
|
#powerform = RowerPowerForm(instance=r)
|
|
powerzonesform = RowerPowerZonesForm(instance=r)
|
|
userform = UserForm(instance=r.user)
|
|
accountform = AccountRowerForm(instance=r)
|
|
return render(request, 'rower_form.html',
|
|
{'form':form,
|
|
'teams':get_my_teams(request.user),
|
|
'powerform':powerform,
|
|
'rower':r,
|
|
'userform':userform,
|
|
'accountform':accountform,
|
|
})
|
|
|
|
|
|
return response
|
|
elif request.method == 'POST' and "ut3name" in request.POST:
|
|
powerzonesform = RowerPowerZonesForm(request.POST)
|
|
if powerzonesform.is_valid():
|
|
cd = powerzonesform.cleaned_data
|
|
pw_ut2 = cd['pw_ut2']
|
|
pw_ut1 = cd['pw_ut1']
|
|
pw_at = cd['pw_at']
|
|
pw_tr = cd['pw_tr']
|
|
pw_an = cd['pw_an']
|
|
ut3name = cd['ut3name']
|
|
ut2name = cd['ut2name']
|
|
ut1name = cd['ut1name']
|
|
atname = cd['atname']
|
|
trname = cd['trname']
|
|
anname = cd['anname']
|
|
powerzones = [ut3name,ut2name,ut1name,atname,trname,anname]
|
|
try:
|
|
r.pw_ut2 = pw_ut2
|
|
r.pw_ut1 = pw_ut1
|
|
r.pw_at = pw_at
|
|
r.pw_tr = pw_tr
|
|
r.pw_an = pw_an
|
|
r.powerzones = powerzones
|
|
r.save()
|
|
successmessage = "Your Power Zone data were changed"
|
|
messages.info(request,successmessage)
|
|
form = RowerForm(instance=r)
|
|
accountform = AccountRowerForm(instance=r)
|
|
userform = UserForm(instance=r.user)
|
|
powerform = RowerPowerForm(instance=r)
|
|
powerzonesform = RowerPowerZonesForm(instance=r)
|
|
return render(request, 'rower_form.html',
|
|
{'form':form,
|
|
'teams':get_my_teams(request.user),
|
|
'powerzonesform':powerzonesform,
|
|
'powerform':powerform,
|
|
'userform':userform,
|
|
'accountform':accountform,
|
|
'rower':r,
|
|
})
|
|
except Rower.DoesNotExist:
|
|
message = "Funny. This user doesn't exist."
|
|
messages.error(request,message)
|
|
url = reverse(workouts_view)
|
|
response = HttpResponseRedirect(url)
|
|
return response
|
|
else:
|
|
form = RowerForm(instance=r)
|
|
powerform = RowerPowerForm(instance=r)
|
|
accountform = AccountRowerForm(instance=r)
|
|
userform = UserForm(instance=r.user)
|
|
#powerzonesform = RowerPowerZonesForm(instance=r)
|
|
message = HttpResponse("invalid form")
|
|
return render(request, 'rower_form.html',
|
|
{'form':form,
|
|
'teams':get_my_teams(request.user),
|
|
'powerform':powerform,
|
|
'powerzonesform':powerzonesform,
|
|
'accountform':accountform,
|
|
'userform':userform,
|
|
'rower':r,
|
|
})
|
|
elif request.method == 'POST' and "weightcategory" in request.POST:
|
|
accountform = AccountRowerForm(request.POST)
|
|
userform = UserForm(request.POST,instance=r.user)
|
|
if accountform.is_valid() and userform.is_valid():
|
|
# process
|
|
cd = accountform.cleaned_data
|
|
ucd = userform.cleaned_data
|
|
first_name = ucd['first_name']
|
|
last_name = ucd['last_name']
|
|
email = ucd['email']
|
|
sex = cd['sex']
|
|
defaultlandingpage = cd['defaultlandingpage']
|
|
weightcategory = cd['weightcategory']
|
|
birthdate = cd['birthdate']
|
|
showfavoritechartnotes = cd['showfavoritechartnotes']
|
|
getemailnotifications = cd['getemailnotifications']
|
|
defaulttimezone=cd['defaulttimezone']
|
|
u = r.user
|
|
if len(first_name):
|
|
u.first_name = first_name
|
|
u.last_name = last_name
|
|
if len(email): ## and check_email_freeforuse(u,email):
|
|
u.email = email
|
|
|
|
|
|
u.save()
|
|
r.defaulttimezone=defaulttimezone
|
|
r.weightcategory = weightcategory
|
|
r.getemailnotifications = getemailnotifications
|
|
r.defaultlandingpage = defaultlandingpage
|
|
r.showfavoritechartnotes = showfavoritechartnotes
|
|
r.sex = sex
|
|
r.birthdate = birthdate
|
|
r.save()
|
|
form = RowerForm(instance=r)
|
|
powerform = RowerPowerForm(instance=r)
|
|
powerzonesform = RowerPowerZonesForm(instance=r)
|
|
accountform = AccountRowerForm(instance=r)
|
|
userform = UserForm(instance=u)
|
|
successmessage = 'Account Information changed'
|
|
messages.info(request,successmessage)
|
|
return render(request, 'rower_form.html',
|
|
{'form':form,
|
|
'teams':get_my_teams(request.user),
|
|
'powerzonesform':powerzonesform,
|
|
'powerform':powerform,
|
|
'accountform':accountform,
|
|
'userform':userform,
|
|
'rower':r,
|
|
})
|
|
else:
|
|
form = RowerForm(instance=r)
|
|
powerform = RowerPowerForm(instance=r)
|
|
powerzonesform = RowerPowerZonesForm(instance=r)
|
|
return render(request, 'rower_form.html',
|
|
{'form':form,
|
|
'teams':get_my_teams(request.user),
|
|
'powerzonesform':powerzonesform,
|
|
'powerform':powerform,
|
|
'accountform':accountform,
|
|
'userform':userform,
|
|
'rower':r,
|
|
})
|
|
|
|
|
|
else:
|
|
try:
|
|
form = RowerForm(instance=r)
|
|
powerform = RowerPowerForm(instance=r)
|
|
powerzonesform = RowerPowerZonesForm(instance=r)
|
|
accountform = AccountRowerForm(instance=r)
|
|
userform = UserForm(instance=r.user)
|
|
grants = AccessToken.objects.filter(user=request.user)
|
|
return render(request, 'rower_form.html',
|
|
{
|
|
'form':form,
|
|
'teams':get_my_teams(request.user),
|
|
'powerform':powerform,
|
|
'powerzonesform':powerzonesform,
|
|
'userform':userform,
|
|
'accountform':accountform,
|
|
'grants':grants,
|
|
'rower':r,
|
|
})
|
|
except Rower.DoesNotExist:
|
|
raise Http404("This user doesn't exist")
|
|
|
|
# Revoke an app that you granted access through the API.
|
|
# this views is called when you press a button on the User edit page
|
|
# the button is only there when you have granted access to an app
|
|
@login_required()
|
|
def rower_revokeapp_view(request,id=0):
|
|
try:
|
|
tokens = AccessToken.objects.filter(user=request.user,application=id)
|
|
refreshtokens = AccessToken.objects.filter(user=request.user,application=id)
|
|
for token in tokens:
|
|
token.revoke()
|
|
for token in refreshtokens:
|
|
token.revoke()
|
|
|
|
r = getrower(request.user)
|
|
form = RowerForm(instance=r)
|
|
powerform = RowerPowerForm(instance=r)
|
|
grants = AccessToken.objects.filter(user=request.user)
|
|
return render(request, 'rower_form.html',
|
|
{
|
|
'form':form,
|
|
'teams':get_my_teams(request.user),
|
|
'powerform':powerform,
|
|
'grants':grants,
|
|
})
|
|
except AccessToken.DoesNotExist:
|
|
raise Http404("Access token doesn't exist")
|
|
|
|
|
|
|
|
# Creates unix time stamp from a datetime object
|
|
def totimestamp(dt, epoch=datetime.datetime(1970,1,1,tzinfo=tz('UTC'))):
|
|
td = dt - epoch
|
|
# return td.total_seconds()
|
|
return (td.microseconds + (td.seconds + td.days * 86400) * 10**6) / 10**6
|
|
# Check if a column of a dataframe has the required (aantal)
|
|
# number of elements. Also checks if the column is a numerical type
|
|
# Replaces any faulty columns with zeros
|
|
def trydf(df,aantal,column):
|
|
try:
|
|
s = df[column]
|
|
if len(s) != aantal:
|
|
return np.zeros(aantal)
|
|
if not np.issubdtype(s,np.number):
|
|
return np.zeros(aantal)
|
|
except KeyError:
|
|
s = np.zeros(aantal)
|
|
|
|
return s
|
|
|
|
# Stroke data form to test API upload
|
|
@login_required()
|
|
def strokedataform(request,id=0):
|
|
if request.method == 'GET':
|
|
form = StrokeDataForm()
|
|
return render(request, 'strokedata_form.html',
|
|
{
|
|
'form':form,
|
|
'teams':get_my_teams(request.user),
|
|
'id':int(id),
|
|
})
|
|
elif request.method == 'POST':
|
|
form = StrokeDataForm()
|
|
|
|
return render(request, 'strokedata_form.html',
|
|
{
|
|
'form':form,
|
|
'teams':get_my_teams(request.user),
|
|
'id':int(id),
|
|
})
|
|
|
|
# Process the POSTed stroke data according to the API definition
|
|
# Return the GET stroke data according to the API definition
|
|
from rest_framework_swagger.renderers import OpenAPIRenderer, SwaggerUIRenderer
|
|
|
|
@csrf_exempt
|
|
@login_required()
|
|
@api_view(['GET','POST'])
|
|
def strokedatajson(request,id):
|
|
"""
|
|
POST: Add Stroke data to workout
|
|
GET: Get stroke data of workout
|
|
"""
|
|
row = get_workout_permitted(request.user,id)
|
|
|
|
try:
|
|
id = int(id)
|
|
except ValueError:
|
|
return HttpResponse("Not a valid workout number",status=400)
|
|
|
|
|
|
if request.method == 'GET':
|
|
# currently only returns a subset.
|
|
columns = ['spm','time','hr','pace','power','distance']
|
|
datadf = dataprep.getsmallrowdata_db(columns,ids=[id])
|
|
with open('media/apilog.log','a') as logfile:
|
|
logfile.write(str(timezone.now())+": ")
|
|
logfile.write(request.user.username+"(GET) \n")
|
|
return JSONResponse(datadf)
|
|
|
|
if request.method == 'POST':
|
|
checkdata,r = dataprep.getrowdata_db(id=row.id)
|
|
if not checkdata.empty:
|
|
return HttpResponse("Duplicate Error",409)
|
|
# strokedata = request.POST['strokedata']
|
|
# checking/validating and cleaning
|
|
try:
|
|
strokedata = json.loads(request.POST['strokedata'])
|
|
except:
|
|
return HttpResponse("No JSON object could be decoded",400)
|
|
|
|
df = pd.DataFrame(strokedata)
|
|
df.index = df.index.astype(int)
|
|
df.sort_index(inplace=True)
|
|
# time, hr, pace, spm, power, drivelength, distance, drivespeed, dragfactor, strokerecoverytime, averagedriveforce, peakdriveforce, lapidx
|
|
time = df['time']/1.e3
|
|
aantal = len(time)
|
|
pace = df['pace']/1.e3
|
|
if len(pace) != aantal:
|
|
return HttpResponse("Pace array has incorrect length",status=400)
|
|
distance = df['distance']
|
|
if len(distance) != aantal:
|
|
return HttpResponse("Distance array has incorrect length",status=400)
|
|
|
|
spm = df['spm']
|
|
if len(spm) != aantal:
|
|
return HttpResponse("SPM array has incorrect length",status=400)
|
|
|
|
res = dataprep.testdata(time,distance,pace,spm)
|
|
if not res:
|
|
return HttpResponse("Data are not numerical",status=400)
|
|
|
|
power = trydf(df,aantal,'power')
|
|
drivelength = trydf(df,aantal,'drivelength')
|
|
drivespeed = trydf(df,aantal,'drivespeed')
|
|
dragfactor = trydf(df,aantal,'dragfactor')
|
|
drivetime = trydf(df,aantal,'drivetime')
|
|
strokerecoverytime = trydf(df,aantal,'strokerecoverytime')
|
|
averagedriveforce = trydf(df,aantal,'averagedriveforce')
|
|
peakdriveforce = trydf(df,aantal,'peakdriveforce')
|
|
lapidx = trydf(df,aantal,'lapidx')
|
|
hr = trydf(df,aantal,'hr')
|
|
|
|
starttime = totimestamp(row.startdatetime)+time
|
|
unixtime = starttime+time
|
|
|
|
with open('media/apilog.log','a') as logfile:
|
|
logfile.write(starttime+": ")
|
|
logfile.write(request.user.username+"(POST) \r\n")
|
|
|
|
data = pd.DataFrame({'TimeStamp (sec)':unixtime,
|
|
' Horizontal (meters)': distance,
|
|
' Cadence (stokes/min)':spm,
|
|
' HRCur (bpm)':hr,
|
|
' DragFactor':dragfactor,
|
|
' Stroke500mPace (sec/500m)':pace,
|
|
' Power (watts)':power,
|
|
' DriveLength (meters)':drivelength,
|
|
' DriveTime (ms)':drivetime,
|
|
' StrokeRecoveryTime (ms)':strokerecoverytime,
|
|
' AverageDriveForce (lbs)':averagedriveforce,
|
|
' PeakDriveForce (lbs)':peakdriveforce,
|
|
' lapIdx':lapidx,
|
|
' ElapsedTime (sec)':time,
|
|
})
|
|
|
|
# Following part should be replaced with dataprep.new_workout_from_df
|
|
|
|
timestr = row.startdatetime.strftime("%Y%m%d-%H%M%S")
|
|
csvfilename ='media/Import_'+timestr+'.csv'
|
|
|
|
res = data.to_csv(csvfilename+'.gz',index_label='index',
|
|
compression='gzip')
|
|
row.csvfilename = csvfilename
|
|
row.save()
|
|
|
|
r = getrower(request.user)
|
|
powerperc = 100*np.array([r.pw_ut2,
|
|
r.pw_ut1,
|
|
r.pw_at,
|
|
r.pw_tr,r.pw_an])/r.ftp
|
|
|
|
ftp = float(r.ftp)
|
|
if row.workouttype in ('water','coastal'):
|
|
ftp = ftp*(100.-r.otwslack)/100.
|
|
|
|
rr = rrower(hrmax=r.max,hrut2=r.ut2,
|
|
hrut1=r.ut1,hrat=r.at,
|
|
hrtr=r.tr,hran=r.an,ftp=ftp,
|
|
powerperc=powerperc,powerzones=r.powerzones)
|
|
rowdata = rdata(row.csvfilename,rower=rr).df
|
|
|
|
datadf = dataprep.dataprep(rowdata,id=row.id,bands=True,barchart=True,otwpower=True,empower=True)
|
|
# mangling
|
|
|
|
#
|
|
return HttpResponse(row.id,status=201)
|
|
|
|
#Method not supported
|
|
return HttpResponseNotAllowed("Method not supported")
|
|
|
|
|
|
# Teams related views
|
|
import teams
|
|
|
|
@login_required()
|
|
def team_view(request,id=0):
|
|
ismember = 0
|
|
hasrequested = 0
|
|
r = getrower(request.user)
|
|
myteams = Team.objects.filter(manager=request.user)
|
|
|
|
try:
|
|
t = Team.objects.get(id=id)
|
|
except Team.DoesNotExist:
|
|
raise Http404("Team doesn't exist")
|
|
|
|
|
|
if request.method == 'POST' and request.user == t.manager:
|
|
inviteform = TeamInviteForm(request.POST)
|
|
inviteform.fields['user'].queryset = User.objects.filter(rower__isnull=False,rower__team__in=myteams).distinct().exclude(rower__team__name=t.name)
|
|
if inviteform.is_valid():
|
|
cd = inviteform.cleaned_data
|
|
newmember = cd['user']
|
|
email = cd['email']
|
|
inviteid,text = teams.create_invite(t,t.manager,
|
|
user=newmember,
|
|
email=email)
|
|
if inviteid:
|
|
teams.send_invite_email(inviteid)
|
|
successmessage = text
|
|
messages.info(request,successmessage)
|
|
else:
|
|
message = text
|
|
messages.error(request,message)
|
|
|
|
elif request.user == t.manager:
|
|
inviteform = TeamInviteForm()
|
|
inviteform.fields['user'].queryset = User.objects.filter(rower__isnull=False,rower__team__in=myteams).distinct().exclude(rower__team__name=t.name)
|
|
else:
|
|
inviteform = ''
|
|
|
|
members = Rower.objects.filter(team=t).order_by('user__last_name','user__first_name')
|
|
thisteammyrequests = TeamRequest.objects.filter(team=t,user=request.user)
|
|
if len(thisteammyrequests):
|
|
hasrequested = 1
|
|
|
|
if r in members:
|
|
ismember = 1
|
|
|
|
|
|
|
|
return render(request, 'team.html',
|
|
{
|
|
'team':t,
|
|
'teams':get_my_teams(request.user),
|
|
'members':members,
|
|
'inviteform':inviteform,
|
|
'ismember':ismember,
|
|
'hasrequested':hasrequested,
|
|
})
|
|
|
|
@login_required()
|
|
def team_leaveconfirm_view(request,id=0):
|
|
try:
|
|
t = Team.objects.get(id=id)
|
|
except Team.DoesNotExist:
|
|
raise Http404("Team doesn't exist")
|
|
|
|
return render(request,'teamleaveconfirm.html',
|
|
{
|
|
'team':t,
|
|
'teams':get_my_teams(request.user),
|
|
})
|
|
|
|
@login_required()
|
|
def rower_calcdps_view(request):
|
|
r = getrower(request.user)
|
|
|
|
ws = [(w.id,w.csvfilename) for w in Workout.objects.filter(user=r)]
|
|
res = myqueue(queue,handle_updatedps,r.user.email,ws,debug=False)
|
|
|
|
messages.info(request,"Your workouts are being updated in the background. You will receive email when this is done.")
|
|
|
|
url = reverse(workouts_view)
|
|
return HttpResponseRedirect(url)
|
|
|
|
@login_required()
|
|
def team_leave_view(request,id=0):
|
|
r = getrower(request.user)
|
|
teams.remove_member(id,r)
|
|
|
|
url = reverse(rower_teams_view)
|
|
response = HttpResponseRedirect(url)
|
|
return response
|
|
|
|
from rowers.forms import TeamInviteCodeForm
|
|
|
|
@login_required()
|
|
def rower_teams_view(request,message='',successmessage=''):
|
|
if request.method == 'POST':
|
|
form = TeamInviteCodeForm(request.POST)
|
|
if form.is_valid():
|
|
code = form.cleaned_data['code']
|
|
res,text = teams.process_invite_code(request.user,code)
|
|
if res:
|
|
successmessage = text
|
|
else:
|
|
message = text
|
|
else:
|
|
form = TeamInviteCodeForm()
|
|
|
|
r = getrower(request.user)
|
|
ts = Team.objects.filter(rower=r)
|
|
myteams = Team.objects.filter(manager=request.user)
|
|
otherteams = Team.objects.filter(private='open').exclude(rower=r).exclude(manager=request.user).order_by('name')
|
|
teams.remove_expired_invites()
|
|
|
|
invites = TeamInvite.objects.filter(user=request.user)
|
|
requests = TeamRequest.objects.filter(user=request.user)
|
|
myrequests = TeamRequest.objects.filter(team__in=myteams)
|
|
myinvites = TeamInvite.objects.filter(team__in=myteams)
|
|
clubsize = teams.count_invites(request.user)+teams.count_club_members(request.user)
|
|
max_clubsize = r.clubsize
|
|
|
|
messages.info(request,successmessage)
|
|
messages.error(request,message)
|
|
return render(request, 'teams.html',
|
|
{
|
|
'teams':ts,
|
|
'clubsize':clubsize,
|
|
'max_clubsize':max_clubsize,
|
|
'myteams':myteams,
|
|
'invites':invites,
|
|
'otherteams':otherteams,
|
|
'requests':requests,
|
|
'myrequests':myrequests,
|
|
'form':form,
|
|
'myinvites':myinvites,
|
|
})
|
|
|
|
@user_passes_test(iscoachmember,login_url="/",redirect_field_name=None)
|
|
def invitation_revoke_view(request,id):
|
|
res,text = teams.revoke_invite(request.user,id)
|
|
if res:
|
|
messages.info(request,text)
|
|
successmessage = text
|
|
else:
|
|
message = text
|
|
messages.error(request,text)
|
|
|
|
url = reverse(rower_teams_view)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
@user_passes_test(iscoachmember,login_url="/",redirect_field_name=None)
|
|
def manager_member_drop_view(request,teamid,userid,
|
|
message='',successmessage=''):
|
|
rower = Rower.objects.get(user__id=userid)
|
|
res, text = teams.mgr_remove_member(teamid,request.user,rower)
|
|
if res:
|
|
messages.info(request,text)
|
|
else:
|
|
messages.error(request,text)
|
|
|
|
url = reverse(rower_teams_view)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
@user_passes_test(iscoachmember,login_url="/",redirect_field_name=None)
|
|
def manager_requests_view(request,code=None,message='',successmessage=''):
|
|
if code:
|
|
res,text = teams.process_request_code(request.user,code)
|
|
if res:
|
|
successmessage = text
|
|
message = ''
|
|
else:
|
|
message = text
|
|
successmessage = ''
|
|
|
|
messages.info(request,successmessage)
|
|
messages.error(request,message)
|
|
url = reverse(rower_teams_view,kwargs={
|
|
})
|
|
return HttpResponseRedirect(url)
|
|
|
|
|
|
@login_required()
|
|
def team_requestmembership_view(request,teamid,userid):
|
|
try:
|
|
t = Team.objects.get(id=teamid)
|
|
except Team.DoesNotExist:
|
|
raise Http404("Team doesn't exist")
|
|
|
|
res,text = teams.create_request(t,userid)
|
|
if res:
|
|
messages.info(request,text)
|
|
else:
|
|
messages.error(request,text)
|
|
|
|
url = reverse(team_view,kwargs={
|
|
'id':int(teamid),
|
|
})
|
|
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
@login_required()
|
|
def request_revoke_view(request,id=0):
|
|
res,text = teams.revoke_request(request.user,id)
|
|
|
|
if res:
|
|
messages.info(request,text)
|
|
|
|
else:
|
|
messages.error(request,text)
|
|
|
|
url = reverse(rower_teams_view)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
@user_passes_test(iscoachmember,login_url="/",redirect_field_name=None)
|
|
def request_reject_view(request,id=0):
|
|
res,text = teams.reject_request(request.user,id)
|
|
|
|
if res:
|
|
messages.info(request,text)
|
|
else:
|
|
messages.error(request,text)
|
|
|
|
url = reverse(rower_teams_view)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
@user_passes_test(iscoachmember,login_url="/",redirect_field_name=None)
|
|
def invitation_reject_view(request,id=0):
|
|
res,text = teams.reject_invitation(request.user,id)
|
|
|
|
if res:
|
|
messages.info(request,text)
|
|
else:
|
|
messages.error(request,text)
|
|
|
|
url = reverse(rower_teams_view)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
@login_required()
|
|
def rower_invitations_view(request,code=None,message='',successmessage=''):
|
|
|
|
if code:
|
|
teams.remove_expired_invites()
|
|
res,text = teams.process_invite_code(request.user,code)
|
|
if res:
|
|
messages.info(request,text)
|
|
teamid=res
|
|
url = reverse(team_view,kwargs={
|
|
'id':teamid,
|
|
})
|
|
else:
|
|
messages.error(request,text)
|
|
|
|
url = reverse(rower_teams_view)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
url = reverse(rower_teams_view,kwargs={
|
|
})
|
|
return HttpResponseRedirect(url)
|
|
|
|
@user_passes_test(iscoachmember,login_url="/",redirect_field_name=None)
|
|
def team_edit_view(request,id=0):
|
|
try:
|
|
t = Team.objects.get(id=id)
|
|
except Team.DoesNotExist:
|
|
raise Http404("Team does not exist")
|
|
|
|
if request.method == 'POST':
|
|
teamcreateform = TeamForm(request.POST,instance=t)
|
|
if teamcreateform.is_valid():
|
|
cd = teamcreateform.cleaned_data
|
|
name = cd['name']
|
|
notes = cd['notes']
|
|
manager = request.user
|
|
private = cd['private']
|
|
viewing = cd['viewing']
|
|
res,message=teams.update_team(t,name,manager,private,notes,
|
|
viewing)
|
|
if res:
|
|
messages.info(request,message)
|
|
else:
|
|
messages.error(request,message)
|
|
|
|
url = reverse(team_view,
|
|
kwargs={
|
|
'id':int(id),
|
|
}
|
|
)
|
|
|
|
response = HttpResponseRedirect(url)
|
|
return response
|
|
|
|
else:
|
|
teamcreateform = TeamForm(instance=t)
|
|
|
|
return render(request,'teamedit.html',
|
|
{
|
|
'form':teamcreateform,
|
|
'teams':get_my_teams(request.user),
|
|
'team':t,
|
|
})
|
|
|
|
@user_passes_test(iscoachmember,login_url="/",redirect_field_name=None)
|
|
def team_create_view(request):
|
|
if request.method == 'POST':
|
|
teamcreateform = TeamForm(request.POST)
|
|
if teamcreateform.is_valid():
|
|
cd = teamcreateform.cleaned_data
|
|
name = cd['name']
|
|
notes = cd['notes']
|
|
manager = request.user
|
|
private = cd['private']
|
|
viewing = cd['viewing']
|
|
res,message=teams.create_team(name,manager,private,notes,
|
|
viewing)
|
|
url = reverse(rower_teams_view)
|
|
response = HttpResponseRedirect(url)
|
|
return response
|
|
|
|
else:
|
|
teamcreateform = TeamForm()
|
|
|
|
return render(request,'teamcreate.html',
|
|
{
|
|
'teams':get_my_teams(request.user),
|
|
'form':teamcreateform,
|
|
})
|
|
|
|
@user_passes_test(iscoachmember,login_url="/",redirect_field_name=None)
|
|
def team_deleteconfirm_view(request,id):
|
|
r = getrower(request.user)
|
|
try:
|
|
t = Team.objects.get(id=id)
|
|
except Team.DoesNotExist:
|
|
raise Http404("This team doesn't exist")
|
|
if t.manager != request.user:
|
|
raise PermissionDenied("You are not allowed to delete this team")
|
|
|
|
return render(request,'teamdeleteconfirm.html',
|
|
{
|
|
'teams':get_my_teams(request.user),
|
|
'team':t
|
|
})
|
|
|
|
@user_passes_test(iscoachmember,login_url="/",redirect_field_name=None)
|
|
def team_delete_view(request,id):
|
|
r = getrower(request.user)
|
|
try:
|
|
t = Team.objects.get(id=id)
|
|
except Team.DoesNotExist:
|
|
raise Http404("This team doesn't exist")
|
|
if t.manager != request.user:
|
|
raise PermissionDenied("You are not allowed to delete this team")
|
|
|
|
teams.remove_team(t.id)
|
|
|
|
url = reverse(rower_teams_view)
|
|
response = HttpResponseRedirect(url)
|
|
return response
|
|
|
|
@user_passes_test(iscoachmember,login_url="/",redirect_field_name=None)
|
|
def team_members_stats_view(request,id):
|
|
r = getrower(request.user)
|
|
try:
|
|
t = Team.objects.get(id=id)
|
|
except Team.DoesNotExist:
|
|
raise Http404("This team doesn't exist")
|
|
if t.manager != request.user:
|
|
raise PermissionDenied("You are not allowed to see this page")
|
|
|
|
members = Rower.objects.filter(team=t).order_by("user__last_name","user__first_name")
|
|
|
|
theusers = [member.user for member in members]
|
|
|
|
|
|
|
|
response = render(request,'teamstats.html',
|
|
{
|
|
'teams':get_my_teams(request.user),
|
|
'team':t,
|
|
'theusers':theusers,
|
|
})
|
|
|
|
return response
|
|
|
|
from rowers.models import C2WorldClassAgePerformance
|
|
|
|
def agegroupcpview(request,age,normalize=0):
|
|
script,div = interactive_agegroupcpchart(age,normalized=normalize)
|
|
|
|
response = render(request,'agegroupcp.html',
|
|
{
|
|
'interactiveplot':script,
|
|
'the_div':div,
|
|
}
|
|
)
|
|
|
|
return response
|
|
|
|
def agegrouprecordview(request,sex='male',weightcategory='hwt',
|
|
distance=2000,duration=None):
|
|
if not duration:
|
|
df = pd.DataFrame(
|
|
list(
|
|
C2WorldClassAgePerformance.objects.filter(
|
|
distance=distance,
|
|
sex=sex,
|
|
weightcategory=weightcategory
|
|
).values()
|
|
)
|
|
)
|
|
else:
|
|
duration = int(duration)*60
|
|
df = pd.DataFrame(
|
|
list(
|
|
C2WorldClassAgePerformance.objects.filter(
|
|
duration=duration,
|
|
sex=sex,
|
|
weightcategory=weightcategory
|
|
).values()
|
|
)
|
|
)
|
|
|
|
|
|
script,div = interactive_agegroup_plot(df,sex=sex,distance=distance,
|
|
duration=duration,
|
|
weightcategory=weightcategory)
|
|
|
|
return render(request, 'agegroupchart.html',
|
|
{
|
|
'interactiveplot':script,
|
|
'the_div':div,
|
|
})
|
|
|
|
# Cloning sessions
|
|
@user_passes_test(hasplannedsessions,login_url="/rowers/planmembership/",
|
|
redirect_field_name=None)
|
|
def plannedsession_multiclone_view(
|
|
request,timeperiod='none',
|
|
rowerid=0,
|
|
startdate=timezone.now()-datetime.timedelta(days=30),
|
|
enddate=timezone.now()+datetime.timedelta(days=1)):
|
|
|
|
if rowerid==0:
|
|
r = getrower(request.user)
|
|
else:
|
|
try:
|
|
r = Rower.objects.get(id=rowerid)
|
|
except Rower.DoesNotExist:
|
|
raise Http404("This rower doesn't exist")
|
|
if not checkaccessuser(request.user,r):
|
|
raise PermissionDenied("You don't have access to this plan")
|
|
|
|
if 'startdate' in request.session:
|
|
startdate = iso8601.parse_date(request.session['startdate'])
|
|
|
|
|
|
if 'enddate' in request.session:
|
|
enddate = iso8601.parse_date(request.session['enddate'])
|
|
|
|
if timeperiod != 'none':
|
|
startdate,enddate = get_dates_timeperiod(timeperiod)
|
|
|
|
|
|
if request.method == 'POST' and 'daterange' in request.POST:
|
|
dateform = DateRangeForm(request.POST)
|
|
if dateform.is_valid():
|
|
startdate = dateform.cleaned_data['startdate']
|
|
enddate = dateform.cleaned_data['enddate']
|
|
startdatestring = startdate.strftime('%Y-%m-%d')
|
|
enddatestring = enddate.strftime('%Y-%m-%d')
|
|
request.session['startdate'] = startdatestring
|
|
request.session['enddate'] = enddatestring
|
|
else:
|
|
dateform = DateRangeForm(initial={
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
})
|
|
|
|
if request.method == 'POST' and 'plannedsessions' in request.POST:
|
|
form = PlannedSessionMultipleCloneForm(request.POST)
|
|
dateshiftform = SessionDateShiftForm(request.POST)
|
|
if form.is_valid() and dateshiftform.is_valid():
|
|
cd = form.cleaned_data
|
|
sps = cd['plannedsessions']
|
|
std = min([ps.startdate for ps in sps])
|
|
shiftstartdate = dateshiftform.cleaned_data['shiftstartdate']
|
|
delta = shiftstartdate-std
|
|
for ps in sps:
|
|
rowers = ps.rower.all()
|
|
teams = ps.team.all()
|
|
ps.pk = None
|
|
ps.startdate += delta
|
|
ps.preferreddate += delta
|
|
ps.enddate += delta
|
|
ps.save()
|
|
for rower in rowers:
|
|
add_rower_session(rower,ps)
|
|
for team in teams:
|
|
add_team_session(team,ps)
|
|
|
|
url = reverse(plannedsession_multicreate_view,
|
|
kwargs = {
|
|
'rowerid':r.id,
|
|
'timeperiod':timeperiod,
|
|
})
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
sps = PlannedSession.objects.filter(
|
|
manager=request.user,
|
|
rower__in=[r],
|
|
startdate__lte=enddate,
|
|
enddate__gte=startdate).order_by(
|
|
"startdate","preferreddate","enddate")
|
|
|
|
query = request.GET.get('q')
|
|
if query:
|
|
query_list = query.split()
|
|
sps = sps.filter(
|
|
reduce(operator.and_,
|
|
(Q(name__icontains=q) for q in query_list)) |
|
|
reduce(operator.and_,
|
|
(Q(comment__icontains=q) for q in query_list))
|
|
)
|
|
|
|
form = PlannedSessionMultipleCloneForm()
|
|
form.fields["plannedsessions"].queryset = sps
|
|
|
|
dateshiftform = SessionDateShiftForm()
|
|
|
|
return render(request, 'plannedsessions_multiclone_select.html',
|
|
{'plannedsessions':sps,
|
|
'dateform':dateform,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
'form':form,
|
|
'dateshiftform':dateshiftform,
|
|
'rower':r,
|
|
'timeperiod':timeperiod,
|
|
}
|
|
)
|
|
|
|
# Individual user creates training for himself
|
|
@user_passes_test(hasplannedsessions,login_url="/rowers/planmembership/",
|
|
redirect_field_name=None)
|
|
def plannedsession_create_view(request,timeperiod='thisweek',rowerid=0):
|
|
if rowerid==0:
|
|
r = getrower(request.user)
|
|
else:
|
|
try:
|
|
r = Rower.objects.get(id=rowerid)
|
|
except Rower.DoesNotExist:
|
|
raise Http404("This rower doesn't exist")
|
|
if not checkaccessuser(request.user,r):
|
|
raise PermissionDenied("You don't have access to this plan")
|
|
|
|
if request.method == 'POST':
|
|
sessioncreateform = PlannedSessionForm(request.POST)
|
|
if sessioncreateform.is_valid():
|
|
cd = sessioncreateform.cleaned_data
|
|
startdate = cd['startdate']
|
|
enddate = cd['enddate']
|
|
preferreddate = cd['preferreddate']
|
|
sessiontype = cd['sessiontype']
|
|
sessionmode = cd['sessionmode']
|
|
criterium = cd['criterium']
|
|
sessionvalue = cd['sessionvalue']
|
|
sessionunit = cd['sessionunit']
|
|
comment = cd['comment']
|
|
course = cd['course']
|
|
name = cd['name']
|
|
|
|
if sessionunit == 'min':
|
|
sessionmode = 'time'
|
|
elif sessionunit in ['km','m']:
|
|
sessionmode = 'distance'
|
|
|
|
ps = PlannedSession(
|
|
name=name,
|
|
startdate=startdate,
|
|
enddate=enddate,
|
|
preferreddate=preferreddate,
|
|
course=course,
|
|
sessiontype=sessiontype,
|
|
sessionmode=sessionmode,
|
|
sessionvalue=sessionvalue,
|
|
sessionunit=sessionunit,
|
|
comment=comment,
|
|
criterium=criterium,
|
|
manager=request.user)
|
|
|
|
ps.save()
|
|
|
|
add_rower_session(r,ps)
|
|
|
|
request.session['fstartdate'] = str(arrow.get(startdate))
|
|
request.session['fenddate'] = str(arrow.get(enddate))
|
|
request.session['fprefdate'] = str(arrow.get(preferreddate))
|
|
|
|
url = reverse(plannedsession_create_view,
|
|
kwargs = {
|
|
'rowerid':r.id,
|
|
'timeperiod':timeperiod,
|
|
})
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
if 'fstartdate' in request.session:
|
|
try:
|
|
fstartdate = arrow.get(request.session['fstartdate']).date()
|
|
except KeyError:
|
|
fstartdate = timezone.now().date()
|
|
try:
|
|
fenddate = arrow.get(request.session['fenddate']).date()
|
|
except KeyError:
|
|
fenddate = timezone.now().date()
|
|
try:
|
|
fprefdate = arrow.get(request.session['fprefdate']).date()
|
|
except KeyError:
|
|
fprefdate = timezone.now().date()
|
|
|
|
forminitial = {
|
|
'startdate':fstartdate,
|
|
'enddate':fenddate,
|
|
'preferreddate':fprefdate
|
|
}
|
|
else:
|
|
forminitial = {}
|
|
|
|
sessioncreateform = PlannedSessionForm(initial=forminitial)
|
|
|
|
startdate,enddate = get_dates_timeperiod(timeperiod)
|
|
sps = get_sessions(r,startdate=startdate,enddate=enddate)
|
|
|
|
return render(request,'plannedsessioncreate.html',
|
|
{
|
|
'teams':get_my_teams(request.user),
|
|
'form':sessioncreateform,
|
|
'plannedsessions':sps,
|
|
'rower':r,
|
|
'timeperiod':timeperiod,
|
|
})
|
|
|
|
@user_passes_test(hasplannedsessions,login_url="/rowers/planmembership/",
|
|
redirect_field_name=None)
|
|
def plannedsession_multicreate_view(request,timeperiod='thisweek',
|
|
teamid=0,rowerid=0,extrasessions=0):
|
|
|
|
extrasessions=int(extrasessions)
|
|
|
|
if rowerid==0:
|
|
r = getrower(request.user)
|
|
else:
|
|
try:
|
|
r = Rower.objects.get(id=rowerid)
|
|
except Rower.DoesNotExist:
|
|
raise Http404("This rower doesn't exist")
|
|
if not checkaccessuser(request.user,r):
|
|
raise PermissionDenied("You don't have access to this plan")
|
|
|
|
startdate,enddate = get_dates_timeperiod(timeperiod)
|
|
sps = get_sessions(r,startdate=startdate,enddate=enddate)
|
|
|
|
m = Rower.objects.get(user=request.user)
|
|
|
|
if m.rowerplan == 'coach':
|
|
teams = Team.objects.filter(manager=request.user)
|
|
members = Rower.objects.filter(team__in=teams).distinct()
|
|
if r in members:
|
|
qset = PlannedSession.objects.filter(
|
|
rower__in=[r],
|
|
startdate__lte=enddate,
|
|
enddate__gte=startdate,
|
|
).order_by("startdate","preferreddate","enddate")
|
|
else:
|
|
qset = []
|
|
else:
|
|
qset = []
|
|
|
|
if not qset:
|
|
qset = PlannedSession.objects.filter(
|
|
rower__in=[r],
|
|
manager = request.user,
|
|
startdate__lte=enddate,
|
|
enddate__gte=startdate,
|
|
).order_by("startdate","preferreddate","enddate")
|
|
|
|
|
|
|
|
initial = {
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
'sessionvalue':60,
|
|
'manager':request.user,
|
|
'name': 'NEW SESSION'
|
|
}
|
|
|
|
|
|
|
|
initials = [initial for i in range(extrasessions)]
|
|
PlannedSessionFormSet = modelformset_factory(
|
|
PlannedSession,
|
|
form=PlannedSessionFormSmall,
|
|
can_delete=True,
|
|
extra=extrasessions,
|
|
)
|
|
if request.method == "POST":
|
|
ps_formset = PlannedSessionFormSet(queryset = qset,
|
|
data = request.POST)
|
|
if ps_formset.is_valid():
|
|
instances = ps_formset.save(commit=False)
|
|
for ps in instances:
|
|
ps.save()
|
|
add_rower_session(r,ps)
|
|
messages.info(request,"Saved changes for Planned Session "+str(ps))
|
|
for obj in ps_formset.deleted_objects:
|
|
messages.info(request,"Deleted Planned Session "+str(obj))
|
|
obj.delete()
|
|
else:
|
|
print ps_formset.errors
|
|
|
|
url = reverse(plannedsession_multicreate_view,
|
|
kwargs = {
|
|
'rowerid':r.id,
|
|
'timeperiod':timeperiod
|
|
}
|
|
)
|
|
return HttpResponseRedirect(url)
|
|
|
|
ps_formset = PlannedSessionFormSet(queryset = qset,
|
|
initial=initials)
|
|
|
|
context = {
|
|
'ps_formset':ps_formset,
|
|
'rower':r,
|
|
'timeperiod':timeperiod,
|
|
'teams':get_my_teams(request.user),
|
|
'extrasessions': extrasessions+1
|
|
}
|
|
|
|
return render(request,'plannedsession_multicreate.html',context)
|
|
|
|
# Manager creates sessions for entire team
|
|
@user_passes_test(iscoachmember,login_url="/rowers/planmembership/",
|
|
redirect_field_name=None)
|
|
def plannedsession_teamcreate_view(request,timeperiod='thisweek',
|
|
teamid=0):
|
|
|
|
|
|
teams = Team.objects.filter(manager=request.user)
|
|
if len(teams)>0:
|
|
teamchoices = [(team.id, team.name) for team in teams]
|
|
teaminitial = [str(teams[0].id)]
|
|
else:
|
|
messages.info(request,"You have no teams established yet. We are redirecting you to the Team Management page.")
|
|
url = reverse(rower_teams_view)
|
|
return HttpResponseRedirect(url)
|
|
|
|
startdate,enddate = get_dates_timeperiod(timeperiod)
|
|
|
|
sps = []
|
|
for team in teams:
|
|
res = get_sessions_manager(request.user,startdate=startdate,enddate=enddate)
|
|
sps += res
|
|
|
|
sps = list(set(sps))
|
|
|
|
if request.method == 'POST':
|
|
sessioncreateform = PlannedSessionForm(request.POST)
|
|
sessionteamselectform = PlannedSessionTeamForm(
|
|
request.user,request.POST
|
|
)
|
|
if sessioncreateform.is_valid():
|
|
cd = sessioncreateform.cleaned_data
|
|
startdate = cd['startdate']
|
|
enddate = cd['enddate']
|
|
preferreddate = cd['preferreddate']
|
|
sessiontype = cd['sessiontype']
|
|
sessionmode = cd['sessionmode']
|
|
criterium = cd['criterium']
|
|
sessionvalue = cd['sessionvalue']
|
|
sessionunit = cd['sessionunit']
|
|
comment = cd['comment']
|
|
course = cd['course']
|
|
name = cd['name']
|
|
|
|
if sessionunit == 'min':
|
|
sessionmode = 'time'
|
|
elif sessionunit in ['km','m']:
|
|
sessionmode = 'distance'
|
|
|
|
ps = PlannedSession(
|
|
name=name,
|
|
startdate=startdate,
|
|
enddate=enddate,
|
|
preferreddate=preferreddate,
|
|
sessiontype=sessiontype,
|
|
sessionmode=sessionmode,
|
|
sessionvalue=sessionvalue,
|
|
sessionunit=sessionunit,
|
|
comment=comment,
|
|
criterium=criterium,
|
|
course=course,
|
|
manager=request.user)
|
|
|
|
ps.save()
|
|
|
|
# some logic when to add all selected rowers
|
|
if sessionteamselectform.is_valid():
|
|
cd = sessionteamselectform.cleaned_data
|
|
teams = cd['team']
|
|
for team in teams:
|
|
add_team_session(team,ps)
|
|
rs = Rower.objects.filter(team__in=[team])
|
|
for r in rs:
|
|
add_rower_session(r,ps)
|
|
|
|
|
|
url = reverse(plannedsession_teamcreate_view,
|
|
kwargs = {
|
|
'timeperiod':timeperiod,
|
|
})
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
sessioncreateform = PlannedSessionForm()
|
|
sessionteamselectform = PlannedSessionTeamForm(
|
|
request.user
|
|
)
|
|
|
|
return render(request,'plannedsessionteamcreate.html',
|
|
{
|
|
'teams':get_my_teams(request.user),
|
|
'form':sessioncreateform,
|
|
'teamform':sessionteamselectform,
|
|
'timeperiod':timeperiod,
|
|
'plannedsessions':sps,
|
|
'rower':getrower(request.user),
|
|
})
|
|
|
|
# Manager edits sessions for entire team
|
|
@user_passes_test(iscoachmember,login_url="/rowers/planmembership/",
|
|
redirect_field_name=None)
|
|
def plannedsession_teamedit_view(request,timeperiod='thisweek',
|
|
sessionid=0):
|
|
|
|
try:
|
|
ps = PlannedSession.objects.get(id=sessionid)
|
|
except PlannedSession.DoesNotExist:
|
|
raise Http404("This session doesn't exist")
|
|
if not ps.manager == request.user:
|
|
raise PermissionDenied("You are not the manager of this session")
|
|
|
|
teams = Team.objects.filter(manager=request.user)
|
|
teamchoices = [(team.id, team.name) for team in teams]
|
|
|
|
teaminitial = ps.team.all()
|
|
|
|
startdate,enddate = get_dates_timeperiod(timeperiod)
|
|
|
|
sps = []
|
|
rowers = []
|
|
for team in teams:
|
|
res = get_sessions_manager(request.user,startdate=startdate,enddate=enddate)
|
|
sps += res
|
|
rowers += Rower.objects.filter(team__in=[team])
|
|
|
|
rowers = list(set(rowers))
|
|
|
|
|
|
sps = list(set(sps))
|
|
|
|
if request.method == 'POST':
|
|
sessioncreateform = PlannedSessionForm(request.POST,instance=ps)
|
|
sessionteamselectform = PlannedSessionTeamForm(
|
|
request.user,request.POST
|
|
)
|
|
sessionrowerform = PlannedSessionTeamMemberForm(ps,request.POST)
|
|
if sessioncreateform.is_valid():
|
|
cd = sessioncreateform.cleaned_data
|
|
|
|
if cd['sessionunit'] == 'min':
|
|
cd['sessionmode'] = 'time'
|
|
elif cd['sessionunit'] in ['km','m']:
|
|
cd['sessionmode'] = 'distance'
|
|
|
|
|
|
res,message = update_plannedsession(ps,cd)
|
|
|
|
if res:
|
|
messages.info(request,message)
|
|
else:
|
|
messages.error(request,message)
|
|
|
|
|
|
# some logic when to add all selected rowers
|
|
if sessionteamselectform.is_valid():
|
|
cd = sessionteamselectform.cleaned_data
|
|
selectedteams = cd['team']
|
|
for team in teams:
|
|
if team in selectedteams:
|
|
add_team_session(team,ps)
|
|
rs = Rower.objects.filter(team__in=[team])
|
|
for r in rs:
|
|
add_rower_session(r,ps)
|
|
else:
|
|
remove_team_session(team,ps)
|
|
else:
|
|
selectedteams = []
|
|
|
|
|
|
if sessionrowerform.is_valid():
|
|
cd = sessionrowerform.cleaned_data
|
|
selectedrowers = cd['members']
|
|
for r in rowers:
|
|
if r in selectedrowers:
|
|
add_rower_session(r,ps)
|
|
else:
|
|
remove_rower_session(r,ps)
|
|
for t in selectedteams:
|
|
if t in r.team.all():
|
|
add_rower_session(r,ps)
|
|
|
|
|
|
url = reverse(plannedsession_teamedit_view,
|
|
kwargs = {
|
|
'sessionid':sessionid,
|
|
'timeperiod':timeperiod,
|
|
})
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
sessioncreateform = PlannedSessionForm(instance=ps)
|
|
sessionteamselectform = PlannedSessionTeamForm(
|
|
request.user
|
|
)
|
|
sessionteamselectform.fields['team'].initial = teaminitial
|
|
sessionrowerform = PlannedSessionTeamMemberForm(
|
|
ps
|
|
)
|
|
|
|
|
|
sessionrowerform.fields['members'].initial = ps.rower.all()
|
|
|
|
return render(request,'plannedsessionteamedit.html',
|
|
{
|
|
'plannedsession':ps,
|
|
'teams':get_my_teams(request.user),
|
|
'form':sessioncreateform,
|
|
'teamform':sessionteamselectform,
|
|
'rowersform':sessionrowerform,
|
|
'timeperiod':timeperiod,
|
|
'plannedsessions':sps,
|
|
})
|
|
|
|
@user_passes_test(iscoachmember,login_url="/rowers/planmembership/",
|
|
redirect_field_name=None)
|
|
def plannedsessions_coach_view(request,timeperiod='thisweek',
|
|
teamid=0):
|
|
|
|
startdate,enddate = get_dates_timeperiod(timeperiod)
|
|
|
|
if teamid != 0:
|
|
try:
|
|
theteam = Team.objects.get(id=teamid)
|
|
except Team.DoesNotExist:
|
|
theteam = False
|
|
else:
|
|
theteam = False
|
|
|
|
sps = get_sessions_manager(request.user,teamid=teamid,
|
|
enddate=enddate,
|
|
startdate=startdate)
|
|
|
|
rowers = []
|
|
for ps in sps:
|
|
rowers += ps.rower.all()
|
|
|
|
rowers = list(set(rowers))
|
|
|
|
statusdict = {}
|
|
|
|
for ps in sps:
|
|
rowerstatus = {}
|
|
for r in rowers:
|
|
ratio, status,completiondate = is_session_complete(r,ps)
|
|
rowerstatus[r.id] = status
|
|
sessiondict = {
|
|
'results':rowerstatus,
|
|
'name': ps.name,
|
|
'startdate': ps.startdate,
|
|
'enddate': ps.enddate,
|
|
}
|
|
statusdict[ps.id] = sessiondict
|
|
|
|
unmatchedworkouts = []
|
|
for r in rowers:
|
|
unmatchedworkouts += Workout.objects.filter(
|
|
user=r,
|
|
plannedsession=None,
|
|
date__gte=startdate,date__lte=enddate)
|
|
|
|
|
|
myteams = Team.objects.filter(manager=request.user)
|
|
|
|
return render(request,'plannedsessionscoach.html',
|
|
{
|
|
'myteams':myteams,
|
|
'plannedsessions':sps,
|
|
'statusdict':statusdict,
|
|
'timeperiod':timeperiod,
|
|
'rowers':rowers,
|
|
'theteam':theteam,
|
|
'unmatchedworkouts':unmatchedworkouts,
|
|
'rower':getrower(request.user)
|
|
}
|
|
)
|
|
|
|
@login_required()
|
|
def plannedsessions_view(request,timeperiod='thisweek',rowerid=0):
|
|
|
|
if rowerid==0:
|
|
r = getrower(request.user)
|
|
else:
|
|
try:
|
|
r = Rower.objects.get(id=rowerid)
|
|
except Rower.DoesNotExist:
|
|
raise Http404("This rower doesn't exist")
|
|
if not checkaccessuser(request.user,r):
|
|
raise PermissionDenied("You don't have access to this plan")
|
|
|
|
startdate,enddate = get_dates_timeperiod(timeperiod)
|
|
|
|
sps = get_sessions(r,startdate=startdate,enddate=enddate)
|
|
|
|
completeness = {}
|
|
actualvalue = {}
|
|
completiondate = {}
|
|
|
|
for ps in sps:
|
|
ratio,status,cdate = is_session_complete(r,ps)
|
|
actualvalue[ps.id] = int(ps.sessionvalue*ratio)
|
|
completeness[ps.id] = status
|
|
ws = Workout.objects.filter(user=r,plannedsession=ps)
|
|
completiondate[ps.id] = cdate
|
|
|
|
unmatchedworkouts = Workout.objects.filter(
|
|
user=r,
|
|
plannedsession=None,
|
|
date__gte=startdate,date__lte=enddate)
|
|
|
|
return render(request,'plannedsessions.html',
|
|
{
|
|
'teams':get_my_teams(request.user),
|
|
'plannedsessions':sps,
|
|
'rower':r,
|
|
'timeperiod':timeperiod,
|
|
'completeness':completeness,
|
|
'actualvalue':actualvalue,
|
|
'completiondate':completiondate,
|
|
'unmatchedworkouts':unmatchedworkouts,
|
|
})
|
|
|
|
@login_required()
|
|
def plannedsessions_print_view(request,timeperiod='thisweek',rowerid=0):
|
|
|
|
if rowerid==0:
|
|
r = getrower(request.user)
|
|
else:
|
|
try:
|
|
r = Rower.objects.get(id=rowerid)
|
|
except Rower.DoesNotExist:
|
|
raise Http404("This rower doesn't exist")
|
|
if not checkaccessuser(request.user,r):
|
|
raise PermissionDenied("You don't have access to this plan")
|
|
|
|
startdate,enddate = get_dates_timeperiod(timeperiod)
|
|
|
|
sps = get_sessions(r,startdate=startdate,enddate=enddate)
|
|
|
|
completeness = {}
|
|
actualvalue = {}
|
|
completiondate = {}
|
|
|
|
return render(request,'plannedsessions_print.html',
|
|
{
|
|
'teams':get_my_teams(request.user),
|
|
'plannedsessions':sps,
|
|
'rower':r,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
'timeperiod':timeperiod,
|
|
})
|
|
|
|
|
|
@login_required()
|
|
def plannedsessions_manage_view(request,timeperiod='thisweek',rowerid=0,
|
|
initialsession=0):
|
|
|
|
is_ajax = False
|
|
if request.is_ajax():
|
|
is_ajax = True
|
|
|
|
if rowerid==0:
|
|
r = getrower(request.user)
|
|
else:
|
|
try:
|
|
r = Rower.objects.get(id=rowerid)
|
|
except Rower.DoesNotExist:
|
|
raise Http404("This rower doesn't exist")
|
|
if not checkaccessuser(request.user,r):
|
|
raise PermissionDenied("You don't have access to this plan")
|
|
|
|
startdate,enddate = get_dates_timeperiod(timeperiod)
|
|
|
|
sps = get_sessions(r,startdate=startdate,enddate=enddate)
|
|
if initialsession==0:
|
|
try:
|
|
initialsession=sps[0].id
|
|
except IndexError:
|
|
initialsession=0
|
|
|
|
if initialsession:
|
|
ps0 = PlannedSession.objects.get(id=initialsession)
|
|
else:
|
|
ps0 = None
|
|
|
|
ws = Workout.objects.filter(
|
|
user=r,date__gte=startdate,
|
|
date__lte=enddate
|
|
).order_by(
|
|
"date","id"
|
|
)
|
|
|
|
|
|
initialworkouts = [w.id for w in Workout.objects.filter(user=r,plannedsession=ps0)]
|
|
|
|
linkedworkouts = []
|
|
for w in ws:
|
|
if w.plannedsession is not None:
|
|
linkedworkouts.append(w.id)
|
|
|
|
plannedsessionstuple = []
|
|
|
|
for ps in sps:
|
|
sessiontpl = (ps.id,ps.__unicode__())
|
|
plannedsessionstuple.append(sessiontpl)
|
|
|
|
plannedsessionstuple = tuple(plannedsessionstuple)
|
|
|
|
workoutdata = {}
|
|
workoutdata['initial'] = []
|
|
|
|
choices = []
|
|
|
|
for w in ws:
|
|
wtpl = (w.id, w.__unicode__())
|
|
choices.append(wtpl)
|
|
if w.id in initialworkouts:
|
|
workoutdata['initial'].append(w.id)
|
|
|
|
workoutdata['choices'] = tuple(choices)
|
|
|
|
if request.method == 'POST':
|
|
ps_form = PlannedSessionSelectForm(plannedsessionstuple,request.POST)
|
|
w_form = WorkoutSessionSelectForm(workoutdata,request.POST)
|
|
|
|
if ps_form.is_valid():
|
|
ps = PlannedSession.objects.get(id=ps_form.cleaned_data['plannedsession'])
|
|
if w_form.is_valid():
|
|
selectedworkouts = w_form.cleaned_data['workouts']
|
|
else:
|
|
selectedworkouts = []
|
|
|
|
if len(selectedworkouts)==0:
|
|
for w in ws:
|
|
remove_workout_plannedsession(w,ps)
|
|
|
|
if selectedworkouts:
|
|
workouts = Workout.objects.filter(user=r,id__in=selectedworkouts)
|
|
for w in ws:
|
|
if w.id not in selectedworkouts:
|
|
remove_workout_plannedsession(w,ps)
|
|
|
|
result,comments,errors = add_workouts_plannedsession(workouts,ps,r)
|
|
for c in comments:
|
|
messages.info(request,c)
|
|
for er in errors:
|
|
messages.error(request,er)
|
|
|
|
|
|
ps_form = PlannedSessionSelectForm(plannedsessionstuple,
|
|
initialsession=initialsession)
|
|
w_form = WorkoutSessionSelectForm(workoutdata=workoutdata)
|
|
|
|
|
|
if is_ajax:
|
|
ajax_workouts = []
|
|
for id,name in workoutdata['choices']:
|
|
ininitial = id in initialworkouts
|
|
inlinked = id in linkedworkouts
|
|
ajax_workouts.append((id,name,ininitial,inlinked))
|
|
|
|
ajax_response = {
|
|
'workouts':ajax_workouts,
|
|
'plannedsessionstuple':plannedsessionstuple,
|
|
}
|
|
|
|
|
|
return JSONResponse(ajax_response)
|
|
|
|
|
|
return render(request,'plannedsessionsmanage.html',
|
|
{
|
|
'teams':get_my_teams(request.user),
|
|
'plannedsessions':sps,
|
|
'workouts':ws,
|
|
'timeperiod':timeperiod,
|
|
'rower':r,
|
|
'ps_form':ps_form,
|
|
'w_form':w_form,
|
|
})
|
|
|
|
|
|
# Clone an existing planned session
|
|
@user_passes_test(hasplannedsessions,login_url="/rowers/planmembership/",
|
|
redirect_field_name=None)
|
|
def plannedsession_clone_view(request,id=0,rowerid=0,
|
|
timeperiod='thisweek'):
|
|
if rowerid==0:
|
|
r = getrower(request.user)
|
|
else:
|
|
try:
|
|
r = Rower.objects.get(id=rowerid)
|
|
except Rower.DoesNotExist:
|
|
raise Http404("This rower doesn't exist")
|
|
if not checkaccessuser(request.user,r):
|
|
raise PermissionDenied("You don't have access to this plan")
|
|
|
|
startdate,enddate = get_dates_timeperiod(timeperiod)
|
|
|
|
try:
|
|
ps = PlannedSession.objects.get(id=id)
|
|
except PlannedSession.DoesNotExist:
|
|
raise Http404("Planned Session does not exist")
|
|
|
|
if ps.manager != request.user:
|
|
raise PermissionDenied("You are not allowed to clone this planned session")
|
|
|
|
rowers = ps.rower.all()
|
|
teams = ps.team.all()
|
|
|
|
ps.pk = None
|
|
|
|
deltadays = ps.enddate-ps.startdate
|
|
|
|
ps.startdate = timezone.now().date()
|
|
ps.enddate = (timezone.now()+deltadays).date()
|
|
ps.name += ' (copy)'
|
|
|
|
ps.save()
|
|
|
|
for rower in rowers:
|
|
add_rower_session(rower,ps)
|
|
for team in teams:
|
|
add_team_session(team,ps)
|
|
|
|
url = reverse(plannedsession_edit_view,
|
|
kwargs = {
|
|
'id':ps.id,
|
|
'timeperiod':timeperiod,
|
|
'rowerid':r.id,
|
|
}
|
|
)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
|
|
# Edit an existing planned session
|
|
@user_passes_test(hasplannedsessions,login_url="/rowers/planmembership/",
|
|
redirect_field_name=None)
|
|
def plannedsession_edit_view(request,id=0,timeperiod='thisweek',rowerid=0):
|
|
|
|
if rowerid==0:
|
|
r = getrower(request.user)
|
|
else:
|
|
try:
|
|
r = Rower.objects.get(id=rowerid)
|
|
except Rower.DoesNotExist:
|
|
raise Http404("This rower doesn't exist")
|
|
if not checkaccessuser(request.user,r):
|
|
raise PermissionDenied("You don't have access to this plan")
|
|
|
|
startdate,enddate = get_dates_timeperiod(timeperiod)
|
|
|
|
|
|
try:
|
|
ps = PlannedSession.objects.get(id=id)
|
|
except PlannedSession.DoesNotExist:
|
|
raise Http404("Planned Session does not exist")
|
|
|
|
if ps.manager != request.user:
|
|
raise PermissionDenied("You are not allowed to edit this planned session")
|
|
|
|
if ps.team.all() or len(ps.rower.all())>1:
|
|
url = reverse(plannedsession_teamedit_view,
|
|
kwargs={
|
|
'timeperiod':timeperiod,
|
|
'sessionid':id,
|
|
})
|
|
return HttpResponseRedirect(url)
|
|
|
|
if request.method == 'POST':
|
|
sessioncreateform = PlannedSessionForm(request.POST,instance=ps)
|
|
if sessioncreateform.is_valid():
|
|
cd = sessioncreateform.cleaned_data
|
|
|
|
if cd['sessionunit'] == 'min':
|
|
cd['sessionmode'] = 'time'
|
|
elif cd['sessionunit'] in ['km','m']:
|
|
cd['sessionmode'] = 'distance'
|
|
|
|
|
|
res,message = update_plannedsession(ps,cd)
|
|
|
|
if res:
|
|
messages.info(request,message)
|
|
else:
|
|
messages.error(request,message)
|
|
|
|
url = reverse(plannedsession_edit_view,
|
|
kwargs={
|
|
'id':int(ps.id),
|
|
'timeperiod':timeperiod,
|
|
'rowerid':r.id,
|
|
})
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
sessioncreateform = PlannedSessionForm(instance=ps)
|
|
|
|
sps = get_sessions(r,startdate=startdate,enddate=enddate)
|
|
|
|
return render(request,'plannedsessionedit.html',
|
|
{
|
|
'teams':get_my_teams(request.user),
|
|
'form':sessioncreateform,
|
|
'plannedsessions':sps,
|
|
'thesession':ps,
|
|
'rower':r,
|
|
'timeperiod':timeperiod,
|
|
})
|
|
|
|
|
|
|
|
@login_required()
|
|
def plannedsession_view(request,id=0,rowerid=0,
|
|
timeperiod='thisweek'):
|
|
|
|
m = getrower(request.user)
|
|
|
|
|
|
if not rowerid:
|
|
r = m
|
|
else:
|
|
r = Rower.objects.get(id=rowerid)
|
|
|
|
try:
|
|
ps = PlannedSession.objects.get(id=id)
|
|
except PlannedSession.DoesNotExist:
|
|
raise Http404("Planned Session does not exist")
|
|
|
|
|
|
if ps.course:
|
|
coursescript,coursediv = course_map(ps.course)
|
|
else:
|
|
coursescript = ''
|
|
coursediv = ''
|
|
|
|
if ps.manager != request.user:
|
|
if r.rowerplan == 'coach':
|
|
teams = Team.objects.filter(manager=request.user)
|
|
members = Rower.objects.filter(team__in=teams).distinct()
|
|
teamusers = [m.user for m in members]
|
|
if ps.manager not in teamusers:
|
|
raise PermissionDenied("You do not have access to this session")
|
|
elif r not in ps.rower.all():
|
|
raise PermissionDenied("You do not have access to this session")
|
|
|
|
resultsdict = get_session_metrics(ps)
|
|
resultsdict = pd.DataFrame(resultsdict).transpose().to_dict()
|
|
|
|
psdict = my_dict_from_instance(ps,PlannedSession)
|
|
|
|
ws = get_workouts_session(r,ps)
|
|
|
|
ratio,status,completiondate = is_session_complete(r,ps)
|
|
|
|
ratio = int(100.*ratio)
|
|
|
|
# ranking for test
|
|
ranking = []
|
|
|
|
if ps.sessiontype in ['test','coursetest']:
|
|
if ps.sessionmode == 'distance':
|
|
rankws = Workout.objects.filter(
|
|
plannedsession=ps).order_by("duration")
|
|
else:
|
|
rankws = Workout.objects.filter(
|
|
plannedsession=ps).order_by("-distance")
|
|
for w in rankws:
|
|
dd = w.duration
|
|
dddelta = datetime.timedelta(hours=dd.hour,
|
|
minutes=dd.minute,
|
|
seconds=dd.second,
|
|
microseconds=dd.microsecond)
|
|
wdict = {
|
|
'name': w.user.user.first_name+' '+w.user.user.last_name,
|
|
'date': w.date,
|
|
'distance': w.distance,
|
|
'time': dddelta,
|
|
'type': w.workouttype,
|
|
}
|
|
if ps.sessiontype == 'coursetest':
|
|
(
|
|
coursetimeseconds,
|
|
coursemeters,
|
|
coursecompleted
|
|
) = courses.get_time_course([w],ps.course)
|
|
intsecs = int(coursetimeseconds)
|
|
microsecs = int(1.e6*(coursetimeseconds-intsecs))
|
|
|
|
wdict['time'] = datetime.timedelta(
|
|
seconds=intsecs,
|
|
microseconds=microsecs
|
|
)
|
|
wdict['distance'] = int(round(coursemeters))
|
|
|
|
|
|
ranking.append(wdict)
|
|
if ps.sessiontype == 'coursetest':
|
|
ranking = sorted(ranking, key=lambda k: k['time'])
|
|
|
|
# if coursetest, need to reorder the ranking
|
|
|
|
|
|
return render(request,'plannedsessionview.html',
|
|
{
|
|
'psdict': psdict,
|
|
'attrs':[
|
|
'name','startdate','enddate','sessiontype',
|
|
'sessionmode','criterium',
|
|
'sessionvalue','sessionunit','comment',
|
|
],
|
|
'workouts': ws,
|
|
'manager':m,
|
|
'rower':r,
|
|
'ratio':ratio,
|
|
'status':status,
|
|
'results':resultsdict,
|
|
'plannedsession':ps,
|
|
'timeperiod':timeperiod,
|
|
'ranking':ranking,
|
|
'coursescript': coursescript,
|
|
'coursediv': coursediv
|
|
}
|
|
)
|
|
|
|
@login_required()
|
|
def plannedsession_delete_view(request,id=0):
|
|
r = getrower(request.user)
|
|
|
|
try:
|
|
ps = PlannedSession.objects.get(id=id)
|
|
except PlannedSession.DoesNotExist:
|
|
raise Http404("Planned Session does not exist")
|
|
|
|
|
|
if ps.manager != request.user:
|
|
raise PermissionDenied("You are not allowed to delete this planned session")
|
|
|
|
ws = Workout.objects.filter(plannedsession=ps)
|
|
for w in ws:
|
|
w.plannedsession=None
|
|
w.save()
|
|
|
|
ps.delete()
|
|
|
|
url = reverse(plannedsessions_view)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
@login_required()
|
|
def plannedsession_deleteconfirm_view(request,id=0):
|
|
|
|
r = getrower(request.user)
|
|
|
|
try:
|
|
ps = PlannedSession.objects.get(id=id)
|
|
except PlannedSession.DoesNotExist:
|
|
raise Http404("Planned Session does not exist")
|
|
|
|
|
|
if ps.manager != request.user:
|
|
raise PermissionDenied("You are not allowed to delete this planned session")
|
|
|
|
|
|
psdict = my_dict_from_instance(ps,PlannedSession)
|
|
|
|
return render(request,'plannedsessiondeleteconfirm.html',
|
|
{
|
|
'ps':ps,
|
|
'psdict': psdict,
|
|
'attrs':[
|
|
'name','startdate','enddate','sessiontype',
|
|
],
|
|
'rower':r,
|
|
}
|
|
|
|
)
|