6224 lines
194 KiB
Python
6224 lines
194 KiB
Python
import time
|
|
import zipfile
|
|
import operator
|
|
import warnings
|
|
from django.views.generic.base import TemplateView
|
|
from django.db.models import Q
|
|
from django.db import IntegrityError, transaction
|
|
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
|
|
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,CNsummaryForm,UpdateWindForm,
|
|
UpdateStreamForm,WorkoutMultipleCompareForm,ChartParamChoiceForm,
|
|
FusionMetricChoiceForm,
|
|
)
|
|
from rowers.models import Workout, User, Rower, WorkoutForm,FavoriteChart
|
|
from rowers.models import (
|
|
RowerPowerForm,RowerForm,GraphImage,AdvancedWorkoutForm,
|
|
RowerPowerZonesForm,AccountRowerForm,UserForm,StrokeData,
|
|
Team,TeamForm,TeamInviteForm,TeamInvite,TeamRequest,
|
|
WorkoutComment,WorkoutCommentForm
|
|
)
|
|
from rowers.models import FavoriteForm,BaseFavoriteFormSet,SiteAnnouncement
|
|
from django.forms.formsets import formset_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
|
|
from iso8601 import ParseError
|
|
import stravastuff
|
|
import sporttracksstuff
|
|
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
|
|
from rowsandall_app.settings import SPORTTRACKS_CLIENT_ID, SPORTTRACKS_REDIRECT_URI, SPORTTRACKS_CLIENT_SECRET
|
|
|
|
import requests
|
|
import json
|
|
from rest_framework.renderers import JSONRenderer
|
|
from rest_framework.parsers import JSONParser
|
|
from rowers.rows import handle_uploaded_file
|
|
from rowers.tasks import handle_makeplot,handle_otwsetpower,handle_sendemailtcx,handle_sendemailcsv
|
|
from rowers.tasks import (
|
|
handle_sendemail_unrecognized,handle_sendemailnewcomment,
|
|
handle_sendemailnewresponse,
|
|
)
|
|
|
|
from scipy.signal import savgol_filter
|
|
from django.shortcuts import render_to_response
|
|
from Cookie import SimpleCookie
|
|
from shutil import copyfile
|
|
|
|
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')
|
|
|
|
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
|
|
|
|
LOCALTIMEZONE = tz('Etc/UTC')
|
|
USER_LANGUAGE = 'en-US'
|
|
|
|
from interactiveplots import *
|
|
|
|
# Define the API documentation
|
|
schema_view = get_swagger_view(title='Rowsandall API (Unstable)')
|
|
|
|
# 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(file,rower=rower)
|
|
except IOError, IndexError:
|
|
try:
|
|
res = rrdata(file+'.gz',rower=rower)
|
|
except IOError, IndexError:
|
|
res = 0
|
|
|
|
return res
|
|
|
|
# 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):
|
|
return uri[len(uri)-8:]
|
|
|
|
# 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
|
|
|
|
# Check if a user is a Coach member
|
|
def iscoachmember(user):
|
|
r = Rower.objects.get(user=user)
|
|
result = user.is_authenticated() and (r.rowerplan=='coach')
|
|
return result
|
|
|
|
# Check if a user is a Pro member
|
|
def ispromember(user):
|
|
if not user.is_anonymous():
|
|
r = Rower.objects.get(user=user)
|
|
result = user.is_authenticated() and (r.rowerplan=='pro' or r.rowerplan=='coach')
|
|
else:
|
|
result = False
|
|
return result
|
|
|
|
# User registration
|
|
def rower_register_view(request):
|
|
if request.method == 'POST':
|
|
form = RegistrationFormUniqueEmail(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']
|
|
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)
|
|
|
|
therower.save()
|
|
|
|
# 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')
|
|
|
|
# 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.\n"
|
|
message += "The first thing you might want to do is check and edit the heart rate band values. After logging in, click the button with your first name.\n"
|
|
message += "You can also check our videos page at http://rowsandall.com/rowers/videos for some helpful instruction videos.\n\n"
|
|
message += "User name:"+username+"\n"
|
|
message += "Password :"+password+"\n\n"
|
|
message += "For all your questions, just reply to this email.\n\n"
|
|
message += "Happy rowing!\n\n\n"
|
|
message += "Oh, one more thing. The site is currently in beta and is developing fast. Bear with us. Don't hesitate to contact me 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 = RegistrationFormUniqueEmail()
|
|
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:
|
|
return HttpResponseRedirect('/rowers/email/')
|
|
else:
|
|
return HttpResponseRedirect('/rowers/email/')
|
|
|
|
# Check if workout belongs to this user
|
|
def checkworkoutuser(user,workout):
|
|
try:
|
|
r = Rower.objects.get(user=user)
|
|
managers = [team.manager for team in workout.team.all()]
|
|
return (workout.user == r or user in managers)
|
|
except Rower.DoesNotExist:
|
|
return(False)
|
|
|
|
# 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):
|
|
workouttype = data['type']
|
|
if workouttype not in [x[0] for x in Workout.workouttypes]:
|
|
workouttype = 'water'
|
|
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 = Rower.objects.get(user=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 = mktime(rowdatetime.utctimetuple())
|
|
|
|
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']
|
|
|
|
spm = strokedata.ix[:,'spm']
|
|
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")
|
|
|
|
# auto smoothing
|
|
pace = df[' Stroke500mPace (sec/500m)'].values
|
|
velo = 500./pace
|
|
|
|
f = df['TimeStamp (sec)'].diff().mean()
|
|
windowsize = 2*(int(10./(f)))+1
|
|
if windowsize <= 3:
|
|
windowsize = 5
|
|
|
|
df['originalvelo'] = velo
|
|
|
|
if windowsize > 3 and windowsize < len(velo):
|
|
velo2 = savgol_filter(velo,windowsize,3)
|
|
else:
|
|
velo2=velo
|
|
|
|
velo3 = pd.Series(velo2)
|
|
velo3 = velo3.replace([-np.inf,np.inf],np.nan)
|
|
velo3 = velo3.fillna(method='ffill')
|
|
|
|
pace2 = 500./abs(velo3)
|
|
df[' Stroke500mPace (sec/500m)'] = pace2
|
|
|
|
df = df.fillna(0)
|
|
|
|
# end autosmoothing
|
|
|
|
# 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)
|
|
|
|
|
|
|
|
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 = 'water'
|
|
try:
|
|
comments = data['comments']
|
|
except:
|
|
comments = ''
|
|
|
|
try:
|
|
thetimezone = tz(data['timezone'])
|
|
except:
|
|
thetimezone = 'UTC'
|
|
|
|
r = Rower.objects.get(user=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)
|
|
|
|
# try:
|
|
# c2intervaltype = data['workout_type']
|
|
|
|
# except:
|
|
# c2intervaltype = ''
|
|
|
|
try:
|
|
title = data['name']
|
|
except:
|
|
title = "Imported data"
|
|
|
|
starttimeunix = mktime(rowdatetime.utctimetuple())
|
|
|
|
res = splitstdata(data['distance'])
|
|
|
|
distance = res[1]
|
|
times_distance = res[0]
|
|
|
|
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 == 'water':
|
|
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)
|
|
|
|
return (id,message)
|
|
|
|
# Checks if user has Concept2 tokens, resets tokens if they are
|
|
# expired.
|
|
def c2_open(user):
|
|
r = Rower.objects.get(user=user)
|
|
if (r.c2token == '') or (r.c2token is None):
|
|
s = "Token doesn't exist. Need to authorize"
|
|
raise C2NoTokenError("User has no token")
|
|
else:
|
|
if (timezone.now()>r.tokenexpirydate):
|
|
res = c2stuff.rower_c2_token_refresh(user)
|
|
if res[0] != None:
|
|
thetoken = res[0]
|
|
else:
|
|
raise C2NoTokenError("User has no token")
|
|
else:
|
|
thetoken = r.c2token
|
|
|
|
return thetoken
|
|
|
|
# Checks if user has SportTracks token, renews them if they are expired
|
|
def sporttracks_open(user):
|
|
r = Rower.objects.get(user=user)
|
|
if (r.sporttrackstoken == '') or (r.sporttrackstoken is None):
|
|
s = "Token doesn't exist. Need to authorize"
|
|
raise SportTracksNoTokenError("User has no token")
|
|
else:
|
|
if (timezone.now()>r.sporttrackstokenexpirydate):
|
|
thetoken = sporttracksstuff.rower_sporttracks_token_refresh(user)
|
|
else:
|
|
thetoken = r.sporttrackstoken
|
|
|
|
return thetoken
|
|
|
|
# Export workout to TCX and send to user's email address
|
|
@login_required()
|
|
def workout_tcxemail_view(request,id=0):
|
|
message = ""
|
|
successmessage = ""
|
|
r = Rower.objects.get(user=request.user)
|
|
try:
|
|
w = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
if (checkworkoutuser(request.user,w)):
|
|
try:
|
|
tcxfile = stravastuff.createstravaworkoutdata(w)
|
|
if tcxfile == 0:
|
|
message = "Something went wrong (TCX export)"
|
|
url = reverse(workout_export_view,
|
|
kwargs = {
|
|
'id':str(w.id),
|
|
'message':message,
|
|
})
|
|
return HttpResponseRedirect(url)
|
|
if settings.DEBUG and tcxfile:
|
|
res = handle_sendemailtcx.delay(r.user.first_name,
|
|
r.user.last_name,
|
|
r.user.email,tcxfile)
|
|
|
|
elif tcxfile:
|
|
res = queuehigh.enqueue(handle_sendemailtcx,r.user.first_name,
|
|
r.user.last_name,
|
|
r.user.email,tcxfile)
|
|
|
|
successmessage = "The TCX file was sent to you per email"
|
|
url = reverse(workout_export_view,
|
|
kwargs = {
|
|
'id':str(w.id),
|
|
'successmessage':successmessage,
|
|
})
|
|
except:
|
|
successmessage = ""
|
|
message = "Something went wrong (TCX export) "+str(sys.exc_info()[0])
|
|
with open("media/c2errors.log","a") as errorlog:
|
|
errorstring = str(sys.exc_info()[0])
|
|
timestr = strftime("%Y%m%d-%H%M%S")
|
|
errorlog.write(timestr+errorstring+"\r\n")
|
|
|
|
url = reverse(workout_export_view,
|
|
kwargs = {
|
|
'id':str(w.id),
|
|
'message':message,
|
|
})
|
|
|
|
response = HttpResponseRedirect(url)
|
|
|
|
else:
|
|
message = "You are not allowed to export this workout"
|
|
url = reverse(workout_export_view,
|
|
kwargs = {
|
|
'id':str(w.id),
|
|
'message':message,
|
|
})
|
|
response = HttpResponseRedirect(url)
|
|
|
|
return response
|
|
|
|
# Get Workout CSV file and send it to user's email address
|
|
@login_required()
|
|
def workout_csvemail_view(request,id=0):
|
|
message = ""
|
|
r = Rower.objects.get(user=request.user)
|
|
try:
|
|
w = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
if (checkworkoutuser(request.user,w)):
|
|
csvfile = w.csvfilename
|
|
if settings.DEBUG:
|
|
res = handle_sendemailcsv.delay(r.user.first_name,
|
|
r.user.last_name,
|
|
r.user.email,csvfile)
|
|
|
|
else:
|
|
res = queuehigh.enqueue(handle_sendemailcsv,r.user.first_name,
|
|
r.user.last_name,
|
|
r.user.email,csvfile)
|
|
|
|
successmessage = "The CSV file was sent to you per email"
|
|
url = reverse(workout_export_view,
|
|
kwargs = {
|
|
'id':str(w.id),
|
|
'successmessage':successmessage,
|
|
})
|
|
response = HttpResponseRedirect(url)
|
|
|
|
else:
|
|
message = "You are not allowed to export this workout"
|
|
url = reverse(workout_export_view,
|
|
kwargs = {
|
|
'id':str(w.id),
|
|
'message':message,
|
|
})
|
|
response = HttpResponseRedirect(url)
|
|
|
|
return response
|
|
|
|
# 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 = Rower.objects.get(user=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
|
|
try:
|
|
w = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
if (checkworkoutuser(request.user,w)):
|
|
try:
|
|
tcxfile = stravastuff.createstravaworkoutdata(w)
|
|
if tcxfile:
|
|
with open(tcxfile,'rb') as f:
|
|
res,mes = stravastuff.handle_stravaexport(f,w.name,
|
|
r.stravatoken,
|
|
description=w.notes)
|
|
if res==0:
|
|
message = 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()
|
|
os.remove(tcxfile)
|
|
url = "/rowers/workout/"+str(w.id)+"/edit"
|
|
successmessage = 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
|
|
else:
|
|
message = "Strava Upload error"
|
|
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
|
|
w.uploadedtostrava = -1
|
|
w.save()
|
|
os.remove(tcxfile)
|
|
url = reverse(workout_export_view,
|
|
kwargs = {
|
|
'id':str(w.id),
|
|
})
|
|
response = HttpResponseRedirect(url)
|
|
# except TimeoutExceeded as e:
|
|
# w.uploadedtostrava = -1
|
|
# w.save()
|
|
# url = reverse(workout_export_view,
|
|
# kwargs = {
|
|
# 'id':str(w.id),
|
|
# 'message':'Strava Upload attempted. No response within 10 seconds. You may be OK. Check on Strava',
|
|
# })
|
|
# response = HttpResponseRedirect(url)
|
|
|
|
|
|
return response
|
|
|
|
# Upload workout to Concept2 logbook
|
|
@login_required()
|
|
def workout_c2_upload_view(request,id=0):
|
|
message = ""
|
|
try:
|
|
thetoken = c2_open(request.user)
|
|
except C2NoTokenError:
|
|
return HttpResponseRedirect("/rowers/me/c2authorize/")
|
|
|
|
# ready to upload. Hurray
|
|
try:
|
|
w = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
if (checkworkoutuser(request.user,w)):
|
|
c2userid = c2stuff.get_userid(thetoken)
|
|
if not c2userid:
|
|
return HttpResponseRedirect("/rowers/me/c2authorize")
|
|
|
|
data = c2stuff.createc2workoutdata(w)
|
|
if data == 0:
|
|
message = "Error: No data file. Contact info@rowsandall.com if this problem persists"
|
|
url = reverse(workout_export_view,
|
|
kwargs = {
|
|
'message':str(message),
|
|
'id':str(w.id),
|
|
})
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
authorizationstring = str('Bearer ' + thetoken)
|
|
headers = {'Authorization': authorizationstring,
|
|
'user-agent': 'sanderroosendaal',
|
|
'Content-Type': 'application/json'}
|
|
import urllib
|
|
try:
|
|
url = "https://log.concept2.com/api/users/%s/results" % (c2userid)
|
|
response = requests.post(url,headers=headers,data=json.dumps(data))
|
|
except:
|
|
message = "Unexpected Error: "+str(sys.exc_info()[0])
|
|
with open("media/c2errors.log","a") as errorlog:
|
|
errorstring = str(sys.exc_info()[0])
|
|
timestr = strftime("%Y%m%d-%H%M%S")
|
|
errorlog.write(timestr+errorstring+"\r\n")
|
|
|
|
# check for duplicate error first
|
|
if (response.status_code == 409 ):
|
|
message = "Duplicate error"
|
|
w.uploadedtoc2 = -1
|
|
w.save()
|
|
elif (response.status_code == 201 or response.status_code == 200):
|
|
try:
|
|
s= json.loads(response.text)
|
|
c2id = s['data']['id']
|
|
w.uploadedtoc2 = c2id
|
|
w.save()
|
|
url = "/rowers/workout/"+str(w.id)+"/export"
|
|
return HttpResponseRedirect(url)
|
|
except:
|
|
message = "Something went wrong in workout_c2_upload_view. Response code 200/201 but C2 sync failed: "+response.text
|
|
with open("media/c2errors.log","a") as errorlog:
|
|
errorstring = str(sys.exc_info()[0])
|
|
timestr = strftime("%Y%m%d-%H%M%S")
|
|
errorlog.write(timestr+errorstring+"\r\n")
|
|
|
|
|
|
else:
|
|
s = response
|
|
message = "Something went wrong in workout_c2_upload_view. C2 sync failed."
|
|
with open("media/c2errors.log","a") as errorlog:
|
|
errorstring = str(sys.exc_info()[0])
|
|
timestr = strftime("%Y%m%d-%H%M%S")
|
|
errorlog.write(timestr+errorstring+"\r\n")
|
|
|
|
else:
|
|
message = "You are not authorized to upload this workout"
|
|
|
|
url = reverse(workout_export_view,
|
|
kwargs = {
|
|
'message':str(message),
|
|
'id':str(w.id),
|
|
})
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
# Upload workout to SportTracks
|
|
@login_required()
|
|
def workout_sporttracks_upload_view(request,id=0):
|
|
message = ""
|
|
try:
|
|
thetoken = sporttracks_open(request.user)
|
|
except SportTracksNoTokenError:
|
|
return HttpResponseRedirect("/rowers/me/sporttracksauthorize/")
|
|
|
|
# ready to upload. Hurray
|
|
try:
|
|
w = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
if (checkworkoutuser(request.user,w)):
|
|
data = sporttracksstuff.createsporttracksworkoutdata(w)
|
|
if not data:
|
|
message = "Data error"
|
|
url = reverse(workout_export_view,
|
|
kwargs = {
|
|
'message':str(message),
|
|
'id':str(w.id),
|
|
})
|
|
return HttpResponseRedirect(url)
|
|
|
|
authorizationstring = str('Bearer ' + thetoken)
|
|
headers = {'Authorization': authorizationstring,
|
|
'user-agent': 'sanderroosendaal',
|
|
'Content-Type': 'application/json'}
|
|
|
|
import urllib
|
|
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"
|
|
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()
|
|
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
|
|
|
|
else:
|
|
message = "You are not authorized to upload this workout"
|
|
|
|
url = reverse(workout_export_view,
|
|
kwargs = {
|
|
'message':str(message),
|
|
'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}
|
|
import urllib
|
|
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"}
|
|
|
|
import urllib
|
|
url = "https://www.strava.com/oauth/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}
|
|
|
|
import urllib
|
|
url = "https://api.sporttracks.mobi/oauth2/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 = Rower.objects.get(user=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 = Rower.objects.get(user=request.user)
|
|
r.c2token = access_token
|
|
r.tokenexpirydate = expirydatetime
|
|
r.c2refreshtoken = refresh_token
|
|
|
|
r.save()
|
|
|
|
successmessage = "Tokens refreshed. Good to go"
|
|
message = ""
|
|
else:
|
|
successmessage = ""
|
|
message = "Something went wrong (refreshing tokens). Please reauthorize:"
|
|
|
|
return imports_view(request,successmessage=successmessage,message=message)
|
|
|
|
# SportTracks token refresh. URL for manual refresh. Not visible to users
|
|
@login_required()
|
|
def rower_sporttracks_token_refresh(request):
|
|
r = Rower.objects.get(user=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 = Rower.objects.get(user=request.user)
|
|
r.sporttrackstoken = access_token
|
|
r.sporttrackstokenexpirydate = expirydatetime
|
|
r.sporttracksrefreshtoken = refresh_token
|
|
|
|
r.save()
|
|
|
|
successmessage = "Tokens refreshed. Good to go"
|
|
return imports_view(request,successmessage=successmessage)
|
|
|
|
|
|
# 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"
|
|
return imports_view(request,message=message)
|
|
|
|
access_token = res[0]
|
|
if access_token == 0:
|
|
message = res[1]
|
|
message += ' Contact info@rowsandall.com if this behavior persists.'
|
|
return imports_view(request,message=message)
|
|
|
|
expires_in = res[1]
|
|
refresh_token = res[2]
|
|
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
|
|
|
|
r = Rower.objects.get(user=request.user)
|
|
r.c2token = access_token
|
|
r.tokenexpirydate = expirydatetime
|
|
r.c2refreshtoken = refresh_token
|
|
|
|
r.save()
|
|
|
|
successmessage = "Tokens stored. Good to go"
|
|
return imports_view(request,successmessage=successmessage)
|
|
|
|
# The imports page
|
|
@login_required()
|
|
def imports_view(request,successmessage="",message=""):
|
|
return render(request,"imports.html",
|
|
{'successmessage': successmessage,
|
|
'message': message,
|
|
})
|
|
|
|
# Just for testing purposes
|
|
@login_required()
|
|
def test_reverse_view(request):
|
|
successmessage = "Tokens stored. Good to go"
|
|
|
|
return imports_view(request,successmessage=successmessage)
|
|
|
|
# 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"
|
|
|
|
return imports_view(request,message=message)
|
|
|
|
res = stravastuff.get_token(code)
|
|
|
|
if res[0]:
|
|
access_token = res[0]
|
|
|
|
r = Rower.objects.get(user=request.user)
|
|
r.stravatoken = access_token
|
|
|
|
r.save()
|
|
|
|
successmessage = "Tokens stored. Good to go"
|
|
return imports_view(request,successmessage=successmessage)
|
|
else:
|
|
message = "Something went wrong with the Strava authorization"
|
|
return imports_view(request,message=message)
|
|
|
|
|
|
# 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 = Rower.objects.get(user=request.user)
|
|
r.sporttrackstoken = access_token
|
|
r.sporttrackstokenexpirydate = expirydatetime
|
|
r.sporttracksrefreshtoken = refresh_token
|
|
|
|
r.save()
|
|
|
|
successmessage = "Tokens stored. Good to go"
|
|
return imports_view(request,successmessage=successmessage)
|
|
|
|
# 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):
|
|
promember=0
|
|
if theuser == 0:
|
|
theuser = request.user.id
|
|
|
|
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
|
|
|
|
if not promember:
|
|
return HttpResponseRedirect("/rowers/about/")
|
|
|
|
# get all indoor rows of past 12 months
|
|
ayearago = timezone.now()-datetime.timedelta(days=365)
|
|
try:
|
|
r2 = Rower.objects.get(user=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,
|
|
})
|
|
|
|
# The Flex plot for a large selection of workouts
|
|
@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=""):
|
|
|
|
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 = Rower.objects.get(user=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)
|
|
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:
|
|
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(initial={
|
|
'startdate': startdate,
|
|
'enddate': enddate,
|
|
})
|
|
deltaform = DeltaDaysForm()
|
|
|
|
try:
|
|
r2 = Rower.objects.get(user=theuser)
|
|
allergworkouts = Workout.objects.filter(user=r2,
|
|
workouttype__in=['rower','dynamic','slides'],
|
|
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_cum_flex_chart2(allergworkouts,xparam=xparam,
|
|
yparam1=yparam1,
|
|
yparam2=yparam2,
|
|
promember=promember)
|
|
script = res[0]
|
|
div = res[1]
|
|
js_resources = res[2]
|
|
css_resources = res[3]
|
|
else:
|
|
script = ''
|
|
div = '<p>No erg pieces uploaded for this date range.</p>'
|
|
js_resources = ''
|
|
css_resources = ''
|
|
|
|
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,
|
|
'deltaform':deltaform,
|
|
'xparam':xparam,
|
|
'yparam1':yparam1,
|
|
'yparam2':yparam2,
|
|
'promember':promember,
|
|
})
|
|
|
|
# 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):
|
|
try:
|
|
row = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
promember=0
|
|
mayedit=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
|
|
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':id,
|
|
'mayedit':mayedit,
|
|
'workstrokesonly': not workstrokesonly,
|
|
})
|
|
|
|
# Show Stroke power histogram for a workout
|
|
@login_required()
|
|
def workout_histo_view(request,id=0):
|
|
try:
|
|
row = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
promember=0
|
|
mayedit=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
|
|
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':id,
|
|
'mayedit':mayedit,
|
|
})
|
|
|
|
|
|
|
|
# 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=""):
|
|
|
|
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 = Rower.objects.get(user=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)
|
|
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:
|
|
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()
|
|
|
|
else:
|
|
form = DateRangeForm(initial={
|
|
'startdate': startdate,
|
|
'enddate': enddate,
|
|
})
|
|
deltaform = DeltaDaysForm()
|
|
|
|
try:
|
|
r2 = Rower.objects.get(user=theuser)
|
|
allergworkouts = Workout.objects.filter(user=r2,
|
|
workouttype__in=['rower','dynamic','slides'],
|
|
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 = ''
|
|
div = '<p>No erg pieces uploaded for this date range.</p>'
|
|
|
|
|
|
return render(request, 'histo.html',
|
|
{'interactiveplot':script,
|
|
'the_div':div,
|
|
'id':theuser,
|
|
'theuser':u,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
'form':form,
|
|
'deltaform':deltaform,
|
|
})
|
|
|
|
# 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 = Rower.objects.get(user=request.user)
|
|
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 != 0:
|
|
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()
|
|
|
|
# get all 2k (if any) - this rower, in date range
|
|
try:
|
|
r = Rower.objects.get(user=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)
|
|
|
|
rankingdistances = [100,500,1000,2000,5000,6000,10000,21097,42195,100000]
|
|
rankingdurations = []
|
|
rankingdurations.append(datetime.time(minute=1))
|
|
rankingdurations.append(datetime.time(minute=4))
|
|
rankingdurations.append(datetime.time(minute=30))
|
|
rankingdurations.append(datetime.time(hour=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'],
|
|
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',
|
|
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(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']
|
|
pieceunit = form.cleaned_data['pieceunit']
|
|
if pieceunit == 'd':
|
|
rankingdistances.append(value)
|
|
else:
|
|
rankingdurations.append(datetime.time(minute=value))
|
|
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)
|
|
|
|
|
|
|
|
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,
|
|
'message':message,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
})
|
|
|
|
# Reload the workout and calculate the summary from the stroke data (lapIDx)
|
|
@login_required()
|
|
def workout_recalcsummary_view(request,id=0):
|
|
try:
|
|
row = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
message = "You are not allowed to edit this workout"
|
|
url = reverse(workouts_view,args=[str(message)])
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
filename = row.csvfilename
|
|
rowdata = rdata(filename)
|
|
row.summary = rowdata.allstats()
|
|
row.save()
|
|
successmessage = "Summary Updated"
|
|
url = reverse(workout_edit_view,
|
|
kwargs = {
|
|
'id':str(id),
|
|
'successmessage':str(successmessage),
|
|
})
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
@login_required()
|
|
def workout_makepublic_view(request,id,
|
|
message='',
|
|
successmessage=''):
|
|
try:
|
|
row = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
message = "You are not allowed to edit this workout"
|
|
url = reverse(workouts_view,args=[str(message)])
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
row.privacy = 'visible'
|
|
row.save()
|
|
rr = Rower.objects.get(user=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"
|
|
|
|
url = reverse(workout_edit_view,
|
|
kwargs = {
|
|
'id':str(id),
|
|
'successmessage':str(message),
|
|
})
|
|
return HttpResponseRedirect(url)
|
|
|
|
@login_required()
|
|
def workout_setprivate_view(request,id,
|
|
message='',
|
|
successmessage=''):
|
|
try:
|
|
row = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
message = "You are not allowed to edit this workout"
|
|
url = reverse(workouts_view,args=[str(message)])
|
|
|
|
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"
|
|
|
|
url = reverse(workout_edit_view,
|
|
kwargs = {
|
|
'id':str(id),
|
|
'successmessage':str(message),
|
|
})
|
|
return HttpResponseRedirect(url)
|
|
|
|
# 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 = Rower.objects.get(user=request.user)
|
|
except Rower.DoesNotExist:
|
|
raise Http404("Rower doesn't exist")
|
|
|
|
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,
|
|
})
|
|
|
|
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:
|
|
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")
|
|
elif theteam.viewing == 'coachonly':
|
|
workouts = Workout.objects.filter(team=theteam,user=r,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate).order_by("-date","-starttime")
|
|
|
|
|
|
else:
|
|
theteam = None
|
|
workouts = Workout.objects.filter(user=r,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate).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))
|
|
)
|
|
|
|
form = WorkoutMultipleCompareForm()
|
|
form.fields["workouts"].queryset = workouts
|
|
|
|
chartform = ChartParamChoiceForm(initial={'teamid':theteam.id})
|
|
|
|
return render(request, 'team_compare_select.html',
|
|
{'workouts': workouts,
|
|
'message': message,
|
|
'successmessage':successmessage,
|
|
'dateform':dateform,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
'team':theteam,
|
|
'form':form,
|
|
'chartform':chartform,
|
|
})
|
|
|
|
@login_required()
|
|
def multi_compare_view(request):
|
|
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
|
|
|
|
|
|
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]
|
|
|
|
return render(request,'multicompare.html',
|
|
{'interactiveplot':script,
|
|
'the_div':div,
|
|
'promember':promember,
|
|
'teamid':teamid,
|
|
'chartform':chartform,
|
|
})
|
|
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,
|
|
})
|
|
|
|
else:
|
|
url = reverse(workouts_view)
|
|
return HttpResponseRedirect(url)
|
|
|
|
# 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):
|
|
try:
|
|
r = Rower.objects.get(user=request.user)
|
|
except Rower.DoesNotExist:
|
|
raise Http404("Rower doesn't exist")
|
|
|
|
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,
|
|
})
|
|
|
|
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
|
|
|
|
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")
|
|
elif theteam.viewing == 'coachonly':
|
|
workouts = Workout.objects.filter(team=theteam,user=r,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate).order_by("-date","-starttime")
|
|
|
|
|
|
else:
|
|
theteam = None
|
|
workouts = Workout.objects.filter(user=r,
|
|
startdatetime__gte=startdate,
|
|
startdatetime__lte=enddate).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))
|
|
)
|
|
|
|
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"
|
|
)
|
|
|
|
return render(request, 'list_workouts.html',
|
|
{'workouts': workouts,
|
|
'message': message,
|
|
'successmessage':successmessage,
|
|
'dateform':dateform,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
'announcements':announcements[0:4],
|
|
'team':theteam,
|
|
})
|
|
|
|
|
|
# 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 = Rower.objects.get(user=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)
|
|
try:
|
|
row = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
|
|
return render(request, 'comparison_list.html',
|
|
{'id':id,
|
|
'workout':row,
|
|
'workouts': workouts,
|
|
'last_name':u.last_name,
|
|
'first_name':u.first_name,
|
|
'message': message,
|
|
'successmessage':successmessage,
|
|
'dateform':dateform,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
})
|
|
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 = Rower.objects.get(user=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)
|
|
try:
|
|
row = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
|
|
return render(request, 'fusion_list.html',
|
|
{'id':id,
|
|
'workout':row,
|
|
'workouts': workouts,
|
|
'last_name':u.last_name,
|
|
'first_name':u.first_name,
|
|
'message': message,
|
|
'successmessage':successmessage,
|
|
'dateform':dateform,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
})
|
|
except Rower.DoesNotExist:
|
|
raise Http404("User has no rower instance")
|
|
|
|
# Basic 'EDIT' view of workout
|
|
def workout_view(request,id=0):
|
|
try:
|
|
# check if valid ID exists (workout exists)
|
|
row = Workout.objects.get(id=id)
|
|
if row.privacy == 'private':
|
|
raise Http404("Not allowed to view this workout")
|
|
|
|
g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime")
|
|
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]
|
|
|
|
|
|
# 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,
|
|
'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,
|
|
'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=""):
|
|
try:
|
|
row = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
message = "You are not allowed to edit this workout"
|
|
url = reverse(workouts_view,args=[str(message)])
|
|
|
|
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=""):
|
|
try:
|
|
row = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
message = "You are not allowed to edit this workout"
|
|
url = reverse(workouts_view,args=[str(message)])
|
|
|
|
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=""):
|
|
try:
|
|
row = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
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"
|
|
url = reverse(workout_edit_view,
|
|
kwargs = {
|
|
'id':str(id),
|
|
'successmessage':str(successmessage),
|
|
})
|
|
|
|
return HttpResponseRedirect(url)
|
|
except:
|
|
os.remove(fname)
|
|
message = "Something went wrong (workout_crewnerd_summary_view)"
|
|
url = reverse(workout_edit_view,
|
|
kwargs = {
|
|
'id':str(id),
|
|
'message':str(message),
|
|
})
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
return render(request,
|
|
"cn_form.html",
|
|
{'form':form,
|
|
'id':row.id})
|
|
else:
|
|
form = CNsummaryForm()
|
|
|
|
return render(request,
|
|
"cn_form.html",
|
|
{'form':form,
|
|
'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=""):
|
|
try:
|
|
row = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
f1 = row.csvfilename
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
message = "You are not allowed to edit this workout"
|
|
url = reverse(workouts_view,args=[str(message)])
|
|
|
|
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(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)
|
|
|
|
kwargs = {'successmessage':str(message),
|
|
'id':str(id)}
|
|
|
|
url = reverse(workout_wind_view,kwargs=kwargs)
|
|
response = HttpResponseRedirect(url)
|
|
except KeyError:
|
|
message = "No latitude/longitude data"
|
|
kwargs = {'message':str(message),
|
|
'id':str(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=""):
|
|
try:
|
|
row = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
f1 = row.csvfilename
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
message = "You are not allowed to edit this workout"
|
|
url = reverse(workouts_view,args=[str(message)])
|
|
|
|
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 = 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)
|
|
|
|
kwargs = {'successmessage':str(message),
|
|
'id':str(id)}
|
|
|
|
url = reverse(workout_wind_view,kwargs=kwargs)
|
|
response = HttpResponseRedirect(url)
|
|
except KeyError:
|
|
message = "No latitude/longitude data"
|
|
kwargs = {'message':str(message),
|
|
'id':str(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=""):
|
|
try:
|
|
row = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
message = "You are not allowed to edit this workout"
|
|
url = reverse(workouts_view,args=[str(message)])
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
|
|
# get data
|
|
f1 = row.csvfilename
|
|
u = row.user.user
|
|
r = Rower.objects.get(user=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 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"
|
|
kwargs = {'message':str(message),
|
|
'id':str(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:
|
|
res = googlemap_chart(rowdata.df[' latitude'],
|
|
rowdata.df[' longitude'],
|
|
row.name)
|
|
gmscript = res[0]
|
|
gmdiv = res[1]
|
|
else:
|
|
gmscript = ""
|
|
gmdiv = "No GPS data available"
|
|
|
|
return render(request,
|
|
'windedit.html',
|
|
{'workout':row,
|
|
'message': message,
|
|
'successmessage': successmessage,
|
|
'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=""):
|
|
try:
|
|
row = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
message = "You are not allowed to edit this workout"
|
|
url = reverse(workouts_view,args=[str(message)])
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
|
|
# create interactive plot
|
|
f1 = row.csvfilename
|
|
u = row.user.user
|
|
r = Rower.objects.get(user=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"
|
|
kwargs = {'message':str(message),
|
|
'id':str(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]
|
|
|
|
return render(request,
|
|
'streamedit.html',
|
|
{'workout':row,
|
|
'message': message,
|
|
'successmessage': successmessage,
|
|
'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=""):
|
|
try:
|
|
row = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
message = "You are not allowed to edit this workout"
|
|
url = reverse(workouts_view,args=[str(message)])
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
|
|
if request.method == 'POST':
|
|
# process form
|
|
form = AdvancedWorkoutForm(request.POST)
|
|
|
|
if form.is_valid():
|
|
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)
|
|
u = request.user
|
|
first_name = u.first_name
|
|
last_name = u.last_name
|
|
emailaddress = u.email
|
|
|
|
if settings.DEBUG:
|
|
res = handle_otwsetpower.delay(f1,boattype,weightvalue,
|
|
first_name,last_name,emailaddress,id)
|
|
else:
|
|
res = queuelow.enqueue(handle_otwsetpower,f1,boattype,
|
|
weightvalue,
|
|
first_name,last_name,emailaddress,id)
|
|
|
|
|
|
successmessage = "Your calculations have been submitted. You will receive an email when they are done."
|
|
kwargs = {'successmessage':str(successmessage),
|
|
'id':str(id)}
|
|
|
|
url = reverse(workout_advanced_view,kwargs=kwargs)
|
|
response = HttpResponseRedirect(url)
|
|
return response
|
|
|
|
else:
|
|
message = "Invalid Form"
|
|
kwargs = {'message':str(message),
|
|
'id':str(id)}
|
|
url = reverse(workout_otwsetpower_view,kwargs=kwargs)
|
|
response = HttpResponseRedirect(url)
|
|
|
|
else:
|
|
form = AdvancedWorkoutForm(instance=row)
|
|
|
|
return render(request,
|
|
'otwsetpower.html',
|
|
{'workout':row,
|
|
'message': message,
|
|
'successmessage': successmessage,
|
|
'form':form,
|
|
})
|
|
|
|
# A special Edit page with all the Geeky functionality for the workout
|
|
@login_required()
|
|
def workout_geeky_view(request,id=0,message="",successmessage=""):
|
|
try:
|
|
row = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
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"
|
|
url = reverse(workouts_view,args=[str(message)])
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
|
|
# create interactive plot
|
|
f1 = row.csvfilename
|
|
u = row.user.user
|
|
r = Rower.objects.get(user=u)
|
|
|
|
# create interactive plot
|
|
try:
|
|
res = interactive_chart(id,promember=1)
|
|
script = res[0]
|
|
div = res[1]
|
|
except ValueError:
|
|
pass
|
|
|
|
if row.workouttype=='water':
|
|
return render(request,
|
|
'otwgeeky.html',
|
|
{'workout':row,
|
|
'message': message,
|
|
'successmessage': successmessage,
|
|
'interactiveplot':script,
|
|
'the_div':div})
|
|
else:
|
|
return render(request,
|
|
'advancededit.html',
|
|
{'workout':row,
|
|
'message': message,
|
|
'successmessage': successmessage,
|
|
'interactiveplot':script,
|
|
'the_div':div})
|
|
|
|
# Cumulative stats page
|
|
@login_required()
|
|
def cumstats(request,theuser=0,
|
|
startdate=timezone.now()-datetime.timedelta(days=365),
|
|
enddate=timezone.now(),
|
|
deltadays=-1,
|
|
startdatestring="",
|
|
enddatestring="",
|
|
options={
|
|
'includereststrokes':False,
|
|
'workouttypes':['rower','dynamic','slides']
|
|
}):
|
|
|
|
if 'options' in request.session:
|
|
options = request.session['options']
|
|
|
|
workouttypes = options['workouttypes']
|
|
includereststrokes = options['includereststrokes']
|
|
workstrokesonly = not includereststrokes
|
|
checktypes = ['water','rower','dynamic','slides','skierg',
|
|
'paddle','snow','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 = Rower.objects.get(user=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:
|
|
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 = []
|
|
for type in checktypes:
|
|
if optionsform.cleaned_data[type]:
|
|
workouttypes.append(type)
|
|
|
|
options = {
|
|
'includereststrokes':includereststrokes,
|
|
'workouttypes':workouttypes,
|
|
}
|
|
form = DateRangeForm()
|
|
deltaform = DeltaDaysForm()
|
|
else:
|
|
form = DateRangeForm()
|
|
deltaform = DeltaDaysForm()
|
|
else:
|
|
form = DateRangeForm(initial={
|
|
'startdate': startdate,
|
|
'enddate': enddate,
|
|
})
|
|
deltaform = DeltaDaysForm()
|
|
optionsform = StatsOptionsForm()
|
|
|
|
try:
|
|
r2 = Rower.objects.get(user=theuser)
|
|
allergworkouts = Workout.objects.filter(user=r2,
|
|
workouttype__in=workouttypes,
|
|
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]
|
|
|
|
fieldlist,fielddict = dataprep.getstatsfields()
|
|
|
|
# prepare data frame
|
|
datadf = dataprep.read_cols_df_sql(ids,fieldlist)
|
|
|
|
datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly)
|
|
|
|
|
|
if datadf.empty:
|
|
return HttpResponse("No data found")
|
|
|
|
|
|
# Create stats
|
|
stats = {}
|
|
fielddict.pop('workoutstate')
|
|
|
|
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
|
|
|
|
# set options form correctly
|
|
initial = {}
|
|
initial['includereststrokes'] = includereststrokes
|
|
|
|
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,
|
|
'options':options,
|
|
'id':theuser,
|
|
'theuser':u,
|
|
'startdate':startdate,
|
|
'enddate':enddate,
|
|
'form':form,
|
|
'deltaform':deltaform,
|
|
'optionsform':optionsform,
|
|
'cordict':cordict,
|
|
})
|
|
|
|
request.session['options'] = options
|
|
|
|
return response
|
|
|
|
# Stats page
|
|
@login_required()
|
|
def workout_stats_view(request,id=0,message="",successmessage=""):
|
|
workstrokesonly = True
|
|
if request.method == 'POST' and 'workstrokesonly' in request.POST:
|
|
workstrokesonly = 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"
|
|
url = reverse(workouts_view,args=[str(message)])
|
|
return HttpResponseRedirect(url)
|
|
|
|
datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly)
|
|
|
|
|
|
if datadf.empty:
|
|
return HttpResponse("CSV data file not found")
|
|
|
|
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')
|
|
|
|
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
|
|
|
|
return render(request,
|
|
'workoutstats.html',
|
|
{
|
|
'stats':stats,
|
|
'workout':row,
|
|
'workstrokesonly':workstrokesonly,
|
|
'cordict':cordict,
|
|
})
|
|
|
|
# The Advanced edit page
|
|
@login_required()
|
|
def workout_advanced_view(request,id=0,message="",successmessage=""):
|
|
try:
|
|
row = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
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"
|
|
url = reverse(workouts_view,args=[str(message)])
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
|
|
# create interactive plot
|
|
f1 = row.csvfilename
|
|
u = row.user.user
|
|
r = Rower.objects.get(user=u)
|
|
|
|
# create interactive plot
|
|
try:
|
|
res = interactive_chart(id,promember=1)
|
|
script = res[0]
|
|
div = res[1]
|
|
except ValueError:
|
|
pass
|
|
|
|
if row.workouttype=='water':
|
|
return render(request,
|
|
'advancedotw.html',
|
|
{'workout':row,
|
|
'message': message,
|
|
'successmessage': successmessage,
|
|
'interactiveplot':script,
|
|
'the_div':div})
|
|
else:
|
|
return render(request,
|
|
'advancededit.html',
|
|
{'workout':row,
|
|
'message': message,
|
|
'successmessage': successmessage,
|
|
'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 = Rower.objects.get(user=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,
|
|
'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 = Rower.objects.get(user=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)
|
|
|
|
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,
|
|
'id1':id1,
|
|
'id2':id2,
|
|
'axchoicesbasic':axchoicesbasic,
|
|
'axchoicespro':axchoicespro,
|
|
'noylist':noylist,
|
|
'xparam':xparam,
|
|
'yparam':yparam,
|
|
'plottype':plottype,
|
|
'promember':promember,
|
|
})
|
|
|
|
|
|
# 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 = 0
|
|
|
|
|
|
try:
|
|
row = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
promember=0
|
|
mayedit=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
|
|
if request.user == row.user.user:
|
|
mayedit=1
|
|
|
|
|
|
workouttype = 'ote'
|
|
if row.workouttype == 'water':
|
|
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
|
|
|
|
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 '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:
|
|
workstrokesonly = request.POST['workstrokesonlysave']
|
|
reststrokes = not workstrokesonly
|
|
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
|
|
|
|
# create interactive plot
|
|
try:
|
|
script,div,js_resources,css_resources = interactive_flex_chart2(id,xparam=xparam,yparam1=yparam1,
|
|
yparam2=yparam2,
|
|
promember=promember,plottype=plottype,
|
|
workstrokesonly=workstrokesonly)
|
|
except ValueError:
|
|
script,div = interactive_flex_chart2(id,xparam=xparam,yparam1=yparam1,
|
|
yparam2=yparam2,
|
|
promember=promember,plottype=plottype,
|
|
workstrokesonly=workstrokesonly)
|
|
js_resources = ""
|
|
css_resources = ""
|
|
|
|
|
|
# script = res[0]
|
|
# div = res[1]
|
|
# js_resources = res[2]
|
|
# css_resources = res[3]
|
|
|
|
|
|
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 == 'water':
|
|
return render(request,
|
|
'flexchart3otw.html',
|
|
{'the_script':script,
|
|
'the_div':div,
|
|
'js_res': js_resources,
|
|
'css_res':css_resources,
|
|
'id':id,
|
|
'xparam':xparam,
|
|
'yparam1':yparam1,
|
|
'yparam2':yparam2,
|
|
'plottype':plottype,
|
|
'mayedit':mayedit,
|
|
'promember':promember,
|
|
'axchoicesbasic':axchoicesbasic,
|
|
'axchoicespro':axchoicespro,
|
|
'noylist':noylist,
|
|
'workstrokesonly': not workstrokesonly,
|
|
'favoritenr':favoritenr,
|
|
'maxfav':maxfav,
|
|
})
|
|
else:
|
|
axchoicespro.pop('slip')
|
|
axchoicespro.pop('wash')
|
|
axchoicespro.pop('catch')
|
|
axchoicespro.pop('finish')
|
|
axchoicespro.pop('totalangle')
|
|
axchoicespro.pop('effectiveangle')
|
|
axchoicespro.pop('peakforceangle')
|
|
|
|
|
|
return render(request,
|
|
'flexchart3otw.html',
|
|
{'the_script':script,
|
|
'the_div':div,
|
|
'js_res': js_resources,
|
|
'css_res':css_resources,
|
|
'id':id,
|
|
'xparam':xparam,
|
|
'yparam1':yparam1,
|
|
'yparam2':yparam2,
|
|
'plottype':plottype,
|
|
'axchoicesbasic':axchoicesbasic,
|
|
'axchoicespro':axchoicespro,
|
|
'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=""):
|
|
try:
|
|
row = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
# check if user is owner of this workout
|
|
|
|
|
|
# create interactive plot
|
|
f1 = row.csvfilename
|
|
u = row.user.user
|
|
# r = Rower.objects.get(user=u)
|
|
|
|
promember=0
|
|
mayedit=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
|
|
if request.user == row.user.user:
|
|
mayedit=1
|
|
|
|
|
|
# create interactive plot
|
|
res = interactive_bar_chart(id,promember=promember)
|
|
script = res[0]
|
|
div = res[1]
|
|
|
|
return render(request,
|
|
'biginteractive1.html',
|
|
{'workout':row,
|
|
'message': message,
|
|
'successmessage': successmessage,
|
|
'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=""):
|
|
try:
|
|
row = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
# check if user is owner of this workout
|
|
|
|
|
|
# create interactive plot
|
|
f1 = row.csvfilename
|
|
u = row.user.user
|
|
# r = Rower.objects.get(user=u)
|
|
|
|
promember=0
|
|
mayedit=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
|
|
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]
|
|
|
|
|
|
return render(request,
|
|
'otwinteractive.html',
|
|
{'workout':row,
|
|
'message': message,
|
|
'successmessage': successmessage,
|
|
'interactiveplot':script,
|
|
'the_div':div,
|
|
'mayedit':mayedit})
|
|
|
|
# the page where you can chose where to export this workout
|
|
@login_required()
|
|
def workout_export_view(request,id=0, message="", successmessage=""):
|
|
request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE
|
|
try:
|
|
row = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
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
|
|
|
|
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"
|
|
url = reverse(workouts_view,args=[str(message)])
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
else:
|
|
return render(request,
|
|
'export.html',
|
|
{'workout':row,
|
|
'message':message,
|
|
'successmessage':successmessage,
|
|
'c2userid':c2userid,
|
|
})
|
|
|
|
#
|
|
@login_required()
|
|
def workout_unsubscribe_view(request,id=0):
|
|
try:
|
|
w = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
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()
|
|
|
|
message = 'You have been unsubscribed from new comment notifications for this workout'
|
|
|
|
return render(request,
|
|
'workout_comments.html',
|
|
{'workout':w,
|
|
'comments':comments,
|
|
'successmessage':message,
|
|
'form':form,
|
|
})
|
|
|
|
|
|
# list of comments to a workout
|
|
@login_required()
|
|
def workout_comment_view(request,id=0):
|
|
try:
|
|
w = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
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']
|
|
notification = cd['notification']
|
|
c = WorkoutComment(workout=w,user=request.user,comment=comment,
|
|
notification=notification)
|
|
c.save()
|
|
if settings.DEBUG and request.user != r.user:
|
|
res = handle_sendemailnewcomment.delay(r.user.first_name,
|
|
r.user.last_name,
|
|
r.user.email,
|
|
request.user.first_name,
|
|
request.user.last_name,
|
|
comment,w.name,
|
|
w.id)
|
|
|
|
|
|
elif request.user != r.user:
|
|
res = queuehigh.enqueue(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:
|
|
if settings.DEBUG and u != request.user and u != r.user:
|
|
res = handle_sendemailnewresponse.delay(u.first_name,
|
|
u.last_name,
|
|
u.email,
|
|
request.user.first_name,
|
|
request.user.last_name,
|
|
comment,
|
|
w.name,
|
|
w.id,
|
|
c.id)
|
|
elif u != request.user and u != r.user:
|
|
res = queuelow.enqueue(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=row).order_by("-creationdatetime")
|
|
|
|
if (len(g)<=3):
|
|
return render(request,
|
|
'workout_comments.html',
|
|
{'workout':w,
|
|
'graphs1':g[0:3],
|
|
'comments':comments,
|
|
'form':form,
|
|
})
|
|
else:
|
|
return render(request,
|
|
'workout_comments.html',
|
|
{'workout':w,
|
|
'graphs1':g[0:3],
|
|
'graphs1':g[3:6],
|
|
'comments':comments,
|
|
'form':form,
|
|
})
|
|
|
|
|
|
# The basic edit page
|
|
@login_required()
|
|
def workout_edit_view(request,id=0,message="",successmessage=""):
|
|
request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE
|
|
|
|
try:
|
|
# check if valid ID exists (workout exists)
|
|
row = Workout.objects.get(id=id)
|
|
form = WorkoutForm(instance=row)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
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']
|
|
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
|
|
startdatetime = (str(date) + ' ' + str(starttime))
|
|
startdatetime = datetime.datetime.strptime(startdatetime,
|
|
"%Y-%m-%d %H:%M:%S")
|
|
startdatetime = timezone.make_aware(startdatetime)
|
|
# 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.save()
|
|
# 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"
|
|
url = reverse(workout_edit_view,
|
|
kwargs = {
|
|
'id':str(row.id),
|
|
'successmessage':str(successmessage),
|
|
})
|
|
response = HttpResponseRedirect(url)
|
|
else:
|
|
message = "You are not allowed to change this workout"
|
|
url = reverse(workouts_view,args=[str(message)])
|
|
|
|
response = HttpResponseRedirect(url)
|
|
|
|
else: # form not POSTed
|
|
form = WorkoutForm(instance=row)
|
|
|
|
|
|
try:
|
|
row = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime")
|
|
# check if user is owner of this workout
|
|
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
raise Http404("You are not allowed to edit this workout")
|
|
|
|
# create interactive plot
|
|
f1 = row.csvfilename
|
|
u = row.user.user
|
|
r = Rower.objects.get(user=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:
|
|
res = googlemap_chart(rowdata.df[' latitude'],
|
|
rowdata.df[' longitude'],
|
|
row.name)
|
|
gmscript = res[0]
|
|
gmdiv = res[1]
|
|
else:
|
|
gmscript = ""
|
|
gmdiv = ""
|
|
|
|
|
|
# render page
|
|
if (len(g)<=3):
|
|
return render(request, 'workout_form.html',
|
|
{'form':form,
|
|
'workout':row,
|
|
'graphs1':g[0:3],
|
|
'message': message,
|
|
'successmessage': successmessage,
|
|
'gmscript': gmscript,
|
|
'gmdiv': gmdiv,
|
|
})
|
|
|
|
else:
|
|
return render(request, 'workout_form.html',
|
|
{'form':form,
|
|
'workout':row,
|
|
'graphs1':g[0:3],
|
|
'graphs2':g[3:6],
|
|
'message': message,
|
|
'successmessage': successmessage,
|
|
'gmscript': gmscript,
|
|
'gmdiv': gmdiv,
|
|
})
|
|
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
# Create the chart image with wind corrected pace (OTW)
|
|
@user_passes_test(ispromember,login_url="/",redirect_field_name=None)
|
|
def workout_add_otw_powerplot_view(request,id):
|
|
try:
|
|
w = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
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'
|
|
fullpathimagename = 'static/plots/'+imagename
|
|
u = w.user.user
|
|
r = Rower.objects.get(user=u)
|
|
powerperc = 100*np.array([r.pw_ut2,
|
|
r.pw_ut1,
|
|
r.pw_at,
|
|
r.pw_tr,r.pw_an])/r.ftp
|
|
|
|
|
|
hrpwrdata = {
|
|
'hrmax':r.max,
|
|
'hrut2':r.ut2,
|
|
'hrut1':r.ut1,
|
|
'hrat':r.at,
|
|
'hrtr':r.tr,
|
|
'hran':r.an,
|
|
'ftp':r.ftp,
|
|
'powerperc':serialize_list(powerperc),
|
|
'powerzones':serialize_list(r.powerzones),
|
|
}
|
|
|
|
# make plot - asynchronous task
|
|
plotnr = 9
|
|
|
|
if (w.workouttype=='water'):
|
|
plotnr = plotnr+3
|
|
|
|
if settings.DEBUG:
|
|
res = handle_makeplot.delay(f1,w.csvfilename,
|
|
w.name,hrpwrdata,plotnr,imagename)
|
|
else:
|
|
res = queue.enqueue(handle_makeplot,f1,w.csvfilename,
|
|
w.name,hrpwrdata,plotnr,imagename)
|
|
|
|
# i = GraphImage(workout=w,creationdatetime=datetime.datetime.now(),
|
|
# filename=fullpathimagename)
|
|
i = GraphImage(workout=w,creationdatetime=timezone.now(),
|
|
filename=fullpathimagename)
|
|
i.save()
|
|
|
|
url = "/rowers/workout/"+str(w.id)+"/edit"
|
|
return HttpResponseRedirect(url)
|
|
|
|
# Create the Heart rate zone pie chart
|
|
@login_required()
|
|
def workout_add_piechart_view(request,id):
|
|
try:
|
|
w = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
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'
|
|
fullpathimagename = 'static/plots/'+imagename
|
|
u = w.user.user
|
|
r = Rower.objects.get(user=u)
|
|
|
|
powerperc = 100*np.array([r.pw_ut2,
|
|
r.pw_ut1,
|
|
r.pw_at,
|
|
r.pw_tr,r.pw_an])/r.ftp
|
|
|
|
|
|
hrpwrdata = {
|
|
'hrmax':r.max,
|
|
'hrut2':r.ut2,
|
|
'hrut1':r.ut1,
|
|
'hrat':r.at,
|
|
'hrtr':r.tr,
|
|
'hran':r.an,
|
|
'ftp':r.ftp,
|
|
'powerperc':serialize_list(powerperc),
|
|
'powerzones':serialize_list(r.powerzones),
|
|
}
|
|
|
|
# make plot - asynchronous task
|
|
plotnr = 3
|
|
|
|
if (w.workouttype=='water'):
|
|
plotnr = plotnr+3
|
|
|
|
if settings.DEBUG:
|
|
res = handle_makeplot.delay(f1,w.csvfilename,
|
|
w.name,hrpwrdata,plotnr,imagename)
|
|
else:
|
|
res = queue.enqueue(handle_makeplot,f1,w.csvfilename,
|
|
w.name,hrpwrdata,plotnr,imagename)
|
|
|
|
# i = GraphImage(workout=w,creationdatetime=datetime.datetime.now(),
|
|
# filename=fullpathimagename)
|
|
i = GraphImage(workout=w,creationdatetime=timezone.now(),
|
|
filename=fullpathimagename)
|
|
i.save()
|
|
|
|
url = "/rowers/workout/"+str(w.id)+"/edit"
|
|
return HttpResponseRedirect(url)
|
|
|
|
# Create the Power zone pie chart
|
|
@login_required()
|
|
def workout_add_power_piechart_view(request,id):
|
|
try:
|
|
w = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
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'
|
|
fullpathimagename = 'static/plots/'+imagename
|
|
u = w.user.user
|
|
r = Rower.objects.get(user=u)
|
|
|
|
powerperc = 100*np.array([r.pw_ut2,
|
|
r.pw_ut1,
|
|
r.pw_at,
|
|
r.pw_tr,r.pw_an])/r.ftp
|
|
|
|
|
|
hrpwrdata = {
|
|
'hrmax':r.max,
|
|
'hrut2':r.ut2,
|
|
'hrut1':r.ut1,
|
|
'hrat':r.at,
|
|
'hrtr':r.tr,
|
|
'hran':r.an,
|
|
'ftp':r.ftp,
|
|
'powerperc':serialize_list(powerperc),
|
|
'powerzones':serialize_list(r.powerzones),
|
|
}
|
|
|
|
# make plot - asynchronous task
|
|
plotnr = 13
|
|
|
|
if (w.workouttype=='water'):
|
|
plotnr = plotnr+3
|
|
|
|
if settings.DEBUG:
|
|
res = handle_makeplot.delay(f1,w.csvfilename,
|
|
w.name,hrpwrdata,plotnr,imagename)
|
|
else:
|
|
res = queue.enqueue(handle_makeplot,f1,w.csvfilename,
|
|
w.name,hrpwrdata,plotnr,imagename)
|
|
|
|
i = GraphImage(workout=w,creationdatetime=timezone.now(),
|
|
filename=fullpathimagename)
|
|
i.save()
|
|
|
|
url = "/rowers/workout/"+str(w.id)+"/edit"
|
|
return HttpResponseRedirect(url)
|
|
|
|
# Create the time based summary chart
|
|
@login_required()
|
|
def workout_add_timeplot_view(request,id):
|
|
try:
|
|
w = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
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'
|
|
fullpathimagename = 'static/plots/'+imagename
|
|
u = w.user.user
|
|
r = Rower.objects.get(user=u)
|
|
powerperc = 100*np.array([r.pw_ut2,
|
|
r.pw_ut1,
|
|
r.pw_at,
|
|
r.pw_tr,r.pw_an])/r.ftp
|
|
|
|
|
|
hrpwrdata = {
|
|
'hrmax':r.max,
|
|
'hrut2':r.ut2,
|
|
'hrut1':r.ut1,
|
|
'hrat':r.at,
|
|
'hrtr':r.tr,
|
|
'hran':r.an,
|
|
'ftp':r.ftp,
|
|
'powerperc':serialize_list(powerperc),
|
|
'powerzones':serialize_list(r.powerzones),
|
|
}
|
|
|
|
# make plot - asynchronous task
|
|
plotnr = 1
|
|
|
|
if (w.workouttype=='water'):
|
|
plotnr = plotnr+3
|
|
|
|
if settings.DEBUG:
|
|
res = handle_makeplot.delay(f1,w.csvfilename,
|
|
w.name,hrpwrdata,plotnr,imagename)
|
|
else:
|
|
res = queue.enqueue(handle_makeplot,f1,w.csvfilename,
|
|
w.name,hrpwrdata,plotnr,imagename)
|
|
|
|
i = GraphImage(workout=w,creationdatetime=timezone.now(),
|
|
filename=fullpathimagename)
|
|
# i = GraphImage(workout=w,creationdatetime=datetime.datetime.now(),
|
|
# filename=fullpathimagename)
|
|
i.save()
|
|
|
|
url = "/rowers/workout/"+str(w.id)+"/edit"
|
|
return HttpResponseRedirect(url)
|
|
|
|
# Create the distance based summary chart
|
|
@login_required()
|
|
def workout_add_distanceplot_view(request,id):
|
|
try:
|
|
w = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
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'
|
|
fullpathimagename = 'static/plots/'+imagename
|
|
u = w.user.user
|
|
r = Rower.objects.get(user=u)
|
|
powerperc = 100*np.array([r.pw_ut2,
|
|
r.pw_ut1,
|
|
r.pw_at,
|
|
r.pw_tr,r.pw_an])/r.ftp
|
|
|
|
|
|
hrpwrdata = {
|
|
'hrmax':r.max,
|
|
'hrut2':r.ut2,
|
|
'hrut1':r.ut1,
|
|
'hrat':r.at,
|
|
'hrtr':r.tr,
|
|
'hran':r.an,
|
|
'ftp':r.ftp,
|
|
'powerperc':serialize_list(powerperc),
|
|
'powerzones':serialize_list(r.powerzones),
|
|
}
|
|
|
|
# make plot - asynchronous task
|
|
plotnr = 2
|
|
|
|
if (w.workouttype=='water'):
|
|
plotnr = plotnr+3
|
|
|
|
if settings.DEBUG:
|
|
res = handle_makeplot.delay(f1,w.csvfilename,
|
|
w.name,hrpwrdata,plotnr,imagename)
|
|
else:
|
|
res = queue.enqueue(handle_makeplot,f1,w.csvfilename,
|
|
w.name,hrpwrdata,plotnr,imagename)
|
|
|
|
i = GraphImage(workout=w,creationdatetime=timezone.now(),
|
|
filename=fullpathimagename)
|
|
i.save()
|
|
|
|
url = "/rowers/workout/"+str(w.id)+"/edit"
|
|
return HttpResponseRedirect(url)
|
|
|
|
# Create the advanced parameters distance overview chart
|
|
@user_passes_test(ispromember,login_url="/",redirect_field_name=None)
|
|
def workout_add_distanceplot2_view(request,id):
|
|
try:
|
|
w = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
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'
|
|
fullpathimagename = 'static/plots/'+imagename
|
|
u = w.user.user
|
|
r = Rower.objects.get(user=u)
|
|
powerperc = 100*np.array([r.pw_ut2,
|
|
r.pw_ut1,
|
|
r.pw_at,
|
|
r.pw_tr,r.pw_an])/r.ftp
|
|
|
|
|
|
hrpwrdata = {
|
|
'hrmax':r.max,
|
|
'hrut2':r.ut2,
|
|
'hrut1':r.ut1,
|
|
'hrat':r.at,
|
|
'hrtr':r.tr,
|
|
'hran':r.an,
|
|
'ftp':r.ftp,
|
|
'powerperc':serialize_list(powerperc),
|
|
'powerzones':serialize_list(r.powerzones),
|
|
}
|
|
|
|
# make plot - asynchronous task
|
|
plotnr = 7
|
|
|
|
if (w.workouttype=='water'):
|
|
plotnr = plotnr+3
|
|
|
|
if settings.DEBUG:
|
|
res = handle_makeplot.delay(f1,w.csvfilename,
|
|
w.name,hrpwrdata,plotnr,imagename)
|
|
else:
|
|
res = queue.enqueue(handle_makeplot,f1,w.csvfilename,
|
|
w.name,hrpwrdata,plotnr,imagename)
|
|
|
|
i = GraphImage(workout=w,creationdatetime=timezone.now(),
|
|
filename=fullpathimagename)
|
|
|
|
i.save()
|
|
|
|
url = "/rowers/workout/"+str(w.id)+"/edit"
|
|
return HttpResponseRedirect(url)
|
|
|
|
|
|
# Create the advanced parameters time based overview chart
|
|
@user_passes_test(ispromember,login_url="/",redirect_field_name=None)
|
|
def workout_add_timeplot2_view(request,id):
|
|
try:
|
|
w = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
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'
|
|
fullpathimagename = 'static/plots/'+imagename
|
|
u = w.user.user
|
|
r = Rower.objects.get(user=u)
|
|
powerperc = 100*np.array([r.pw_ut2,
|
|
r.pw_ut1,
|
|
r.pw_at,
|
|
r.pw_tr,r.pw_an])/r.ftp
|
|
|
|
|
|
hrpwrdata = {
|
|
'hrmax':r.max,
|
|
'hrut2':r.ut2,
|
|
'hrut1':r.ut1,
|
|
'hrat':r.at,
|
|
'hrtr':r.tr,
|
|
'hran':r.an,
|
|
'ftp':r.ftp,
|
|
'powerperc':serialize_list(powerperc),
|
|
'powerzones':serialize_list(r.powerzones),
|
|
}
|
|
|
|
# make plot - asynchronous task
|
|
plotnr = 8
|
|
|
|
if (w.workouttype=='water'):
|
|
plotnr = plotnr+3
|
|
|
|
if settings.DEBUG:
|
|
res = handle_makeplot.delay(f1,w.csvfilename,
|
|
w.name,hrpwrdata,plotnr,imagename)
|
|
else:
|
|
res = queue.enqueue(handle_makeplot,f1,w.csvfilename,
|
|
w.name,hrpwrdata,plotnr,imagename)
|
|
|
|
i = GraphImage(workout=w,creationdatetime=timezone.now(),
|
|
filename=fullpathimagename)
|
|
|
|
i.save()
|
|
|
|
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 = Rower.objects.get(user=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"
|
|
if settings.DEBUG:
|
|
return HttpResponse(res)
|
|
else:
|
|
url = reverse(workouts_view,
|
|
kwargs = {
|
|
'message': str(message)
|
|
})
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
data = res.json()
|
|
|
|
return render(request,'strava_list_import.html',
|
|
{'data':data,
|
|
'message':message,
|
|
})
|
|
|
|
return HttpResponse(res)
|
|
|
|
# The page where you select which SportTracks page 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 = Rower.objects.get(user=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"
|
|
if settings.DEBUG:
|
|
return HttpResponse(res)
|
|
else:
|
|
url = reverse(workouts_view,
|
|
kwargs = {
|
|
'message': str(message)
|
|
})
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
workouts = []
|
|
for item in res.json()['items']:
|
|
d = int(float(item['total_distance']))
|
|
i = getidfromsturi(item['uri'])
|
|
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']
|
|
values = [i,d,ttot,s,r,n]
|
|
res = dict(zip(keys,values))
|
|
workouts.append(res)
|
|
return render(request,'sporttracks_list_import.html',
|
|
{'workouts':workouts,
|
|
'message':message,
|
|
})
|
|
|
|
return HttpResponse(res)
|
|
|
|
# List of workouts on Concept2 logbook. This view only used for debugging
|
|
@login_required()
|
|
def c2listdebug_view(request,message=""):
|
|
try:
|
|
thetoken = c2_open(request.user)
|
|
except C2NoTokenError:
|
|
return HttpResponseRedirect("/rowers/me/c2authorize/")
|
|
|
|
r = Rower.objects.get(user=request.user)
|
|
|
|
res = c2stuff.get_c2_workout_list(request.user)
|
|
|
|
if (res.status_code != 200):
|
|
message = "Something went wrong in workout_c2import_view (C2 token renewal)"
|
|
if settings.DEBUG:
|
|
return HttpResponse(res)
|
|
else:
|
|
url = reverse(workouts_view,
|
|
kwargs = {
|
|
'message': str(message)
|
|
})
|
|
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,
|
|
'message':message})
|
|
|
|
# List of workouts available on Concept2 logbook - for import
|
|
@login_required()
|
|
def workout_c2import_view(request,message=""):
|
|
try:
|
|
thetoken = c2_open(request.user)
|
|
except C2NoTokenError:
|
|
return HttpResponseRedirect("/rowers/me/c2authorize/")
|
|
|
|
res = c2stuff.get_c2_workout_list(request.user)
|
|
|
|
if (res.status_code != 200):
|
|
message = "Something went wrong in workout_c2import_view (C2 token refresh)"
|
|
if settings.DEBUG:
|
|
return HttpResponse(res)
|
|
else:
|
|
url = reverse(workouts_view,
|
|
kwargs = {
|
|
'message': str(message)
|
|
})
|
|
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,
|
|
'message':message})
|
|
|
|
# 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]:
|
|
message = res[1]
|
|
return imports_view(request,message=message)
|
|
|
|
strokedata = res[1]
|
|
data = res[0]
|
|
id,message = add_workout_from_strokedata(request.user,stravaid,data,strokedata,
|
|
source='strava')
|
|
try:
|
|
w = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
w.uploadedtostrava=stravaid
|
|
w.save()
|
|
if message:
|
|
url = reverse(workout_edit_view,
|
|
kwargs = {
|
|
'message':message,
|
|
'id':id,
|
|
})
|
|
else:
|
|
url = reverse(workout_edit_view,
|
|
kwargs = {
|
|
'id':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)
|
|
w = Workout.objects.get(id=id)
|
|
w.uploadedtosporttracks=sporttracksid
|
|
w.save()
|
|
if message:
|
|
url = reverse(workout_edit_view,
|
|
kwargs = {
|
|
'id':id,
|
|
'message':message,
|
|
})
|
|
else:
|
|
url = reverse(workout_edit_view,
|
|
kwargs = {
|
|
'id':id,
|
|
})
|
|
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:
|
|
message = "This workout does not have any stroke data associated with it"
|
|
url = reverse(workout_c2import_view,
|
|
kwargs={
|
|
'message':message,
|
|
})
|
|
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:
|
|
url = reverse(workout_edit_view,
|
|
kwargs = {
|
|
'message':message,
|
|
'id':id,
|
|
})
|
|
else:
|
|
url = reverse(workout_edit_view,
|
|
kwargs = {
|
|
'id':id,
|
|
})
|
|
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
# message = json.loads(s.text)['message']
|
|
message = json.loads(res2.text)['message']
|
|
url = reverse(workout_c2import_view,
|
|
kwargs={
|
|
'message':message,
|
|
})
|
|
return HttpResponseRedirect(url)
|
|
|
|
url = reverse(workout_c2import_view,
|
|
kwargs={
|
|
'message':message,
|
|
})
|
|
return HttpResponseRedirect(url)
|
|
|
|
else:
|
|
message = "Received error code from Concept2"
|
|
if settings.DEBUG:
|
|
return HttpResponse(res)
|
|
else:
|
|
url = reverse(workout_c2import_view,
|
|
kwargs={
|
|
'message':message,
|
|
})
|
|
return HttpResponseRedirect(url)
|
|
|
|
# This is the main view for processing uploaded files
|
|
@login_required()
|
|
def workout_upload_view(request,message="",
|
|
uploadoptions={
|
|
'makeprivate':False,
|
|
'make_plot':False,
|
|
'upload_to_C2':False,
|
|
'plottype':'timeplot',
|
|
}):
|
|
|
|
if 'uploadoptions' in request.session:
|
|
uploadoptions = request.session['uploadoptions']
|
|
else:
|
|
request.session['uploadoptions'] = uploadoptions
|
|
|
|
|
|
|
|
makeprivate = uploadoptions['makeprivate']
|
|
make_plot = uploadoptions['make_plot']
|
|
plottype = uploadoptions['plottype']
|
|
upload_toc2 = uploadoptions['upload_to_C2']
|
|
|
|
r = Rower.objects.get(user=request.user)
|
|
if request.method == 'POST':
|
|
form = DocumentsForm(request.POST,request.FILES)
|
|
optionsform = UploadOptionsForm(request.POST)
|
|
if form.is_valid():
|
|
f = request.FILES['file']
|
|
res = handle_uploaded_file(f)
|
|
t = form.cleaned_data['title']
|
|
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']
|
|
upload_to_c2 = optionsform.cleaned_data['upload_to_C2']
|
|
makeprivate = optionsform.cleaned_data['makeprivate']
|
|
|
|
uploadoptions = {
|
|
'makeprivate':makeprivate,
|
|
'make_plot':make_plot,
|
|
'plottype':plottype,
|
|
'upload_to_C2':upload_to_c2,
|
|
}
|
|
|
|
|
|
request.session['uploadoptions'] = uploadoptions
|
|
|
|
f1 = res[0] # file name
|
|
f2 = res[1] # file name incl media directory
|
|
|
|
|
|
id,message,f2 = dataprep.new_workout_from_file(r,f2,
|
|
workouttype=workouttype,
|
|
makeprivate=makeprivate,
|
|
title = t,
|
|
notes='')
|
|
if not id:
|
|
url = reverse(workout_upload_view,
|
|
args=[str(message)])
|
|
response = HttpResponseRedirect(url)
|
|
return response
|
|
|
|
else:
|
|
if message:
|
|
url = reverse(workout_edit_view,
|
|
kwargs = {
|
|
'id':id,
|
|
'message':message,
|
|
})
|
|
else:
|
|
url = reverse(workout_edit_view,
|
|
kwargs = {
|
|
'id':id,
|
|
})
|
|
|
|
response = HttpResponseRedirect(url)
|
|
w = Workout.objects.get(id=id)
|
|
|
|
if (make_plot):
|
|
imagename = f1[:-4]+'.png'
|
|
fullpathimagename = 'static/plots/'+imagename
|
|
u = request.user
|
|
r = Rower.objects.get(user=request.user)
|
|
powerperc = 100*np.array([r.pw_ut2,
|
|
r.pw_ut1,
|
|
r.pw_at,
|
|
r.pw_tr,r.pw_an])/r.ftp
|
|
|
|
hrpwrdata = {
|
|
'hrmax':r.max,
|
|
'hrut2':r.ut2,
|
|
'hrut1':r.ut1,
|
|
'hrat':r.at,
|
|
'hrtr':r.tr,
|
|
'hran':r.an,
|
|
'ftp':r.ftp,
|
|
'powerperc':serialize_list(powerperc),
|
|
'powerzones':serialize_list(r.powerzones),
|
|
}
|
|
|
|
# make plot - asynchronous task
|
|
plotnrs = {
|
|
'timeplot':1,
|
|
'distanceplot':2,
|
|
'pieplot':3,
|
|
}
|
|
|
|
plotnr = plotnrs[plottype]
|
|
if (workouttype=='water'):
|
|
plotnr = plotnr+3
|
|
|
|
|
|
if settings.DEBUG:
|
|
res = handle_makeplot.delay(f1,f2,t,
|
|
hrpwrdata,plotnr,
|
|
imagename)
|
|
else:
|
|
res = queue.enqueue(handle_makeplot,f1,f2,
|
|
t,hrpwrdata,
|
|
plotnr,imagename)
|
|
|
|
|
|
i = GraphImage(workout=w,
|
|
creationdatetime=timezone.now(),
|
|
filename=fullpathimagename)
|
|
i.save()
|
|
|
|
# upload to C2
|
|
if (upload_to_c2):
|
|
try:
|
|
thetoken = c2_open(request.user)
|
|
except C2NoTokenError:
|
|
return HttpResponseRedirect("/rowers/me/c2authorize/")
|
|
try:
|
|
c2userid = c2stuff.get_userid(thetoken)
|
|
data = c2stuff.createc2workoutdata(w)
|
|
authorizationstring = str('Bearer ' + thetoken)
|
|
headers = {'Authorization': authorizationstring,
|
|
'user-agent': 'sanderroosendaal',
|
|
'Content-Type': 'application/json'}
|
|
import urllib
|
|
url = "httpvs://log.concept2.com/api/users/%s/results" % (c2userid)
|
|
response = requests.post(url,headers=headers,data=json.dumps(data))
|
|
|
|
# response = c2stuff.workout_c2_upload(request.user,w)
|
|
if (response.status_code != 201):
|
|
if settings.DEBUG:
|
|
return HttpResponse(response)
|
|
else:
|
|
message = "C2 upload failed"
|
|
url = reverse(workout_edit_view,
|
|
kwargs={
|
|
'message':message,
|
|
'id':str(w.id),
|
|
})
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
s= json.loads(response.text)
|
|
c2id = s['data']['id']
|
|
w.uploadedtoc2 = c2id
|
|
w.save()
|
|
except:
|
|
message = "C2 upload failed"
|
|
url = reverse(workout_edit_view,
|
|
kwargs={
|
|
'message':message,
|
|
'id':str(w.id),
|
|
})
|
|
return HttpResponseRedirect(url)
|
|
|
|
|
|
if message:
|
|
url = reverse(workout_edit_view,
|
|
kwargs={
|
|
'message':message,
|
|
'id':w.id,
|
|
})
|
|
|
|
else:
|
|
url = reverse(workout_edit_view,
|
|
kwargs = {
|
|
'id':w.id,
|
|
})
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
response = render(request,
|
|
'document_form.html',
|
|
{'form':form,
|
|
'optionsform': optionsform,
|
|
'message':message})
|
|
|
|
return response
|
|
else:
|
|
form = DocumentsForm()
|
|
optionsform = UploadOptionsForm(initial=uploadoptions)
|
|
return render(request, 'document_form.html',
|
|
{'form':form,
|
|
'optionsform': optionsform,
|
|
'message':message})
|
|
|
|
|
|
|
|
# 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:
|
|
return render(request,'workout_delete_confirm.html',
|
|
{'id':id,
|
|
'workout':row})
|
|
|
|
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()
|
|
|
|
url = reverse(workouts_view,kwargs={
|
|
'successmessage': "Workout deleted",
|
|
}
|
|
)
|
|
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:
|
|
return render(request,'graphimage_delete_confirm.html',
|
|
{'id':id,
|
|
'graph':img})
|
|
|
|
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()
|
|
|
|
url = reverse(workouts_view,kwargs={
|
|
'successmessage': "Graph deleted",
|
|
}
|
|
|
|
)
|
|
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):
|
|
try:
|
|
r = Rower.objects.get(user=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]})
|
|
else:
|
|
return render(request, 'list_graphs.html',
|
|
{'graphs1': g[0:5],
|
|
'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)
|
|
w = Workout.objects.get(id=g.workout.id)
|
|
r = Rower.objects.get(id=w.user.id)
|
|
|
|
return render(request,'show_graph.html',
|
|
{'graph':g,
|
|
'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 = Rower.objects.get(user=u)
|
|
powerperc = 100*np.array([r.pw_ut2,
|
|
r.pw_ut1,
|
|
r.pw_at,
|
|
r.pw_tr,r.pw_an])/r.ftp
|
|
|
|
rr = rrower(hrmax=r.max,hrut2=r.ut2,
|
|
hrut1=r.ut1,hrat=r.at,
|
|
hrtr=r.tr,hran=r.an,ftp=r.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
|
|
|
|
|
|
url = reverse(workout_summary_edit_view,kwargs={
|
|
'id':id,
|
|
'successmessage': "Original Interval Data Restored",
|
|
}
|
|
|
|
)
|
|
return HttpResponseRedirect(url)
|
|
|
|
# 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
|
|
df = dataprep.datafusion(id1,id2,columns,timeoffset)
|
|
idnew,message = dataprep.new_workout_from_df(r,df,
|
|
title='Fused data',
|
|
parent=w1)
|
|
if message != None:
|
|
url = reverse(workout_edit_view,
|
|
kwargs={
|
|
'message':message,
|
|
'id':idnew,
|
|
})
|
|
else:
|
|
successmessage = 'Data fused'
|
|
url = reverse(workout_edit_view,
|
|
kwargs={
|
|
'successmessage':successmessage,
|
|
'id':idnew,
|
|
})
|
|
|
|
return HttpResponseRedirect(url)
|
|
else:
|
|
return render(request, 'fusion.html',
|
|
{'form':form,
|
|
'workout1':w1,
|
|
'workout2':w2,
|
|
})
|
|
|
|
|
|
form = FusionMetricChoiceForm(instance=w2)
|
|
|
|
return render(request, 'fusion.html',
|
|
{'form':form,
|
|
'workout1':w1,
|
|
'workout2':w2,
|
|
})
|
|
|
|
|
|
# Edit the splits/summary
|
|
@login_required()
|
|
def workout_summary_edit_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 = Rower.objects.get(user=u)
|
|
powerperc = 100*np.array([r.pw_ut2,
|
|
r.pw_ut1,
|
|
r.pw_at,
|
|
r.pw_tr,r.pw_an])/r.ftp
|
|
|
|
rr = rrower(hrmax=r.max,hrut2=r.ut2,
|
|
hrut1=r.ut1,hrat=r.at,
|
|
hrtr=r.tr,hran=r.an,ftp=r.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"]
|
|
rowdata.updateinterval_string(s)
|
|
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)
|
|
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 range(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)
|
|
|
|
|
|
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 range(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 range(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,
|
|
'intervalstats':intervalstats,
|
|
'nrintervals':nrintervals,
|
|
'message': message,
|
|
'successmessage': successmessage,
|
|
'interactiveplot':script,
|
|
'the_div':div,
|
|
'intervalstring':s,
|
|
'savebutton':savebutton,
|
|
})
|
|
|
|
# Page where user can manage his favorite charts
|
|
@user_passes_test(ispromember,login_url="/rowers/me/edit",redirect_field_name=None)
|
|
def rower_favoritecharts_view(request):
|
|
message = ''
|
|
successmessage = ''
|
|
r = Rower.objects.get(user=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}
|
|
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')
|
|
new_instances.append(FavoriteChart(user=r,
|
|
yparam1=yparam1,
|
|
yparam2=yparam2,
|
|
xparam=xparam,
|
|
plottype=plottype,
|
|
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"
|
|
if len(new_instances)==0:
|
|
FavoriteChartFormSet=formset_factory(FavoriteForm,formset=BaseFavoriteFormSet,extra=1)
|
|
favorites_formset = FavoriteChartFormSet()
|
|
except IntegrityError:
|
|
message = "something went wrong"
|
|
|
|
else:
|
|
favorites_formset = FavoriteChartFormSet(initial=favorites_data)
|
|
|
|
|
|
context = {
|
|
'favorites_formset':favorites_formset,
|
|
'message':message,
|
|
'successmessage':successmessage,
|
|
}
|
|
|
|
|
|
|
|
return render(request,'favoritecharts.html',context)
|
|
|
|
# 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,message=""):
|
|
r = Rower.objects.get(user=request.user)
|
|
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 = Rower.objects.get(user=request.user)
|
|
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"
|
|
form = RowerForm(instance=r)
|
|
powerform = RowerPowerForm(instance=r)
|
|
powerzonesform = RowerPowerZonesForm(instance=r)
|
|
accountform = AccountRowerForm(instance=r)
|
|
userform = UserForm(instance=request.user)
|
|
return render(request, 'rower_form.html',
|
|
{'form':form,
|
|
'powerzonesform':powerzonesform,
|
|
'powerform':powerform,
|
|
'rower':r,
|
|
'accountform':accountform,
|
|
'userform':userform,
|
|
'successmessage':successmessage,
|
|
})
|
|
except Rower.DoesNotExist:
|
|
message = "Funny. This user doesn't exist."
|
|
url = reverse(workouts_view,args=[str(message)])
|
|
response = HttpResponseRedirect(url)
|
|
else:
|
|
message = HttpResponse("invalid form")
|
|
#form = RowerForm(instance=r)
|
|
powerform = RowerPowerForm(instance=r)
|
|
powerzonesform = RowerPowerZonesForm(instance=r)
|
|
userform = UserForm(instance=request.user)
|
|
accountform = AccountRowerForm(instance=r)
|
|
return render(request, 'rower_form.html',
|
|
{'form':form,
|
|
'powerzonesform':powerzonesform,
|
|
'userform':userform,
|
|
'accountform':accountform,
|
|
'powerform':powerform,
|
|
'rower':r,
|
|
})
|
|
# url = reverse(rower_edit_view,args=[str(message)])
|
|
# response = HttpResponseRedirect(url)
|
|
|
|
|
|
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']
|
|
try:
|
|
r = Rower.objects.get(user=request.user)
|
|
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)
|
|
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 = "Functional Threshold Value Changed"
|
|
url = reverse(rower_edit_view,args=[str(message)])
|
|
response = HttpResponseRedirect(url)
|
|
except Rower.DoesNotExist:
|
|
message = "Funny. This user doesn't exist."
|
|
url = reverse(rower_edit_view,args=[str(message)])
|
|
response = HttpResponseRedirect(url)
|
|
else:
|
|
message = HttpResponse("invalid form")
|
|
form = RowerForm(instance=r)
|
|
#powerform = RowerPowerForm(instance=r)
|
|
powerzonesform = RowerPowerZonesForm(instance=r)
|
|
userform = UserForm(instance=request.user)
|
|
accountform = AccountRowerForm(instance=r)
|
|
return render(request, 'rower_form.html',
|
|
{'form':form,
|
|
'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 = Rower.objects.get(user=request.user)
|
|
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"
|
|
form = RowerForm(instance=r)
|
|
accountform = AccountRowerForm(instance=r)
|
|
userform = UserForm(instance=request.user)
|
|
powerform = RowerPowerForm(instance=r)
|
|
powerzonesform = RowerPowerZonesForm(instance=r)
|
|
return render(request, 'rower_form.html',
|
|
{'form':form,
|
|
'powerzonesform':powerzonesform,
|
|
'powerform':powerform,
|
|
'userform':userform,
|
|
'accountform':accountform,
|
|
'rower':r,
|
|
'successmessage':successmessage,
|
|
})
|
|
except Rower.DoesNotExist:
|
|
message = "Funny. This user doesn't exist."
|
|
url = reverse(workouts_view,args=[str(message)])
|
|
response = HttpResponseRedirect(url)
|
|
return response
|
|
else:
|
|
form = RowerForm(instance=r)
|
|
powerform = RowerPowerForm(instance=r)
|
|
accountform = AccountRowerForm(instance=r)
|
|
userform = UserForm(instance=request.user)
|
|
#powerzonesform = RowerPowerZonesForm(instance=r)
|
|
message = HttpResponse("invalid form")
|
|
return render(request, 'rower_form.html',
|
|
{'form':form,
|
|
'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)
|
|
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']
|
|
weightcategory = cd['weightcategory']
|
|
u = request.user
|
|
if len(first_name):
|
|
u.first_name = first_name
|
|
u.last_name = last_name
|
|
if len(email):
|
|
u.email = email
|
|
u.save()
|
|
r = Rower.objects.get(user=u)
|
|
r.weightcategory = weightcategory
|
|
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'
|
|
return render(request, 'rower_form.html',
|
|
{'form':form,
|
|
'powerzonesform':powerzonesform,
|
|
'powerform':powerform,
|
|
'accountform':accountform,
|
|
'userform':userform,
|
|
'rower':r,
|
|
'successmessage':successmessage,
|
|
})
|
|
else:
|
|
form = RowerForm(instance=r)
|
|
powerform = RowerPowerForm(instance=r)
|
|
powerzonesform = RowerPowerZonesForm(instance=r)
|
|
return render(request, 'rower_form.html',
|
|
{'form':form,
|
|
'powerzonesform':powerzonesform,
|
|
'powerform':powerform,
|
|
'accountform':accountform,
|
|
'userform':userform,
|
|
'rower':r,
|
|
})
|
|
|
|
|
|
else:
|
|
try:
|
|
r = Rower.objects.get(user=request.user)
|
|
|
|
form = RowerForm(instance=r)
|
|
powerform = RowerPowerForm(instance=r)
|
|
powerzonesform = RowerPowerZonesForm(instance=r)
|
|
accountform = AccountRowerForm(instance=r)
|
|
userform = UserForm(instance=request.user)
|
|
grants = AccessToken.objects.filter(user=request.user)
|
|
return render(request, 'rower_form.html',
|
|
{
|
|
'form':form,
|
|
'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 = Rower.objects.get(user=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,
|
|
'powerform':powerform,
|
|
'grants':grants,
|
|
})
|
|
except AccessToken.DoesNotExist:
|
|
raise Http404("Access token doesn't exist")
|
|
|
|
|
|
# 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)
|
|
|
|
# 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,
|
|
'id':id,
|
|
})
|
|
elif request.method == 'POST':
|
|
form = StrokeDataForm()
|
|
|
|
return render(request, 'strokedata_form.html',
|
|
{
|
|
'form':form,
|
|
'id':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
|
|
from django.views.decorators.csrf import csrf_exempt
|
|
@csrf_exempt
|
|
@login_required()
|
|
@api_view(['GET','POST'])
|
|
def strokedatajson(request,id):
|
|
"""
|
|
POST: Add Stroke data to workout
|
|
GET: Get stroke data of workout
|
|
"""
|
|
try:
|
|
row = Workout.objects.get(id=id)
|
|
if (checkworkoutuser(request.user,row)==False):
|
|
return HttpResponseForbidden("Permission error")
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
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])
|
|
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
|
|
|
|
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,
|
|
})
|
|
|
|
|
|
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 = Rower.objects.get(user=request.user)
|
|
powerperc = 100*np.array([r.pw_ut2,
|
|
r.pw_ut1,
|
|
r.pw_at,
|
|
r.pw_tr,r.pw_an])/r.ftp
|
|
|
|
rr = rrower(hrmax=r.max,hrut2=r.ut2,
|
|
hrut1=r.ut1,hrat=r.at,
|
|
hrtr=r.tr,hran=r.an,ftp=r.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,message='',successmessage=''):
|
|
ismember = 0
|
|
hasrequested = 0
|
|
r = Rower.objects.get(user=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
|
|
else:
|
|
message = text
|
|
|
|
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,
|
|
'members':members,
|
|
'inviteform':inviteform,
|
|
'message':message,
|
|
'successmessage':successmessage,
|
|
'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
|
|
})
|
|
|
|
@login_required()
|
|
def team_leave_view(request,id=0):
|
|
r = Rower.objects.get(user=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 = Rower.objects.get(user=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)
|
|
|
|
return render(request, 'teams.html',
|
|
{
|
|
'teams':ts,
|
|
'myteams':myteams,
|
|
'invites':invites,
|
|
'otherteams':otherteams,
|
|
'requests':requests,
|
|
'myrequests':myrequests,
|
|
'form':form,
|
|
'message':message,
|
|
'successmessage':successmessage,
|
|
'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:
|
|
successmessage = text
|
|
url = reverse(rower_teams_view,kwargs={
|
|
'successmessage':successmessage
|
|
})
|
|
else:
|
|
message = text
|
|
url = reverse(rower_teams_view,kwargs={
|
|
'message':message
|
|
})
|
|
|
|
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:
|
|
successmessage = text
|
|
url = reverse(rower_teams_view,kwargs={
|
|
'successmessage':successmessage
|
|
})
|
|
else:
|
|
message = text
|
|
url = reverse(rower_teams_view,kwargs={
|
|
'message':message
|
|
})
|
|
|
|
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 = ''
|
|
|
|
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:
|
|
successmessage = text
|
|
url = reverse(team_view,kwargs={
|
|
'id':teamid,
|
|
'successmessage': successmessage,
|
|
})
|
|
|
|
else:
|
|
message = text
|
|
url = reverse(team_view,kwargs={
|
|
'id':teamid,
|
|
'message': message,
|
|
})
|
|
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
@login_required()
|
|
def request_revoke_view(request,id=0):
|
|
res,text = teams.revoke_request(request.user,id)
|
|
|
|
if res:
|
|
successmessage = text
|
|
url = reverse(rower_teams_view,kwargs={
|
|
'successmessage':successmessage,
|
|
})
|
|
else:
|
|
message = text
|
|
url = reverse(rower_teams_view,kwargs={
|
|
'message':message,
|
|
})
|
|
|
|
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:
|
|
successmessage = text
|
|
url = reverse(rower_teams_view,kwargs={
|
|
'successmessage':successmessage,
|
|
})
|
|
else:
|
|
message = text
|
|
url = reverse(rower_teams_view,kwargs={
|
|
'message':message,
|
|
})
|
|
|
|
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:
|
|
successmessage = text
|
|
url = reverse(rower_teams_view,kwargs={
|
|
'successmessage':successmessage,
|
|
})
|
|
else:
|
|
message = text
|
|
url = reverse(rower_teams_view,kwargs={
|
|
'message':message,
|
|
})
|
|
|
|
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:
|
|
successmessage = text
|
|
teamid=res
|
|
url = reverse(team_view,kwargs={
|
|
'id':teamid,
|
|
'successmessage': successmessage,
|
|
})
|
|
else:
|
|
message = text
|
|
url = reverse(rower_teams_view,kwargs={
|
|
'message':message,
|
|
})
|
|
|
|
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:
|
|
url = reverse(team_view,
|
|
kwargs={
|
|
'id':id,
|
|
'successmessage':message
|
|
}
|
|
)
|
|
else:
|
|
url = reverse(team_view,
|
|
kwargs={
|
|
'id':id,
|
|
'message':message
|
|
}
|
|
)
|
|
|
|
response = HttpResponseRedirect(url)
|
|
return response
|
|
|
|
else:
|
|
teamcreateform = TeamForm(instance=t)
|
|
|
|
return render(request,'teamedit.html',
|
|
{
|
|
'form':teamcreateform,
|
|
'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',
|
|
{
|
|
'form':teamcreateform,
|
|
})
|
|
|
|
@user_passes_test(iscoachmember,login_url="/",redirect_field_name=None)
|
|
def team_deleteconfirm_view(request,id):
|
|
r = Rower.objects.get(user=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',
|
|
{
|
|
'team':t
|
|
})
|
|
|
|
@user_passes_test(iscoachmember,login_url="/",redirect_field_name=None)
|
|
def team_delete_view(request,id):
|
|
r = Rower.objects.get(user=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
|