Private
Public Access
1
0
Files
rowsandall/rowers/views.py
2018-12-02 12:26:21 +01:00

18401 lines
558 KiB
Python

import time
import colorsys
import timestring
import zipfile
import bleach
import arrow
import pytz
from pytz import UnknownTimeZoneError
import operator
import warnings
import urllib
import yaml
from PIL import Image
from numbers import Number
from django.views.generic.base import TemplateView
from django.contrib.auth import views as auth_views
from django.db.models import Q
from django import template
from django.db import IntegrityError, transaction
from django.views.decorators.csrf import csrf_exempt
from matplotlib.backends.backend_agg import FigureCanvas
import gc
from pyparsing import ParseException
from uuid import uuid4
import codecs
import isodate
import re
import cgi
from icalendar import Calendar, Event
from django.shortcuts import render
from django.template.loader import render_to_string
from django.views.generic.edit import UpdateView,DeleteView
from django.http import (
HttpResponse, HttpResponseRedirect,
HttpResponseForbidden, HttpResponseNotAllowed,
HttpResponseNotFound,Http404
)
from django.contrib.auth import authenticate, login, logout
from rowers.forms import (
LoginForm,DocumentsForm,UploadOptionsForm,ImageForm,CourseForm,
TeamUploadOptionsForm,WorkFlowLeftPanelForm,WorkFlowMiddlePanelForm,
WorkFlowLeftPanelElement,WorkFlowMiddlePanelElement,
LandingPageForm,PlannedSessionSelectForm,WorkoutSessionSelectForm,
PlannedSessionTeamForm,PlannedSessionTeamMemberForm,
VirtualRaceSelectForm,WorkoutRaceSelectForm,CourseSelectForm,
RaceResultFilterForm,PowerIntervalUpdateForm,FlexAxesForm,
FlexOptionsForm,DataFrameColumnsForm,OteWorkoutTypeForm,
MetricsForm,DisqualificationForm,disqualificationreasons,
disqualifiers
)
from django.core.urlresolvers import reverse, reverse_lazy
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,
FitnessMetricForm,PredictedPieceFormNoDistance,
EmailForm, RegistrationForm, RegistrationFormTermsOfService,
RegistrationFormUniqueEmail,RegistrationFormSex,
CNsummaryForm,UpdateWindForm,
UpdateStreamForm,WorkoutMultipleCompareForm,ChartParamChoiceForm,
FusionMetricChoiceForm,BoxPlotChoiceForm,MultiFlexChoiceForm,
TrendFlexModalForm,WorkoutSplitForm,WorkoutJoinParamForm,
PlannedSessionMultipleCloneForm,SessionDateShiftForm,
)
from rowers.models import (
Workout, User, Rower, WorkoutForm,FavoriteChart,
PlannedSession, DeactivateUserForm,DeleteUserForm,
TrainingPlan,TrainingPlanForm,TrainingTarget,TrainingTargetForm,
TrainingMacroCycle,TrainingMesoCycle,TrainingMicroCycle,
TrainingTarget,TrainingTargetForm,
TrainingMacroCycleForm,createmacrofillers,
createmicrofillers, createmesofillers,
microcyclecheckdates,mesocyclecheckdates,macrocyclecheckdates,
TrainingMesoCycleForm, TrainingMicroCycleForm,
RaceLogo,
)
from rowers.models import (
RowerPowerForm,RowerForm,GraphImage,AdvancedWorkoutForm,
RowerPowerZonesForm,AccountRowerForm,UserForm,StrokeData,
Team,TeamForm,TeamInviteForm,TeamInvite,TeamRequest,
WorkoutComment,WorkoutCommentForm,RowerExportForm,
CalcAgePerformance,PowerTimeFitnessMetric,PlannedSessionForm,
PlannedSessionFormSmall,GeoCourseEditForm,VirtualRace,
VirtualRaceForm,VirtualRaceResultForm,RowerImportExportForm,
IndoorVirtualRaceResultForm,IndoorVirtualRaceResult,
IndoorVirtualRaceForm,
)
from rowers.models import (
FavoriteForm,BaseFavoriteFormSet,SiteAnnouncement,BasePlannedSessionFormSet,
get_course_timezone
)
from rowers.metrics import rowingmetrics,defaultfavoritecharts
from rowers import metrics
from rowers import courses
import rowers.uploads as uploads
from django.forms.formsets import formset_factory
from django.forms import modelformset_factory
from django.contrib.auth.decorators import login_required #,user_passes_test
from rowers.decorators import user_passes_test
from time import strftime,strptime,mktime,time,daylight
import os,sys
import datetime
import iso8601
import c2stuff
from c2stuff import c2_open
from runkeeperstuff import runkeeper_open
from sporttracksstuff import sporttracks_open
from tpstuff import tp_open
from iso8601 import ParseError
import stravastuff
from stravastuff import strava_open
import polarstuff
import sporttracksstuff
import underarmourstuff
from underarmourstuff import underarmour_open
import tpstuff
import runkeeperstuff
import ownapistuff
from ownapistuff import TEST_CLIENT_ID, TEST_CLIENT_SECRET, TEST_REDIRECT_URI
from rowsandall_app.settings import (
C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET,
STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET,
POLAR_CLIENT_ID, POLAR_REDIRECT_URI, POLAR_CLIENT_SECRET,
SPORTTRACKS_CLIENT_ID, SPORTTRACKS_REDIRECT_URI,
SPORTTRACKS_CLIENT_SECRET,
UNDERARMOUR_CLIENT_ID, UNDERARMOUR_REDIRECT_URI,
UNDERARMOUR_CLIENT_SECRET,UNDERARMOUR_CLIENT_KEY,
RUNKEEPER_CLIENT_ID,RUNKEEPER_REDIRECT_URI,RUNKEEPER_CLIENT_SECRET,
TP_CLIENT_ID,TP_REDIRECT_URI,TP_CLIENT_KEY,TP_CLIENT_SECRET,
)
from rowers.tasks_standalone import addcomment2
from django.contrib import messages
from async_messages import messages as a_messages
from django.contrib.admin.widgets import AdminDateWidget,AdminTimeWidget,AdminSplitDateTime
import requests
import json
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
from rowers.rows import handle_uploaded_file,handle_uploaded_image
from rowers.plannedsessions import *
from rowers.tasks import handle_makeplot,handle_otwsetpower,handle_sendemailtcx,handle_sendemailcsv
from rowers.tasks import (
handle_sendemail_unrecognized,handle_sendemailnewcomment,
handle_sendemailsummary,
handle_send_disqualification_email,
handle_sendemailfile,
handle_sendemailkml,
handle_sendemailnewresponse, handle_updatedps,
handle_updatecp,long_test_task,long_test_task2,
handle_zip_file,handle_getagegrouprecords,
handle_updatefitnessmetric,
handle_update_empower,
handle_sendemailics,
handle_sendemail_userdeleted,
handle_sendemail_raceregistration,
handle_sendemail_racesubmission,
)
from scipy.signal import savgol_filter
from django.shortcuts import render_to_response
from Cookie import SimpleCookie
from shutil import copyfile,move
import mytypes
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 rowers.emails import send_template_email
from pytz import timezone as tz,utc
from timezonefinder import TimezoneFinder
import dateutil
import mpld3
from mpld3 import plugins
import stravalib
from stravalib.exc import ActivityUploadFailed,TimeoutExceeded
from weather import get_wind_data,get_airport_code,get_metar_data
from oauth2_provider.models import Application,Grant,AccessToken
import django_rq
queue = django_rq.get_queue('default')
queuelow = django_rq.get_queue('low')
queuehigh = django_rq.get_queue('low')
import redis
import threading
from redis import StrictRedis,Redis
from rq.exceptions import NoSuchJobError
from rq.registry import StartedJobRegistry
from rq import Queue,cancel_job
from django.core.cache import cache
from django_mailbox.models import Message,Mailbox,MessageAttachment
# Utility to get stroke data in a JSON response
class JSONResponse(HttpResponse):
def __init__(self, data, **kwargs):
content = JSONRenderer().render(data)
kwargs['content_type'] = 'application/json'
super(JSONResponse, self).__init__(content, **kwargs)
def getrequestrower(request,rowerid=0,userid=0,notpermanent=False):
userid = int(userid)
rowerid = int(rowerid)
if notpermanent == False:
if rowerid == 0 and 'rowerid' in request.session:
rowerid = request.session['rowerid']
if userid != 0:
rowerid = 0
try:
if rowerid != 0:
r = Rower.objects.get(id=rowerid)
elif userid != 0:
u = User.objects.get(id=userid)
r = getrower(u)
else:
r = getrower(request.user)
except Rower.DoesNotExist:
raise Http404("Rower doesn't exist")
if not checkaccessuser(request.user,r):
raise PermissionDenied("You have no access to this user")
if notpermanent == False:
request.session['rowerid'] = r.id
return r
def getrower(user):
try:
if user.is_anonymous():
return None
except AttributeError:
if User.objects.get(id=user).is_anonymous():
return None
try:
r = Rower.objects.get(user=user)
except Rower.DoesNotExist:
r = Rower(user=user)
r.save()
return r
def get_workout(id):
try:
w = Workout.objects.get(id=id)
except Workout.DoesNotExist:
raise Http404("Workout doesn't exist")
return w
def get_workout_permitted(user,id):
w = get_workout(id)
if (checkworkoutuser(user,w)==False):
raise PermissionDenied("Access denied")
return w
def getvalue(data):
perc = 0
total = 1
done = 0
id = 0
session_key = 'noot'
for i in data.iteritems():
if i[0] == 'total':
total = float(i[1])
if i[0] == 'done':
done = float(i[1])
if i[0] == 'id':
id = i[1]
if i[0] == 'session_key':
session_key = i[1]
return total,done,id,session_key
class SessionTaskListener(threading.Thread):
def __init__(self, r, channels):
threading.Thread.__init__(self)
self.redis = r
self.pubsub = self.redis.pubsub()
self.pubsub.subscribe(channels)
def work(self, item):
try:
data = json.loads(item['data'])
total,done,id,session_key = getvalue(data)
perc = int(100.*done/total)
cache.set(id,perc,3600)
except TypeError:
pass
def run(self):
for item in self.pubsub.listen():
if item['data'] == "KILL":
self.pubsub.unsubscribe()
print self, "unsubscribed and finished"
break
else:
self.work(item)
queuefailed = Queue("failed",connection=Redis())
redis_connection = StrictRedis()
r = Redis()
# this doesn't yet work on production
#if settings.DEBUG:
# client = SessionTaskListener(r,['tasks'])
# client.start()
rq_registry = StartedJobRegistry(queue.name,connection=redis_connection)
rq_registryhigh = StartedJobRegistry(queuehigh.name,connection=redis_connection)
rq_registrylow = StartedJobRegistry(queuelow.name,connection=redis_connection)
from rq.job import Job
from rest_framework_swagger.views import get_swagger_view
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
from rest_framework.response import Response
from rowers.serializers import RowerSerializer,WorkoutSerializer
from rest_framework import status,permissions,generics
from rest_framework.decorators import api_view, renderer_classes
from permissions import IsOwnerOrNot
import plots
import mailprocessing
from io import BytesIO
from scipy.special import lambertw
from dataprep import timedeltaconv
from dataprep import getsmallrowdata_db
from scipy.interpolate import griddata
#LOCALTIMEZONE = tz('Etc/UTC')
USER_LANGUAGE = 'en-US'
from interactiveplots import *
from rowers.celery import result as celery_result
# Define the API documentation
schema_view = get_swagger_view(title='Rowsandall API')
def remove_asynctask(request,id):
try:
oldtasks = request.session['async_tasks']
except KeyError:
oldtasks = []
newtasks = []
for task in oldtasks:
if id not in task[0]:
newtasks += [(task[0],task[1])]
request.session['async_tasks'] = newtasks
def get_job_result(jobid):
if settings.DEBUG:
result = celery_result.AsyncResult(jobid).result
else:
running_job_ids = rq_registry.get_job_ids()
if len(running_job_ids) and jobid in running_job_ids:
# job is running
return None
else:
# job is ready
try:
job = Job.fetch(jobid,connection=redis_connection)
result = job.result
except NoSuchJobError:
return None
return result
verbose_job_status = {
'updatecp': 'Critical Power Calculation for Ergometer Workouts',
'updatecpwater': 'Critical Power Calculation for OTW Workouts',
'otwsetpower': 'Rowing Physics OTW Power Calculation',
'agegrouprecords': 'Calculate age group records',
'make_plot': 'Create static chart',
'long_test_task': 'Long Test Task',
'long_test_task2': 'Long Test Task 2',
'update_empower': 'Correct Empower Inflated Power Bug',
'submit_race': 'Checking Race Course Result',
}
def get_job_status(jobid):
if settings.DEBUG:
job = celery_result.AsyncResult(jobid)
jobresult = job.result
if 'fail' in job.status.lower():
jobresult = '0'
summary = {
'status': job.status,
'result': jobresult,
'started_at': None
}
else:
try:
job = Job.fetch(jobid,connection=redis_connection)
summary = {
'status':job.status,
'result':job.result,
'started_at':job.started_at
}
except NoSuchJobError:
summary = {
'status': 'success',
'result': 1,
'started_at':None,
}
try:
if 'fail' in summary['status'].lower():
summary['failed'] = True
else:
summary['failed'] = False
if 'success' in summary['status'].lower():
summary['finished'] = True
elif 'finished' in summary['status'].lower():
summary['finished'] = True
else:
summary['finished'] = False
except AttributeError:
summary = {
'status': 'failed',
'result': 0,
'finished': True,
'failed': True,
'started_at':None,
}
return summary
def kill_async_job(request,id='aap'):
if settings.DEBUG:
job = celery_result.AsyncResult(id)
job.revoke()
else:
try:
cancel_job(id,connection=redis_connection)
except NoSuchJobError:
pass
remove_asynctask(request,id)
cache.delete(id)
url = reverse(session_jobs_status)
return HttpResponseRedirect(url)
@login_required()
def raise_500(request):
if request.user.is_superuser:
raise ValueError
else:
return HttpResponse("invalid")
@login_required()
def test_job_view(request,aantal=100):
session_key = request.session._session_key
job = myqueue(queuehigh,long_test_task,int(aantal),
session_key=session_key)
try:
request.session['async_tasks'] += [(job.id,'long_test_task')]
except KeyError:
request.session['async_tasks'] = [(job.id,'long_test_task')]
url = reverse(session_jobs_status)
return HttpResponseRedirect(url)
@login_required()
def test_job_view2(request,aantal=100):
job = myqueue(queuehigh,long_test_task2,int(aantal),
secret=settings.PROGRESS_CACHE_SECRET)
try:
request.session['async_tasks'] += [(job.id,'long_test_task2')]
except KeyError:
request.session['async_tasks'] = [(job.id,'long_test_task2')]
url = reverse(session_jobs_status)
return HttpResponseRedirect(url)
@csrf_exempt
def post_progress(request,id=None,value=0):
if request.method == 'POST':
try:
secret = request.POST['secret']
except KeyError:
return HttpResponse('Access Denied',status=401)
if secret == settings.PROGRESS_CACHE_SECRET:
if not id:
try:
id = request.POST['id']
except KeyError:
return HttpResponse('Invalid request',400)
try:
value = request.POST['value']
except KeyError:
pass
cache.set(id,value,3600)
# test
result = cache.get(id)
return HttpResponse('progress cached '+str(result),
status=201)
else: # secret not given
return HttpResponse('access denied',status=401)
else: # request method is not POST
return HttpResponse('GET method not allowed',status=405)
def get_all_queued_jobs(userid=0):
r = StrictRedis()
jobs = []
celerykeys = r.keys('celery*')
for key in celerykeys:
id= key[17:]
job = celery_result.AsyncResult(id)
jobresult = job.result
if 'fail' in job.status.lower():
jobresult = '0'
jobs.append(
(id,{
'status':job.status,
'result':jobresult,
'function':'',
'meta':job.info,
}))
ids = [j.id for j in queue.jobs]
ids += [j.id for j in queuehigh.jobs]
ids += [j.id for j in queuelow.jobs]
ids += [j.id for j in queuefailed.jobs]
for id in ids:
job = Job.fetch(id,connection=redis_connection)
jobs.append(
(id,{
'status':job.get_status(),
'result':job.result,
'function':job.func_name,
'meta':job.meta,
}))
return jobs
def get_stored_tasks_status(request):
try:
taskids = request.session['async_tasks']
except KeyError:
taskids = []
taskstatus = []
for id,func_name in reversed(taskids):
progress = 0
cached_progress = cache.get(id)
finished = get_job_status(id)['finished']
if finished:
cache.set(id,100)
progress = 100
elif cached_progress>0:
progress = cached_progress
else:
progress = 0
this_task_status = {
'id':id,
'status':get_job_status(id)['status'],
'failed':get_job_status(id)['failed'],
'finished':get_job_status(id)['finished'],
'func_name':func_name,
'verbose': verbose_job_status[func_name],
'progress': progress,
}
taskstatus.append(this_task_status)
return taskstatus
@login_required()
def get_thumbnails(request,id):
row = get_workout_permitted(request.user,id)
r = getrower(request.user)
result = request.user.is_authenticated() and ispromember(request.user)
if result:
promember=1
if request.user == row.user.user:
mayedit=1
comments = WorkoutComment.objects.filter(workout=row)
aantalcomments = len(comments)
favorites,maxfav = getfavorites(r,row)
charts = []
charts = thumbnails_set(r,id,favorites)
try:
if charts[0]['script'] == '':
charts = []
except IndexError:
charts = []
return JSONResponse(charts)
def get_blog_posts(request):
response = requests.get(
'https://analytics.rowsandall.com/wp-json/wp/v2/posts')
if response.status_code == 200:
blogs_json = response.json()
# with open('blogs.txt','w') as o:
# o.write(json.dumps(blogs_json,indent=2,sort_keys=True))
else:
blogs_json = []
blogposts = []
for postdata in blogs_json[0:3]:
try:
featuredmedia = postdata['featured_media']
url = 'https://analytics.rowsandall.com/wp-json/wp/v2/media/%d' % featuredmedia
response = requests.get(url)
if response.status_code == 200:
image_json = response.json()
image_url = image_json[
'media_details'
][
'sizes'
][
'thumbnail'
][
'source_url'
]
else:
image_url = ''
except KeyError:
image_url = ''
title = postdata['title']['rendered'].encode(
'ascii','xmlcharrefreplace')
excerpt = postdata['excerpt']['rendered'].encode(
'ascii','xmlcharrefreplace')
ptester = re.compile('\<p\>(\w.*)\<\/p\>')
excerpt_first = ptester.match(excerpt).group(1)
thedict = {
'title': title,
'author': '',
'image': image_url,
'excerpt': excerpt_first,
'link': postdata['link'],
}
blogposts.append(thedict)
return JSONResponse(blogposts)
@login_required()
def deactivate_user(request):
pk = request.user.id
user = User.objects.get(pk=pk)
user_form = DeactivateUserForm(instance=user)
if request.user.is_authenticated() and request.user.id == user.id:
if request.method == "POST":
user_form = DeactivateUserForm(request.POST, instance=user)
if user_form.is_valid():
deactivate_user = user_form.save(commit=False)
user.is_active = False
deactivate_user.save()
# url = reverse(auth_views.logout_then_login)
url = '/logout/?next=/login'
return HttpResponseRedirect(url)
return render(request, "userprofile_deactivate.html", {
"user_form": user_form,
})
else:
raise PermissionDenied
@login_required()
def user_gdpr_optin(request):
r = getrower(request.user)
r.gdproptin = False
r.gdproptindate = None
r.save()
nexturl = request.GET.get('next','/rowers/list-workouts/')
if r.gdproptin:
return HttpResponseRedirect(nexturl)
return render(request,'gdpr_optin.html',{
"next": nexturl
})
@login_required()
def user_gdpr_confirm(request):
r = getrower(request.user)
r.gdproptin = True
r.gdproptindate = timezone.now()
r.save()
nexturl = request.GET.get('next','/rowers/list-workouts/')
return HttpResponseRedirect(nexturl)
@login_required()
def remove_user(request):
pk = request.user.id
user = User.objects.get(pk=pk)
user_form = DeleteUserForm(instance=user)
if request.user.is_authenticated() and request.user.id == user.id:
if request.method == "POST":
user_form = DeleteUserForm(request.POST,instance=user)
if user_form.is_valid():
cd = user_form.cleaned_data
name = user.first_name+' '+user.last_name
email = user.email
if cd['delete_user']:
user.delete()
res = myqueue(queuehigh,
handle_sendemail_userdeleted,
name, email)
url = '/logout/?next=/login'
# url = reverse(auth_views.logout_then_login)
return HttpResponseRedirect(url)
return render(request, "userprofile_delete.html", {
"user_form": user_form,
})
else:
raise PermissionDenied
@login_required()
def get_testscript(request,id):
row = get_workout_permitted(request.user,id)
r = getrower(request.user)
object = {
"script":"""
<div id="id_script">
<script>alert("hi")</script>
</div>
""",
"div":"""
<div id="id_div">
Hoi
</div>
"""
}
return JSONResponse([object,object])
@login_required()
def session_jobs_view(request):
taskstatus = get_stored_tasks_status(request)
return HttpResponse(json.dumps(taskstatus))
@login_required()
def session_jobs_status(request):
taskstatus = get_stored_tasks_status(request)
return render(request,
'async_tasks.html',
{'taskstatus':taskstatus})
# Test if row data include candidates
def rowhascoordinates(row):
# create interactive plot
f1 = row.csvfilename
u = row.user.user
r = getrower(u)
rowdata = rdata(f1)
hascoordinates = 1
if rowdata != 0:
try:
latitude = rowdata.df[' latitude']
if not latitude.std():
hascoordinates = 0
except KeyError,AttributeError:
hascoordinates = 0
else:
hascoordinates = 0
return hascoordinates
# Custom error pages with Rowsandall headers
def error500_view(request):
response = render_to_response('500.html', {},
context_instance = RequestContext(request))
response.status_code = 500
return response
def error404_view(request):
response = render_to_response('404.html', {},
context_instance = RequestContext(request))
response.status_code = 404
return response
def error400_view(request):
response = render_to_response('400.html', {},
context_instance = RequestContext(request))
response.status_code = 400
return response
def error403_view(request):
response = render_to_response('403.html', {},
context_instance = RequestContext(request))
response.status_code = 403
return response
# Wrapper around the rowingdata call to catch some exceptions
# Checks for CSV file, then for gzipped CSV file, and if all fails, returns 0
def rdata(file,rower=rrower()):
try:
res = rrdata(csvfile=file,rower=rower)
except IOError, IndexError:
try:
res = rrdata(csvfile=file+'.gz',rower=rower)
except IOError, IndexError:
res = 0
return res
# Query to get teams managed and member of
def get_my_teams(user):
try:
therower = Rower.objects.get(user=user)
try:
teams1 = therower.team.all()
except AttributeError:
teams1 = []
teams2 = Team.objects.filter(manager=user)
teams = list(set(teams1).union(set(teams2)))
except TypeError:
teams = []
return teams
# Used for the interval editor - translates seconds to a time object
def get_time(second):
if (second<=0) or (second>1e9):
hours = 0
minutes=0
sec=0
microsecond = 0
elif math.isnan(second):
hours = 0
minutes=0
sec=0
microsecond = 0
else:
days = int(second/(24.*3600.)) % (24*3600)
hours = int((second-24.*3600.*days)/3600.) % 24
minutes = int((second-3600.*(hours+24.*days))/60.) % 60
sec = int(second-3600.*(hours+24.*days)-60.*minutes) % 60
microsecond = int(1.0e6*(second-3600.*(hours+24.*days)-60.*minutes-sec))
return datetime.time(hours,minutes,sec,microsecond)
# get the workout ID from the SportTracks URI
def getidfromsturi(uri,length=8):
return uri[len(uri)-length:]
import re
def getidfromuri(uri):
m = re.search('/(\w.*)\/(\d+)',uri)
return m.group(2)
from utils import (
geo_distance,serialize_list,deserialize_list,uniqify,
str2bool,range_to_color_hex,absolute,myqueue,get_call,
calculate_age,rankingdistances,rankingdurations,
is_ranking_piece,my_dict_from_instance,wavg,NoTokenError
)
import datautils
from rowers.models import checkworkoutuser,checkaccessuser
# Check if a user is a Coach member
def iscoachmember(user):
if not user.is_anonymous():
try:
r = Rower.objects.get(user=user)
except Rower.DoesNotExist:
r = Rower(user=user)
r.save()
result = user.is_authenticated() and (r.rowerplan=='coach')
else:
result = False
return result
# Check if a user can create planned sessions
def hasplannedsessions(user):
if not user.is_anonymous():
try:
r = Rower.objects.get(user=user)
except Rower.DoesNotExist:
r = Rower(user=user)
r.save()
result = user.is_authenticated() and (r.rowerplan=='coach' or r.rowerplan=='plan')
if not result and r.plantrialexpires:
result = user.is_authenticated() and r.rowerplan=='basic' and r.plantrialexpires >= datetime.date.today()
else:
result = False
return result
from rowers.utils import isprorower
# Check if a user is a Pro member
def ispromember(user):
if not user.is_anonymous():
try:
r = Rower.objects.get(user=user)
except Rower.DoesNotExists:
r = Rower(user=user)
r.save()
result = user.is_authenticated() and isprorower(r)
else:
result = False
return result
# More User/Rower utils
def add_defaultfavorites(r):
for c in defaultfavoritecharts:
f = FavoriteChart(user=r,
yparam1=c['yparam1'],
yparam2=c['yparam2'],
xparam=c['xparam'],
plottype=c['plottype'],
workouttype=c['workouttype'],
reststrokes=c['reststrokes'],
notes=c['notes'])
f.save()
return 1
# User registration
def rower_register_view(request):
nextpage = request.GET.get('next','/rowers/list-workouts/')
if request.method == 'POST':
#form = RegistrationFormUniqueEmail(request.POST)
form = RegistrationFormSex(request.POST)
if form.is_valid():
first_name = form.cleaned_data['first_name']
last_name = form.cleaned_data['last_name']
email = form.cleaned_data['email']
password = form.cleaned_data['password1']
username = form.cleaned_data['username']
sex = form.cleaned_data['sex']
birthdate = form.cleaned_data['birthdate']
weightcategory = form.cleaned_data['weightcategory']
nextpage = request.POST['next']
theuser = User.objects.create_user(username,password=password)
theuser.first_name = first_name
theuser.last_name = last_name
theuser.email = email
theuser.save()
therower = Rower(user=theuser,sex=sex,birthdate=birthdate,
weightcategory=weightcategory)
therower.save()
# create default favorite charts
add_defaultfavorites(therower)
# Create Sample workout
f = 'media/testdata.csv.gz'
timestr = strftime("%Y%m%d-%H%M%S")
f2 = f[:-7]+timestr+'.csv.gz'
copyfile(f,f2)
response = dataprep.new_workout_from_file(therower,f2,
title='New User Sample Data',
notes='This is an example workout to get you started')
newworkoutid = response[0]
w = Workout.objects.get(id=newworkoutid)
w.startdatetime = timezone.now()
w.save()
# Create and send email
fullemail = first_name + " " + last_name + " " + "<" + email + ">"
subject = "Thank you for registering on rowsandall.com"
from_address = 'Sander Roosendaal <info@rowsandall.com>'
d = {'first_name':theuser.first_name}
send_template_email(from_address,[fullemail],
subject,'registeremail.html',d)
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'])
theuser = authenticate(username=username,password=password)
login(request,theuser)
return HttpResponseRedirect(nextpage)
# '/rowers/register/thankyou/')
else:
return render(request,
"registration_form.html",
{'form':form,
'next':nextpage,})
else:
form = RegistrationFormSex()
return render(request,
"registration_form.html",
{'form':form,
'next':nextpage,})
# Shows analysis page
@login_required()
def analysis_view(request,userid=0):
r = getrequestrower(request,userid=userid)
return render(request,
"analysis.html",
{
'active':'nav-analysis',
'rower':r,
}
)
# Shows laboratory page
@login_required()
def laboratory_view(request,userid=0):
r = getrequestrower(request,userid=userid)
return render(request,
"laboratory.html",
{
'active':'nav-analysis',
'rower':r,
}
)
# Shows email form and sends it if submitted
def sendmail(request):
if request.method == 'POST':
form = EmailForm(request.POST)
if form.is_valid():
firstname = form.cleaned_data['firstname']
lastname = form.cleaned_data['lastname']
email = form.cleaned_data['email']
subject = form.cleaned_data['subject']
botcheck = form.cleaned_data['botcheck'].lower()
message = form.cleaned_data['message']
if botcheck == 'yes':
try:
fullemail = firstname + " " + lastname + " " + "<" + email + ">"
send_mail(subject, message, fullemail, ['info@rowsandall.com'])
return HttpResponseRedirect('/rowers/email/thankyou/')
except:
return HttpResponseRedirect('/rowers/email/')
else:
messages.error(request,'You have to answer YES to the question')
return HttpResponseRedirect('/rowers/email/')
else:
return HttpResponseRedirect('/rowers/email/')
else:
return HttpResponseRedirect('/rowers/email/')
@login_required()
def start_trial_view(request):
r = getrower(request.user)
if r.protrialexpires is not None:
messages.error(request,'You do not qualify for a trial')
url = '/rowers/promembership'
return HttpResponseRedirect(url)
r.protrialexpires = datetime.date.today()+datetime.timedelta(13)
r.save()
url = reverse(workouts_view)
messages.info(request,'We have started your 14 day trial period')
subject2 = "User started Pro Trial"
message2 = "User Started Pro Trial.\n"
message2 += request.user.email + "\n"
message2 += "User name: "+request.user.username
send_mail(subject2, message2,
'Rowsandall Server <info@rowsandall.com>',
['roosendaalsander@gmail.com'])
return HttpResponseRedirect(url)
@login_required()
def start_plantrial_view(request):
r = getrower(request.user)
if r.plantrialexpires is not None:
messages.error(request,'You do not qualify for a trial')
url = '/rowers/promembership'
return HttpResponseRedirect(url)
r.plantrialexpires = datetime.date.today()+datetime.timedelta(13)
r.protrialexpires = datetime.date.today()+datetime.timedelta(13)
r.save()
url = reverse(workouts_view)
messages.info(request,'We have started your 14 day trial period')
subject2 = "User started Plan Trial"
message2 = "User Started Plan Trial.\n"
message2 += request.user.email + "\n"
message2 += "User name: "+request.user.username
send_mail(subject2, message2,
'Rowsandall Server <info@rowsandall.com>',
['roosendaalsander@gmail.com'])
return HttpResponseRedirect(url)
# Create workout data from Strava or Concept2
# data and create the associated Workout object and save it
def add_workout_from_strokedata(user,importid,data,strokedata,
source='c2',splitdata=None,
workoutsource='concept2'):
try:
workouttype = data['type']
except KeyError:
workouttype = 'rower'
if workouttype not in [x[0] for x in Workout.workouttypes]:
workouttype = 'other'
try:
comments = data['comments']
except:
comments = ' '
# comments = "Imported data \n %s" % comments
# comments = "Imported data \n"+comments # str(comments)
try:
thetimezone = tz(data['timezone'])
except:
thetimezone = 'UTC'
r = getrower(user)
try:
rowdatetime = iso8601.parse_date(data['date_utc'])
except KeyError:
rowdatetime = iso8601.parse_date(data['start_date'])
except ParseError:
rowdatetime = iso8601.parse_date(data['date'])
try:
c2intervaltype = data['workout_type']
except KeyError:
c2intervaltype = ''
try:
title = data['name']
except KeyError:
title = ""
try:
t = data['comments'].split('\n', 1)[0]
title += t[:20]
except:
title = 'Imported'
starttimeunix = arrow.get(rowdatetime).timestamp
res = make_cumvalues(0.1*strokedata['t'])
cum_time = res[0]
lapidx = res[1]
unixtime = cum_time+starttimeunix
# unixtime[0] = starttimeunix
seconds = 0.1*strokedata.ix[:,'t']
nr_rows = len(unixtime)
try:
latcoord = strokedata.ix[:,'lat']
loncoord = strokedata.ix[:,'lon']
except:
latcoord = np.zeros(nr_rows)
loncoord = np.zeros(nr_rows)
try:
strokelength = strokedata.ix[:,'strokelength']
except:
strokelength = np.zeros(nr_rows)
dist2 = 0.1*strokedata.ix[:,'d']
try:
spm = strokedata.ix[:,'spm']
except KeyError:
spm = 0*dist2
try:
hr = strokedata.ix[:,'hr']
except KeyError:
hr = 0*spm
pace = strokedata.ix[:,'p']/10.
pace = np.clip(pace,0,1e4)
pace = pace.replace(0,300)
velo = 500./pace
power = 2.8*velo**3
# save csv
# Create data frame with all necessary data to write to csv
df = pd.DataFrame({'TimeStamp (sec)':unixtime,
' Horizontal (meters)': dist2,
' Cadence (stokes/min)':spm,
' HRCur (bpm)':hr,
' longitude':loncoord,
' latitude':latcoord,
' Stroke500mPace (sec/500m)':pace,
' Power (watts)':power,
' DragFactor':np.zeros(nr_rows),
' DriveLength (meters)':np.zeros(nr_rows),
' StrokeDistance (meters)':strokelength,
' DriveTime (ms)':np.zeros(nr_rows),
' StrokeRecoveryTime (ms)':np.zeros(nr_rows),
' AverageDriveForce (lbs)':np.zeros(nr_rows),
' PeakDriveForce (lbs)':np.zeros(nr_rows),
' lapIdx':lapidx,
' ElapsedTime (sec)':seconds
})
df.sort_values(by='TimeStamp (sec)',ascending=True)
timestr = strftime("%Y%m%d-%H%M%S")
# Create CSV file name and save data to CSV file
csvfilename ='media/{code}_{importid}.csv'.format(
importid=importid,
code = uuid4().hex[:16]
)
res = df.to_csv(csvfilename+'.gz',index_label='index',
compression='gzip')
# with Concept2
if source=='c2':
try:
totaldist = data['distance']
totaltime = data['time']/10.
except KeyError:
totaldist = 0
totaltime = 0
else:
totaldist = 0
totaltime = 0
id,message = dataprep.save_workout_database(
csvfilename,r,
workouttype=workouttype,
title=title,notes=comments,
# totaldist=totaldist,
# totaltime=totaltime,
workoutsource=workoutsource,
dosummary=True
)
return id,message
# Export workout to TCX and send to user's email address
@login_required()
def workout_tcxemail_view(request,id=0):
message = ""
successmessage = ""
r = getrower(request.user)
w = get_workout(id)
if (checkworkoutuser(request.user,w)):
if r.emailbounced:
message = "Please check your email address first. Email to this address bounced."
messages.error(request, message)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':int(id)
})
return HttpResponseRedirect(url)
tcxfile,tcxmessg = stravastuff.createstravaworkoutdata(w,dozip=False)
if tcxfile == 0:
message = "Something went wrong (TCX export) "+tcxmessg
messages.error(request,message)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':int(id)
})
return HttpResponseRedirect(url)
if tcxfile:
res = myqueue(queuehigh,handle_sendemailtcx,
r.user.first_name,
r.user.last_name,
r.user.email,
tcxfile,
emailbounced = r.emailbounced
)
successmessage = "The TCX file was sent to you per email"
messages.info(request,successmessage)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':int(id)
})
response = HttpResponseRedirect(url)
else:
message = "You are not allowed to export this workout"
messages.error(request,message)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':int(id)
})
response = HttpResponseRedirect(url)
return response
@login_required()
def plannedsessions_icsemail_view(request,userid=0):
r = getrequestrower(request,userid=userid)
startdate,enddate = get_dates_timeperiod(request)
sps = get_sessions(r,startdate=startdate,enddate=enddate)
cal = Calendar()
cal.add('prodid','rowsandall')
cal.add('version','1.0')
for ps in sps:
event = Event()
comment = '{d} {u} {c}'.format(
d=ps.sessionvalue,
u = ps.sessionunit,
c = ps.criterium)
event.add('summary',ps.name)
event.add('dtstart',ps.preferreddate)
event.add('dtend',ps.preferreddate)
event['uid'] = 'plannedsession_'+str(ps.id)
event.add('description',ps.comment)
event.add('comment',comment)
cal.add_component(event)
response = HttpResponse(cal.to_ical())
response['Content-Disposition'] = 'attachment; filename="training_plan_%s.ics"' % request.user.id
response['Content-Type'] = 'application/octet-stream'
return response
@login_required()
def course_kmlemail_view(request,id=0):
r = getrower(request.user)
if r.emailbounced:
message = "Please check your email address first. Email to this address bounced."
messages.error(request,message)
return HttpResponseRedirect(
reverse(course_view,
kwargs = {
'id':str(id),
})
)
course = GeoCourse.objects.get(id=id)
kmlstring = courses.coursetokml(course)
kmlfilename = 'course_{id}.kml'.format(id=id)
with codecs.open(kmlfilename,'w','utf-8') as fop:
fop.write(kmlstring)
res = myqueue(queuehigh,handle_sendemailkml,
r.user.first_name,
r.user.last_name,
r.user.email,kmlfilename,
emailbounced = r.emailbounced
)
successmessage = "The KML file was sent to you per email"
messages.info(request,successmessage)
url = reverse(course_view,
kwargs = {
'id':str(id),
})
response = HttpResponseRedirect(url)
return response
# Export workout to GPX and send to user's email address
@login_required()
def workout_gpxemail_view(request,id=0):
message = ""
successmessage = ""
r = Rower.objects.get(user=request.user)
if r.emailbounced:
message = "Please check your email address first. Email to this address bounced."
messages.error(request, message)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':int(id)
})
return HttpResponseRedirect(url)
w = get_workout(id)
if (checkworkoutuser(request.user,w)):
filename = w.csvfilename
row = rdata(filename)
gpxfilename = filename[:-4]+'.gpx'
row.exporttogpx(gpxfilename)
res = myqueue(queuehigh,handle_sendemailtcx,
r.user.first_name,
r.user.last_name,
r.user.email,gpxfilename,
emailbounced = r.emailbounced
)
successmessage = "The GPX file was sent to you per email"
messages.info(request,successmessage)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':int(id)
})
response = HttpResponseRedirect(url)
else:
message = "You are not allowed to export this workout"
messages.error(request,message)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':str(w.id),
})
response = HttpResponseRedirect(url)
return response
# Get Workout summary CSV file
@login_required()
def workouts_summaries_email_view(request):
r = getrower(request.user)
if r.emailbounced:
message = "Please check your email address first. Email to this address bounced."
messages.error(request, message)
return HttpResponseRedirect(
reverse(r.defaultlandingpage,
kwargs = {
'id':str(w.id),
})
)
if request.method == 'POST':
form = DateRangeForm(request.POST)
if form.is_valid():
startdate = form.cleaned_data['startdate']
enddate = form.cleaned_data['enddate']
filename = 'rowsandall_workouts_{first}_{last}.csv'.format(
first=startdate,
last=enddate
)
df = dataprep.workout_summary_to_df(r,startdate=startdate,enddate=enddate)
df.to_csv(filename,encoding='utf-8')
res = myqueue(queuehigh,handle_sendemailsummary,
r.user.first_name,
r.user.last_name,
r.user.email,
filename,
emailbounced = r.emailbounced
)
messages.info(request,'The summary CSV file was sent to you per email')
else:
form = DateRangeForm()
return render(request,"export_workouts.html",
{
'form':form
})
# Get Workout CSV file and send it to user's email address
@login_required()
def workout_csvemail_view(request,id=0):
message = ""
r = getrower(request.user)
if r.emailbounced:
message = "Please check your email address first. Email to this address bounced."
messages.error(request, message)
return HttpResponseRedirect(
reverse(r.defaultlandingpage,
kwargs = {
'id':str(w.id),
})
)
w = get_workout(id)
if (checkworkoutuser(request.user,w)):
csvfile = w.csvfilename
res = myqueue(queuehigh,handle_sendemailcsv,r.user.first_name,
r.user.last_name,
r.user.email,csvfile,
emailbounced = r.emailbounced
)
successmessage = "The CSV file was sent to you per email"
messages.info(request,successmessage)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':str(w.id),
})
response = HttpResponseRedirect(url)
else:
message = "You are not allowed to export this workout"
messages.error(request,message)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':str(w.id),
})
response = HttpResponseRedirect(url)
return response
# Get Workout CSV file and send it to user's email address
@login_required()
def workout_csvtoadmin_view(request,id=0):
message = ""
r = getrower(request.user)
w = get_workout(id)
csvfile = w.csvfilename
res = myqueue(queuehigh,
handle_sendemailcsv,
'Sander',
'Roosendaal',
'roosendaalsander@gmail.com',
csvfile)
successmessage = "The CSV file was sent to the site admin per email"
messages.info(request,successmessage)
url = reverse(workout_view,
kwargs = {
'id':str(w.id),
})
response = HttpResponseRedirect(url)
return response
# Send workout to TP
@login_required()
def workout_tp_upload_view(request,id=0):
message = ""
r = getrower(request.user)
res = -1
try:
thetoken = tp_open(r.user)
except NoTokenError:
return HttpResponseRedirect("/rowers/me/tpauthorize/")
# ready to upload. Hurray
w = get_workout_permitted(request.user,id)
r = w.user
if (checkworkoutuser(request.user,w)):
tcxfile = tpstuff.createtpworkoutdata(w)
if tcxfile:
res,reason,status_code,headers = tpstuff.uploadactivity(
r.tptoken,tcxfile,
name=w.name
)
if res == 0:
message = "Upload to TrainingPeaks failed with status code "+str(status_code)+": "+reason
try:
os.remove(tcxfile)
except WindowsError:
pass
messages.error(request,message)
else: # res != 0
w.uploadedtotp = res
w.save()
os.remove(tcxfile)
messages.info(request,'Uploaded to TrainingPeaks')
else: # no tcxfile
message = "Upload to TrainingPeaks failed"
w.uploadedtotp = -1
w.save()
messages.error(request,message)
else: # not allowed to upload
message = "You are not allowed to export this workout to TP"
messages.error(request,message)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':str(w.id),
})
return HttpResponseRedirect(url)
# Send workout to Strava
# abundance of error logging here because there were/are some bugs
@login_required()
def workout_strava_upload_view(request,id=0):
message = ""
r = getrower(request.user)
res = -1
try:
thetoken = strava_open(request.user)
except NoTokenError:
return HttpResponseRedirect("/rowers/me/stravaauthorize")
if (r.stravatoken == '') or (r.stravatoken is None):
s = "Token doesn't exist. Need to authorize"
return HttpResponseRedirect("/rowers/me/stravaauthorize/")
else:
# ready to upload. Hurray
w = get_workout_permitted(request.user,id)
r = w.user
if (checkworkoutuser(request.user,w)):
try:
tcxfile,tcxmessg = stravastuff.createstravaworkoutdata(w)
if tcxfile:
with open(tcxfile,'rb') as f:
try:
newnotes = w.notes+'\n from '+w.workoutsource+' via rowsandall.com'
except TypeError:
newnotes = 'from '+w.workoutsource+' via rowsandall.com'
if w.workouttype in mytypes.rowtypes:
activity_type = r.stravaexportas
else:
activity_type = mytypes.stravamapping[w.workouttype]
res,mes = stravastuff.handle_stravaexport(
f,w.name,
r.stravatoken,
description=newnotes,
activity_type=activity_type)
if res==0:
messages.error(request,mes)
w.uploadedtostrava = -1
w.save()
try:
os.remove(tcxfile)
except WindowsError:
pass
url = reverse(r.defaultlandingpage,
kwargs = {
'id':str(w.id),
})
response = HttpResponseRedirect(url)
return response
try:
w.uploadedtostrava = res
w.save()
try:
os.remove(tcxfile)
except WindowsError:
pass
url = reverse(workout_edit_view,kwargs={'id':w.id})
messages.info(request,mes)
except:
with open("media/stravaerrors.log","a") as errorlog:
errorstring = str(sys.exc_info()[0])
timestr = strftime("%Y%m%d-%H%M%S")
errorlog.write(timestr+errorstring+"\r\n")
errorlog.write("views.py line 826\r\n")
message = 'Error: '+errorstring
messages.error(request,message)
else: # No tcxfile
message = "Strava Data error "+tcxmessg
messages.error(request,message)
w.uploadedtostrava = -1
w.save()
url = reverse(r.defaultlandingpage,
kwargs = {
'id':str(w.id),
})
response = HttpResponseRedirect(url)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':str(w.id),
}
)
response = HttpResponseRedirect(url)
except ActivityUploadFailed as e:
message = "Strava Upload error: %s" % e
messages.error(request,message)
w.uploadedtostrava = -1
w.save()
os.remove(tcxfile)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':str(w.id),
})
response = HttpResponseRedirect(url)
return response
# Upload workout to Concept2 logbook
@login_required()
def workout_c2_upload_view(request,id=0):
message = ""
# ready to upload. Hurray
w = get_workout(id)
r = w.user
try:
message,c2id = c2stuff.workout_c2_upload(request.user,w)
except NoTokenError:
return HttpResponseRedirect("/rowers/me/c2authorize/")
if message and c2id <=0:
messages.error(request,message)
elif message:
messages.info(request,message)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':int(id)
})
response = HttpResponseRedirect(url)
return response
# Upload workout to RunKeeper
@login_required()
def workout_runkeeper_upload_view(request,id=0):
message = ""
w = get_workout(id)
r = w.user
try:
thetoken = runkeeper_open(r.user)
except NoTokenError:
return HttpResponseRedirect("/rowers/me/runkeeperauthorize/")
# ready to upload. Hurray
if (checkworkoutuser(request.user,w)):
data = runkeeperstuff.createrunkeeperworkoutdata(w)
if not data:
message = "Data error"
messages.error(request,message)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':str(w.id),
})
return HttpResponseRedirect(url)
authorizationstring = str('Bearer ' + thetoken)
headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal',
'Content-Type': 'application/vnd.com.runkeeper.NewFitnessActivity+json',
'Content-Length':'nnn'}
url = "https://api.runkeeper.com/fitnessActivities"
response = requests.post(url,headers=headers,data=json.dumps(data))
# check for duplicate error first
if (response.status_code == 409 ):
message = "Duplicate error"
messages.error(request,message)
w.uploadedtorunkeeper = -1
w.save()
elif (response.status_code == 201 or response.status_code==200):
runkeeperid = runkeeperstuff.getidfromresponse(response)
w.uploadedtorunkeeper = runkeeperid
w.save()
url = reverse(workout_edit_view, kwargs={'id':w.id})
return HttpResponseRedirect(url)
else:
s = response
message = "Something went wrong in workout_runkeeper_upload_view: %s - %s" % (s.reason,s.text)
messages.error(request,message)
else:
message = "You are not authorized to upload this workout"
messages.error(request,message)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':str(w.id),
})
return HttpResponseRedirect(url)
# Upload workout to Underarmour
@login_required()
def workout_underarmour_upload_view(request,id=0):
message = ""
w = get_workout(id)
r = w.user
try:
thetoken = underarmour_open(r.user)
except NoTokenError:
return HttpResponseRedirect("/rowers/me/underarmourauthorize/")
# ready to upload. Hurray
if (checkworkoutuser(request.user,w)):
data = underarmourstuff.createunderarmourworkoutdata(w)
if not data:
message = "Data error"
messages.error(request,message)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':str(w.id),
})
return HttpResponseRedirect(url)
authorizationstring = str('Bearer ' + thetoken)
headers = {'Authorization': authorizationstring,
'Api-Key': UNDERARMOUR_CLIENT_KEY,
'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json',
}
url = "https://api.ua.com/v7.1/workout/"
response = requests.post(url,headers=headers,data=json.dumps(data))
# check for duplicate error first
if (response.status_code == 409 ):
message = "Duplicate error"
messages.error(request,message)
w.uploadedtounderarmour = -1
w.save()
elif (response.status_code == 201 or response.status_code==200):
underarmourid = underarmourstuff.getidfromresponse(response)
w.uploadedtounderarmour = underarmourid
w.save()
url = reverse(workout_edit_view,kwargs={'id':w.id})
return HttpResponseRedirect(url)
else:
s = response
message = "Something went wrong in workout_underarmour_upload_view: %s " % s.reason
messages.error(request,message)
else:
message = "You are not authorized to upload this workout"
messages.error(request,message)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':str(w.id),
})
return HttpResponseRedirect(url)
# Upload workout to SportTracks
@login_required()
def workout_sporttracks_upload_view(request,id=0):
message = ""
# ready to upload. Hurray
w = get_workout(id)
r = w.user
try:
thetoken = sporttracks_open(r.user)
except NoTokenError:
return HttpResponseRedirect("/rowers/me/sporttracksauthorize/")
if (checkworkoutuser(request.user,w)):
data = sporttracksstuff.createsporttracksworkoutdata(w)
if not data:
message = "Data error"
messages.error(request,message)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':str(w.id),
})
return HttpResponseRedirect(url)
authorizationstring = str('Bearer ' + thetoken)
headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json'}
url = "https://api.sporttracks.mobi/api/v2/fitnessActivities.json"
response = requests.post(url,headers=headers,data=json.dumps(data))
# check for duplicate error first
if (response.status_code == 409 ):
message = "Duplicate error"
messages.error(request,message)
w.uploadedtosporttracks = -1
w.save()
elif (response.status_code == 201 or response.status_code==200):
s= response.json()
sporttracksid = sporttracksstuff.getidfromresponse(response)
w.uploadedtosporttracks = sporttracksid
w.save()
message = "Upload to SportTracks was successful"
messages.info(request,message)
url = reverse(workout_edit_view,kwargs={'id':w.id})
return HttpResponseRedirect(url)
else:
s = response
message = "Something went wrong in workout_sporttracks_upload_view: %s" % s.reason
messages.error(request,message)
else:
message = "You are not authorized to upload this workout"
messages.error(request,message)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':str(w.id),
})
return HttpResponseRedirect(url)
# Concept2 authorization
@login_required()
def rower_c2_authorize(request):
# Generate a random string for the state parameter
# Save it for use later to prevent xsrf attacks
state = str(uuid4())
scope = "user:read,results:write"
params = {"client_id": C2_CLIENT_ID,
"response_type": "code",
"redirect_uri": C2_REDIRECT_URI}
url = "http://log.concept2.com/oauth/authorize?"+ urllib.urlencode(params)
url += "&scope="+scope
return HttpResponseRedirect(url)
# Strava Authorization
@login_required()
def rower_strava_authorize(request):
# Generate a random string for the state parameter
# Save it for use later to prevent xsrf attacks
state = str(uuid4())
params = {"client_id": STRAVA_CLIENT_ID,
"response_type": "code",
"redirect_uri": STRAVA_REDIRECT_URI,
"scope": "activity:write,activity:read_all"}
url = "https://www.strava.com/oauth/authorize?"+ urllib.urlencode(params)
return HttpResponseRedirect(url)
# Polar Authorization
@login_required()
def rower_polar_authorize(request):
state = str(uuid4())
params = {"client_id": POLAR_CLIENT_ID,
"response_type": "code",
"redirect_uri": POLAR_REDIRECT_URI,
"state": state,
# "scope":"accesslink.read_all"
}
url = "https://flow.polar.com/oauth2/authorization?" +urllib.urlencode(params)
return HttpResponseRedirect(url)
# Runkeeper authorization
@login_required()
def rower_runkeeper_authorize(request):
# Generate a random string for the state parameter
# Save it for use later to prevent xsrf attacks
state = str(uuid4())
params = {"client_id": RUNKEEPER_CLIENT_ID,
"response_type": "code",
"state": state,
"redirect_uri": RUNKEEPER_REDIRECT_URI}
url = "https://runkeeper.com/apps/authorize?"+ urllib.urlencode(params)
return HttpResponseRedirect(url)
# SportTracks Authorization
@login_required()
def rower_sporttracks_authorize(request):
# Generate a random string for the state parameter
# Save it for use later to prevent xsrf attacks
state = str(uuid4())
params = {"client_id": SPORTTRACKS_CLIENT_ID,
"response_type": "code",
"state": state,
"redirect_uri": SPORTTRACKS_REDIRECT_URI}
url = "https://api.sporttracks.mobi/oauth2/authorize?"+ urllib.urlencode(params)
return HttpResponseRedirect(url)
# Underarmour Authorization
@login_required()
def rower_underarmour_authorize(request):
# Generate a random string for the state parameter
# Save it for use later to prevent xsrf attacks
state = str(uuid4())
redirect_uri = UNDERARMOUR_REDIRECT_URI
url = 'https://www.mapmyfitness.com/v7.1/oauth2/authorize/?' \
'client_id={0}&response_type=code&redirect_uri={1}'.format(
UNDERARMOUR_CLIENT_KEY, redirect_uri
)
return HttpResponseRedirect(url)
# Underarmour Authorization
@login_required()
def rower_tp_authorize(request):
# Generate a random string for the state parameter
# Save it for use later to prevent xsrf attacks
state = str(uuid4())
params = {"client_id": TP_CLIENT_KEY,
"response_type": "code",
"redirect_uri": TP_REDIRECT_URI,
"scope": "file:write",
}
url = "https://oauth.trainingpeaks.com/oauth/authorize/?" +urllib.urlencode(params)
return HttpResponseRedirect(url)
# Concept2 token refresh. URL for manual refresh. Not visible to users
@login_required()
def rower_c2_token_refresh(request):
r = getrower(request.user)
res = c2stuff.do_refresh_token(r.c2refreshtoken)
if res[0] != None:
access_token = res[0]
expires_in = res[1]
refresh_token = res[2]
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
r = getrower(request.user)
r.c2token = access_token
r.tokenexpirydate = expirydatetime
r.c2refreshtoken = refresh_token
r.save()
successmessage = "Tokens refreshed. Good to go"
messages.info(request,successmessage)
else:
message = "Something went wrong (refreshing tokens). Please reauthorize:"
messages.error(request,message)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
# Underarmour token refresh. URL for manual refresh. Not visible to users
@login_required()
def rower_underarmour_token_refresh(request):
r = getrower(request.user)
res = underarmourstuff.do_refresh_token(
r.underarmourrefreshtoken,
r.underarmourtoken
)
access_token = res[0]
expires_in = res[1]
refresh_token = res[2]
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
r = getrower(request.user)
r.underarmourtoken = access_token
r.underarmourtokenexpirydate = expirydatetime
r.underarmourrefreshtoken = refresh_token
r.save()
successmessage = "Tokens refreshed. Good to go"
messages.info(request,successmessage)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
# TrainingPeaks token refresh. URL for manual refresh. Not visible to users
@login_required()
def rower_tp_token_refresh(request):
r = getrower(request.user)
res = tpstuff.do_refresh_token(
r.tprefreshtoken,
)
access_token = res[0]
expires_in = res[1]
refresh_token = res[2]
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
r = getrower(request.user)
r.tptoken = access_token
r.tptokenexpirydate = expirydatetime
r.tprefreshtoken = refresh_token
r.save()
successmessage = "Tokens refreshed. Good to go"
messages.info(request,successmessage)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
# SportTracks token refresh. URL for manual refresh. Not visible to users
@login_required()
def rower_sporttracks_token_refresh(request):
r = getrower(request.user)
res = sporttracksstuff.do_refresh_token(
r.sporttracksrefreshtoken,
)
access_token = res[0]
expires_in = res[1]
refresh_token = res[2]
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
r = getrower(request.user)
r.sporttrackstoken = access_token
r.sporttrackstokenexpirydate = expirydatetime
r.sporttracksrefreshtoken = refresh_token
r.save()
successmessage = "Tokens refreshed. Good to go"
messages.info(request,successmessage)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
# Concept2 Callback
@login_required()
def rower_process_callback(request):
try:
code = request.GET['code']
res = c2stuff.get_token(code)
except MultiValueDictKeyError:
message = "The resource owner or authorization server denied the request"
messages.error(request,message)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
access_token = res[0]
if access_token == 0:
message = res[1]
message += ' Contact info@rowsandall.com if this behavior persists.'
messages.error(request,message)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
expires_in = res[1]
refresh_token = res[2]
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
r = getrower(request.user)
r.c2token = access_token
r.tokenexpirydate = expirydatetime
r.c2refreshtoken = refresh_token
r.save()
successmessage = "Tokens stored. Good to go"
messages.info(request,successmessage)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
# The imports page
@login_required()
def imports_view(request):
r = getrower(request.user)
if request.method == 'POST':
form = RowerImportExportForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
for attr, value in cd.items():
setattr(r,attr,value)
r.save()
messages.info(request,'Settings Saved')
else:
form = RowerImportExportForm(instance=r)
return render(request,"imports.html",
{
'teams':get_my_teams(request.user),
'form':form,
})
# Just for testing purposes
@login_required()
def test_reverse_view(request):
successmessage = "Tokens stored. Good to go"
messages.info(request,successmessage)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
# dummy
@login_required()
def rower_process_twittercallback(request):
return "dummy"
# Process Polar Callback
@login_required()
def rower_process_polarcallback(request):
try:
code = request.GET['code']
except MultiValueDictKeyError:
try:
message = request.GET['error']
except MultiValueDictKeyError:
message = "access error"
messages.error(request,message)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
access_token, expires_in, user_id = polarstuff.get_token(code)
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
r = getrower(request.user)
r.polartoken = access_token
r.polartokenexpirydate = expirydatetime
r.polaruserid = user_id
r.save()
successmessage = "Tokens stored. Good to go"
messages.info(request,successmessage)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
# Process Strava Callback
@login_required()
def rower_process_stravacallback(request):
try:
code = request.GET['code']
scope = request.GET['scope']
except MultiValueDictKeyError:
try:
message = request.GET['error']
except MultiValueDictKeyError:
message = "access error"
messages.error(request,message)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
res = stravastuff.get_token(code)
if res[0]:
access_token = res[0]
expires_in = res[1]
refresh_token = res[2]
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
r = getrower(request.user)
r.stravatoken = access_token
r.stravatokenexpirydate = expirydatetime
r.stravarefreshtoken = refresh_token
r.save()
successmessage = "Tokens stored. Good to go"
messages.info(request,successmessage)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
else:
message = "Something went wrong with the Strava authorization"
messages.error(request,message)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
# Process Runkeeper callback
@login_required()
def rower_process_runkeepercallback(request):
code = request.GET['code']
res = runkeeperstuff.get_token(code)
access_token = res[0]
if access_token == 0:
messages.error(request,"Something went wrong importing the token")
url = reverse(workouts_view)
return HttpResponseRedirect(url)
r = getrower(request.user)
r.runkeepertoken = access_token
r.save()
successmessage = "Tokens stored. Good to go"
messages.info(request,successmessage)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
# Process SportTracks callback
@login_required()
def rower_process_sporttrackscallback(request):
code = request.GET['code']
res = sporttracksstuff.get_token(code)
access_token = res[0]
expires_in = res[1]
refresh_token = res[2]
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
r = getrower(request.user)
r.sporttrackstoken = access_token
r.sporttrackstokenexpirydate = expirydatetime
r.sporttracksrefreshtoken = refresh_token
r.save()
successmessage = "Tokens stored. Good to go"
messages.info(request,successmessage)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
# Process Underarmour callback
@login_required()
def rower_process_underarmourcallback(request):
code = request.GET['code']
res = underarmourstuff.get_token(code)
access_token = res[0]
expires_in = res[1]
refresh_token = res[2]
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
r = getrower(request.user)
r.underarmourtoken = access_token
r.underarmourtokenexpirydate = expirydatetime
r.underarmourrefreshtoken = refresh_token
r.save()
successmessage = "Tokens stored. Good to go"
messages.info(request,successmessage)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
# Process TrainingPeaks callback
@login_required()
def rower_process_tpcallback(request):
code = request.GET['code']
res = tpstuff.get_token(code)
access_token = res[0]
expires_in = res[1]
refresh_token = res[2]
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
r = getrower(request.user)
r.tptoken = access_token
r.tptokenexpirydate = expirydatetime
r.tprefreshtoken = refresh_token
r.save()
successmessage = "Tokens stored. Good to go"
messages.info(request,successmessage)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
# 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)
def keyvalue_get_default(key,options,def_options):
try:
return options[key]
except KeyError:
return def_options[key]
# The Flex plot for a large selection of workouts
@login_required()
def cum_flex_data(
request,
options={
'includereststrokes':False,
'rankingonly':False,
'modality':'all',
'waterboattype':mytypes.waterboattype,
'theuser':0,
'xparam':'spm',
'yparam1':'power',
'yparam2':'None',
'enddatestring':timezone.now().strftime("%Y-%m-%d"),
'startdatestring':(timezone.now()-datetime.timedelta(days=30)).strftime("%Y-%m-%d"),
'deltadays':-1,
}):
def_options = options
if 'options' in request.session:
options = request.session['options']
modality = keyvalue_get_default('modality',options,def_options)
rankingonly = keyvalue_get_default('rankingonly',options,def_options)
includereststrokes = keyvalue_get_default('includereststrokes',options,def_options)
waterboattype = keyvalue_get_default('waterboattype',options,def_options)
workstrokesonly = not includereststrokes
theuser = keyvalue_get_default('theuser',options,def_options)
xparam = keyvalue_get_default('xparam',options,def_options)
yparam1 = keyvalue_get_default('yparam1',options,def_options)
yparam2 = keyvalue_get_default('yparam2',options,def_options)
startdatestring = keyvalue_get_default('startdatestring',options,def_options)
enddatestring = keyvalue_get_default('enddatestring',options,def_options)
if modality == 'all':
modalities = [m[0] for m in mytypes.workouttypes]
else:
modalities = [modality]
try:
startdate = iso8601.parse_date(startdatestring)
except ParseError:
startdate = timezone.now()-datetime.timedelta(days=7)
try:
enddate = iso8601.parse_date(enddatestring)
except ParseError:
enddate = timezone.now()
if enddate < startdate:
s = enddate
enddate = startdate
startdate = s
promember=0
if theuser == 0:
theuser = request.user.id
if not request.user.is_anonymous():
r = getrower(request.user)
result = request.user.is_authenticated() and ispromember(request.user)
if result:
promember=1
r2 = getrower(theuser)
if rankingonly:
rankingpiece = [True,]
else:
rankingpiece = [True,False]
allworkouts = Workout.objects.filter(user=r2,
workouttype__in=modalities,
boattype__in=waterboattype,
startdatetime__gte=startdate,
startdatetime__lte=enddate,
rankingpiece__in=rankingpiece)
if allworkouts:
res = interactive_cum_flex_chart2(allworkouts,xparam=xparam,
yparam1=yparam1,
yparam2=yparam2,
promember=promember,
workstrokesonly=workstrokesonly,
)
script = res[0]
div = res[1]
else:
script = ''
div = '<p>No pieces uploaded for this date range.</p>'
scripta = script.split('\n')[2:-1]
script = ''.join(scripta)
data = {
"script":script,
"div":div,
}
return JSONResponse(data)
# The Flex plot for a large selection of workouts
@login_required()
def histo_data(
request,
options={
'includereststrokes':False,
'rankingonly':False,
'modality':'all',
'waterboattype':mytypes.waterboattype,
'theuser':0,
'enddatestring':timezone.now().strftime("%Y-%m-%d"),
'startdatestring':(timezone.now()-datetime.timedelta(days=30)).strftime("%Y-%m-%d"),
'deltadays':-1,
}):
def_options = options
if 'options' in request.session:
options = request.session['options']
modality = keyvalue_get_default('modality',options,def_options)
rankingonly = keyvalue_get_default('rankingonly',options,def_options)
includereststrokes = keyvalue_get_default('includereststrokes',options,def_options)
waterboattype = keyvalue_get_default('waterboattype',options,def_options)
workstrokesonly = not includereststrokes
theuser = keyvalue_get_default('theuser',options,def_options)
startdatestring = keyvalue_get_default('startdatestring',options,def_options)
enddatestring = keyvalue_get_default('enddatestring',options,def_options)
if modality == 'all':
modalities = [m[0] for m in mytypes.workouttypes]
else:
modalities = [modality]
try:
startdate = iso8601.parse_date(startdatestring)
except ParseError:
startdate = timezone.now()-datetime.timedelta(days=7)
try:
enddate = iso8601.parse_date(enddatestring)
except ParseError:
enddate = timezone.now()
if enddate < startdate:
s = enddate
enddate = startdate
startdate = s
promember=0
if theuser == 0:
theuser = request.user.id
if not request.user.is_anonymous():
r = getrower(request.user)
result = request.user.is_authenticated() and ispromember(request.user)
if result:
promember=1
r2 = getrower(theuser)
if rankingonly:
rankingpiece = [True,]
else:
rankingpiece = [True,False]
allworkouts = Workout.objects.filter(user=r2,
workouttype__in=modalities,
boattype__in=waterboattype,
startdatetime__gte=startdate,
startdatetime__lte=enddate,
rankingpiece__in=rankingpiece)
if allworkouts:
res = interactive_histoall(allworkouts)
script = res[0]
div = res[1]
else:
script = ''
div = '<p>No pieces uploaded for this date range.</p>'
scripta = script.split('\n')[2:-1]
script = ''.join(scripta)
data = {
"script":script,
"div":div,
}
return JSONResponse(data)
@login_required()
def cum_flex(request,theuser=0,
xparam='spm',
yparam1='power',
yparam2='None',
startdate=timezone.now()-datetime.timedelta(days=10),
enddate=timezone.now(),
deltadays=-1,
enddatestring=timezone.now().strftime("%Y-%m-%d"),
startdatestring=(timezone.now()-datetime.timedelta(days=30)).strftime("%Y-%m-%d"),
options={
'includereststrokes':False,
'workouttypes':[i[0] for i in mytypes.workouttypes],
'waterboattype':mytypes.waterboattype,
'rankingonly':False,
}):
r = getrequestrower(request,userid=theuser)
theuser = r.user
if 'waterboattype' in request.session:
waterboattype = request.session['waterboattype']
else:
waterboattype = mytypes.waterboattype
if 'rankingonly' in request.session:
rankingonly = request.session['rankingonly']
else:
rankingonly = False
if 'modalities' in request.session:
modalities = request.session['modalities']
if len(modalities) > 1:
modality = 'all'
else:
modality = modalities[0]
else:
modalities = [m[0] for m in mytypes.workouttypes]
modality = 'all'
try:
rankingonly = options['rankingonly']
except KeyError:
rankingonly = False
try:
includereststrokes = options['includereststrokes']
except KeyError:
includereststrokes = False
workstrokesonly = not includereststrokes
waterboattype = mytypes.waterboattype
if startdatestring != "":
startdate = iso8601.parse_date(startdatestring)
if enddatestring != "":
enddate = iso8601.parse_date(enddatestring)
if enddate < startdate:
s = enddate
enddate = startdate
startdate = s
# get all indoor rows of in date range
# process form
if request.method == 'POST':
form = DateRangeForm(request.POST)
modalityform = TrendFlexModalForm(request.POST)
flexaxesform = FlexAxesForm(request,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
startdatestring = startdate.strftime('%Y-%m-%d')
enddatestring = enddate.strftime('%Y-%m-%d')
if modalityform.is_valid():
modality = modalityform.cleaned_data['modality']
waterboattype = modalityform.cleaned_data['waterboattype']
rankingonly = modalityform.cleaned_data['rankingonly']
if modality == 'all':
modalities = [m[0] for m in mytypes.workouttypes]
else:
modalities = [modality]
if modality != 'water':
waterboattype = [b[0] for b in mytypes.boattypes]
request.session['modalities'] = modalities
request.session['waterboattype'] = waterboattype
request.session['rankingonly'] = rankingonly
form = DateRangeForm(initial={
'startdate': startdate,
'enddate': enddate,
})
if flexaxesform.is_valid():
xparam = flexaxesform.cleaned_data['xaxis']
yparam1 = flexaxesform.cleaned_data['yaxis1']
yparam2 = flexaxesform.cleaned_data['yaxis2']
else:
form = DateRangeForm(initial={
'startdate': startdate,
'enddate': enddate,
})
includereststrokes = False
workstrokesonly = not includereststrokes
modalityform = TrendFlexModalForm(
initial={
'modality':modality,
'waterboattype':waterboattype,
'rankingonly':rankingonly,
}
)
initial = {
'xaxis':xparam,
'yaxis1':yparam1,
'yaxis2':yparam2
}
flexaxesform = FlexAxesForm(request,initial=initial)
negtypes = []
for b in mytypes.boattypes:
if b[0] not in waterboattype:
negtypes.append(b[0])
script = ''
div = get_call()
js_resources = ''
css_resources = ''
options = {
'xparam': xparam,
'yparam1': yparam1,
'yparam2': yparam2,
'modality': modality,
'theuser': theuser.id,
'waterboattype':waterboattype,
'startdatestring':startdatestring,
'enddatestring':enddatestring,
'rankingonly':rankingonly,
'includereststrokes':includereststrokes,
}
request.session['options'] = options
promember=0
mayedit=0
if not request.user.is_anonymous():
result = request.user.is_authenticated() and ispromember(request.user)
if result:
promember = 1
request.session['options'] = options
return render(request, 'cum_flex.html',
{'interactiveplot':script,
'the_div':div,
'js_res': js_resources,
'css_res':css_resources,
'id':theuser,
'rower':r,
'active':'nav-analysis',
'theuser':theuser,
'startdate':startdate,
'enddate':enddate,
'form':form,
'optionsform':modalityform,
'xparam':xparam,
'yparam1':yparam1,
'yparam2':yparam2,
'promember':promember,
'teams':get_my_teams(request.user),
'flexaxesform':flexaxesform,
})
def planrequired_view(request):
messages.info(request,"This functionality requires Coach or Self-Coach membership")
return render(request,'promembership.html')
@user_passes_test(hasplannedsessions,login_url="/rowers/promembership",
message="This functionality requires a Coach or Self-Coach plan",
redirect_field_name=None)
def fitnessmetric_view(request,id=0,mode='rower',
startdate=timezone.now()-timezone.timedelta(days=365),
enddate=timezone.now()):
therower = getrequestrower(request,userid=id)
theuser = therower.user
if request.method == 'POST':
form = FitnessMetricForm(request.POST)
if form.is_valid():
startdate = form.cleaned_data['startdate']
enddate = form.cleaned_data['enddate']
mode = form.cleaned_data['mode']
else:
form = FitnessMetricForm()
fitnessmetrics = PowerTimeFitnessMetric.objects.filter(
user=theuser,
date__gte=startdate,
date__lte=enddate)
script,thediv = fitnessmetric_chart(
fitnessmetrics,theuser,
workoutmode=mode
)
return render(request,'fitnessmetric.html',
{
'rower':therower,
'active':'nav-analysis',
'chartscript':script,
'the_div':thediv,
'mode':mode,
'form':form,
})
# Show the EMpower Oarlock generated Stroke Profile
@user_passes_test(ispromember,login_url="/rowers/promembership/",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def workout_forcecurve_view(request,id=0,workstrokesonly=False):
row = get_workout(id)
promember=0
mayedit=0
if not request.user.is_anonymous():
r = getrower(request.user)
result = request.user.is_authenticated() and ispromember(request.user)
if result:
promember=1
if request.user == row.user.user:
mayedit=1
if not promember:
return HttpResponseRedirect("/rowers/about/")
if request.method == 'POST' and 'workstrokesonly' in request.POST:
workstrokesonly = request.POST['workstrokesonly']
if workstrokesonly == 'True':
workstrokesonly = True
else:
workstrokesonly = False
script,div,js_resources,css_resources = interactive_forcecurve([row],
workstrokesonly=workstrokesonly)
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,id),
'name': row.name
},
{
'url':reverse(workout_forcecurve_view,kwargs={'id':id}),
'name': 'Empower Force Curve'
}
]
r = getrower(request.user)
return render(request,
'forcecurve_single.html',
{
'the_script':script,
'rower':r,
'workout':row,
'breadcrumbs':breadcrumbs,
'active':'nav-workouts',
'the_div':div,
'js_res': js_resources,
'css_res':css_resources,
'id':int(id),
'mayedit':mayedit,
'workstrokesonly': not workstrokesonly,
'teams':get_my_teams(request.user),
})
# Test asynchronous tasking and messaging
@login_required()
def workout_test_task_view(request,id=0):
row = Workout.objects.get(id=id)
res = myqueue(queuehigh,addcomment2,request.user.id,row.id)
url = reverse(workout_edit_view,
kwargs = {
'id':int(id),
})
return HttpResponseRedirect(url)
# Show Stroke power histogram for a workout
@login_required()
def workout_histo_view(request,id=0):
w = get_workout(id)
promember=0
mayedit=0
if not request.user.is_anonymous():
r = getrower(request.user)
result = request.user.is_authenticated() and ispromember(request.user)
if result:
promember=1
if request.user == w.user.user:
mayedit=1
if not promember:
return HttpResponseRedirect("/rowers/about/")
res = interactive_histoall([w])
script = res[0]
div = res[1]
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,id),
'name': w.name
},
{
'url':reverse(workout_histo_view,kwargs={'id':id}),
'name': 'Histogram'
}
]
return render(request,
'histo_single.html',
{'interactiveplot':script,
'breadcrumbs':breadcrumbs,
'active':'nav-workouts',
'workout':w,
'rower':r,
'the_div':div,
'id':int(id),
'mayedit':mayedit,
'teams':get_my_teams(request.user),
})
# Histogram for a date/time range
@user_passes_test(ispromember,login_url="/rowers/promembership",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def histo(request,theuser=0,
startdate=timezone.now()-datetime.timedelta(days=365),
enddate=timezone.now(),
deltadays=-1,
enddatestring=timezone.now().strftime("%Y-%m-%d"),
startdatestring=(timezone.now()-datetime.timedelta(days=30)).strftime("%Y-%m-%d"),
options={
'includereststrokes':False,
'workouttypes':[i[0] for i in mytypes.workouttypes],
'waterboattype':mytypes.waterboattype,
'rankingonly': False,
}):
r = getrequestrower(request,userid=theuser)
theuser = r.user
if 'waterboattype' in request.session:
waterboattype = request.session['waterboattype']
else:
waterboattype = mytypes.waterboattype
if 'rankingonly' in request.session:
rankingonly = request.session['rankingonly']
else:
rankingonly = False
if 'modalities' in request.session:
modalities = request.session['modalities']
if len(modalities) > 1:
modality = 'all'
else:
modality = modalities[0]
else:
modalities = [m[0] for m in mytypes.workouttypes]
modality = 'all'
try:
rankingonly = options['rankingonly']
except KeyError:
rankingonly = False
try:
includereststrokes = options['includereststrokes']
except KeyError:
includereststrokes = False
workstrokesonly = not includereststrokes
waterboattype = mytypes.waterboattype
if startdatestring != "":
startdate = iso8601.parse_date(startdatestring)
if enddatestring != "":
enddate = iso8601.parse_date(enddatestring)
if enddate < startdate:
s = enddate
enddate = startdate
startdate = s
# get all indoor rows of in date range
# process form
if request.method == 'POST':
form = DateRangeForm(request.POST)
modalityform = TrendFlexModalForm(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
startdatestring = startdate.strftime('%Y-%m-%d')
enddatestring = enddate.strftime('%Y-%m-%d')
if modalityform.is_valid():
modality = modalityform.cleaned_data['modality']
waterboattype = modalityform.cleaned_data['waterboattype']
rankingonly = modalityform.cleaned_data['rankingonly']
if modality == 'all':
modalities = [m[0] for m in mytypes.workouttypes]
else:
modalities = [modality]
if modality != 'water':
waterboattype = [b[0] for b in mytypes.boattypes]
request.session['modalities'] = modalities
request.session['waterboattype'] = waterboattype
request.session['rankingonly'] = rankingonly
form = DateRangeForm(initial={
'startdate': startdate,
'enddate': enddate,
})
else:
form = DateRangeForm(initial={
'startdate': startdate,
'enddate': enddate,
})
includereststrokes = False
workstrokesonly = not includereststrokes
modalityform = TrendFlexModalForm(
initial={
'modality':modality,
'waterboattype':waterboattype,
'rankingonly':rankingonly,
}
)
negtypes = []
for b in mytypes.boattypes:
if b[0] not in waterboattype:
negtypes.append(b[0])
script = ''
div = get_call()
js_resources = ''
css_resources = ''
options = {
'modality': modality,
'theuser': theuser.id,
'waterboattype':waterboattype,
'startdatestring':startdatestring,
'enddatestring':enddatestring,
'rankingonly':rankingonly,
'includereststrokes':includereststrokes,
}
request.session['options'] = options
promember=0
mayedit=0
if not request.user.is_anonymous():
result = request.user.is_authenticated() and ispromember(request.user)
if result:
promember = 1
request.session['options'] = options
return render(request, 'histo.html',
{'interactiveplot':script,
'the_div':div,
'id':theuser,
'active':'nav-analysis',
'theuser':theuser,
'rower':r,
'startdate':startdate,
'enddate':enddate,
'form':form,
'optionsform':modalityform,
'teams':get_my_teams(request.user),
})
# add a workout manually
@login_required()
def addmanual_view(request):
r = Rower.objects.get(user=request.user)
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':reverse(addmanual_view),
'name': 'Add Manual Entry'
},
]
if request.method == 'POST':
# Form was submitted
form = WorkoutForm(request.POST)
metricsform = MetricsForm(request.POST)
if form.is_valid() and metricsform.is_valid():
# Get values from form
name = form.cleaned_data['name']
if name == '':
name = 'Manual Entry'
date = form.cleaned_data['date']
starttime = form.cleaned_data['starttime']
workouttype = form.cleaned_data['workouttype']
duration = form.cleaned_data['duration']
weightcategory = form.cleaned_data['weightcategory']
distance = form.cleaned_data['distance']
notes = form.cleaned_data['notes']
thetimezone = form.cleaned_data['timezone']
private = form.cleaned_data['private']
avghr = metricsform.cleaned_data['avghr']
avgpwr = metricsform.cleaned_data['avgpwr']
avgspm = metricsform.cleaned_data['avgspm']
try:
ps = form.cleaned_data['plannedsession']
except KeyError:
ps = None
try:
boattype = request.POST['boattype']
except KeyError:
boattype = '1x'
try:
privacy = request.POST['privacy']
except KeyError:
privacy = 'visible'
try:
rankingpiece = form.cleaned_data['rankingpiece']
except KeyError:
rankingpiece = False
try:
duplicate = form.cleaned_data['duplicate']
except KeyError:
duplicate = False
if private:
privacy = 'private'
else:
privacy = 'visible'
startdatetime = (str(date) + ' ' + str(starttime))
startdatetime = datetime.datetime.strptime(startdatetime,
"%Y-%m-%d %H:%M:%S")
startdatetime = timezone.make_aware(startdatetime)
startdatetime = startdatetime.astimezone(
pytz.timezone(thetimezone)
)
id,message = dataprep.create_row_df(r,
distance,
duration,startdatetime,
weightcategory=weightcategory,
avghr=avghr,
rankingpiece=rankingpiece,
avgpwr=avgpwr,
duplicate=duplicate,
avgspm=avgspm,
title = name,
notes=notes,
workouttype=workouttype)
if message:
messages.error(request,message)
if id:
w = Workout.objects.get(id=id)
w.rankingpiece = rankingpiece
w.privacy = privacy
w.weightcategory = weightcategory
w.notes = notes
w.plannedsession = ps
w.name = name
w.workouttype = workouttype
w.boattype = boattype
w.save()
if ps:
add_workouts_plannedsession([w],ps,w.user)
messages.info(request,'New workout created')
url = reverse(
workout_edit_view,
kwargs={'id':id}
)
return HttpResponseRedirect(url)
else:
return render(request,'manualadd.html',
{'form':form,
'metricsform':metricsform,
'breadcrumbs':breadcrumbs,
'active':'nav-workouts',
})
initial = {
'workouttype':'rower',
'date':datetime.date.today(),
'starttime':timezone.now(),
'timezone':r.defaulttimezone,
'duration':datetime.timedelta(minutes=2),
'distance':500,
}
form = WorkoutForm(initial=initial)
metricsform = MetricsForm()
return render(request,'manualadd.html',
{'form':form,
'metricsform':metricsform,
'breadcrumbs':breadcrumbs,
'active':'nav-workouts',
})
@login_required()
def fitness_metric_view(request,mode='rower',days=42):
r = getrower(request.user)
startdate = timezone.now()-datetime.timedelta(days=days)
# test if not something already done
ms = PowerTimeFitnessMetric.objects.filter(user=request.user)
max_workout_id = max([m.last_workout for m in ms])
last_update_date = max([m.date.strftime('%Y-%m-%d') for m in ms])
now_date = timezone.now().strftime('%Y-%m-%d')
if mode == 'rower':
workouts = Workout.objects.filter(
user=r,
workouttype__in=['rower','dynamic','slides'],
startdatetime__gte=startdate)
else:
workouts = Workout.objects.filter(
user=r,
workouttype__in=['water','coastal'],
startdatetime__gte=startdate)
theids = [int(w.id) for w in workouts]
max_id = max(theids)
if last_update_date >= now_date or max_workout_id >= max_id:
return HttpResponse("already done today or no new workouts")
job = myqueue(queue,
handle_updatefitnessmetric,
request.user.id,mode,theids,
)
return HttpResponse("job queued")
# Show ranking distances including predicted paces
@login_required()
def rankings_view(request,theuser=0,
startdate=timezone.now()-datetime.timedelta(days=365),
enddate=timezone.now(),
deltadays=-1,
startdatestring="",
enddatestring=""):
if deltadays>0:
startdate = enddate-datetime.timedelta(days=int(deltadays))
if startdatestring != "":
startdate = iso8601.parse_date(startdatestring)
if enddatestring != "":
enddate = iso8601.parse_date(enddatestring)
if enddate < startdate:
s = enddate
enddate = startdate
startdate = s
if theuser == 0:
theuser = request.user.id
promember=0
if not request.user.is_anonymous():
r = getrower(request.user)
if r.birthdate:
age = calculate_age(r.birthdate)
worldclasspower = int(metrics.getagegrouprecord(
age,
sex=r.sex,
weightcategory=r.weightcategory
))
else:
worldclasspower = None
result = request.user.is_authenticated() and ispromember(request.user)
if result:
promember=1
# get all indoor rows in date range
# process form
if request.method == 'POST' and "daterange" in request.POST:
dateform = DateRangeForm(request.POST)
deltaform = DeltaDaysForm(request.POST)
if dateform.is_valid():
startdate = dateform.cleaned_data['startdate']
enddate = dateform.cleaned_data['enddate']
if startdate > enddate:
s = enddate
enddate = startdate
startdate = s
elif request.method == 'POST' and "datedelta" in request.POST:
deltaform = DeltaDaysForm(request.POST)
if deltaform.is_valid():
deltadays = deltaform.cleaned_data['deltadays']
if deltadays:
enddate = timezone.now()
startdate = enddate-datetime.timedelta(days=deltadays)
if startdate > enddate:
s = enddate
enddate = startdate
startdate = s
dateform = DateRangeForm(initial={
'startdate': startdate,
'enddate': enddate,
})
else:
dateform = DateRangeForm()
deltaform = DeltaDaysForm()
else:
dateform = DateRangeForm(initial={
'startdate': startdate,
'enddate': enddate,
})
deltaform = DeltaDaysForm()
# get all 2k (if any) - this rower, in date range
try:
r = getrower(theuser)
except Rower.DoesNotExist:
allergworkouts = []
r=0
try:
uu = User.objects.get(id=theuser)
except User.DoesNotExist:
uu = ''
# test to fix bug
startdate = datetime.datetime.combine(startdate,datetime.time())
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
#enddate = enddate+datetime.timedelta(days=1)
thedistances = []
theworkouts = []
thesecs = []
rankingdistances.sort()
rankingdurations.sort()
for rankingdistance in rankingdistances:
workouts = Workout.objects.filter(
user=r,distance=rankingdistance,
workouttype__in=['rower','dynamic','slides'],
rankingpiece=True,
startdatetime__gte=startdate,
startdatetime__lte=enddate
).order_by('duration')
if workouts:
thedistances.append(rankingdistance)
theworkouts.append(workouts[0])
timesecs = 3600*workouts[0].duration.hour
timesecs += 60*workouts[0].duration.minute
timesecs += workouts[0].duration.second
timesecs += 1.e-6*workouts[0].duration.microsecond
thesecs.append(timesecs)
for rankingduration in rankingdurations:
workouts = Workout.objects.filter(
user=r,duration=rankingduration,
workouttype='rower',
rankingpiece=True,
startdatetime__gte=startdate,
startdatetime__lte=enddate
).order_by('-distance')
if workouts:
thedistances.append(workouts[0].distance)
theworkouts.append(workouts[0])
timesecs = 3600*workouts[0].duration.hour
timesecs += 60*workouts[0].duration.minute
timesecs += workouts[0].duration.second
timesecs += 1.e-5*workouts[0].duration.microsecond
thesecs.append(timesecs)
thedistances = np.array(thedistances)
thesecs = np.array(thesecs)
thevelos = thedistances/thesecs
theavpower = 2.8*(thevelos**3)
# create interactive plot
if len(thedistances) !=0 :
res = interactive_cpchart(
r,thedistances,thesecs,theavpower,
theworkouts,promember=promember
)
script = res[0]
div = res[1]
paulslope = res[2]
paulintercept = res[3]
p1 = res[4]
message = res[5]
else:
script = ''
div = '<p>No ranking pieces found.</p>'
paulslope = 1
paulintercept = 1
p1 = [1,1,1,1]
message = ""
if request.method == 'POST' and "piece" in request.POST:
form = PredictedPieceForm(request.POST)
if form.is_valid():
value = form.cleaned_data['value']
hourvalue,value = divmod(value,60)
if hourvalue >= 24:
hourvalue = 23
pieceunit = form.cleaned_data['pieceunit']
if pieceunit == 'd':
rankingdistances.append(value)
else:
rankingdurations.append(datetime.time(minute=int(value),hour=int(hourvalue)))
else:
form = PredictedPieceForm()
rankingdistances.sort()
rankingdurations.sort()
predictions = []
cpredictions = []
for rankingdistance in rankingdistances:
# Paul's model
p = paulslope*np.log10(rankingdistance)+paulintercept
velo = 500./p
t = rankingdistance/velo
pwr = 2.8*(velo**3)
a = {'distance':rankingdistance,
'duration':timedeltaconv(t),
'pace':timedeltaconv(p),
'power':int(pwr)}
predictions.append(a)
# CP model -
pwr2 = p1[0]/(1+t/p1[2])
pwr2 += p1[1]/(1+t/p1[3])
if pwr2 <= 0:
pwr2 = 50.
velo2 = (pwr2/2.8)**(1./3.)
if np.isnan(velo2) or velo2 <= 0:
velo2 = 1.0
t2 = rankingdistance/velo2
pwr3 = p1[0]/(1+t2/p1[2])
pwr3 += p1[1]/(1+t2/p1[3])
if pwr3 <= 0:
pwr3 = 50.
velo3 = (pwr3/2.8)**(1./3.)
if np.isnan(velo3) or velo3 <= 0:
velo3 = 1.0
t3 = rankingdistance/velo3
p3 = 500./velo3
a = {'distance':rankingdistance,
'duration':timedeltaconv(t3),
'pace':timedeltaconv(p3),
'power':int(pwr3)}
cpredictions.append(a)
for rankingduration in rankingdurations:
t = 3600.*rankingduration.hour
t += 60.*rankingduration.minute
t += rankingduration.second
t += rankingduration.microsecond/1.e6
# Paul's model
ratio = paulintercept/paulslope
u = ((2**(2+ratio))*(5.**(3+ratio))*t*np.log(10))/paulslope
d = 500*t*np.log(10.)
d = d/(paulslope*lambertw(u))
d = d.real
velo = d/t
p = 500./velo
pwr = 2.8*(velo**3)
a = {'distance':int(d),
'duration':timedeltaconv(t),
'pace':timedeltaconv(p),
'power':int(pwr)}
predictions.append(a)
# CP model
pwr = p1[0]/(1+t/p1[2])
pwr += p1[1]/(1+t/p1[3])
if pwr <= 0:
pwr = 50.
velo = (pwr/2.8)**(1./3.)
if np.isnan(velo) or velo <=0:
velo = 1.0
d = t*velo
p = 500./velo
a = {'distance':int(d),
'duration':timedeltaconv(t),
'pace':timedeltaconv(p),
'power':int(pwr)}
cpredictions.append(a)
messages.error(request,message)
return render(request, 'rankings.html',
{'rankingworkouts':theworkouts,
'interactiveplot':script,
'the_div':div,
'predictions':predictions,
'cpredictions':cpredictions,
'nrdata':len(thedistances),
'form':form,
'rower':r,
'active':'nav-analysis',
'dateform':dateform,
'deltaform':deltaform,
'worldclasspower':worldclasspower,
'id': theuser,
'theuser':uu,
'startdate':startdate,
'enddate':enddate,
'teams':get_my_teams(request.user),
})
@login_required()
def ajax_agegrouprecords(request,
age=25,
sex='female',
weightcategory='hwt',
userid=0):
wcdurations = []
wcpower = []
durations = [1,4,30,60]
distances = [100,500,1000,2000,5000,6000,10000,21097,42195]
df = pd.DataFrame(
list(
C2WorldClassAgePerformance.objects.filter(
sex=sex,
weightcategory=weightcategory
).values()
)
)
jsondf = df.to_json()
job = myqueue(queue,
handle_getagegrouprecords,
jsondf,distances,durations,age,sex,weightcategory,
)
return JSONResponse(
{
'job':job.id
}
)
# Show ranking distances including predicted paces
@login_required()
def rankings_view2(request,theuser=0,
startdate=timezone.now()-datetime.timedelta(days=365),
enddate=timezone.now(),
deltadays=-1,
startdatestring="",
enddatestring=""):
if deltadays>0:
startdate = enddate-datetime.timedelta(days=int(deltadays))
if startdatestring != "":
startdate = iso8601.parse_date(startdatestring)
if enddatestring != "":
enddate = iso8601.parse_date(enddatestring)
if enddate < startdate:
s = enddate
enddate = startdate
startdate = s
if theuser == 0:
theuser = request.user.id
else:
lastupdated = "01-01-1900"
promember=0
if not request.user.is_anonymous():
r = getrower(request.user)
wcdurations = []
wcpower = []
lastupdated = "01-01-1900"
userid = 0
if 'options' in request.session:
options = request.session['options']
try:
wcdurations = options['wcdurations']
wcpower = options['wcpower']
lastupdated = options['lastupdated']
except KeyError:
pass
try:
userid = options['userid']
except KeyError:
userid = 0
else:
options = {}
lastupdatedtime = arrow.get(lastupdated).timestamp
current_time = arrow.utcnow().timestamp
deltatime_seconds = current_time - lastupdatedtime
recalc = False
if str(userid) != str(theuser) or deltatime_seconds > 3600:
recalc = True
options['lastupdated'] = arrow.utcnow().isoformat()
else:
recalc = False
options['userid'] = theuser
if r.birthdate:
age = calculate_age(r.birthdate)
else:
worldclasspower = None
age = 0
agerecords = CalcAgePerformance.objects.filter(
age = age,
sex = r.sex,
weightcategory = r.weightcategory)
if len(agerecords) == 0:
recalc = True
wcpower = []
wcduration = []
else:
wcdurations = []
wcpower = []
for record in agerecords:
wcdurations.append(record.duration)
wcpower.append(record.power)
options['wcpower'] = wcpower
options['wcdurations'] = wcdurations
if theuser:
options['userid'] = theuser
request.session['options'] = options
result = request.user.is_authenticated() and ispromember(request.user)
if result:
promember=1
# get all indoor rows in date range
# process form
if request.method == 'POST' and "daterange" in request.POST:
dateform = DateRangeForm(request.POST)
deltaform = DeltaDaysForm(request.POST)
if dateform.is_valid():
startdate = dateform.cleaned_data['startdate']
enddate = dateform.cleaned_data['enddate']
if startdate > enddate:
s = enddate
enddate = startdate
startdate = s
elif request.method == 'POST' and "datedelta" in request.POST:
deltaform = DeltaDaysForm(request.POST)
if deltaform.is_valid():
deltadays = deltaform.cleaned_data['deltadays']
if deltadays:
enddate = timezone.now()
startdate = enddate-datetime.timedelta(days=deltadays)
if startdate > enddate:
s = enddate
enddate = startdate
startdate = s
dateform = DateRangeForm(initial={
'startdate': startdate,
'enddate': enddate,
})
else:
dateform = DateRangeForm()
deltaform = DeltaDaysForm()
else:
dateform = DateRangeForm(initial={
'startdate': startdate,
'enddate': enddate,
})
deltaform = DeltaDaysForm()
# get all 2k (if any) - this rower, in date range
try:
r = getrower(theuser)
except Rower.DoesNotExist:
allergworkouts = []
r=0
try:
uu = User.objects.get(id=theuser)
except User.DoesNotExist:
uu = ''
# test to fix bug
startdate = datetime.datetime.combine(startdate,datetime.time())
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
#enddate = enddate+datetime.timedelta(days=1)
thedistances = []
theworkouts = []
thesecs = []
rankingdistances.sort()
rankingdurations.sort()
for rankingdistance in rankingdistances:
workouts = Workout.objects.filter(
user=r,distance=rankingdistance,
workouttype__in=['rower','dynamic','slides'],
rankingpiece=True,
startdatetime__gte=startdate,
startdatetime__lte=enddate).order_by('duration')
if workouts:
thedistances.append(rankingdistance)
theworkouts.append(workouts[0])
timesecs = 3600*workouts[0].duration.hour
timesecs += 60*workouts[0].duration.minute
timesecs += workouts[0].duration.second
timesecs += 1.e-6*workouts[0].duration.microsecond
thesecs.append(timesecs)
for rankingduration in rankingdurations:
workouts = Workout.objects.filter(
user=r,duration=rankingduration,
workouttype='rower',
rankingpiece=True,
startdatetime__gte=startdate,
startdatetime__lte=enddate).order_by('-distance')
if workouts:
thedistances.append(workouts[0].distance)
theworkouts.append(workouts[0])
timesecs = 3600*workouts[0].duration.hour
timesecs += 60*workouts[0].duration.minute
timesecs += workouts[0].duration.second
timesecs += 1.e-5*workouts[0].duration.microsecond
thesecs.append(timesecs)
thedistances = np.array(thedistances)
thesecs = np.array(thesecs)
thevelos = thedistances/thesecs
theavpower = 2.8*(thevelos**3)
# create interactive plot
if len(thedistances) !=0 :
res = interactive_cpchart(
r,thedistances,thesecs,theavpower,
theworkouts,promember=promember,
wcdurations=wcdurations,wcpower=wcpower
)
script = res[0]
div = res[1]
paulslope = res[2]
paulintercept = res[3]
p1 = res[4]
message = res[5]
try:
testcalc = pd.Series(res[6])*3
except TypeError:
age = 0
else:
script = ''
div = '<p>No ranking pieces found.</p>'
paulslope = 1
paulintercept = 1
p1 = [1,1,1,1]
message = ""
if request.method == 'POST' and "piece" in request.POST:
form = PredictedPieceForm(request.POST)
if form.is_valid():
value = form.cleaned_data['value']
hourvalue,value = divmod(value,60)
if hourvalue >= 24:
hourvalue = 23
pieceunit = form.cleaned_data['pieceunit']
if pieceunit == 'd':
rankingdistances.append(value)
else:
rankingdurations.append(datetime.time(minute=int(value),hour=int(hourvalue)))
else:
form = PredictedPieceForm()
rankingdistances.sort()
rankingdurations.sort()
predictions = []
cpredictions = []
for rankingdistance in rankingdistances:
# Paul's model
p = paulslope*np.log10(rankingdistance)+paulintercept
velo = 500./p
t = rankingdistance/velo
pwr = 2.8*(velo**3)
a = {'distance':rankingdistance,
'duration':timedeltaconv(t),
'pace':timedeltaconv(p),
'power':int(pwr)}
predictions.append(a)
# CP model -
pwr2 = p1[0]/(1+t/p1[2])
pwr2 += p1[1]/(1+t/p1[3])
if pwr2 <= 0:
pwr2 = 50.
velo2 = (pwr2/2.8)**(1./3.)
if np.isnan(velo2) or velo2 <= 0:
velo2 = 1.0
t2 = rankingdistance/velo2
pwr3 = p1[0]/(1+t2/p1[2])
pwr3 += p1[1]/(1+t2/p1[3])
if pwr3 <= 0:
pwr3 = 50.
velo3 = (pwr3/2.8)**(1./3.)
if np.isnan(velo3) or velo3 <= 0:
velo3 = 1.0
t3 = rankingdistance/velo3
p3 = 500./velo3
a = {'distance':rankingdistance,
'duration':timedeltaconv(t3),
'pace':timedeltaconv(p3),
'power':int(pwr3)}
cpredictions.append(a)
for rankingduration in rankingdurations:
t = 3600.*rankingduration.hour
t += 60.*rankingduration.minute
t += rankingduration.second
t += rankingduration.microsecond/1.e6
# Paul's model
ratio = paulintercept/paulslope
u = ((2**(2+ratio))*(5.**(3+ratio))*t*np.log(10))/paulslope
d = 500*t*np.log(10.)
d = d/(paulslope*lambertw(u))
d = d.real
velo = d/t
p = 500./velo
pwr = 2.8*(velo**3)
try:
a = {'distance':int(d),
'duration':timedeltaconv(t),
'pace':timedeltaconv(p),
'power':int(pwr)}
predictions.append(a)
except:
pass
# CP model
pwr = p1[0]/(1+t/p1[2])
pwr += p1[1]/(1+t/p1[3])
if pwr <= 0:
pwr = 50.
velo = (pwr/2.8)**(1./3.)
if np.isnan(velo) or velo <=0:
velo = 1.0
d = t*velo
p = 500./velo
a = {'distance':int(d),
'duration':timedeltaconv(t),
'pace':timedeltaconv(p),
'power':int(pwr)}
cpredictions.append(a)
if recalc:
wcdurations = []
wcpower = []
durations = [1,4,30,60]
distances = [100,500,1000,2000,5000,6000,10000,21097,42195]
df = pd.DataFrame(
list(
C2WorldClassAgePerformance.objects.filter(
sex=r.sex,
weightcategory=r.weightcategory
).values()
)
)
jsondf = df.to_json()
job = myqueue(queue,
handle_getagegrouprecords,
jsondf,distances,durations,age,r.sex,r.weightcategory)
try:
request.session['async_tasks'] += [(job.id,'agegrouprecords')]
except KeyError:
request.session['async_tasks'] = [(job.id,'agegrouprecords')]
messages.error(request,message)
return render(request, 'rankings.html',
{'rankingworkouts':theworkouts,
'interactiveplot':script,
'the_div':div,
'predictions':predictions,
'cpredictions':cpredictions,
'nrdata':len(thedistances),
'form':form,
'dateform':dateform,
'deltaform':deltaform,
'id': theuser,
'theuser':uu,
'rower':r,
'active':'nav-analysis',
'age':age,
'sex':r.sex,
'recalc':recalc,
'weightcategory':r.weightcategory,
'startdate':startdate,
'enddate':enddate,
'teams':get_my_teams(request.user),
})
@user_passes_test(ispromember,login_url="/rowers/promembership",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def workout_update_cp_view(request,id=0):
row = get_workout(id)
if (checkworkoutuser(request.user,row)==False):
message = "You are not allowed to edit this workout"
messages.error(request,message)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
row.rankingpiece = True
row.save()
r = getrower(request.user)
dataprep.runcpupdate(r)
if row.workouttype in mytypes.otwtypes:
url = reverse(otwrankings_view)
else:
url = reverse(oterankings_view)
return HttpResponseRedirect(url)
# Show ranking distances including predicted paces
@user_passes_test(ispromember,login_url="/rowers/promembership",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def otwrankings_view(request,theuser=0,
startdate=timezone.now()-datetime.timedelta(days=365),
enddate=timezone.now(),
startdatestring="",
enddatestring=""):
if startdatestring != "":
try:
startdate = iso8601.parse_date(startdatestring)
except ParseError:
pass
if enddatestring != "":
try:
enddate = iso8601.parse_date(enddatestring)
except ParseError:
pass
if enddate < startdate:
s = enddate
enddate = startdate
startdate = s
if theuser == 0:
if 'rowerid' in request.session:
try:
r = Rower.objects.get(id=request.session['rowerid'])
theuser = r.user.id
except Rower.DoesNotExist:
theuser = request.user.id
else:
theuser = request.user.id
promember=0
if not request.user.is_anonymous():
r = Rower.objects.get(user=request.user)
result = request.user.is_authenticated() and ispromember(request.user)
if result:
promember=1
# get all OTW rows in date range
# process form
if request.method == 'POST':
dateform = DateRangeForm(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
form = PredictedPieceFormNoDistance(request.POST)
if form.is_valid():
value = form.cleaned_data['value']
else:
value = None
trankingdurations = form.cleaned_data['trankingdurations']
trankingdurations = [
datetime.datetime.strptime(d,"%H:%M:%S").time() for d in trankingdurations
]
if value:
hourvalue,tvalue = divmod(value,60)
hourvalue = int(hourvalue)
minutevalue = int(tvalue)
tvalue = int(60*(tvalue-minutevalue))
if hourvalue >= 24:
hourvalue = 23
trankingdurations.append(datetime.time(
minute=minutevalue,
hour=hourvalue,
second=tvalue
))
else:
form = PredictedPieceFormNoDistance()
dateform = DateRangeForm(initial={
'startdate': startdate,
'enddate': enddate,
})
workouttypes = ['rower','slides','dynamic']
trankingdurations = rankingdurations
# get all 2k (if any) - this rower, in date range
try:
r = Rower.objects.get(user=theuser)
request.session['rowerid'] = r.id
except Rower.DoesNotExist:
raise Http404("Rower doesn't exist")
try:
uu = User.objects.get(id=theuser)
except User.DoesNotExist:
uu = ''
# test to fix bug
startdate = datetime.datetime.combine(startdate,datetime.time())
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
#enddate = enddate+datetime.timedelta(days=1)
thedistances = []
theworkouts = []
thesecs = []
theworkouts = Workout.objects.filter(
user=r,rankingpiece=True,
workouttype='water',
startdatetime__gte=startdate,
startdatetime__lte=enddate
).order_by(
"-startdatetime"
)
delta,cpvalue,avgpower = dataprep.fetchcp(r,theworkouts)
runningjob = 0
taskstatus = get_stored_tasks_status(request)
for task in taskstatus:
if task['func_name'] == 'updatecpwater':
if 'success' in task['status'].lower() or 'finished' in task['status'].lower():
runningjob = 1
messages.info(request,'CP chart data have been updated')
remove_asynctask(request,task['id'])
elif 'fail' in task['status'].lower():
runningjob = 0
try:
remove_asynctask(request,task[id])
messages.error(request,'Oh, your task failed')
except KeyError:
pass
elif 'started' in task['status'].lower():
messages.info(request,'Busy updating CP chart data')
runningjob = 1
elif 'queued' in task['status'].lower() or 'pending' in task['status'].lower():
messages.info(request,'Getting ready to update CP chart data')
runningjob = 1
if not runningjob:
job = dataprep.runcpupdate(
r,type='water',
startdate=startdate,
enddate=enddate
)
request.session['job_id'] = job.id
try:
request.session['async_tasks'] += [(job.id,'updatecpwater')]
except KeyError:
request.session['async_tasks'] = [(job.id,'updatecpwater')]
messages.info(request,'New calculation queued. Page will reload automatically. You can check the status of your calculations <a href="/rowers/jobs-status/" target="_blank">here</a>')
powerdf = pd.DataFrame({
'Delta':delta,
'CP':cpvalue,
})
if powerdf.empty:
messages.info(request,'Your calculations are running in the background. Page will reload automatically. You can check the status of your calculations <a href="/rowers/jobs-status/" target="_blank">here</a>')
powerdf = powerdf[powerdf['CP']>0]
powerdf.dropna(axis=0,inplace=True)
powerdf.sort_values(['Delta','CP'],ascending=[1,0],inplace=True)
powerdf.drop_duplicates(subset='Delta',keep='first',inplace=True)
rowername = r.user.first_name+" "+r.user.last_name
# create interactive plot
if len(powerdf) !=0 :
res = interactive_otwcpchart(powerdf,promember=promember,rowername=rowername)
script = res[0]
div = res[1]
p1 = res[2]
ratio = res[3]
r.p0 = p1[0]
r.p1 = p1[1]
r.p2 = p1[2]
r.p3 = p1[3]
r.cpratio = ratio
r.save()
paulslope = 1
paulintercept = 1
message = res[4]
else:
script = ''
div = '<p>No ranking pieces found.</p>'
paulslope = 1
paulintercept = 1
p1 = [1,1,1,1]
message = ""
cpredictions = []
for rankingduration in trankingdurations:
t = 3600.*rankingduration.hour
t += 60.*rankingduration.minute
t += rankingduration.second
t += rankingduration.microsecond/1.e6
# CP model
pwr = p1[0]/(1+t/p1[2])
pwr += p1[1]/(1+t/p1[3])
if pwr <= 0:
pwr = 50.
if not np.isnan(pwr):
try:
pwr2 = pwr*ratio
except:
pwr2 = pwr
a = {
'duration':timedeltaconv(t),
'power':int(pwr),
'upper':int(pwr2)}
cpredictions.append(a)
startdatestring = startdate.strftime('%Y-%m-%d')
enddatestring = enddate.strftime('%Y-%m-%d')
request.session['startdate'] = startdatestring
request.session['enddate'] = enddatestring
messages.error(request,message)
return render(request, 'otwrankings.html',
{'rankingworkouts':theworkouts,
'interactiveplot':script,
'the_div':div,
'cpredictions':cpredictions,
'rower':r,
'active':'nav-analysis',
'avgpower':avgpower,
'form':form,
'dateform':dateform,
'id': theuser,
'theuser':uu,
'startdate':startdate,
'enddate':enddate,
'teams':get_my_teams(request.user),
'workouttype':'water',
})
@login_required()
def otecp_toadmin_view(request,theuser=0,
startdate=timezone.now()-datetime.timedelta(days=365),
enddate=timezone.now(),
startdatestring="",
enddatestring="",
):
if startdatestring != "":
try:
startdate = iso8601.parse_date(startdatestring)
except ParseError:
pass
if enddatestring != "":
try:
enddate = iso8601.parse_date(enddatestring)
except ParseError:
pass
if theuser == 0:
theuser = request.user.id
u = User.objects.get(id=theuser)
r = Rower.objects.get(user=u)
startdate = datetime.datetime.combine(startdate,datetime.time())
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
theworkouts = Workout.objects.filter(
user=r,rankingpiece=True,
workouttype__in=[
'rower',
'dynamic',
'slides'
],
startdatetime__gte=startdate,
startdatetime__lte=enddate
).order_by("-startdatetime")
delta,cpvalue,avgpower = dataprep.fetchcp(
r,theworkouts,table='cpergdata'
)
powerdf = pd.DataFrame({
'Delta':delta,
'CP':cpvalue,
})
csvfilename = 'CP_data_user_{id}.csv'.format(
id = theuser
)
powerdf = powerdf[powerdf['CP']>0]
powerdf.dropna(axis=0,inplace=True)
powerdf.sort_values(['Delta','CP'],ascending=[1,0],inplace=True)
powerdf.drop_duplicates(subset='Delta',keep='first',inplace=True)
powerdf.to_csv(csvfilename)
res = myqueue(queuehigh,
handle_sendemailfile,
'Sander',
'Roosendaal',
'roosendaalsander@gmail.com',
csvfilename,
delete=True)
successmessage = "The CSV file was sent to the site admin per email"
messages.info(request,successmessage)
response = HttpResponseRedirect('/rowers/list-workouts/')
return response
@login_required()
def otwcp_toadmin_view(request,theuser=0,
startdate=timezone.now()-datetime.timedelta(days=365),
enddate=timezone.now(),
startdatestring="",
enddatestring="",
):
if startdatestring != "":
try:
startdate = iso8601.parse_date(startdatestring)
except ParseError:
pass
if enddatestring != "":
try:
enddate = iso8601.parse_date(enddatestring)
except ParseError:
pass
if theuser == 0:
theuser = request.user.id
u = User.objects.get(id=theuser)
r = Rower.objects.get(user=u)
startdate = datetime.datetime.combine(startdate,datetime.time())
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
theworkouts = Workout.objects.filter(
user=r,rankingpiece=True,
workouttype='water',
startdatetime__gte=startdate,
startdatetime__lte=enddate
).order_by("-startdatetime")
delta,cpvalue,avgpower = dataprep.fetchcp(
r,theworkouts,table='cpdata'
)
powerdf = pd.DataFrame({
'Delta':delta,
'CP':cpvalue,
})
csvfilename = 'CP_data_user_{id}.csv'.format(
id = theuser
)
powerdf = powerdf[powerdf['CP']>0]
powerdf.dropna(axis=0,inplace=True)
powerdf.sort_values(['Delta','CP'],ascending=[1,0],inplace=True)
powerdf.drop_duplicates(subset='Delta',keep='first',inplace=True)
powerdf.to_csv(csvfilename)
res = myqueue(queuehigh,
handle_sendemailfile,
'Sander',
'Roosendaal',
'roosendaalsander@gmail.com',
csvfilename,
delete=True)
successmessage = "The CSV file was sent to the site admin per email"
messages.info(request,successmessage)
response = HttpResponseRedirect('/rowers/list-workouts/')
return response
# Show ranking distances including predicted paces
@user_passes_test(ispromember,login_url="/rowers/promembership",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def oterankings_view(request,theuser=0,
startdate=timezone.now()-datetime.timedelta(days=365),
enddate=timezone.now(),
startdatestring="",
enddatestring=""):
if startdatestring != "":
try:
startdate = iso8601.parse_date(startdatestring)
except ParseError:
pass
if enddatestring != "":
try:
enddate = iso8601.parse_date(enddatestring)
except ParseError:
pass
if enddate < startdate:
s = enddate
enddate = startdate
startdate = s
if theuser == 0:
if 'rowerid' in request.session:
try:
r = Rower.objects.get(id=request.session['rowerid'])
theuser = r.user.id
except Rower.DoesNotExist:
theuser = request.user.id
else:
theuser = request.user.id
promember=0
if not request.user.is_anonymous():
r = Rower.objects.get(user=request.user)
result = request.user.is_authenticated() and ispromember(request.user)
if result:
promember=1
# get all OTW rows in date range
# process form
if request.method == 'POST':
dateform = DateRangeForm(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
workouttypeform = OteWorkoutTypeForm(request.POST)
if workouttypeform.is_valid():
workouttypes = workouttypeform.cleaned_data['workouttypes']
form = PredictedPieceForm(request.POST)
if form.is_valid():
value = form.cleaned_data['value']
pieceunit = form.cleaned_data['pieceunit']
else:
value = None
try:
trankingdistances = form.cleaned_data['trankingdistances']
except KeyError:
trankingdistances = []
trankingdistances = [int(d) for d in trankingdistances]
try:
trankingdurations = form.cleaned_data['trankingdurations']
except KeyError:
trankingdurations = []
trankingdurations = [
datetime.datetime.strptime(d,"%H:%M:%S").time() for d in trankingdurations
]
if value:
hourvalue,tvalue = divmod(value,60)
hourvalue = int(hourvalue)
minutevalue = int(tvalue)
tvalue = int(60*(tvalue-minutevalue))
if hourvalue >= 24:
hourvalue = 23
if pieceunit == 'd':
trankingdistances.append(value)
else:
trankingdurations.append(datetime.time(
minute=minutevalue,
hour=hourvalue,
second=tvalue
))
else:
form = PredictedPieceForm()
dateform = DateRangeForm(initial={
'startdate': startdate,
'enddate': enddate,
})
workouttypeform = OteWorkoutTypeForm()
workouttypes = ['rower','slides','dynamic']
trankingdistances = rankingdistances
trankingdurations = rankingdurations
# get all 2k (if any) - this rower, in date range
try:
r = Rower.objects.get(user=theuser)
request.session['rowerid'] = r.id
except Rower.DoesNotExist:
allergworkouts = []
raise Http404("Rower doesn't exist")
try:
uu = User.objects.get(id=theuser)
except User.DoesNotExist:
uu = ''
# test to fix bug
startdate = datetime.datetime.combine(startdate,datetime.time())
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
thedistances = []
theworkouts = []
thesecs = []
theworkouts = Workout.objects.filter(
user=r,rankingpiece=True,
workouttype__in=workouttypes,
startdatetime__gte=startdate,
startdatetime__lte=enddate
).order_by("-startdatetime")
delta,cpvalue,avgpower = dataprep.fetchcp(
r,theworkouts,table='cpergdata'
)
runningjob = 0
taskstatus = get_stored_tasks_status(request)
for task in taskstatus:
if task['func_name'] == 'updatecp':
if 'success' in task['status'].lower() or 'finished' in task['status'].lower():
runningjob = 1
messages.info(request,'CP chart data have been updated')
remove_asynctask(request,task['id'])
elif 'fail' in task['status'].lower():
runningjob = 0
try:
remove_asynctask(request,task[id])
messages.error(request,'Oh, your task failed')
except KeyError:
pass
elif 'started' in task['status'].lower():
messages.info(request,'Busy updating CP chart data')
runningjob = 1
elif 'queued' in task['status'].lower():
messages.info(request,'Getting ready to update CP chart data')
runningjob = 1
if not runningjob:
job = dataprep.runcpupdate(
r,type='rower',
startdate=startdate,
enddate=enddate
)
request.session['job_id'] = job.id
try:
request.session['async_tasks'] += [(job.id,'updatecp')]
except KeyError:
request.session['async_tasks'] = [(job.id,'updatecp')]
messages.info(request,'New calculation queued. Page will reload automatically. You can check the status of your calculations <a href="/rowers/jobs-status/" target="_blank">here</a>')
powerdf = pd.DataFrame({
'Delta':delta,
'CP':cpvalue,
})
if powerdf.empty:
messages.info(request,'Your calculations are running in the background. Page will reload automatically. You can check the status of your calculations <a href="/rowers/jobs-status/" target="_blank">here</a>')
powerdf = powerdf[powerdf['CP']>0]
powerdf.dropna(axis=0,inplace=True)
powerdf.sort_values(['Delta','CP'],ascending=[1,0],inplace=True)
powerdf.drop_duplicates(subset='Delta',keep='first',inplace=True)
rowername = r.user.first_name+" "+r.user.last_name
# create interactive plot
if len(powerdf) !=0 :
res = interactive_otwcpchart(powerdf,promember=promember,rowername=rowername)
script = res[0]
div = res[1]
p1 = res[2]
ratio = res[3]
r.ep0 = p1[0]
r.ep1 = p1[1]
r.ep2 = p1[2]
r.ep3 = p1[3]
r.ecpratio = ratio
r.save()
paulslope = 1
paulintercept = 1
message = res[4]
else:
ratio = 1
script = ''
div = '<p>No ranking pieces found.</p>'
paulslope = 1
paulintercept = 1
p1 = [1,1,1,1]
message = ""
cpredictions = []
for rankingduration in trankingdurations:
t = 3600.*rankingduration.hour
t += 60.*rankingduration.minute
t += rankingduration.second
t += rankingduration.microsecond/1.e6
# CP model
pwr = p1[0]/(1+t/p1[2])
pwr += p1[1]/(1+t/p1[3])
velo = (pwr/2.8)**(1./3.)
p = 500./velo
d = t*velo
if pwr <= 0:
pwr = 50.
if not np.isnan(pwr):
try:
pwr2 = pwr*ratio
except:
pwr2 = pwr
a = {
'distance':int(d),
'duration':timedeltaconv(t),
'power':int(pwr),
'upper':int(pwr2),
'pace':timedeltaconv(p)}
cpredictions.append(a)
# initiation - get 10 min power, then use Paul's law
t_10 = 600.
power_10 = p1[0]/(1+t_10/p1[2])
power_10 += p1[1]/(1+t_10/p1[3])
velo_10 = (power_10/2.8)**(1./3.)
pace_10 = 500./velo_10
distance_10 = t_10*velo_10
paulslope = 5.
for rankingdistance in trankingdistances:
delta = paulslope * np.log(rankingdistance/distance_10)/np.log(2)
p = pace_10+delta
velo = 500./p
t = rankingdistance/velo
pwr2 = p1[0]/(1+t/p1[2])
pwr2 += p1[1]/(1+t/p1[3])
try:
pwr2 *= ratio
except UnboundLocalError:
pass
if pwr2 <= 0:
pwr2 = 50.
velo2 = (pwr2/2.8)**(1./3.)
if np.isnan(velo2) or velo2 <= 0:
velo2 = 1.0
t2 = rankingdistance/velo2
pwr3 = p1[0]/(1+t2/p1[2])
pwr3 += p1[1]/(1+t2/p1[3])
pwr3 *= ratio
if pwr3 <= 0:
pwr3 = 50.
velo3 = (pwr3/2.8)**(1./3.)
if np.isnan(velo3) or velo3 <= 0:
velo3 = 1.0
t3 = rankingdistance/velo3
p3 = 500./velo3
a = {
'distance':rankingdistance,
'duration':timedeltaconv(t3),
'power':'--',
'upper':int(pwr3),
'pace':timedeltaconv(p3)}
cpredictions.append(a)
# del form.fields["pieceunit"]
messages.error(request,message)
return render(request, 'oterankings.html',
{'rankingworkouts':theworkouts,
'interactiveplot':script,
'the_div':div,
'rower':r,
'active':'nav-analysis',
'cpredictions':cpredictions,
'avgpower':avgpower,
'form':form,
'dateform':dateform,
'workouttypeform':workouttypeform,
'id': theuser,
'theuser':uu,
'startdate':startdate,
'enddate':enddate,
'teams':get_my_teams(request.user),
'workouttype':'rower',
})
# Reload the workout and calculate the summary from the stroke data (lapIDx)
@login_required()
def workout_recalcsummary_view(request,id=0):
row = get_workout(id)
if (checkworkoutuser(request.user,row)==False):
message = "You are not allowed to edit this workout"
messages.error(request,message)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
filename = row.csvfilename
rowdata = rdata(filename)
if rowdata:
row.summary = rowdata.allstats()
row.save()
successmessage = "Summary Updated"
messages.info(request,successmessage)
url = reverse(workout_edit_view,
kwargs = {
'id':int(id),
})
else:
message = "Something went wrong. Could not update summary"
messages.error(request,message)
url = reverse(workout_edit_view,
kwargs = {
'id':int(id),
})
return HttpResponseRedirect(url)
@login_required()
def workout_makepublic_view(request,id,
message='',
successmessage=''):
row = get_workout(id)
if (checkworkoutuser(request.user,row)==False):
message = "You are not allowed to edit this workout"
messages.error(request,message)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
row.privacy = 'visible'
row.save()
rr = getrower(request.user)
teams = rr.team.all()
for team in teams:
row.team.add(team)
message = "Workout set to public. Your followers and team members will see it"
messages.info(request,message)
url = reverse(workout_edit_view,
kwargs = {
'id':int(id),
})
return HttpResponseRedirect(url)
@login_required()
def workout_setprivate_view(request,id,
message='',
successmessage=''):
row = get_workout(id)
if (checkworkoutuser(request.user,row)==False):
message = "You are not allowed to edit this workout"
messages.error(request,message)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
row.privacy = 'private'
row.save()
for team in row.team.all():
row.team.remove(team)
message = "Workout set to private. Only you will see it"
messages.info(request,message)
url = reverse(workout_edit_view,
kwargs = {
'id':int(id),
})
return HttpResponseRedirect(url)
# Joining workout
@user_passes_test(ispromember,login_url="/rowers/promembership",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def workouts_join_view(request):
promember=0
if not request.user.is_anonymous():
r = getrower(request.user)
result = request.user.is_authenticated() and ispromember(request.user)
if result:
promember=1
if request.method == 'POST' and 'workouts' in request.POST:
form = WorkoutMultipleCompareForm(request.POST)
paramform = WorkoutJoinParamForm(request.POST)
if form.is_valid() and paramform.is_valid():
workout_name = paramform.cleaned_data['workout_name']
set_private = paramform.cleaned_data['set_private']
cd = form.cleaned_data
workouts = cd['workouts']
ids = [int(w.id) for w in workouts]
request.session['ids'] = ids
id,message = dataprep.join_workouts(r,ids,
title=workout_name,
setprivate=set_private)
if message:
messages.error(request,message)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':int(id),
})
return HttpResponseRedirect(url)
else:
return HttpResponse("form is not valid")
else:
url = reverse(workouts_join_select)
return HttpResponseRedirect(url)
@user_passes_test(ispromember,login_url="/rowers/promembership",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def workouts_join_select(request,
startdatestring="",
enddatestring="",
message='',
successmessage='',
startdate=timezone.now()-datetime.timedelta(days=30),
enddate=timezone.now()+datetime.timedelta(days=1),
teamid=0):
try:
r = getrower(request.user)
except Rower.DoesNotExist:
raise Http404("Rower doesn't exist")
if 'waterboattype' in request.session:
waterboattype = request.session['waterboattype']
else:
waterboattype = mytypes.waterboattype
if 'modalities' in request.session:
modalities = request.session['modalities']
if len(modalities) > 1:
modality = 'all'
else:
modality = modalities[0]
else:
modalities = [m[0] for m in mytypes.workouttypes]
modality = 'all'
if request.method == 'POST' and 'daterange' in request.POST:
dateform = DateRangeForm(request.POST)
if dateform.is_valid():
startdate = dateform.cleaned_data['startdate']
enddate = dateform.cleaned_data['enddate']
startdatestring = startdate.strftime('%Y-%m-%d')
enddatestring = enddate.strftime('%Y-%m-%d')
request.session['startdate'] = startdatestring
request.session['enddate'] = enddatestring
else:
dateform = DateRangeForm(initial={
'startdate':startdate,
'enddate':enddate,
})
if request.method == 'POST' and 'modality' in request.POST:
modalityform = TrendFlexModalForm(request.POST)
if modalityform.is_valid():
modality = modalityform.cleaned_data['modality']
waterboattype = modalityform.cleaned_data['waterboattype']
if modality == 'all':
modalities = [m[0] for m in mytypes.workouttypes]
else:
modalities = [modality]
if modality != 'water':
waterboattype = [b[0] for b in mytypes.boattypes]
request.session['modalities'] = modalities
request.session['waterboattype'] = waterboattype
negtypes = []
for b in mytypes.boattypes:
if b[0] not in waterboattype:
negtypes.append(b[0])
startdate = datetime.datetime.combine(startdate,datetime.time())
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
#enddate = enddate+datetime.timedelta(days=1)
if startdatestring:
startdate = iso8601.parse_date(startdatestring)
if enddatestring:
enddate = iso8601.parse_date(enddatestring)
if enddate < startdate:
s = enddate
enddate = startdate
startdate = s
try:
theteam = Team.objects.get(id=teamid)
except Team.DoesNotExist:
theteam = 0
if r.rowerplan == 'basic' and theteam==0:
raise PermissionDenied("Access denied")
if theteam and (theteam.viewing == 'allmembers' or theteam.manager == request.user):
workouts = Workout.objects.filter(team=theteam,
startdatetime__gte=startdate,
startdatetime__lte=enddate,
workouttype__in=modalities).order_by("-date", "-starttime").exclude(boattype__in=negtypes)
elif theteam and theteam.viewing == 'coachonly':
workouts = Workout.objects.filter(team=theteam,user=r,
startdatetime__gte=startdate,
startdatetime__lte=enddate,
workouttype__in=modalities).order_by("-date","-starttime").exclude(boattype__in=negtypes)
else:
theteam = None
workouts = Workout.objects.filter(user=r,
startdatetime__gte=startdate,
startdatetime__lte=enddate,
workouttype__in=modalities).order_by("-date", "-starttime").exclude(boattype__in=negtypes)
query = request.GET.get('q')
if query:
query_list = query.split()
workouts = workouts.filter(
reduce(operator.and_,
(Q(name__icontains=q) for q in query_list)) |
reduce(operator.and_,
(Q(notes__icontains=q) for q in query_list))
)
form = WorkoutMultipleCompareForm()
form.fields["workouts"].queryset = workouts
if theteam:
theid = theteam.id
else:
theid = 0
joinparamform = WorkoutJoinParamForm()
modalityform = TrendFlexModalForm(initial={
'modality':modality,
'waterboattype':waterboattype
})
messages.info(request,successmessage)
messages.error(request,message)
return render(request, 'workout_join_select.html',
{'workouts': workouts,
'dateform':dateform,
'startdate':startdate,
'enddate':enddate,
'active':'nav-workouts',
'team':theteam,
'form':form,
'joinparamform':joinparamform,
'modalityform':modalityform,
'teams':get_my_teams(request.user),
})
# Team comparison
@login_required()
def team_comparison_select(request,
startdatestring="",
enddatestring="",
message='',
successmessage='',
userid=0,
startdate=timezone.now()-datetime.timedelta(days=30),
enddate=timezone.now(),
id=0,
teamid=0):
r = getrequestrower(request,userid=userid)
requestrower = getrower(request.user)
request.session.pop('ps',None)
if 'waterboattype' in request.session:
waterboattype = request.session['waterboattype']
else:
waterboattype = mytypes.waterboattype
if 'rankingonly' in request.session:
rankingonly = request.session['rankingonly']
else:
rankingonly = False
if 'modalities' in request.session:
modalities = request.session['modalities']
if len(modalities) > 1:
modality = 'all'
else:
modality = modalities[0]
else:
modalities = [m[0] for m in mytypes.workouttypes]
modality = 'all'
if request.method == 'POST':
dateform = DateRangeForm(request.POST)
if dateform.is_valid():
startdate = dateform.cleaned_data['startdate']
enddate = dateform.cleaned_data['enddate']
startdatestring = startdate.strftime('%Y-%m-%d')
enddatestring = enddate.strftime('%Y-%m-%d')
request.session['startdate'] = startdatestring
request.session['enddate'] = enddatestring
modalityform = TrendFlexModalForm(request.POST)
if modalityform.is_valid():
modality = modalityform.cleaned_data['modality']
waterboattype = modalityform.cleaned_data['waterboattype']
if modality == 'all':
modalities = [m[0] for m in mytypes.workouttypes]
else:
modalities = [modality]
if modality != 'water':
waterboattype = [b[0] for b in mytypes.boattypes]
if 'rankingonly' in modalityform.cleaned_data:
rankingonly = modalityform.cleaned_data['rankingonly']
else:
rankingonly = False
request.session['modalities'] = modalities
request.session['waterboattype'] = waterboattype
else:
dateform = DateRangeForm(initial={
'startdate':startdate,
'enddate':enddate,
})
modalityform = TrendFlexModalForm(initial={
'modality':modality,
'waterboattype':waterboattype,
'rankingonly':rankingonly,
})
negtypes = []
for b in mytypes.boattypes:
if b[0] not in waterboattype:
negtypes.append(b[0])
startdate = datetime.datetime.combine(startdate,datetime.time())
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
#enddate = enddate+datetime.timedelta(days=1)
if startdatestring:
startdate = iso8601.parse_date(startdatestring)
if enddatestring:
enddate = iso8601.parse_date(enddatestring)
if enddate < startdate:
s = enddate
enddate = startdate
startdate = s
try:
theteam = Team.objects.get(id=teamid)
except Team.DoesNotExist:
theteam = 0
if requestrower.rowerplan == 'basic' and theteam==0:
if requestrower.protrialexpires is None or requestrower.protrialexpires<datetime.date.today():
raise PermissionDenied("Access denied")
if theteam and (theteam.viewing == 'allmembers' or theteam.manager == request.user):
workouts = Workout.objects.filter(team=theteam,
startdatetime__gte=startdate,
startdatetime__lte=enddate,
workouttype__in=modalities).order_by("-date", "-starttime").exclude(boattype__in=negtypes)
elif theteam and theteam.viewing == 'coachonly':
workouts = Workout.objects.filter(team=theteam,user=r,
startdatetime__gte=startdate,
startdatetime__lte=enddate,
workouttype__in=modalities).order_by("-date","-starttime").exclude(boattype__in=negtypes)
else:
theteam = None
workouts = Workout.objects.filter(user=r,
startdatetime__gte=startdate,
startdatetime__lte=enddate,
workouttype__in=modalities).order_by("-date", "-starttime").exclude(boattype__in=negtypes)
if rankingonly:
workouts = workouts.exclude(rankingpiece=False)
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))
)
if id:
firstworkout = get_workout(id)
if not checkworkoutuser(request.user,firstworkout):
raise PermissionDenied("You are not allowed to sue this workout")
firstworkoutquery = Workout.objects.filter(id=id)
workouts = firstworkoutquery | workouts
else:
firstworkout = None
form = WorkoutMultipleCompareForm()
form.fields["workouts"].queryset = workouts
if id:
form.fields["workouts"].initial = [firstworkout]
if theteam:
theid = theteam.id
else:
theid = 0
chartform = ChartParamChoiceForm(initial={'teamid':0})
messages.info(request,successmessage)
messages.error(request,message)
if id:
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,id),
'name': firstworkout.name
},
{
'url':reverse(team_comparison_select,kwargs={'id':id,'teamid':teamid}),
'name':'Compare Select'
},
]
else:
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':reverse(team_comparison_select,kwargs={'teamid':teamid}),
'name': 'Compare Select'
},
]
return render(request, 'team_compare_select.html',
{'workouts': workouts,
'workout':firstworkout,
'dateform':dateform,
'startdate':startdate,
'enddate':enddate,
'team':theteam,
'form':form,
'rower':r,
'breadcrumbs':breadcrumbs,
'active':'nav-workouts',
'chartform':chartform,
'modalityform':modalityform,
'teams':get_my_teams(request.user),
})
@login_required()
def plannedsession_compare_view(request,id=0,userid=0):
r = getrequestrower(request,userid=userid)
try:
ps = PlannedSession.objects.get(id=id)
except PlannedSession.DoesNotExist:
raise Http404("Planned session does not exist")
m = ps.manager
mm = m.rower
if ps.manager != request.user:
if r.rowerplan == 'coach':
teams = Team.objects.filter(manager=request.user)
members = Rower.objects.filter(team__in=teams).distinct()
teamusers = [m.user for m in members]
if ps.manager not in teamusers:
raise PermissionDenied("You do not have access to this session")
elif r not in ps.rower.all():
raise PermissionDenied("You do not have access to this session")
workouts = Workout.objects.filter(plannedsession=ps)
ids = [int(w.id) for w in workouts]
labeldict = {
int(w.id): w.__unicode__() for w in workouts
}
xparam = 'time'
yparam = 'hr'
plottype = 'line'
request.session['ids'] = ids
request.session['xparam'] = xparam
request.session['yparam'] = yparam
request.session['plottype'] = plottype
request.session['ps'] = ps.id
url = reverse(multi_compare_view,kwargs={'userid':userid,'id':ids[0]})
return HttpResponseRedirect(url)
# Team comparison
@login_required()
def multi_compare_view(request,id=0,userid=0):
promember=0
if not request.user.is_anonymous():
r = getrower(request.user)
result = request.user.is_authenticated() and ispromember(request.user)
if result:
promember=1
if request.method == 'POST' and 'workouts' in request.POST:
form = WorkoutMultipleCompareForm(request.POST)
chartform = ChartParamChoiceForm(request.POST)
if form.is_valid() and chartform.is_valid():
cd = form.cleaned_data
workouts = cd['workouts']
xparam = chartform.cleaned_data['xparam']
yparam = chartform.cleaned_data['yparam']
plottype = chartform.cleaned_data['plottype']
teamid = chartform.cleaned_data['teamid']
ids = [int(w.id) for w in workouts]
request.session['ids'] = ids
labeldict = {
int(w.id): w.__unicode__() for w in workouts
}
else:
return HttpResponse("Form is not valid")
elif 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 = []
for id in ids:
try:
workouts.append(Workout.objects.get(id=id))
except Workout.DoesNotExist:
pass
labeldict = {
int(w.id): w.__unicode__() for w in workouts
}
elif 'ids' in request.session and 'plottype' in request.session:
xparam = request.session['xparam']
yparam = request.session['yparam']
plottype = request.session['plottype']
teamid = 0
ids = request.session['ids']
workouts = []
for id in ids:
try:
workouts.append(Workout.objects.get(id=id))
except Workout.DoesNotExist:
pass
labeldict = {
int(w.id): w.__unicode__() for w in workouts
}
chartform = ChartParamChoiceForm(
initial = {
'xparam':xparam,
'yparam':yparam,
'plottype':plottype,
'teamid':teamid
}
)
else:
url = reverse(team_comparison_select,
kwargs={
'id':id,
'teamid':0})
return HttpResponseRedirect(url)
res = interactive_multiple_compare_chart(ids,xparam,yparam,
promember=promember,
plottype=plottype,
labeldict=labeldict)
script = res[0]
div = res[1]
errormessage = res[3]
if errormessage != '':
messages.error(request,errormessage)
r = getrower(request.user)
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':reverse(team_comparison_select,kwargs={'teamid':teamid}),
'name': 'Compare Select'
},
{
'url':reverse(multi_compare_view),
'name': 'Comparison Chart'
}
]
if 'ps' in request.session:
ps = PlannedSession.objects.get(id=int(request.session['ps']))
breadcrumbs = [
{
'url': reverse(plannedsessions_view,
kwargs={'userid':userid}),
'name': 'Sessions'
},
{
'url':reverse(plannedsession_view,
kwargs={
'userid':userid,
'id':ps.id,
}
),
'name': ps.id
},
{
'url':reverse(plannedsession_compare_view,
kwargs={
'userid':userid,
'id':ps.id,
}
),
'name': 'Compare'
}
]
return render(request,'multicompare.html',
{'interactiveplot':script,
'the_div':div,
'breadcrumbs':breadcrumbs,
'rower':r,
'active':'nav-workouts',
'promember':promember,
'teamid':teamid,
'chartform':chartform,
'teams':get_my_teams(request.user),
})
# Multi Flex Chart with Grouping
@user_passes_test(ispromember,login_url="/rowers/promembership",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def user_multiflex_select(request,
startdatestring="",
enddatestring="",
message='',
successmessage='',
startdate=timezone.now()-datetime.timedelta(days=30),
enddate=timezone.now(),
userid=0):
r = getrequestrower(request,userid=userid)
user = r.user
if 'options' in request.session:
options = request.session['options']
else:
options = {}
try:
palette = request.session['palette']
except KeyError:
palette = 'monochrome_blue'
try:
includereststrokes = request.session['includereststrokes']
except KeyError:
includereststrokes = False
try:
ploterrorbars = request.session['ploterrorbars']
except:
ploterrorbars = False
if 'startdate' in request.session:
startdate = iso8601.parse_date(request.session['startdate'])
if 'enddate' in request.session:
enddate = iso8601.parse_date(request.session['enddate'])
try:
waterboattype = request.session['waterboattype']
except KeyError:
waterboattype = mytypes.waterboattype
else:
waterboattype = mytypes.waterboattype
if 'rankingonly' in request.session:
rankingonly = request.session['rankingonly']
else:
rankingonly = False
if 'modalities' in request.session:
modalities = request.session['modalities']
if len(modalities) > 1:
modality = 'all'
else:
modality = modalities[0]
else:
modalities = [m[0] for m in mytypes.workouttypes]
modality = 'all'
if request.method == 'POST':
dateform = DateRangeForm(request.POST)
if dateform.is_valid():
startdate = dateform.cleaned_data['startdate']
enddate = dateform.cleaned_data['enddate']
startdatestring = startdate.strftime('%Y-%m-%d')
enddatestring = enddate.strftime('%Y-%m-%d')
request.session['startdate'] = startdatestring
request.session['enddate'] = enddatestring
modalityform = TrendFlexModalForm(request.POST)
if modalityform.is_valid():
modality = modalityform.cleaned_data['modality']
waterboattype = modalityform.cleaned_data['waterboattype']
rankingonly = modalityform.cleaned_data['rankingonly']
if modality == 'all':
modalities = [m[0] for m in mytypes.workouttypes]
else:
modalities = [modality]
if modality != 'water':
waterboattype = [b[0] for b in mytypes.boattypes]
request.session['modalities'] = modalities
request.session['waterboattype'] = waterboattype
request.session['rankingonly'] = rankingonly
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
negtypes = []
for b in mytypes.boattypes:
if b[0] not in waterboattype:
negtypes.append(b[0])
if rankingonly:
rankingpiece = [True]
else:
rankingpiece = [True,False]
workouts = Workout.objects.filter(
user=r,
startdatetime__gte=startdate,
startdatetime__lte=enddate,
workouttype__in=modalities,
rankingpiece__in=rankingpiece
).order_by(
"-date", "-starttime"
).exclude(
boattype__in=negtypes
)
query = request.GET.get('q')
if query:
query_list = query.split()
workouts = workouts.filter(
reduce(operator.and_,
(Q(name__icontains=q) for q in query_list)) |
reduce(operator.and_,
(Q(notes__icontains=q) for q in query_list))
)
form = WorkoutMultipleCompareForm()
form.fields["workouts"].queryset = workouts
chartform = MultiFlexChoiceForm(initial={
'palette':palette,
'ploterrorbars':ploterrorbars,
'includereststrokes':includereststrokes,
})
modalityform = TrendFlexModalForm(initial={
'modality':modality,
'waterboattype':waterboattype,
'rankingonly':rankingonly,
})
messages.info(request,successmessage)
messages.error(request,message)
startdatestring = startdate.strftime('%Y-%m-%d')
enddatestring = enddate.strftime('%Y-%m-%d')
request.session['startdate'] = startdatestring
request.session['enddate'] = enddatestring
request.session['waterboattype'] = waterboattype
request.session['rankingonly'] = rankingonly
request.session['modalities'] = modalities
breadcrumbs = [
{
'url':'/rowers/analysis',
'name':'Analysis'
},
{
'url':reverse(user_multiflex_select,kwargs={'userid':userid}),
'name': 'Compare Select'
},
{
'url':reverse(multi_compare_view),
'name': 'Comparison Chart'
}
]
return render(request, 'user_multiflex_select.html',
{'workouts': workouts,
'dateform':dateform,
'breadcrumbs':breadcrumbs,
'startdate':startdate,
'enddate':enddate,
'theuser':user,
'rower':r,
'form':form,
'chartform':chartform,
'modalityform':modalityform,
'teams':get_my_teams(request.user),
})
@user_passes_test(ispromember,login_url="/rowers/promembership",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def multiflex_data(request,userid=0,
options={
'includereststrokes':False,
'ploterrorbars':False,
'userid':0,
'palette': 'monochrome_blue',
'groupby': 'spm',
'binsize': 1,
'xparam': 'hr',
'yparam': 'pace',
'spmmin': 15,
'spmmax': 55,
'workmin': 400,
'workmax': 1500,
'ids': [],
'ploterrorbars':False,
}):
if 'options' in request.session:
options = request.session['options']
try:
includereststrokes = options['includereststrokes']
except KeyError:
includereststrokes = False
try:
ploterrorbars = options['ploterrorbars']
except KeyError:
ploterrorbars = False
try:
palette = request.session['palette']
except KeyError:
palette = 'monochrome_blue'
workstrokesonly = not includereststrokes
if userid==0:
userid = request.user.id
palette = options['palette']
groupby = options['groupby']
binsize = options['binsize']
xparam = options['xparam']
yparam = options['yparam']
spmmin = options['spmmin']
spmmax = options['spmmax']
workmin = options['workmin']
workmax = options['workmax']
ids = options['ids']
workouts = []
for id in ids:
try:
workouts.append(Workout.objects.get(id=id))
except Workout.DoesNotExist:
pass
labeldict = {
int(w.id): w.__unicode__() for w in workouts
}
fieldlist,fielddict = dataprep.getstatsfields()
fieldlist = [xparam,yparam,groupby,
'workoutid','spm','driveenergy',
'workoutstate']
# prepare data frame
datadf,extracols = dataprep.read_cols_df_sql(ids,fieldlist)
if xparam == groupby:
datadf['groupby'] = datadf[xparam]
groupy = 'groupby'
datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly)
datadf = dataprep.filter_df(datadf,'spm',spmmin,
largerthan=True)
datadf = dataprep.filter_df(datadf,'spm',spmmax,
largerthan=False)
datadf = dataprep.filter_df(datadf,'driveenergy',workmin,
largerthan=True)
datadf = dataprep.filter_df(datadf,'driveneergy',workmax,
largerthan=False)
datadf.dropna(axis=0,how='any',inplace=True)
datemapping = {
w.id:w.date for w in workouts
}
datadf['date'] = datadf['workoutid']
datadf['date'].replace(datemapping,inplace=True)
today = datetime.date.today()
datadf['days ago'] = map(lambda x : x.days, datadf.date - today)
if groupby != 'date':
try:
bins = np.arange(datadf[groupby].min()-binsize,
datadf[groupby].max()+binsize,
binsize)
groups = datadf.groupby(pd.cut(datadf[groupby],bins,labels=False))
except ValueError:
messages.error(
request,
"Unable to compete. Probably not enough data selected"
)
url = reverse(user_multiflex_select)
return HttpResponseRedirect(url)
else:
bins = np.arange(datadf['days ago'].min()-binsize,
datadf['days ago'].max()+binsize,
binsize,
)
groups = datadf.groupby(pd.cut(datadf['days ago'], bins,
labels=False))
xvalues = groups.mean()[xparam]
yvalues = groups.mean()[yparam]
xerror = groups.std()[xparam]
yerror = groups.std()[yparam]
groupsize = groups.count()[xparam]
mask = groupsize <= min([0.01*groupsize.sum(),0.2*groupsize.mean()])
xvalues.loc[mask] = np.nan
yvalues.loc[mask] = np.nan
xerror.loc[mask] = np.nan
yerror.loc[mask] = np.nan
groupsize.loc[mask] = np.nan
xvalues.dropna(inplace=True)
yvalues.dropna(inplace=True)
xerror.dropna(inplace=True)
yerror.dropna(inplace=True)
groupsize.dropna(inplace=True)
if len(groupsize) == 0:
messages.error(request,'No data in selection')
url = reverse(user_multiflex_select)
return HttpResponseRedirect(url)
else:
groupsize = 30.*np.sqrt(groupsize/float(groupsize.max()))
df = pd.DataFrame({
xparam:xvalues,
yparam:yvalues,
'x':xvalues,
'y':yvalues,
'xerror':xerror,
'yerror':yerror,
'groupsize':groupsize,
})
if yparam == 'pace':
df['y'] = dataprep.paceformatsecs(df['y']/1.0e3)
aantal = len(df)
if groupby != 'date':
try:
df['groupval'] = groups.mean()[groupby]
df['groupval'].loc[mask] = np.nan
groupcols = df['groupval']
except ValueError:
df['groupval'] = groups.mean()[groupby].fillna(value=0)
df['groupval'].loc[mask] = np.nan
groupcols = df['groupval']
except KeyError:
messages.error(request,'Data selection error')
url = reverse(user_multiflex_select)
return HttpResponseRedirect(url)
else:
try:
dates = groups.min()[groupby]
dates.loc[mask] = np.nan
dates.dropna(inplace=True)
df['groupval'] = [x.strftime("%Y-%m-%d") for x in dates]
df['groupval'].loc[mask] = np.nan
groupcols = 100.*np.arange(aantal)/float(aantal)
except AttributeError:
df['groupval'] = groups.mean()['days ago'].fillna(value=0)
groupcols = 100.*np.arange(aantal)/float(aantal)
groupcols = (groupcols-groupcols.min())/(groupcols.max()-groupcols.min())
if aantal == 1:
groupcols = np.array([1.])
colors = range_to_color_hex(groupcols,palette=palette)
df['color'] = colors
clegendx = np.arange(0,1.2,.2)
legcolors = range_to_color_hex(clegendx,palette=palette)
if groupby != 'date':
clegendy = df['groupval'].min()+clegendx*(df['groupval'].max()-df['groupval'].min())
else:
clegendy = df.index.min()+clegendx*(df.index.max()-df.index.min())
colorlegend = zip(range(6),clegendy,legcolors)
if userid == 0:
extratitle = ''
else:
u = User.objects.get(id=userid)
extratitle = ' '+u.first_name+' '+u.last_name
script,div = interactive_multiflex(df,xparam,yparam,
groupby,
extratitle=extratitle,
ploterrorbars=ploterrorbars,
binsize=binsize,
colorlegend=colorlegend)
scripta= script.split('\n')[2:-1]
script = ''.join(scripta)
return JSONResponse({
"script":script,
"div":div,
})
@user_passes_test(ispromember,login_url="/rowers/promembership",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def multiflex_view(request,userid=0,
options={
'includereststrokes':False,
'ploterrorbars':False,
}):
if 'options' in request.session:
options = request.session['options']
try:
includereststrokes = options['includereststrokes']
except KeyError:
includereststrokes = False
try:
ploterrorbars = options['ploterrorbars']
except KeyError:
ploterrorbars = False
try:
palette = request.session['palette']
except KeyError:
palette = 'monochrome_blue'
if 'startdate' in request.session:
startdate = iso8601.parse_date(request.session['startdate'])
if 'enddate' in request.session:
enddate = iso8601.parse_date(request.session['enddate'])
workstrokesonly = not includereststrokes
if userid==0:
userid = request.user.id
if request.method == 'POST' and 'workouts' in request.POST:
form = WorkoutMultipleCompareForm(request.POST)
chartform = MultiFlexChoiceForm(request.POST)
if form.is_valid() and chartform.is_valid():
cd = form.cleaned_data
workouts = cd['workouts']
xparam = chartform.cleaned_data['xparam']
yparam = chartform.cleaned_data['yparam']
includereststrokes = chartform.cleaned_data['includereststrokes']
ploterrorbars = chartform.cleaned_data['ploterrorbars']
workstrokesonly = not includereststrokes
palette = chartform.cleaned_data['palette']
groupby = chartform.cleaned_data['groupby']
binsize = chartform.cleaned_data['binsize']
if binsize <= 0:
binsize = 1
if groupby == 'pace':
binsize *= 1000
spmmin = chartform.cleaned_data['spmmin']
spmmax = chartform.cleaned_data['spmmax']
workmin = chartform.cleaned_data['workmin']
workmax = chartform.cleaned_data['workmax']
ids = [int(w.id) for w in workouts]
request.session['ids'] = ids
else:
return HttpResponse("Form is not valid")
elif request.method == 'POST' and 'ids' in request.session:
chartform = MultiFlexChoiceForm(request.POST)
if chartform.is_valid():
xparam = chartform.cleaned_data['xparam']
yparam = chartform.cleaned_data['yparam']
includereststrokes = chartform.cleaned_data['includereststrokes']
ploterrorbars = chartform.cleaned_data['ploterrorbars']
request.session['ploterrorbars'] = ploterrorbars
request.session['includereststrokes'] = includereststrokes
workstrokesonly = not includereststrokes
palette = chartform.cleaned_data['palette']
groupby = chartform.cleaned_data['groupby']
binsize = chartform.cleaned_data['binsize']
if binsize <= 0:
binsize = 1
if groupby == 'pace':
binsize *= 1000.
spmmin = chartform.cleaned_data['spmmin']
spmmax = chartform.cleaned_data['spmmax']
workmin = chartform.cleaned_data['workmin']
workmax = chartform.cleaned_data['workmax']
ids = request.session['ids']
request.session['ids'] = ids
workouts = dataprep.get_workouts(ids,userid)
if not workouts:
message = 'Error: Workouts in session storage do not belong to this user.'
messages.error(request,message)
url = reverse(user_multiflex_select,
kwargs={
'userid':userid,
}
)
return HttpResponseRedirect(url)
# workouts = [Workout.objects.get(id=id) for id in ids]
else:
return HttpResponse("invalid form")
else:
url = reverse(user_multiflex_select)
return HttpResponseRedirect(url)
div = get_call()
options['includereststrokes'] = includereststrokes
options['ploterrorbars'] = ploterrorbars
options['userid'] = userid
options['palette'] = palette
options['groupby'] = groupby
options['binsize'] = binsize
options['xparam'] = xparam
options['yparam'] = yparam
options['spmmin'] = spmmin
options['spmmax'] = spmmax
options['workmin'] = workmin
options['workmax'] = workmax
options['ids'] = ids
request.session['options'] = options
r = getrequestrower(request,userid=userid)
breadcrumbs = [
{
'url':'/rowers/analysis',
'name':'Analysis'
},
{
'url':reverse(user_multiflex_select,kwargs={'userid':userid}),
'name': 'Trend Flex Select'
},
{
'url':reverse(multiflex_view),
'name': 'Trend Flex Chart'
}
]
return render(request,'multiflex.html',
{'interactiveplot':'',
'active':'nav-analysis',
'rower':r,
'breadcrumbs':breadcrumbs,
'the_div':div,
'active':'nav-analysis',
'chartform':chartform,
'userid':userid,
'teams':get_my_teams(request.user),
})
# Box plots
@user_passes_test(ispromember,login_url="/rowers/promembership",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def user_boxplot_select(request,
startdatestring="",
enddatestring="",
message='',
successmessage='',
startdate=timezone.now()-datetime.timedelta(days=30),
enddate=timezone.now(),
options={
'includereststrokes':False,
'workouttypes':['rower','dynamic','slides'],
'waterboattype':mytypes.waterboattype,
'rankingonly':False,
},
userid=0):
r = getrequestrower(request,userid=userid)
user = r.user
userid = user.id
if 'options' in request.session:
options = request.session['options']
try:
workouttypes = options['workouttypes']
except KeyError:
workouttypes = ['rower','dynamic','slides']
try:
rankingonly = options['rankingonly']
except KeyError:
rankingonly = False
try:
includereststrokes = options['includereststrokes']
except KeyError:
includereststrokes = False
if 'startdate' in request.session:
startdate = iso8601.parse_date(request.session['startdate'])
if 'enddate' in request.session:
enddate = iso8601.parse_date(request.session['enddate'])
workstrokesonly = not includereststrokes
waterboattype = mytypes.waterboattype
if startdatestring != "":
startdate = iso8601.parse_date(startdatestring)
if enddatestring != "":
enddate = iso8601.parse_date(enddatestring)
if enddate < startdate:
s = enddate
enddate = startdate
startdate = s
if request.method == 'POST':
dateform = DateRangeForm(request.POST)
if dateform.is_valid():
startdate = dateform.cleaned_data['startdate']
enddate = dateform.cleaned_data['enddate']
startdatestring = startdate.strftime('%Y-%m-%d')
enddatestring = enddate.strftime('%Y-%m-%d')
request.session['startdate'] = startdatestring
request.session['enddate'] = enddatestring
optionsform = TrendFlexModalForm(request.POST)
if optionsform.is_valid():
modality = optionsform.cleaned_data['modality']
waterboattype = optionsform.cleaned_data['waterboattype']
if modality == 'all':
modalities = [m[0] for m in mytypes.workouttypes]
else:
modalities = [modality]
if modality != 'water':
waterboattype = [b[0] for b in mytypes.boattypes]
if 'rankingonly' in optionsform.cleaned_data:
rankingonly = optionsform.cleaned_data['rankingonly']
else:
rankingonly = False
request.session['modalities'] = modalities
request.session['waterboattype'] = waterboattype
else:
dateform = DateRangeForm(initial={
'startdate':startdate,
'enddate':enddate,
})
if 'modalities' in request.session:
modalities = request.session['modalities']
if len(modalities) > 1:
modality = 'all'
else:
modality = modalities[0]
else:
modalities = [m[0] for m in mytypes.workouttypes]
modality = 'all'
negtypes = []
for b in mytypes.boattypes:
if b[0] not in waterboattype:
negtypes.append(b[0])
startdate = datetime.datetime.combine(startdate,datetime.time())
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
#enddate = enddate+datetime.timedelta(days=1)
if startdatestring:
startdate = iso8601.parse_date(startdatestring)
if enddatestring:
enddate = iso8601.parse_date(enddatestring)
if enddate < startdate:
s = enddate
enddate = startdate
startdate = s
negtypes = []
for b in mytypes.boattypes:
if b[0] not in waterboattype:
negtypes.append(b[0])
workouts = Workout.objects.filter(user=r,
startdatetime__gte=startdate,
startdatetime__lte=enddate,
workouttype__in=modalities,
).order_by(
"-date", "-starttime"
).exclude(boattype__in=negtypes)
# workouttypes = [w for w in workouttypes if w not in mytypes.otwtypes]
if rankingonly:
workouts = workouts.exclude(rankingpiece=False)
query = request.GET.get('q')
if query:
query_list = query.split()
workouts = workouts.filter(
reduce(operator.and_,
(Q(name__icontains=q) for q in query_list)) |
reduce(operator.and_,
(Q(notes__icontains=q) for q in query_list))
)
form = WorkoutMultipleCompareForm()
form.fields["workouts"].queryset = workouts
chartform = BoxPlotChoiceForm()
optionsform = TrendFlexModalForm(initial={
'modality':modality,
'waterboattype':waterboattype,
'rankingonly':rankingonly,
})
messages.info(request,successmessage)
messages.error(request,message)
startdatestring = startdate.strftime('%Y-%m-%d')
enddatestring = enddate.strftime('%Y-%m-%d')
request.session['startdate'] = startdatestring
request.session['enddate'] = enddatestring
breadcrumbs = [
{
'url':'/rowers/analysis',
'name':'Analysis'
},
{
'url':reverse(user_boxplot_select,kwargs={'userid':userid}),
'name': 'BoxPlot Select'
},
]
return render(request, 'user_boxplot_select.html',
{'workouts': workouts,
'dateform':dateform,
'startdate':startdate,
'enddate':enddate,
'rower':r,
'breadcrumbs':breadcrumbs,
'theuser':user,
'form':form,
'active':'nav-analysis',
'chartform':chartform,
'optionsform':optionsform,
'teams':get_my_teams(request.user),
})
@user_passes_test(ispromember,login_url="/rowers/promembership",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def boxplot_view_data(request,userid=0,
options={
'includereststrokes':False,
'spmmin':15,
'spmmax':55,
'workmin':0,
'workmax':1500,
'ids':[],
'userid':0,
'plotfield':'spm',
}):
if 'options' in request.session:
options = request.session['options']
try:
includereststrokes = options['includereststrokes']
spmmin = options['spmmin']
spmmax = options['spmmax']
workmin = options['workmin']
workmax = options['workmax']
ids = options['ids']
userid = options['userid']
plotfield = options['plotfield']
except KeyError:
includereststrokes = False
spmmin = 15
spmmax = 55
workmin = 0
workmax = 55
ids = []
userid = 0
plotfield = 'spm'
workstrokesonly = not includereststrokes
if userid==0:
userid = request.user.id
workouts = []
for id in ids:
try:
workouts.append(Workout.objects.get(id=id))
except Workout.DoesNotExist:
pass
labeldict = {
int(w.id): w.__unicode__() for w in workouts
}
datemapping = {
w.id:w.date for w in workouts
}
fieldlist,fielddict = dataprep.getstatsfields()
fieldlist = [plotfield,'workoutid','spm','driveenergy',
'workoutstate']
# prepare data frame
datadf,extracols = dataprep.read_cols_df_sql(ids,fieldlist)
datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly)
datadf = dataprep.filter_df(datadf,'spm',spmmin,
largerthan=True)
datadf = dataprep.filter_df(datadf,'spm',spmmax,
largerthan=False)
datadf = dataprep.filter_df(datadf,'driveenergy',workmin,
largerthan=True)
datadf = dataprep.filter_df(datadf,'driveneergy',workmax,
largerthan=False)
datadf.dropna(axis=0,how='any',inplace=True)
datadf['workoutid'].replace(datemapping,inplace=True)
datadf.rename(columns={"workoutid":"date"},inplace=True)
datadf = datadf.sort_values(['date'])
if userid == 0:
extratitle = ''
else:
u = User.objects.get(id=userid)
extratitle = ' '+u.first_name+' '+u.last_name
script,div = interactive_boxchart(datadf,plotfield,
extratitle=extratitle)
scripta = script.split('\n')[2:-1]
script = ''.join(scripta)
return JSONResponse({
"script":script,
"div":div,
})
@user_passes_test(ispromember,login_url="/rowers/promembership",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def boxplot_view(request,userid=0,
options={
'includereststrokes':False,
'rankingonly':False,
}):
if 'options' in request.session:
options = request.session['options']
try:
includereststrokes = options['includereststrokes']
except KeyError:
includereststrokes = False
options['includereststrokes'] = False
try:
rankingonly = options['rankingonly']
except KeyError:
rankingonly = False
options['rankingonly'] = False
workstrokesonly = not includereststrokes
if userid==0:
userid = request.user.id
if request.method == 'POST' and 'workouts' in request.POST:
form = WorkoutMultipleCompareForm(request.POST)
chartform = BoxPlotChoiceForm(request.POST)
if form.is_valid() and chartform.is_valid():
cd = form.cleaned_data
workouts = cd['workouts']
plotfield = chartform.cleaned_data['yparam']
includereststrokes = chartform.cleaned_data['includereststrokes']
request.session['includereststrokes'] = includereststrokes
workstrokesonly = not includereststrokes
spmmin = chartform.cleaned_data['spmmin']
spmmax = chartform.cleaned_data['spmmax']
workmin = chartform.cleaned_data['workmin']
workmax = chartform.cleaned_data['workmax']
ids = [int(w.id) for w in workouts]
request.session['ids'] = ids
else:
return HttpResponse("Form is not valid")
elif request.method == 'POST' and 'ids' in request.session:
chartform = BoxPlotChoiceForm(request.POST)
if chartform.is_valid():
plotfield = chartform.cleaned_data['yparam']
includereststrokes = chartform.cleaned_data['includereststrokes']
spmmin = chartform.cleaned_data['spmmin']
spmmax = chartform.cleaned_data['spmmax']
workmin = chartform.cleaned_data['workmin']
workmax = chartform.cleaned_data['workmax']
request.session['includereststrokes'] = includereststrokes
workstrokesonly = not includereststrokes
ids = request.session['ids']
request.session['ids'] = ids
else:
return HttpResponse("invalid form")
else:
url = reverse(user_boxplot_select,kwargs={'userid':userid})
return HttpResponseRedirect(url)
div = get_call()
options['spmmin'] = spmmin
options['spmmax'] = spmmax
options['workmin'] = workmin
options['workmax'] = workmax
options['ids'] = ids
options['userid'] = userid
options['plotfield'] = plotfield
options['rankingonly'] = rankingonly
request.session['options'] = options
r = getrequestrower(request,userid=userid)
breadcrumbs = [
{
'url':'/rowers/Analysis',
'name':'Analysis'
},
{
'url':reverse(user_boxplot_select,kwargs={'userid':userid}),
'name': 'BoxPlot Select'
},
{
'url':reverse(boxplot_view,kwargs={'userid':userid}),
'name': 'BoxPlot Select'
},
]
return render(request,'boxplot.html',
{'interactiveplot':'',
'the_div':div,
'rower':r,
'breadcrumbs':breadcrumbs,
'active':'nav-analysis',
'chartform':chartform,
'userid':userid,
'teams':get_my_teams(request.user),
})
# List Courses
@login_required()
def courses_view(request):
r = getrower(request.user)
courses = GeoCourse.objects.all().order_by("country","name")
# add search processing
query = request.GET.get('q')
if query:
query_list = query.split()
courses = GeoCourse.objects.filter(
reduce(operator.and_,
(Q(name__icontains=q) for q in query_list)) |
reduce(operator.and_,
(Q(country__icontains=q) for q in query_list)) |
reduce(operator.and_,
(Q(notes__icontains=q) for q in query_list))
)
return render(request,'list_courses.html',
{'courses':courses,
'active':'nav-racing',
'rower':r,
})
# List Workouts
@login_required()
def workouts_view(request,message='',successmessage='',
startdatestring='',
enddatestring='',
teamid=0,rankingonly=False,rowerid=0,userid=0):
request.session['referer'] = absolute(request)['PATH']
r = getrequestrower(request,rowerid=rowerid,userid=userid)
# check if access is allowed
if not checkaccessuser(request.user,r):
raise PermissionDenied("Access denied")
if startdatestring:
startdate = iso8601.parse_date(startdatestring)
else:
startdate = datetime.date.today()-datetime.timedelta(days=365)
if enddatestring:
enddate = iso8601.parse_date(enddatestring)
else:
enddate = datetime.date.today()
startdate = datetime.datetime.combine(startdate,datetime.time())
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
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 enddate < startdate:
s = enddate
enddate = startdate
startdate = s
startdatestring = startdate.strftime('%Y-%m-%d')
enddatestring = enddate.strftime('%Y-%m-%d')
# start date for the small graph
activity_startdate = enddate-datetime.timedelta(days=15)
try:
if utc.localize(enddate) > timezone.now():
activity_enddate = timezone.now()
activity_startdate = activity_enddate-datetime.timedelta(days=15)
else:
activity_enddate = enddate
except ValueError:
activity_enddate = enddate
g_startdate = activity_startdate
g_enddate = activity_enddate
if teamid:
try:
theteam = Team.objects.get(id=teamid)
except Team.DoesNotExist:
raise Http404("Team doesn't exist")
if theteam.viewing == 'allmembers' or theteam.manager == request.user:
workouts = Workout.objects.filter(
team=theteam,
startdatetime__gte=startdate,
startdatetime__lte=enddate,
privacy='visible').order_by("-date","-starttime")
g_workouts = Workout.objects.filter(
team=theteam,
startdatetime__gte=activity_startdate,
startdatetime__lte=activity_enddate,
duplicate=False,
privacy='visible').order_by("-date", "-starttime")
elif theteam.viewing == 'coachonly':
workouts = Workout.objects.filter(
team=theteam,user=r,
startdatetime__gte=startdate,
startdatetime__lte=enddate,
privacy='visible').order_by("-startdatetime")
g_workouts = Workout.objects.filter(
team=theteam,user=r,
startdatetime__gte=activity_startdate,
enddatetime__lte=activity_enddate,
duplicate=False,
privacy='visible').order_by("-startdatetime")
elif request.user != r.user:
theteam = None
workouts = Workout.objects.filter(
user=r,
startdatetime__gte=startdate,
startdatetime__lte=enddate,
privacy='visible').order_by("-date", "-starttime")
g_workouts = Workout.objects.filter(
user=r,
startdatetime__gte=activity_startdate,
startdatetime__lte=activity_enddate,
duplicate=False,
privacy='visible').order_by("-startdatetime")
else:
theteam = None
workouts = Workout.objects.filter(
user=r,
startdatetime__gte=startdate,
startdatetime__lte=enddate).order_by("-date", "-starttime")
g_workouts = Workout.objects.filter(
user=r,
duplicate=False,
startdatetime__gte=activity_startdate,
startdatetime__lte=activity_enddate).order_by("-startdatetime")
if len(g_workouts) == 0:
g_workouts = Workout.objects.filter(
user=r,
startdatetime__gte=timezone.now()-timedelta(days=15)).order_by("-startdatetime")
g_enddate = timezone.now()
g_startdate = (timezone.now()-timedelta(days=15))
if rankingonly:
workouts = workouts.exclude(rankingpiece=False)
workoutsnohr = workouts.exclude(averagehr__isnull=False)
for w in workoutsnohr:
res = dataprep.workout_trimp(w)
query = request.GET.get('q')
if query:
query_list = query.split()
workouts = workouts.filter(
reduce(operator.and_,
(Q(name__icontains=q) for q in query_list)) |
reduce(operator.and_,
(Q(notes__icontains=q) for q in query_list))
)
paginator = Paginator(workouts,20) # show 25 workouts per page
page = request.GET.get('page')
try:
workouts = paginator.page(page)
except PageNotAnInteger:
workouts = paginator.page(1)
except EmptyPage:
workouts = paginator.page(paginator.num_pages)
today = timezone.now()
announcements = SiteAnnouncement.objects.filter(
expires__gte=today
).order_by(
"-created",
"-id"
)
if theteam:
stack='rower'
else:
stack='type'
script,div = interactive_activitychart(g_workouts,
g_startdate,
g_enddate,
stack=stack)
messages.info(request,successmessage)
messages.error(request,message)
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
]
return render(request, 'list_workouts.html',
{'workouts': workouts,
'active': 'nav-workouts',
'rower':r,
'breadcrumbs':breadcrumbs,
'dateform':dateform,
'startdate':startdate,
'enddate':enddate,
'announcements':announcements[0:4],
'team':theteam,
'rankingonly':rankingonly,
'teams':get_my_teams(request.user),
'interactiveplot':script,
'the_div':div,
})
# List of workouts to compare a selected workout to
@user_passes_test(ispromember,login_url="/rowers/promembership",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def workout_comparison_list(request,id=0,message='',successmessage='',
startdatestring="",enddatestring="",
startdate=timezone.now()-datetime.timedelta(days=365),
enddate=timezone.now()):
try:
r = getrower(request.user)
except Rower.DoesNotExist:
raise Http404("User has no rower instance")
u = User.objects.get(id=r.user.id)
if request.method == 'POST':
dateform = DateRangeForm(request.POST)
if dateform.is_valid():
startdate = dateform.cleaned_data['startdate']
enddate = dateform.cleaned_data['enddate']
else:
dateform = DateRangeForm(initial={
'startdate':startdate,
'enddate':enddate,
})
if startdatestring:
startdate = iso8601.parse_date(startdatestring)
if enddatestring:
enddate = iso8601.parse_date(enddatestring)
startdate = datetime.datetime.combine(startdate,datetime.time())
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
#enddate = enddate+datetime.timedelta(days=1)
if enddate < startdate:
s = enddate
enddate = startdate
startdate = s
workouts = Workout.objects.filter(user=r,
startdatetime__gte=startdate,
startdatetime__lte=enddate).order_by("-date", "-starttime").exclude(id=id)
query = request.GET.get('q')
if query:
query_list = query.split()
workouts = workouts.filter(
reduce(operator.and_,
(Q(name__icontains=q) for q in query_list)) |
reduce(operator.and_,
(Q(notes__icontains=q) for q in query_list))
)
paginator = Paginator(workouts,15) # show 25 workouts per page
page = request.GET.get('page')
try:
workouts = paginator.page(page)
except PageNotAnInteger:
workouts = paginator.page(1)
except EmptyPage:
workouts = paginator.page(paginator.num_pages)
row = get_workout(id)
messages.error(request,message)
messages.info(request,successmessage)
return render(request, 'comparison_list.html',
{'id':int(id),
'workout':row,
'workouts': workouts,
'last_name':u.last_name,
'first_name':u.first_name,
'dateform':dateform,
'startdate':startdate,
'enddate':enddate,
'teams':get_my_teams(request.user),
})
# List of workouts to compare a selected workout to
@user_passes_test(ispromember,login_url="/rowers/promembership",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def workout_fusion_list(request,id=0,message='',successmessage='',
startdatestring="",enddatestring="",
startdate=timezone.now()-datetime.timedelta(days=365),
enddate=timezone.now()):
try:
r = getrower(request.user)
except Rower.DoesNotExist:
raise Http404("User has no rower instance")
u = User.objects.get(id=r.user.id)
if request.method == 'POST':
dateform = DateRangeForm(request.POST)
if dateform.is_valid():
startdate = dateform.cleaned_data['startdate']
enddate = dateform.cleaned_data['enddate']
else:
dateform = DateRangeForm(initial={
'startdate':startdate,
'enddate':enddate,
})
if startdatestring:
startdate = iso8601.parse_date(startdatestring)
if enddatestring:
enddate = iso8601.parse_date(enddatestring)
startdate = datetime.datetime.combine(startdate,datetime.time())
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
#enddate = enddate+datetime.timedelta(days=1)
if enddate < startdate:
s = enddate
enddate = startdate
startdate = s
workouts = Workout.objects.filter(user=r,
startdatetime__gte=startdate,
startdatetime__lte=enddate).order_by("-date", "-starttime").exclude(id=id)
query = request.GET.get('q')
if query:
query_list = query.split()
workouts = workouts.filter(
reduce(operator.and_,
(Q(name__icontains=q) for q in query_list)) |
reduce(operator.and_,
(Q(notes__icontains=q) for q in query_list))
)
paginator = Paginator(workouts,15) # show 25 workouts per page
page = request.GET.get('page')
try:
workouts = paginator.page(page)
except PageNotAnInteger:
workouts = paginator.page(1)
except EmptyPage:
workouts = paginator.page(paginator.num_pages)
row = get_workout(id)
messages.info(request,successmessage)
messages.error(request,message)
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,row.id),
'name': row.name
},
{
'url':reverse(workout_fusion_list,kwargs={'id':id}),
'name': 'Sensor Fusion'
}
]
return render(request, 'fusion_list.html',
{'id':int(id),
'workout':row,
'rower':r,
'active':'nav-workouts',
'breadcrumbs':breadcrumbs,
'workouts': workouts,
'last_name':u.last_name,
'first_name':u.first_name,
'dateform':dateform,
'startdate':startdate,
'enddate':enddate,
'teams':get_my_teams(request.user),
})
# Basic view of workout
def workout_view(request,id=0):
request.session['referer'] = absolute(request)['PATH']
if not request.user.is_anonymous():
rower = getrower(request.user)
else:
rower = None
try:
row = Workout.objects.get(id=id)
except Workout.DoesNotExist:
raise Http404("Workout doesn't exist")
comments = WorkoutComment.objects.filter(workout=row)
aantalcomments = len(comments)
if row.privacy == 'private' and not checkworkoutuser(request.user,row):
raise PermissionDenied("Access denied")
g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime")
for i in g:
try:
width,height = Image.open(i.filename).size
i.width = width
i.height = height
i.save()
except:
pass
# create interactive plot
res = interactive_chart(id)
script = res[0]
div = res[1]
# create map
f1 = row.csvfilename
rowdata = rdata(f1)
hascoordinates = 1
if rowdata != 0:
try:
latitude = rowdata.df[' latitude']
if not latitude.std():
hascoordinates = 0
except KeyError,AttributeError:
hascoordinates = 0
else:
hascoordinates = 0
if hascoordinates:
mapscript,mapdiv = leaflet_chart(rowdata.df[' latitude'],
rowdata.df[' longitude'],
row.name)
else:
mapscript = ""
mapdiv = ""
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':reverse(workout_view,kwargs={'id':id}),
'name': row.name,
}
]
u = row.user.user
return render(request, 'workout_view.html',
{'workout':row,
'rower':rower,
'breadcrumbs':breadcrumbs,
'active':'nav-workouts',
'graphs':g,
'last_name':u.last_name,
'first_name':u.first_name,
'interactiveplot':script,
'aantalcomments':aantalcomments,
'mapscript':mapscript,
'mapdiv':mapdiv,
'teams':get_my_teams(request.user),
'the_div':div})
# Resets stroke data to raw data (pace)
@user_passes_test(ispromember,login_url="/rowers/promembership",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def workout_undo_smoothenpace_view(
request,id=0,message="",successmessage=""
):
row = get_workout(id)
r = getrower(request.user)
if (checkworkoutuser(request.user,row)==False):
message = "You are not allowed to edit this workout"
messages.error(request,message)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
filename = row.csvfilename
row = rdata(filename)
if row == 0:
return HttpResponse("Error: CSV Data File Not Found")
if 'originalvelo' in row.df:
velo = row.df['originalvelo'].values
row.df[' Stroke500mPace (sec/500m)'] = 500./velo
row.write_csv(filename,gzip=True)
dataprep.update_strokedata(id,row.df)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':id,
}
)
return HttpResponseRedirect(url)
# Data smoothing of pace data
@user_passes_test(ispromember,login_url="/rowers/promembership",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def workout_smoothenpace_view(request,id=0,message="",successmessage=""):
row = get_workout(id)
previousurl = request.META.get('HTTP_REFERER')
r = getrower(request.user)
if (checkworkoutuser(request.user,row)==False):
message = "You are not allowed to edit this workout"
messages.error(request,message)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
filename = row.csvfilename
row = rdata(filename)
if row == 0:
return HttpResponse("Error: CSV Data File Not Found")
pace = row.df[' Stroke500mPace (sec/500m)'].values
velo = 500./pace
if not 'originalvelo' in row.df:
row.df['originalvelo'] = velo
velo2 = stravastuff.ewmovingaverage(velo,5)
pace2 = 500./abs(velo2)
row.df[' Stroke500mPace (sec/500m)'] = pace2
row.df = row.df.fillna(0)
row.write_csv(filename,gzip=True)
dataprep.update_strokedata(id,row.df)
messages.info(request,'A smoothening filter was applied to your pace data')
if previousurl:
url = previousurl
else:
url = reverse(r.defaultlandingpage,
kwargs = {
'id':id,
}
)
return HttpResponseRedirect(url)
# Process CrewNerd Summary CSV and update summary
@user_passes_test(ispromember,login_url="/rowers/promembership",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def workout_crewnerd_summary_view(request,id=0,message="",successmessage=""):
row = get_workout(id)
r = getrower(request.user)
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,id),
'name': row.name
},
{
'url':reverse(workout_crewnerd_summary_view,kwargs={'id':id}),
'name': 'CrewNerd Summary'
}
]
if request.method == 'POST':
form = CNsummaryForm(request.POST,request.FILES)
if form.is_valid():
f = request.FILES['file']
res = handle_uploaded_file(f)
fname = res[1]
try:
sumd = summarydata(fname)
row.summary = sumd.allstats()
row.save()
os.remove(fname)
successmessage = "CrewNerd summary added"
messages.info(request,successmessage)
url = reverse(workout_edit_view,
kwargs = {
'id':int(id),
})
return HttpResponseRedirect(url)
except:
try:
os.remove(fname)
except:
pass
message = "Something went wrong (workout_crewnerd_summary_view)"
messages.error(request,message)
url = reverse(workout_edit_view,
kwargs = {
'id':int(id),
})
return HttpResponseRedirect(url)
else:
return render(request,
"cn_form.html",
{'form':form,
'active':'nav-workouts',
'rower':r,
'workout':row,
'breadcrumbs':breadcrumbs,
'teams':get_my_teams(request.user),
'id':row.id})
else:
form = CNsummaryForm()
return render(request,
"cn_form.html",
{'form':form,
'active':'nav-workouts',
'rower':r,
'workout':row,
'breadcrumbs':breadcrumbs,
'teams':get_my_teams(request.user),
'id':row.id})
# Get weather for given location and date/time
@user_passes_test(ispromember,login_url="/rowers/promembership",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def workout_downloadwind_view(request,id=0,
airportcode=None,
message="",successmessage=""):
row = get_workout(id)
f1 = row.csvfilename
if (checkworkoutuser(request.user,row)==False):
message = "You are not allowed to edit this workout"
messages.error(request,message)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
# create bearing
rowdata = rdata(f1)
if rowdata == 0:
return HttpResponse("Error: CSV Data File Not Found")
try:
bearing = rowdata.df.ix[:,'bearing'].values
except KeyError:
rowdata.add_bearing()
rowdata.write_csv(f1,gzip=True)
# get wind
try:
avglat = rowdata.df[' latitude'].mean()
avglon = rowdata.df[' longitude'].mean()
avgtime = int(rowdata.df['TimeStamp (sec)'].mean()-rowdata.df.ix[0,'TimeStamp (sec)'])
startdatetime = dateutil.parser.parse("{}, {}".format(row.date,
row.starttime))
starttimeunix = int(arrow.get(row.startdatetime).timestamp)
#starttimeunix = int(mktime(startdatetime.utctimetuple()))
avgtime = starttimeunix+avgtime
winddata = get_wind_data(avglat,avglon,avgtime)
windspeed = winddata[0]
windbearing = winddata[1]
message = winddata[2]
row.notes += "\n"+message
row.save()
rowdata.add_wind(windspeed,windbearing)
rowdata.write_csv(f1,gzip=True)
messages.info(request,message)
kwargs = {
'id':int(id)}
url = reverse(workout_wind_view,kwargs=kwargs)
response = HttpResponseRedirect(url)
except KeyError:
message = "No latitude/longitude data"
messages.error(request,message)
kwargs = {
'id':int(id)
}
url = reverse(workout_wind_view,kwargs=kwargs)
response = HttpResponseRedirect(url)
return response
# Get weather for given location and date/time
@user_passes_test(ispromember,login_url="/rowers/promembership",message="This functionality requires a Pro plan or higher",redirect_field_name=None)
def workout_downloadmetar_view(request,id=0,
airportcode=None,
message="",successmessage=""):
row = get_workout(id)
f1 = row.csvfilename
if (checkworkoutuser(request.user,row)==False):
message = "You are not allowed to edit this workout"
messages.error(request,message)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
# create bearing
rowdata = rdata(f1)
if rowdata == 0:
return HttpResponse("Error: CSV Data File Not Found")
try:
bearing = rowdata.df.ix[:,'bearing'].values
except KeyError:
rowdata.add_bearing()
rowdata.write_csv(f1,gzip=True)
# get wind
try:
avglat = rowdata.df[' latitude'].mean()
avglon = rowdata.df[' longitude'].mean()
airportcode = get_airport_code(avglat,avglon)[0]
avgtime = int(rowdata.df['TimeStamp (sec)'].mean()-rowdata.df.ix[0,'TimeStamp (sec)'])
startdatetime = dateutil.parser.parse("{}, {}".format(row.date,
row.starttime))
starttimeunix = arrow.get(row.startdatetime).timestamp
#starttimeunix = int(mktime(startdatetime.utctimetuple()))
avgtime = starttimeunix +avgtime
winddata = get_metar_data(airportcode,avgtime)
windspeed = winddata[0]
windbearing = winddata[1]
message = winddata[2]
row.notes += "\n"+message
row.save()
rowdata.add_wind(windspeed,windbearing)
rowdata.write_csv(f1,gzip=True)
messages.info(request,message)
kwargs = {
'id':int(id)}
url = reverse(workout_wind_view,kwargs=kwargs)
response = HttpResponseRedirect(url)
except KeyError:
message = "No latitude/longitude data"
messages.error(request,message)
kwargs = {
'id':int(id)
}
url = reverse(workout_wind_view,kwargs=kwargs)
response = HttpResponseRedirect(url)
return response
# Show form to update wind data
@user_passes_test(ispromember,login_url="/rowers/promembership",message="This functionality requires a Pro plan or higher",redirect_field_name=None)
def workout_wind_view(request,id=0,message="",successmessage=""):
row = get_workout(id)
r = getrower(request.user)
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,id),
'name': row.name
},
{
'url':reverse(workout_wind_view,kwargs={'id':id}),
'name': 'Wind'
}
]
if (checkworkoutuser(request.user,row)==False):
message = "You are not allowed to edit this workout"
messages.error(request,message)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
# get data
f1 = row.csvfilename
u = row.user.user
r = getrower(u)
# create bearing
rowdata = rdata(f1)
if row == 0:
return HttpResponse("Error: CSV Data File Not Found")
hascoordinates = 1
try:
latitude = rowdata.df.ix[:,' latitude']
except KeyError:
hascoordinates = 0
if hascoordinates and not latitude.std():
hascoordinates = 0
try:
bearing = rowdata.df.ix[:,'bearing'].values
except KeyError:
rowdata.add_bearing()
rowdata.write_csv(f1,gzip=True)
if hascoordinates:
avglat = rowdata.df[' latitude'].mean()
avglon = rowdata.df[' longitude'].mean()
airportcode,newlat,newlon,airportdistance = get_airport_code(avglat,avglon)
airportcode = airportcode.upper()
airportdistance = airportdistance[0]
else:
airportcode = 'UNKNOWN'
airportdistance = 0
if request.method == 'POST':
# process form
form = UpdateWindForm(request.POST)
if form.is_valid():
vwind1 = form.cleaned_data['vwind1']
vwind2 = form.cleaned_data['vwind2']
dist1 = form.cleaned_data['dist1']
dist2 = form.cleaned_data['dist2']
winddirection1 = form.cleaned_data['winddirection1']
winddirection2 = form.cleaned_data['winddirection2']
windunit = form.cleaned_data['windunit']
rowdata.update_wind(vwind1,vwind2,
winddirection1,
winddirection2,
dist1,dist2,
units=windunit)
rowdata.write_csv(f1,gzip=True)
else:
message = "Invalid Form"
messages.error(request,message)
kwargs = {
'id':int(id)
}
url = reverse(workout_wind_view,kwargs=kwargs)
response = HttpResponseRedirect(url)
else:
form = UpdateWindForm()
# create interactive plot
res = interactive_windchart(id,promember=1)
script = res[0]
div = res[1]
if hascoordinates:
gmscript,gmdiv = leaflet_chart(
rowdata.df[' latitude'],
rowdata.df[' longitude'],
row.name)
else:
gmscript = ""
gmdiv = "No GPS data available"
messages.info(request,successmessage)
messages.error(request,message)
return render(request,
'windedit.html',
{'workout':row,
'rower':r,
'breadcrumbs':breadcrumbs,
'active':'nav-workouts',
'teams':get_my_teams(request.user),
'interactiveplot':script,
'form':form,
'airport':airportcode,
'airportdistance':airportdistance,
'the_div':div,
'gmap':gmscript,
'gmapdiv':gmdiv})
# Show form to update River stream data (for river dwellers)
@user_passes_test(ispromember,login_url="/rowers/promembership",message="This functionality requires a Pro plan or higher",redirect_field_name=None)
def workout_stream_view(request,id=0,message="",successmessage=""):
row = get_workout(id)
r = getrower(request.user)
if (checkworkoutuser(request.user,row)==False):
message = "You are not allowed to edit this workout"
messages.error(request,message)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
# create interactive plot
f1 = row.csvfilename
u = row.user.user
r = getrower(u)
rowdata = rdata(f1)
if rowdata == 0:
return HttpResponse("Error: CSV Data File Not Found")
if request.method == 'POST':
# process form
form = UpdateStreamForm(request.POST)
if form.is_valid():
dist1 = form.cleaned_data['dist1']
dist2 = form.cleaned_data['dist2']
stream1 = form.cleaned_data['stream1']
stream2 = form.cleaned_data['stream2']
streamunit = form.cleaned_data['streamunit']
rowdata.update_stream(stream1,stream2,dist1,dist2,
units=streamunit)
rowdata.write_csv(f1,gzip=True)
else:
message = "Invalid Form"
messages.error(request,message)
kwargs = {
'id':int(id)}
url = reverse(workout_wind_view,kwargs=kwargs)
response = HttpResponseRedirect(url)
else:
form = UpdateStreamForm()
# create interactive plot
res = interactive_streamchart(id,promember=1)
script = res[0]
div = res[1]
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,id),
'name': row.name
},
{
'url':reverse(workout_stream_view,kwargs={'id':id}),
'name': 'Stream'
}
]
messages.info(request,successmessage)
messages.error(request,message)
return render(request,
'streamedit.html',
{'workout':row,
'rower':r,
'breadcrumbs':breadcrumbs,
'active':'nav-workouts',
'teams':get_my_teams(request.user),
'interactiveplot':script,
'form':form,
'the_div':div})
# Form to set average crew weight and boat type, then run power calcs
@user_passes_test(ispromember, login_url="/rowers/promembership",redirect_field_name=None)
def workout_otwsetpower_view(request,id=0,message="",successmessage=""):
w = get_workout(id)
r = getrower(request.user)
mayedit = 0
if request.user == w.user.user:
mayedit=1
if checkworkoutuser(request.user,w):
mayedit=1
if (checkworkoutuser(request.user,w)==False):
message = "You are not allowed to edit this workout"
messages.error(request,message)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
if request.method == 'POST':
# process form
form = AdvancedWorkoutForm(request.POST)
if form.is_valid():
quick_calc = form.cleaned_data['quick_calc']
boattype = form.cleaned_data['boattype']
weightvalue = form.cleaned_data['weightvalue']
w.boattype = boattype
w.weightvalue = weightvalue
w.save()
# load row data & create power/wind/bearing columns if not set
f1 = w.csvfilename
rowdata = rdata(f1)
if rowdata == 0:
return HttpResponse("Error: CSV Data File Not Found")
try:
vstream = rowdata.df['vstream']
except KeyError:
rowdata.add_stream(0)
rowdata.write_csv(f1,gzip=True)
try:
bearing = rowdata.df['bearing']
except KeyError:
rowdata.add_bearing()
rowdata.write_csv(f1,gzip=True)
try:
vwind = rowdata.df['vwind']
except KeyError:
rowdata.add_wind(0,0)
rowdata.write_csv(f1,gzip=True)
# do power calculation (asynchronous)
r = w.user
u = r.user
first_name = u.first_name
last_name = u.last_name
emailaddress = u.email
job = myqueue(queuelow,
handle_otwsetpower,f1,boattype,
weightvalue,
first_name,last_name,emailaddress,id,
ps=[r.p0,r.p1,r.p2,r.p3],
ratio=r.cpratio,
quick_calc = quick_calc,
emailbounced = r.emailbounced
)
try:
request.session['async_tasks'] += [(job.id,'otwsetpower')]
except KeyError:
request.session['async_tasks'] = [(job.id,'otwsetpower')]
successmessage = 'Your calculations have been submitted. You will receive an email when they are done. You can check the status of your calculations <a href="/rowers/jobs-status/" target="_blank">here</a>'
messages.info(request,successmessage)
kwargs = {
'id':int(id)}
try:
url = request.session['referer']
except KeyError:
url = reverse(workout_advanced_view,kwargs=kwargs)
response = HttpResponseRedirect(url)
return response
else:
message = "Invalid Form"
messages.error(request,message)
kwargs = {
'id':int(id)}
url = reverse(workout_otwsetpower_view,kwargs=kwargs)
response = HttpResponseRedirect(url)
else:
form = AdvancedWorkoutForm(instance=w)
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,id),
'name': w.name
},
{
'url':reverse(workout_otwsetpower_view,kwargs={'id':id}),
'name': 'OTW Power'
}
]
messages.error(request,message)
messages.info(request,successmessage)
return render(request,
'otwsetpower.html',
{'workout':w,
'rower':w,
'mayedit':mayedit,
'active':'nav-workouts',
'breadcrumbs':breadcrumbs,
'teams':get_my_teams(request.user),
'form':form,
})
@login_required()
def instroke_view(request,id=0):
w = get_workout(id)
r = getrower(request.user)
mayedit = 0
if request.user == w.user.user:
mayedit=1
if checkworkoutuser(request.user,w):
mayedit=1
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,id),
'name': w.name
},
{
'url':reverse(instroke_view,kwargs={'id':id}),
'name': 'In-Stroke Metrics'
}
]
# form = WorkoutForm(instance=row)
g = GraphImage.objects.filter(workout=w).order_by("-creationdatetime")
# check if user is owner of this workout
if (checkworkoutuser(request.user,w)==False):
message = "You are not allowed to edit this workout"
messages.error(request,message)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
from metrics import nometrics
rowdata = rrdata(csvfile=w.csvfilename)
try:
instrokemetrics = rowdata.get_instroke_columns()
instrokemetrics = [m for m in instrokemetrics if not m in nometrics]
except AttributeError:
instrokemetrics = []
return render(request,
'instroke.html',
{'workout':w,
'rower':r,
'active':'nav-workouts',
'breadcrumbs':breadcrumbs,
'mayedit':mayedit,
'teams':get_my_teams(request.user),
'instrokemetrics':instrokemetrics,
})
# A special Edit page with all the Geeky functionality for the workout
@login_required()
def workout_geeky_view(request,id=0,message="",successmessage=""):
row = get_workout(id)
# form = WorkoutForm(instance=row)
g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime")
rowdata = rrdata(csvfile=row.csvfilename)
instrokemetrics = rowdata.get_instroke_columns()
for i in g:
try:
width,height = Image.open(i.filename).size
i.width = width
i.height = height
i.save()
except:
pass
# check if user is owner of this workout
if (checkworkoutuser(request.user,row)==False):
message = "You are not allowed to edit this workout"
messages.error(request,message)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
# create interactive plot
f1 = row.csvfilename
u = row.user.user
r = getrower(u)
# create interactive plot
try:
res = interactive_chart(id,promember=1)
script = res[0]
div = res[1]
except ValueError:
pass
messages.error(request,message)
messages.info(request,successmessage)
if row.workouttype in mytypes.otwtypes:
return render(request,
'otwgeeky.html',
{'workout':row,
'teams':get_my_teams(request.user),
'interactiveplot':script,
'instrokemetrics':instrokemetrics,
'the_div':div})
else:
return render(request,
'advancededit.html',
{'workout':row,
'teams':get_my_teams(request.user),
'interactiveplot':script,
'the_div':div})
# generate instroke chart
@login_required()
def instroke_chart(request,id=0,metric=''):
w = get_workout(id)
if (checkworkoutuser(request.user,w)==False):
message = "You are not allowed to edit this workout"
messages.error(request,message)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
rowdata = rrdata(csvfile=w.csvfilename)
instrokemetrics = rowdata.get_instroke_columns()
if metric in instrokemetrics:
f1 = w.csvfilename[6:-4]
timestr = strftime("%Y%m%d-%H%M%S")
imagename = f1+timestr+'.png'
fullpathimagename = 'static/plots/'+imagename
u = w.user.user
r = getrower(u)
title = w.name
fig1 = rowdata.get_plot_instroke(metric)
canvas = FigureCanvas(fig1)
canvas.print_figure('static/plots/'+imagename)
plt.close(fig1)
fig1.clf()
gc.collect()
try:
width,height = Image.open(fullpathimagename).size
except:
width = 1200
height = 600
imgs = GraphImage.objects.filter(workout=w)
if len(imgs) < 7:
i = GraphImage(workout=w,
creationdatetime=timezone.now(),
filename=fullpathimagename,
width=width,height=height)
i.save()
else:
messages.error(request,'You have reached the maximum number of static images for this workout. Delete an image first')
r = getrower(request.user)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':id,
})
return HttpResponseRedirect(url)
# Cumulative stats page
@user_passes_test(ispromember,login_url="/rowers/promembership",message="This functionality requires a Pro plan or higher",redirect_field_name=None)
def cumstats(request,theuser=0,
startdate=timezone.now()-datetime.timedelta(days=30),
enddate=timezone.now(),
deltadays=-1,
startdatestring="",
enddatestring="",
options={
'includereststrokes':False,
'workouttypes':['rower','dynamic','slides'],
'waterboattype':mytypes.waterboattype,
'rankingonly':False,
}):
r = getrequestrower(request,userid=theuser)
theuser = r.user
if 'waterboattype' in request.session:
waterboattype = request.session['waterboattype']
else:
waterboattype = mytypes.waterboattype
if 'rankingonly' in request.session:
rankingonly = request.session['rankingonly']
else:
rankingonly = False
if 'modalities' in request.session:
modalities = request.session['modalities']
if len(modalities) > 1:
modality = 'all'
else:
modality = modalities[0]
else:
modalities = [m[0] for m in mytypes.workouttypes]
modality = 'all'
try:
rankingonly = options['rankingonly']
except KeyError:
rankingonly = False
try:
includereststrokes = options['includereststrokes']
except KeyError:
includereststrokes = False
workstrokesonly = not includereststrokes
waterboattype = mytypes.waterboattype
if startdatestring != "":
startdate = iso8601.parse_date(startdatestring)
if enddatestring != "":
enddate = iso8601.parse_date(enddatestring)
if enddate < startdate:
s = enddate
enddate = startdate
startdate = s
# get all indoor rows of in date range
# process form
if request.method == 'POST':
form = DateRangeForm(request.POST)
modalityform = TrendFlexModalForm(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
startdatestring = startdate.strftime('%Y-%m-%d')
enddatestring = enddate.strftime('%Y-%m-%d')
if modalityform.is_valid():
modality = modalityform.cleaned_data['modality']
waterboattype = modalityform.cleaned_data['waterboattype']
rankingonly = modalityform.cleaned_data['rankingonly']
if modality == 'all':
modalities = [m[0] for m in mytypes.workouttypes]
else:
modalities = [modality]
if modality != 'water':
waterboattype = [b[0] for b in mytypes.boattypes]
request.session['modalities'] = modalities
request.session['waterboattype'] = waterboattype
request.session['rankingonly'] = rankingonly
form = DateRangeForm(initial={
'startdate': startdate,
'enddate': enddate,
})
else:
form = DateRangeForm(initial={
'startdate': startdate,
'enddate': enddate,
})
includereststrokes = False
workstrokesonly = not includereststrokes
modalityform = TrendFlexModalForm(
initial={
'modality':modality,
'waterboattype':waterboattype,
'rankingonly':rankingonly,
}
)
negtypes = []
for b in mytypes.boattypes:
if b[0] not in waterboattype:
negtypes.append(b[0])
script = ''
div = get_call()
js_resources = ''
css_resources = ''
options = {
'modality': modality,
'theuser': theuser.id,
'waterboattype':waterboattype,
'startdatestring':startdatestring,
'enddatestring':enddatestring,
'rankingonly':rankingonly,
'includereststrokes':includereststrokes,
}
request.session['options'] = options
if modality == 'all':
modalities = [m[0] for m in mytypes.workouttypes]
else:
modalities = [modality]
try:
startdate = iso8601.parse_date(startdatestring)
except ParseError:
startdate = timezone.now()-datetime.timedelta(days=7)
try:
enddate = iso8601.parse_date(enddatestring)
except ParseError:
enddate = timezone.now()
if enddate < startdate:
s = enddate
enddate = startdate
startdate = s
promember=0
if theuser == 0:
theuser = request.user.id
if not request.user.is_anonymous():
r = getrower(request.user)
result = request.user.is_authenticated() and ispromember(request.user)
if result:
promember=1
r2 = getrower(theuser)
if rankingonly:
rankingpiece = [True,]
else:
rankingpiece = [True,False]
allworkouts = Workout.objects.filter(
user=r2,
workouttype__in=modalities,
boattype__in=waterboattype,
startdatetime__gte=startdate,
startdatetime__lte=enddate,
rankingpiece__in=rankingpiece
).order_by("-date", "-starttime")
ids = [int(workout.id) for workout in allworkouts]
datemapping = {
w.id:w.date for w in allworkouts
}
fieldlist,fielddict = dataprep.getstatsfields()
# prepare data frame
datadf,extracols = dataprep.read_cols_df_sql(ids,fieldlist)
datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly)
request.session['rowerid'] = r.id
if datadf.empty:
stats = {}
cordict = {}
response = render(request,
'cumstats.html',
{
'stats':stats,
'teams':get_my_teams(request.user),
'options':options,
'active':'nav-analysis',
'rower':r,
'id':theuser,
'theuser':theuser,
'startdate':startdate,
'enddate':enddate,
'form':form,
'optionsform':modalityform,
'cordict':cordict,
})
request.session['options'] = options
return response
# Create stats
stats = {}
fielddict.pop('workoutstate')
fielddict.pop('workoutid')
for field,verbosename in fielddict.iteritems():
thedict = {
'mean':datadf[field].mean(),
'min': datadf[field].min(),
'std': datadf[field].std(),
'max': datadf[field].max(),
'median': datadf[field].median(),
'firstq':datadf[field].quantile(q=0.25),
'thirdq':datadf[field].quantile(q=0.75),
'verbosename':verbosename,
}
stats[field] = thedict
# Create a dict with correlation values
cor = datadf.corr(method='spearman')
cor.fillna(value=0,inplace=True)
cordict = {}
for field1,verbosename in fielddict.iteritems():
thedict = {}
for field2,verbosename in fielddict.iteritems():
try:
thedict[field2] = cor.ix[field1,field2]
except KeyError:
thedict[field2] = 0
cordict[field1] = thedict
# interactive box plot
datadf['workoutid'].replace(datemapping,inplace=True)
datadf.rename(columns={"workoutid":"date"},inplace=True)
datadf = datadf.sort_values(['date'])
# set options form correctly
initial = {}
initial['includereststrokes'] = includereststrokes
initial['waterboattype'] = waterboattype
initial['rankingonly'] = rankingonly
response = render(request,
'cumstats.html',
{
'stats':stats,
'teams':get_my_teams(request.user),
'active':'nav-analysis',
'rower':r,
'options':options,
'id':theuser,
'theuser':theuser,
'startdate':startdate,
'enddate':enddate,
'form':form,
'optionsform':modalityform,
'cordict':cordict,
})
request.session['options'] = options
return response
# data explorer
@login_required()
def workout_data_view(request, id=0):
r = getrower(request.user)
w = get_workout(id)
if not checkworkoutuser(request.user,w):
raise PermissionDenied('Access Denied')
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,id),
'name': w.name
},
{
'url':reverse(workout_data_view,kwargs={'id':id}),
'name': 'Data Explorer'
}
]
datadf,row = dataprep.getrowdata_db(id=id)
datadf.sort_values(['ftime'],inplace=True)
datadf.drop(labels=[
'id','time','hr_an','hr_at','hr_bottom','hr_max',
'hr_tr','hr_ut1','hr_ut2','x_right',
],inplace=True,axis=1)
cols = ['ftime','cumdist','fpace','spm',
'hr','power','driveenergy','drivelength','averageforce',
'peakforce','distance','drivespeed','workoutstate',
'catch','finish','peakforceangle','wash','slip','rhythm',
'effectiveangle','totalangle','distanceperstroke','velo']
tcols = ['ftime','cumdist','fpace','spm','hr','power']
datadf = datadf[cols]
datadf.loc[:,'hr'] = datadf['hr'].astype('int')
datadf.loc[:,'power'] = datadf['power'].astype('int')
datadf.loc[:,'distance'] = datadf['distance'].astype('int')
datadf.loc[:,'spm'] = 10*datadf['spm'].astype('int')/10.
if request.method == 'POST':
form = DataFrameColumnsForm(request.POST)
if form.is_valid():
tcols = form.cleaned_data['cols']
else:
form = DataFrameColumnsForm(initial = {'cols':tcols})
datadf = datadf[tcols]
for col in cols:
try:
if datadf[col].mean() == 0 and datadf[col].std() == 0:
datadf.drop(labels=[col],axis=1,inplace=True)
except (TypeError,KeyError):
pass
# pd.set_option('display.width', 1000)
pd.set_option('colheader_justify', 'left')
htmltable = datadf.to_html(
bold_rows=True,
show_dimensions=True,border=1,
classes='pandastable',justify='justify'
)
return render(request,
'workout_data.html',
{
'htmltable': htmltable,
'form':form,
'teams':get_my_teams(request.user),
'workout': w,
'breadcrumbs': breadcrumbs,
}
)
# Stats page
@login_required()
def workout_stats_view(request,id=0,message="",successmessage=""):
r = getrower(request.user)
w = get_workout(id)
mayedit = 0
if request.user == w.user.user:
mayedit=1
if checkworkoutuser(request.user,w):
mayedit=1
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,id),
'name': w.name
},
{
'url':reverse(workout_stats_view,kwargs={'id':id}),
'name': 'Stats'
}
]
workstrokesonly = True
if request.method == 'POST' and 'workstrokesonly' in request.POST:
workstrokesonly = str2bool(request.POST['workstrokesonly'])
# prepare data frame
datadf,row = dataprep.getrowdata_db(id=id)
if (checkworkoutuser(request.user,row)==False):
raise PermissionDenied('Access Denied')
datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly)
if datadf.empty:
datadf,row = dataprep.getrowdata_db(id=id)
datadf = dataprep.clean_df_stats(datadf,workstrokesonly=False)
workstrokesonly=False
if datadf.empty:
return HttpResponse("CSV data file not found")
#datadf['deltat'] = datadf['time'].diff()
workoutstateswork = [1,4,5,8,9,6,7]
workoutstatesrest = [3]
workoutstatetransition = [0,2,10,11,12,13]
# Create stats
stats = {}
fieldlist,fielddict = dataprep.getstatsfields()
fielddict.pop('workoutstate')
fielddict.pop('workoutid')
for field,verbosename in fielddict.iteritems():
thedict = {
'mean':datadf[field].mean(),
'wmean': wavg(datadf, field, 'deltat'),
'min': datadf[field].min(),
'std': datadf[field].std(),
'max': datadf[field].max(),
'median': datadf[field].median(),
'firstq':datadf[field].quantile(q=0.25),
'thirdq':datadf[field].quantile(q=0.75),
'verbosename':verbosename,
}
stats[field] = thedict
# Create a dict with correlation values
cor = datadf.corr(method='spearman')
cor.fillna(value=0,inplace=True)
cordict = {}
for field1,verbosename in fielddict.iteritems():
thedict = {}
for field2,verbosename in fielddict.iteritems():
try:
thedict[field2] = cor.ix[field1,field2]
except KeyError:
thedict[field2] = 0
cordict[field1] = thedict
# additional non-automated stats
otherstats = {}
# Normalized power & TSS
tss,normp = dataprep.workout_rscore(w)
if not np.isnan(tss) and tss != 0:
otherstats['tss'] = {
'verbose_name':'rScore',
'value':int(tss),
'unit':''
}
if not np.isnan(normp):
otherstats['np'] = {
'verbose_name':'rPower',
'value':int(10*normp)/10.,
'unit':'Watt'
}
# HR Drift
tmax = datadf['time'].max()
tmin = datadf['time'].min()
thalf = tmin+0.5*(tmax-tmin)
mask1 = datadf['time'] < thalf
mask2 = datadf['time'] > thalf
hr1 = datadf.loc[mask1,'hr'].mean()
hr2 = datadf.loc[mask2,'hr'].mean()
pwr1 = datadf.loc[mask1,'power'].mean()
pwr2 = datadf.loc[mask2,'power'].mean()
try:
hrdrift = ((pwr1/hr1)-(pwr2/hr2))/(pwr1/hr1)
hrdrift *= 100.
if not np.isnan(hrdrift):
hrdrift = int(100*hrdrift)/100.
otherstats['hrdrift'] = {
'verbose_name': 'Heart Rate Drift',
'value': hrdrift,
'unit': '%',
}
except ZeroDivisionError,ValueError:
pass
# TRIMP
trimp,hrtss = dataprep.workout_trimp(w)
otherstats['trimp'] = {
'verbose_name': 'TRIMP',
'value': trimp,
'unit': ''
}
otherstats['hrScore'] = {
'verbose_name': 'rScore (HR)',
'value': hrtss,
'unit':''
}
return render(request,
'workoutstats.html',
{
'stats':stats,
'teams':get_my_teams(request.user),
'workout':w,
'rower':r,
'mayedit':mayedit,
'breadcrumbs':breadcrumbs,
'active':'nav-workouts',
'workstrokesonly':workstrokesonly,
'cordict':cordict,
'otherstats':otherstats,
})
# The Advanced edit page
@login_required()
def workout_advanced_view(request,id=0,message="",successmessage=""):
row = get_workout(id)
# form = WorkoutForm(instance=row)
g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime")
for i in g:
try:
width,height = Image.open(i.filename).size
i.width = width
i.height = height
i.save()
except:
pass
# check if user is owner of this workout
if (checkworkoutuser(request.user,row)==False):
message = "You are not allowed to edit this workout"
messages.error(request,message)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
# create interactive plot
f1 = row.csvfilename
u = row.user.user
r = getrower(u)
# create interactive plot
try:
res = interactive_chart(id,promember=1)
script = res[0]
div = res[1]
except ValueError:
pass
messages.error(request,message)
messages.info(request,successmessage)
if row.workouttype in mytypes.otwtypes:
return render(request,
'advancedotw.html',
{'workout':row,
'teams':get_my_teams(request.user),
'interactiveplot':script,
'the_div':div})
else:
return render(request,
'advancededit.html',
{'workout':row,
'teams':get_my_teams(request.user),
'interactiveplot':script,
'the_div':div})
# The interactive plot comparing two workouts (obsolete version)
def workout_comparison_view(request,id1=0,id2=0,xparam='distance',yparam='spm'):
promember=0
if not request.user.is_anonymous():
r = getrower(request.user)
result = request.user.is_authenticated() and ispromember(request.user)
if result:
promember=1
# create interactive plot
res = interactive_comparison_chart(id1,id2,xparam=xparam,yparam=yparam,
promember=promember)
script = res[0]
div = res[1]
axchoicesbasic = {ax[0]:ax[1] for ax in axes if ax[4]=='basic'}
axchoicespro = {ax[0]:ax[1] for ax in axes if ax[4]=='pro'}
noylist = ["time","distance"]
axchoicesbasic.pop("cumdist")
return render(request,
'comparisonchart.html',
{'interactiveplot':script,
'the_div':div,
'teams':get_my_teams(request.user),
'id1':id1,
'id2':id2,
'axchoicesbasic':axchoicesbasic,
'axchoicespro':axchoicespro,
'noylist':noylist,
'xparam':xparam,
'yparam':yparam,
})
# Updated version of comparison plot
def workout_comparison_view2(request,id1=0,id2=0,xparam='distance',
yparam='spm',plottype='line'):
promember=0
if not request.user.is_anonymous():
r = getrower(request.user)
result = request.user.is_authenticated() and ispromember(request.user)
if result:
promember=1
if not Workout.objects.filter(id=id1).exists() or not Workout.objects.filter(id=id2).exists():
raise Http404("Workout doesn't exist")
# create interactive plot
res = interactive_comparison_chart(id1,id2,xparam=xparam,yparam=yparam,
promember=promember,plottype=plottype)
script = res[0]
div = res[1]
axchoicesbasic = {ax[0]:ax[1] for ax in axes if ax[4]=='basic'}
axchoicespro = {ax[0]:ax[1] for ax in axes if ax[4]=='pro'}
noylist = ["time","distance"]
axchoicesbasic.pop("cumdist")
row1 = Workout.objects.get(id=id1)
row2 = Workout.objects.get(id=id2)
mayedit = 0
if request.user == row1.user.user:
mayedit=1
if checkworkoutuser(request.user,row1):
mayedit=1
if row1.workouttype != 'water' or row2.workouttype != 'water':
axchoicespro.pop('slip')
axchoicespro.pop('wash')
axchoicespro.pop('catch')
axchoicespro.pop('finish')
axchoicespro.pop('totalangle')
axchoicespro.pop('effectiveangle')
axchoicespro.pop('peakforceangle')
return render(request,
'comparisonchart2.html',
{'interactiveplot':script,
'the_div':div,
'teams':get_my_teams(request.user),
'id1':id1,
'id2':id2,
'mayedit':mayedit,
'axchoicesbasic':axchoicesbasic,
'axchoicespro':axchoicespro,
'noylist':noylist,
'xparam':xparam,
'yparam':yparam,
'plottype':plottype,
'promember':promember,
})
# Change default landing page
@login_required()
def workflow_default_view(request):
r = getrower(request.user)
if r.defaultlandingpage == 'workout_edit_view':
r.defaultlandingpage = 'workout_workflow_view'
else:
r.defaultlandingpage = 'workout_edit_view'
r.save()
url = reverse(workout_workflow_config2_view)
return HttpResponseRedirect(url)
def get_workout_default_page(request,id):
if request.user.is_anonymous():
return reverse(workout_view,kwargs={'id':str(id)})
else:
r = Rower.objects.get(user=request.user)
if r.defaultlandingpage == 'workout_edit_view':
return reverse(workout_edit_view,kwargs={'id':str(id)})
else:
return reverse(workout_workflow_view,kwargs={'id':str(id)})
# Workflow Configuration
@login_required()
def workout_workflow_config_view(request):
request.session['referer'] = absolute(request)['PATH']
request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE
try:
workoutid = request.session['lastworkout']
except KeyError:
workoutid = 0
r = getrower(request.user)
if request.method == 'POST' and 'leftpanel' in request.POST:
formleft = WorkFlowLeftPanelForm(request.POST)
if formleft.is_valid():
leftpanel = formleft.cleaned_data['leftpanel']
r.workflowleftpanel = leftpanel
r.save()
if request.method == 'POST' and 'middlepanel' in request.POST:
formmiddle = WorkFlowMiddlePanelForm(request.POST)
if formmiddle.is_valid():
middlepanel = formmiddle.cleaned_data['middlepanel']
r.workflowmiddlepanel = middlepanel
r.save()
formleft = WorkFlowLeftPanelForm(instance=r)
formmiddle = WorkFlowMiddlePanelForm(instance=r)
tmplt = 'workflowconfig.html'
return render(request,tmplt,
{
'rower':r,
'formleft':formleft,
'formmiddle':formmiddle,
'workoutid': workoutid,
})
@login_required()
def workout_workflow_config2_view(request,userid=0):
request.session['referer'] = absolute(request)['PATH']
request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE
try:
workoutid = request.session['lastworkout']
except KeyError:
workoutid = 0
r = getrequestrower(request,userid=userid,notpermanent=True)
MiddlePanelFormSet = formset_factory(WorkFlowMiddlePanelElement,extra=1)
LeftPanelFormSet = formset_factory(WorkFlowLeftPanelElement,extra=1)
if request.method == 'POST':
wasmiddle = [1 for key,value in request.POST.items() if 'middlepanel' in key.lower()]
wasleft = [1 for key,valye in request.POST.items() if 'leftpanel' in key.lower()]
if wasmiddle:
middlepanel_formset = MiddlePanelFormSet(request.POST,
prefix='middlepanel')
newmiddlepanel = []
if middlepanel_formset.is_valid():
for form in middlepanel_formset:
value = form.cleaned_data.get('panel')
if value != 'None':
newmiddlepanel.append(value)
newmiddlepanel = [i for i in newmiddlepanel if i != None]
r.workflowmiddlepanel = newmiddlepanel
try:
r.save()
except IntegrityError:
messages.error(request,'Something went wrong')
if wasleft:
leftpanel_formset = LeftPanelFormSet(request.POST,
prefix='leftpanel')
newleftpanel = []
if leftpanel_formset.is_valid():
for form in leftpanel_formset:
value = form.cleaned_data.get('panel')
if value != 'None':
newleftpanel.append(value)
newleftpanel = [i for i in newleftpanel if i != None]
r.workflowleftpanel = newleftpanel
try:
r.save()
except IntegrityError:
messages.error(request,'Something went wrong')
leftpanelform_data = [{'panel':panel}
for panel in r.workflowleftpanel]
middlepanelform_data = [{'panel':panel}
for panel in r.workflowmiddlepanel]
leftpanel_formset = LeftPanelFormSet(initial=leftpanelform_data,
prefix='leftpanel')
middlepanel_formset = MiddlePanelFormSet(initial=middlepanelform_data,
prefix='middlepanel')
tmplt = 'workflowconfig2.html'
return render(request,tmplt,
{
'rower':r,
'leftpanel_formset':leftpanel_formset,
'middlepanel_formset':middlepanel_formset,
'workoutid': workoutid,
})
def getfavorites(r,row):
workouttype = 'ote'
if row.workouttype in mytypes.otwtypes:
workouttype = 'otw'
matchworkouttypes = [workouttype,'all']
workoutsource = row.workoutsource
if 'speedcoach2' in row.workoutsource:
workoutsource = 'speedcoach2'
try:
favorites = FavoriteChart.objects.filter(user=r,
workouttype__in=matchworkouttypes).order_by("id")
favorites2 = FavoriteChart.objects.filter(user=r,
workouttype__in=[workoutsource]).order_by("id")
favorites = favorites | favorites2
maxfav = len(favorites)-1
except:
favorites = None
maxfav = 0
return favorites,maxfav
# Workflow View
@login_required()
def workout_workflow_view(request,id):
request.session['referer'] = absolute(request)['PATH']
request.session['lastworkout'] = id
request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE
row = get_workout_permitted(request.user,id)
r = getrower(request.user)
result = request.user.is_authenticated() and ispromember(request.user)
if result:
promember=1
if request.user == row.user.user:
mayedit=1
comments = WorkoutComment.objects.filter(workout=row)
aantalcomments = len(comments)
favorites,maxfav = getfavorites(r,row)
charts = get_call()
if 'panel_map.html' in r.workflowmiddlepanel and rowhascoordinates(row):
rowdata = rdata(row.csvfilename)
mapscript,mapdiv = leaflet_chart2(rowdata.df[' latitude'],
rowdata.df[' longitude'],
row.name)
else:
mapscript = ''
mapdiv = ''
statcharts = GraphImage.objects.filter(workout=row)
middleTemplates = []
for t in r.workflowmiddlepanel:
try:
template.loader.get_template(t)
middleTemplates.append(t)
except template.TemplateDoesNotExist:
pass
leftTemplates = []
for t in r.workflowleftpanel:
try:
template.loader.get_template(t)
leftTemplates.append(t)
except template.TemplateDoesNotExist:
pass
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,id),
'name': row.name
},
{
'url':reverse(workout_workflow_view,kwargs={'id':id}),
'name': 'View'
}
]
return render(request,
'workflow.html',
{
'middleTemplates':middleTemplates,
'leftTemplates':leftTemplates,
'active':'nav-workouts',
'breadcrumbs':breadcrumbs,
'charts':charts,
'workout':row,
'mapscript':mapscript,
'mapdiv':mapdiv,
'statcharts':statcharts,
'rower':r,
'aantalcomments':aantalcomments,
})
# The famous flex chart
@login_required()
def workout_flexchart3_view(request,*args,**kwargs):
try:
id = kwargs['id']
except KeyError:
raise Http404("Invalid workout number")
if 'promember' in kwargs:
promember = kwargs['promember']
else:
promember = 0
try:
favoritenr = int(request.GET['favoritechart'])
except:
favoritenr = -1
row = get_workout(id)
promember=0
mayedit=0
if not request.user.is_anonymous():
r = getrower(request.user)
result = request.user.is_authenticated() and ispromember(request.user)
if result:
promember=1
if request.user == row.user.user:
mayedit=1
if checkworkoutuser(request.user,row):
mayedit=1
workouttype = 'ote'
if row.workouttype in mytypes.otwtypes:
workouttype = 'otw'
favorites,maxfav = getfavorites(r,row)
# check if favoritenr is not out of range
if favorites:
try:
t = favorites[favoritenr].xparam
except IndexError:
favoritenr=0
except AssertionError:
favoritenr=0
if 'xparam' in kwargs:
xparam = kwargs['xparam']
else:
if favorites:
xparam = favorites[favoritenr].xparam
else:
xparam = 'distance'
if 'yparam1' in kwargs:
yparam1 = kwargs['yparam1']
else:
if favorites:
yparam1 = favorites[favoritenr].yparam1
else:
yparam1 = 'pace'
if 'yparam2' in kwargs:
yparam2 = kwargs['yparam2']
if yparam2 == '':
yparam2 = 'None'
else:
if favorites:
yparam2 = favorites[favoritenr].yparam2
if yparam2 == '':
yparam2 = 'None'
else:
yparam2 = 'hr'
if not request.user.is_anonymous():
r = getrower(request.user)
if favoritenr>=0 and r.showfavoritechartnotes:
try:
favoritechartnotes = favorites[favoritenr].notes
except IndexError:
favoritechartnotes = ''
else:
favoritechartnotes = ''
else:
favoritechartnotes = ''
favoritenr = 0
if 'plottype' in kwargs:
plottype = kwargs['plottype']
else:
if favorites:
plottype = favorites[favoritenr].plottype
else:
plottype = 'line'
if 'workstrokesonly' in kwargs:
workstrokesonly = kwargs['workstrokesonly']
else:
if favorites:
workstrokesonly = not favorites[favoritenr].reststrokes
else:
workstrokesonly = False
if request.method == 'POST' and 'savefavorite' in request.POST:
if not request.user.is_anonymous():
workstrokesonly = request.POST['workstrokesonlysave']
reststrokes = not workstrokesonly
r = getrower(request.user)
try:
range = metrics.yaxmaxima[xparam]
if yparam1 is not None:
range = metrics.yaxmaxima[yparam1]
if yparam2 is not None:
range = metrics.yaxmaxima[yparam2]
f = FavoriteChart(user=r,xparam=xparam,
yparam1=yparam1,yparam2=yparam2,
plottype=plottype,workouttype=workouttype,
reststrokes=reststrokes)
f.save()
except KeyError:
messages.error(request,'We cannot save the ad hoc metrics in a favorite chart')
if request.method == 'POST' and 'xaxis' in request.POST:
flexoptionsform = FlexOptionsForm(request.POST)
if flexoptionsform.is_valid():
cd = flexoptionsform.cleaned_data
includereststrokes = cd['includereststrokes']
plottype = cd['plottype']
workstrokesonly = not includereststrokes
flexaxesform = FlexAxesForm(request,request.POST)
print request.POST
if flexaxesform.is_valid():
print 'form valid'
cd = flexaxesform.cleaned_data
xparam = cd['xaxis']
yparam1 = cd['yaxis1']
yparam2 = cd['yaxis2']
else:
print flexaxesform.errors
if not promember:
for name,d in rowingmetrics:
if d['type'] != 'basic':
if xparam == name:
xparam = 'time'
messages.info(request,'To use '+d['verbose_name']+', you have to be Pro member')
if yparam1 == name:
yparam1 = 'pace'
messages.info(request,'To use '+d['verbose_name']+', you have to be Pro member')
if yparam2 == name:
yparam2 = 'spm'
messages.info(request,'To use '+d['verbose_name']+', you have to be Pro member')
# bring back slashes
# yparam1 = yparam1.replace('_slsh_','/')
# yparam2 = yparam2.replace('_slsh_','/')
# xparam = xparam.replace('_slsh_','/')
# create interactive plot
try:
(
script,
div,
js_resources,
css_resources,
workstrokesonly
) = interactive_flex_chart2(
id,xparam=xparam,yparam1=yparam1,
yparam2=yparam2,
promember=promember,plottype=plottype,
workstrokesonly=workstrokesonly
)
except ValueError:
(
script,
div,
js_resources,
css_resources,
workstrokesonly
) = interactive_flex_chart2(
id,xparam=xparam,yparam1=yparam1,
yparam2=yparam2,
promember=promember,plottype=plottype,
workstrokesonly=workstrokesonly
)
js_resources = ""
css_resources = ""
axchoicesbasic = {ax[0]:ax[1] for ax in axes if ax[4]=='basic'}
axchoicespro = {ax[0]:ax[1] for ax in axes if ax[4]=='pro'}
noylist = ["time","distance"]
axchoicesbasic.pop("cumdist")
if row.workouttype in mytypes.otwtypes:
for name,d in rowingmetrics:
if d['mode'] == 'erg':
axchoicespro.pop(name)
else:
for name,d in rowingmetrics:
if d['mode'] == 'water':
axchoicespro.pop(name)
from rowers.metrics import nometrics
rowdata = rdata(row.csvfilename)
try:
rowdata.set_instroke_metrics()
except AttributeError:
pass
try:
additionalmetrics = rowdata.get_additional_metrics()
additionalmetrics = [m for m in additionalmetrics if not m in nometrics]
except AttributeError:
additionalmetrics = []
# extrametrics = {m.replace('/','_slsh_'):m for m in additionalmetrics}
extrametrics = additionalmetrics
# xparam = xparam.replace('/','_slsh_')
# yparam1 = yparam1.replace('/','_slsh_')
# yparam2 = yparam2.replace('/','_slsh_')
# for metric in nometrics:
# try:
# extrametrics.pop(metric)
# except KeyError:
# pass
initial = {
'xaxis':xparam,
'yaxis1':yparam1,
'yaxis2':yparam2,
}
flexaxesform = FlexAxesForm(request,initial=initial,
extrametrics=extrametrics)
initial = {
'includereststrokes': not workstrokesonly,
'plottype':plottype
}
flexoptionsform = FlexOptionsForm(initial=initial)
row = Workout.objects.get(id=id)
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,id),
'name': row.name
},
{
'url':reverse(workout_flexchart3_view,kwargs=kwargs),
'name': 'Flex Chart'
}
]
return render(request,
'flexchart3otw.html',
{'the_script':script,
'the_div':div,
'breadcrumbs':breadcrumbs,
'rower':r,
'active':'nav-workouts',
'workout':row,
'chartform':flexaxesform,
'optionsform':flexoptionsform,
'js_res': js_resources,
'css_res':css_resources,
'teams':get_my_teams(request.user),
'id':int(id),
'xparam':xparam,
'yparam1':yparam1,
'yparam2':yparam2,
'plottype':plottype,
'axchoicesbasic':axchoicesbasic,
'axchoicespro':axchoicespro,
'extrametrics':extrametrics,
'favoritechartnotes':favoritechartnotes,
'noylist':noylist,
'mayedit':mayedit,
'promember':promember,
'workstrokesonly': not workstrokesonly,
'favoritenr':favoritenr,
'maxfav':maxfav,
})
# The interactive plot with the colored Heart rate zones
def workout_biginteractive_view(request,id=0,message="",successmessage=""):
row = get_workout(id)
# check if user is owner of this workout
# create interactive plot
f1 = row.csvfilename
u = row.user.user
# r = getrower(u)
promember=0
mayedit=0
if not request.user.is_anonymous():
r = getrower(request.user)
result = request.user.is_authenticated() and ispromember(request.user)
if result:
promember=1
if request.user == row.user.user:
mayedit=1
# create interactive plot
res = interactive_bar_chart(id,promember=promember)
script = res[0]
div = res[1]
messages.error(request,message)
messages.info(request,successmessage)
return render(request,
'biginteractive1.html',
{'workout':row,
'teams':get_my_teams(request.user),
'interactiveplot':script,
'the_div':div,
'promember':promember,
'mayedit':mayedit})
# The interactive plot with wind corrected pace for OTW outings
def workout_otwpowerplot_view(request,id=0,message="",successmessage=""):
w = get_workout(id)
r = getrower(request.user)
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,id),
'name': w.name
},
{
'url':reverse(workout_otwpowerplot_view,kwargs={'id':id}),
'name': 'Interactive OTW Power Plot'
}
]
# check if user is owner of this workout
# create interactive plot
f1 = w.csvfilename
u = w.user.user
# r = getrower(u)
promember=0
mayedit=0
if not request.user.is_anonymous():
r = getrower(request.user)
result = request.user.is_authenticated() and ispromember(request.user)
if result:
promember=1
if request.user == w.user.user:
mayedit=1
# create interactive plot
res = interactive_otw_advanced_pace_chart(id,promember=promember)
script = res[0]
div = res[1]
messages.error(request,message)
messages.info(request,successmessage)
return render(request,
'otwinteractive.html',
{'workout':w,
'rower':r,
'active':'nav-workouts',
'breadcrumbs':breadcrumbs,
'teams':get_my_teams(request.user),
'interactiveplot':script,
'the_div':div,
'mayedit':mayedit})
# the page where you can choose where to export this workout
@login_required()
def workout_export_view(request,id=0, message="", successmessage=""):
request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE
row = get_workout(id)
try:
thetoken = c2_open(request.user)
except NoTokenError:
thetoken = 0
if (checkworkoutuser(request.user,row)) and thetoken:
c2userid = c2stuff.get_userid(thetoken)
else:
c2userid = 0
try:
rktoken = runkeeper_open(request.user)
except NoTokenError:
rktoken = 0
if (checkworkoutuser(request.user,row)) and rktoken:
rkuserid = runkeeperstuff.get_userid(rktoken)
else:
rkuserid = 0
# form = WorkoutForm(instance=row)
g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime")
for i in g:
try:
width,height = Image.open(i.filename).size
i.width = width
i.height = height
i.save()
except:
pass
# check if user is owner of this workout
if (checkworkoutuser(request.user,row)==False):
message = "You are not allowed to edit this workout"
messages.error(request,message)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
messages.error(request,message)
messages.info(request,successmessage)
return render(request,
'export.html',
{'workout':row,
'teams':get_my_teams(request.user),
'c2userid':c2userid,
'rkuserid':rkuserid,
})
#
@login_required()
def workout_unsubscribe_view(request,id=0):
w = get_workout(id)
if w.privacy == 'private' and w.user.user != request.user:
return HttpResponseForbidden("Permission error")
comments = WorkoutComment.objects.filter(workout=w,
user=request.user).order_by("created")
for c in comments:
c.notification = False
c.save()
form = WorkoutCommentForm()
successmessage = 'You have been unsubscribed from new comment notifications for this workout'
messages.info(request,successmessage)
return render(request,
'workout_comments.html',
{'workout':w,
'teams':get_my_teams(request.user),
'comments':comments,
'form':form,
})
# list of comments to a workout
@login_required()
def workout_comment_view(request,id=0):
w = get_workout(id)
if w.privacy == 'private' and w.user.user != request.user:
return HttpResponseForbidden("Permission error")
comments = WorkoutComment.objects.filter(workout=w).order_by("created")
# ok we're permitted
if request.method == 'POST':
r = w.user
form = WorkoutCommentForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
comment = cd['comment']
comment = bleach.clean(comment)
if isinstance(comment,unicode):
comment = comment.encode('utf8')
elif isinstance(comment, str):
comment = comment.decode('utf8')
notification = cd['notification']
c = WorkoutComment(workout=w,user=request.user,comment=comment,
notification=notification)
c.save()
url = reverse(workout_comment_view,
kwargs={
'id':id
})
message = '{name} says: <a href="{url}">{comment}</a>'.format(
name = request.user.first_name,
comment = comment,
url = url,
)
if request.user != r.user:
a_messages.info(r.user,message.encode('ascii','ignore'))
res = myqueue(queuehigh,
handle_sendemailnewcomment,r.user.first_name,
r.user.last_name,
r.user.email,
request.user.first_name,
request.user.last_name,
comment,w.name,w.id,
emailbounced = r.emailbounced
)
commenters = {oc.user for oc in comments if oc.notification}
for u in commenters:
a_messages.info(u,message)
if u != request.user and u != r.user:
ocr = Rower.objects.get(user=u)
res = myqueue(queuelow,
handle_sendemailnewresponse,
u.first_name,
u.last_name,
u.email,
request.user.first_name,
request.user.last_name,
comment,
w.name,
w.id,
c.id,
emailbounced = ocr.emailbounced
)
url = reverse(workout_comment_view,kwargs = {
'id':id})
return HttpResponseRedirect(url)
form = WorkoutCommentForm()
g = GraphImage.objects.filter(workout=w).order_by("-creationdatetime")
for i in g:
try:
width,height = Image.open(i.filename).size
i.width = width
i.height = height
i.save()
except:
pass
rower = getrower(request.user)
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,id),
'name': w.name
},
{
'url':reverse(workout_comment_view,kwargs={'id':id}),
'name': 'Comments'
}
]
return render(request,
'workout_comments.html',
{'workout':w,
'rower':rower,
'breadcrumbs':breadcrumbs,
'active':'nav-workouts',
'teams':get_my_teams(request.user),
'graphs':g,
'comments':comments,
'form':form,
})
# for ajax calls
def course_map_view(request,id=0):
try:
course = GeoCourse.objects.get(id=id)
except GeoCourse.DoesNotExist:
return Http404("Course doesn't exist")
script,div = course_map(course)
breadcrumbs = [
{
'url': reverse(virtualevents_view),
'name': 'Racing'
},
{
'url': reverse(courses_view),
'name': 'Courses'
},
{
'url': reverse(course_view,kwargs={'id':course.id}),
'name': course.name
},
{
'url': reverse(course_map_view,kwargs={'id':course.id}),
'name': 'Map'
}
]
r = getrower(request.user)
return render(request,
'coursemap.html',
{
'mapdiv':div,
'course':course,
'mapscript':script,
'active':'nav-racing',
'rower':r,
'breadcrumbs':breadcrumbs,
})
@login_required()
def course_replace_view(request,id=0):
try:
course = GeoCourse.objects.get(id=id)
except GeoCourse.DoesNotExist:
return Http404("Course doesn't exist")
r = getrower(request.user)
if course.manager != r:
raise PermissionDenied("Access denied")
thecourses = GeoCourse.objects.filter(manager=r).exclude(id=id)
if request.method == 'POST':
form = CourseSelectForm(request.POST)
if form.is_valid():
course2 = form.cleaned_data['course']
res = courses.replacecourse(course,course2)
url = reverse(course_view,
kwargs = {
'id':course2.id
})
return HttpResponseRedirect(url)
else:
form = CourseSelectForm()
form.fields["course"].queryset = thecourses
script,div = course_map(course)
breadcrumbs = [
{
'url': reverse(virtualevents_view),
'name': 'Racing'
},
{
'url': reverse(courses_view),
'name': 'Courses'
},
{
'url': reverse(course_view,kwargs={'id':course.id}),
'name': course.name
},
{
'url': reverse(course_replace_view,kwargs={'id':course.id}),
'name': 'Replace Markers'
}
]
return render(request,
'course_replace.html',
{'course':course,
'active':'nav-racing',
'breadcrumbs':breadcrumbs,
'rower':r,
'mapdiv':div,
'mapscript':script,
'form':form})
@login_required()
def course_delete_view(request,id=0):
try:
course = GeoCourse.objects.get(id=id)
except GeoCourse.DoesNotExist:
return Http404("Course doesn't exist")
r = getrower(request.user)
if course.manager != r:
raise PermissionDenied("Access denied")
ps = PlannedSession.objects.filter(course=course)
nosessions = len(ps) == 0
if nosessions:
course.delete()
url = reverse(courses_view)
return HttpResponseRedirect(url)
@login_required()
def course_edit_view(request,id=0):
try:
course = GeoCourse.objects.get(id=id)
except GeoCourse.DoesNotExist:
return Http404("Course doesn't exist")
r = getrower(request.user)
if course.manager != r:
raise PermissionDenied("Access denied")
ps = PlannedSession.objects.filter(course=course)
nosessions = len(ps) == 0
script,div = course_map(course)
if request.method == 'POST':
form = GeoCourseEditForm(request.POST)
if form.is_valid():
name = form.cleaned_data['name']
country = form.cleaned_data['country']
notes = form.cleaned_data['notes']
if isinstance(name,unicode):
name = name.encode('utf8')
elif isinstance(name, str):
name = name.decode('utf8')
course.name = name
course.country = country
course.notes = notes
course.save()
form = GeoCourseEditForm(instance=course)
breadcrumbs = [
{
'url': reverse(virtualevents_view),
'name': 'Racing'
},
{
'url': reverse(courses_view),
'name': 'Courses'
},
{
'url': reverse(course_view,kwargs={'id':course.id}),
'name': course.name
},
{
'url': reverse(course_edit_view,kwargs={'id':course.id}),
'name': 'Edit'
}
]
return render(request, 'course_edit_view.html',
{
'course':course,
'active':'nav-racing',
'breadcrumbs':breadcrumbs,
'mapscript':script,
'mapdiv':div,
'nosessions':nosessions,
'rower':r,
'form':form,
}
)
@login_required()
def course_view(request,id=0):
try:
course = GeoCourse.objects.get(id=id)
except GeoCourse.DoesNotExist:
return Http404("Course doesn't exist")
r = getrower(request.user)
script,div = course_map(course)
breadcrumbs = [
{
'url': reverse(virtualevents_view),
'name': 'Racing'
},
{
'url': reverse(courses_view),
'name': 'Courses'
},
{
'url': reverse(course_view,kwargs={'id':course.id}),
'name': course.name
},
]
return render(request, 'course_view.html',
{
'active':'nav-racing',
'breadcrumbs':breadcrumbs,
'course':course,
'mapscript':script,
'mapdiv':div,
'nosessions':False,
'rower':r,
}
)
# The basic edit page
@login_required()
def workout_edit_view(request,id=0,message="",successmessage=""):
request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE
request.session['referer'] = absolute(request)['PATH']
row = get_workout(id)
indoorraces = get_indoorraces(row)
if (checkworkoutuser(request.user,row)==False):
raise PermissionDenied("Access denied")
form = WorkoutForm(instance=row)
if request.method == 'POST':
# Form was submitted
form = WorkoutForm(request.POST,instance=row)
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']
weightcategory = form.cleaned_data['weightcategory']
duration = form.cleaned_data['duration']
distance = form.cleaned_data['distance']
private = form.cleaned_data['private']
notes = form.cleaned_data['notes']
thetimezone = form.cleaned_data['timezone']
try:
ps = form.cleaned_data['plannedsession']
except KeyError:
ps = None
try:
boattype = request.POST['boattype']
except KeyError:
boattype = Workout.objects.get(id=id).boattype
try:
privacy = request.POST['privacy']
except KeyError:
privacy = Workout.objects.get(id=id).privacy
try:
rankingpiece = form.cleaned_data['rankingpiece']
except KeyError:
rankingpiece =- Workout.objects.get(id=id).rankingpiece
try:
duplicate = form.cleaned_data['duplicate']
except KeyError:
duplicate = Workout.objects.get(id=id).duplicate
if private:
privacy = 'private'
else:
privacy = 'visible'
startdatetime = datetime.datetime.combine(
date,starttime
)
try:
startdatetime = pytz.timezone(thetimezone).localize(
startdatetime
)
except UnknownTimeZoneError:
pass
try:
# aware object can be in any timezone
out = startdatetime.astimezone(pytz.utc)
except (ValueError, TypeError):
startdatetime = timezone.make_aware(startdatetime)
try:
startdatetime = startdatetime.astimezone(pytz.timezone(thetimezone))
except UnknownTimeZoneError:
thetimezone = 'UTC'
row.name = name
row.date = date
row.starttime = starttime
row.startdatetime = startdatetime
row.workouttype = workouttype
row.weightcategory = weightcategory
row.notes = notes
row.duration = duration
row.distance = distance
row.boattype = boattype
row.duplicate = duplicate
row.privacy = privacy
row.rankingpiece = rankingpiece
row.timezone = thetimezone
row.plannedsession = ps
try:
row.save()
except IntegrityError:
pass
if ps:
add_workouts_plannedsession([row],ps,row.user)
# change data in csv file
r = rdata(row.csvfilename)
if r == 0:
return HttpResponse("Error: CSV Data File Not Found")
r.rowdatetime = startdatetime
r.write_csv(row.csvfilename,gzip=True)
dataprep.update_strokedata(id,r.df)
successmessage = "Changes saved"
if rankingpiece:
dataprep.runcpupdate(row.user,type=row.workouttype)
messages.info(request,successmessage)
url = reverse(workout_edit_view,
kwargs = {
'id':str(row.id),
})
response = HttpResponseRedirect(url)
#else: # form not POSTed
form = WorkoutForm(instance=row)
row = get_workout(id)
g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime")
for i in g:
try:
width,height = Image.open(i.filename).size
i.width = width
i.height = height
i.save()
except:
pass
# create interactive plot
f1 = row.csvfilename
u = row.user.user
r = getrower(u)
rowdata = rdata(f1)
hascoordinates = 1
if rowdata != 0:
try:
latitude = rowdata.df[' latitude']
longitude = rowdata.df[' longitude']
if not latitude.std():
hascoordinates = 0
if not longitude.std():
hascoordinates = 0
except (KeyError,AttributeError):
hascoordinates = 0
else:
hascoordinates = 0
mapscript = ""
mapdiv = ""
if hascoordinates:
try:
mapscript,mapdiv = leaflet_chart(
rowdata.df[' latitude'],
rowdata.df[' longitude'],
row.name)
except KeyError:
pass
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,id),
'name': row.name
},
{
'url':reverse(workout_edit_view,kwargs={'id':id}),
'name': 'Edit'
}
]
r = getrower(request.user)
# render page
return render(request, 'workout_form.html',
{'form':form,
'workout':row,
'teams':get_my_teams(request.user),
'graphs':g,
'breadcrumbs':breadcrumbs,
'rower':r,
'indoorraces':indoorraces,
'active':'nav-workouts',
'mapscript':mapscript,
'mapdiv':mapdiv,
'rower':r,
})
@login_required()
def workout_map_view(request,id=0):
request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE
request.session['referer'] = absolute(request)['PATH']
w = get_workout(id)
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,id),
'name': w.name
},
{
'url':reverse(workout_map_view,kwargs={'id':id}),
'name': 'Map'
}
]
# create interactive plot
f1 = w.csvfilename
u = w.user.user
r = getrower(u)
rowdata = rdata(f1)
hascoordinates = 1
if rowdata != 0:
try:
latitude = rowdata.df[' latitude']
if not latitude.std():
hascoordinates = 0
except KeyError,AttributeError:
hascoordinates = 0
else:
hascoordinates = 0
if hascoordinates:
mapscript,mapdiv = leaflet_chart2(rowdata.df[' latitude'],
rowdata.df[' longitude'],
w.name)
else:
mapscript = ""
mapdiv = ""
mayedit=0
if not request.user.is_anonymous():
r = getrower(request.user)
result = request.user.is_authenticated() and ispromember(request.user)
if result:
promember=1
if request.user == w.user.user:
mayedit=1
return render(request, 'map_view.html',
{'mapscript':mapscript,
'workout':w,
'rower':r,
'breadcrumbs':breadcrumbs,
'active':'nav-workouts',
'mapdiv':mapdiv,
'mayedit':mayedit,
})
@login_required()
def logo_delete_view(request,id=0):
try:
logo = RaceLogo.objects.get(id=id)
except RaceLogo.DoesNotExist:
raise Http404("Logo doesn't exist")
if logo.user == request.user:
logo.delete()
messages.info(request,"Logo Deleted")
url = reverse(virtualevents_view)
return HttpResponseRedirect(url)
@login_required()
def virtualevent_setlogo_view(request,id=0,logoid=0):
try:
race = VirtualRace.objects.get(id=id)
except VirtualRace.DoesNotExist:
raise Http404("Race doesn't exist")
try:
logo = RaceLogo.objects.get(id=logoid)
except RaceLogo.DoesNotExist:
raise Http404("Logo doesn't exist")
if logo.user == request.user and race.manager == request.user:
otherlogos = race.logos.all()
for otherlogo in otherlogos:
otherlogo.race.remove(race)
logo.race.add(race)
logo.save()
else:
message = "You do not own this race or this image"
messages.error(request,message)
url = reverse(virtualevent_view,
kwargs={'id':id})
return HttpResponseRedirect(url)
# Image upload to virtual event
@login_required()
def virtualevent_uploadimage_view(request,id=0):
is_ajax = False
if request.is_ajax():
is_ajax = True
r = getrower(request.user)
try:
race = VirtualRace.objects.get(id=id)
except VirtualRace.DoesNotExist:
raise Http404("Race doesn't exist")
logos = RaceLogo.objects.filter(user=request.user).order_by("-creationdatetime")
breadcrumbs = [
{
'url': reverse(virtualevents_view),
'name': 'Racing'
},
{
'url': reverse(virtualevent_view,kwargs={'id':id}),
'name': race.name
},
{
'url': reverse(virtualevent_uploadimage_view,
kwargs={'id':id}),
'name': 'Add Image'
}
]
if request.method == 'POST':
if len(logos) >= 6:
messages.error(request,"You cannot have more than 6 logos")
url = reverse(virtualevent_imageupload_view,
kwargs={'id':id})
return HttpResponseRedirect(url)
form = ImageForm(request.POST, request.FILES)
if form.is_valid():
f = form.cleaned_data['file']
if f is not None:
filename, path_and_filename = handle_uploaded_image(f)
try:
width, height = Image.open(path_and_filename).size
except:
message = "Not a valid image"
messages.error(request,message)
os.remove(path_and_filename)
url = reverse(virtualevent_image_view,
kwargs={'id':id})
if is_ajax:
return JSONResponse({'result':0, 'url':0})
else:
return HttpResponseRedirect(url)
otherlogos = race.logos.all()
for logo in otherlogos:
logo.race.remove(race)
logo = RaceLogo(user = request.user,
creationdatetime=timezone.now(),
filename = path_and_filename,
width=width, height=height)
logo.save()
logo.race.add(race)
logo.save()
url = reverse(virtualevent_view,
kwargs={'id':id})
if is_ajax:
return JSONResponse({'result':1, 'url':url})
else:
return HttpResponseRedirect(url)
else:
messages.error(request,"Something went wrong - no file attached")
url = reverse(virtualevent_uploadimage_view,
kwargs = {'id':id})
if is_ajax:
return JSONResponse({'result':0,'url':1})
else:
return HttpResponseRedirect(url)
else:
form = ImageForm()
if is_ajax:
return {'result':0}
return render(request,'logo_form.html',
{'form':form,
'rower':r,
'logos':logos,
'active':'nav-racing',
'breadcrumbs':breadcrumbs,
'race':race,
})
# Image upload
@login_required()
def workout_uploadimage_view(request,id):
is_ajax = False
if request.is_ajax():
is_ajax = True
r = getrower(request.user)
w = get_workout(id)
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,id),
'name': w.name
},
{
'url':reverse(workout_uploadimage_view,kwargs={'id':id}),
'name': 'Upload Image'
}
]
if not checkworkoutuser(request.user,w):
raise PermissionDenied("You are not allowed to edit this workout")
images = GraphImage.objects.filter(workout=w)
if len(images) >= 6:
message = "You have reached the maximum number of static images for this workout"
messages.error(request,message)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':int(id),
})
return HttpResponseRedirect(url)
if request.method == 'POST':
form = ImageForm(request.POST,request.FILES)
if form.is_valid():
f = form.cleaned_data['file']
if f is not None:
filename,path_and_filename = handle_uploaded_image(f)
try:
width,height = Image.open(path_and_filename).size
except:
message = "Not a valid image"
messages.error(request,message)
os.remove(path_and_filename)
url = reverse(workout_uploadimage_view,
kwargs = {'id':id})
if is_ajax:
return JSONResponse({'result':0,'url':0})
else:
return HttpResponseRedirect(url)
i = GraphImage(workout=w,
creationdatetime=timezone.now(),
filename = path_and_filename,
width=width,height=height)
i.save()
url = reverse(r.defaultlandingpage,
kwargs = {'id':id})
if is_ajax:
return JSONResponse({'result':1,'url':url})
else:
return HttpResponseRedirect(url)
else:
messages.error(request,'Something went wrong - no file attached')
url = reverse(workout_uploadimage_view,
kwargs = {'id':id})
if is_ajax:
return JSONResponse({'result':0,'url':0})
else:
return HttpResponseRedirect(url)
else:
return HttpResponse("Form is not valid")
else:
if not is_ajax:
form = ImageForm()
return render(request,'image_form.html',
{'form':form,
'rower':r,
'active':'nav-workouts',
'breadcrumbs':breadcrumbs,
'teams':get_my_teams(request.user),
'workout': w,
})
else:
return {'result':0}
# Image upload
@login_required()
def course_upload_view(request):
is_ajax = False
if request.is_ajax():
is_ajax = True
r = getrower(request.user)
if request.method == 'POST':
form = CourseForm(request.POST,request.FILES)
if form.is_valid():
f = form.cleaned_data['file']
name = form.cleaned_data['name']
notes = form.cleaned_data['notes']
if f is not None:
filename,path_and_filename = handle_uploaded_file(f)
cs = courses.kmltocourse(path_and_filename)
for course in cs:
cname = name+' - '+course['name']
cnotes = notes+'\n\n'+course['description']
polygons = course['polygons']
course = courses.createcourse(r,cname,polygons,notes=cnotes)
os.remove(path_and_filename)
url = reverse(courses_view)
if is_ajax:
return JSONResponse({'result':1,'url':url})
else:
return HttpResponseRedirect(url)
else:
messages.error(request,'Something went wrong - no file attached')
url = reverse(course_upload_view)
if is_ajax:
return JSONResponse({'result':0,'url':0})
else:
return HttpResponseRedirect(url)
else:
messages.error(request,'Form is not valid')
return render(request,'course_form.html',
{'form':form,
})
else:
if not is_ajax:
form = CourseForm()
return render(request,'course_form.html',
{'form':form,
'active':'nav-racing',
})
else:
return {'result':0}
# Generic chart creation
@login_required()
def workout_add_chart_view(request,id,plotnr=1):
w = get_workout(id)
r = getrower(request.user)
plotnr = int(plotnr)
if (checkworkoutuser(request.user,w)==False):
raise PermissionDenied("You are not allowed add plots to this workout")
else:
f1 = w.csvfilename[6:-4]
timestr = strftime("%Y%m%d-%H%M%S")
imagename = f1+timestr+'.png'
u = w.user.user
r = getrower(u)
title = w.name
res,jobid = uploads.make_plot(
r,w,f1,w.csvfilename,'timeplot',title,plotnr=plotnr,
imagename=imagename
)
if res == 0:
messages.error(request,jobid)
else:
try:
request.session['async_tasks'] += [(jobid,'make_plot')]
except KeyError:
request.session['async_tasks'] = [(jobid,'make_plot')]
url = reverse(r.defaultlandingpage,kwargs={'id':str(w.id)})
return HttpResponseRedirect(url)
# The page where you select which Strava workout to import
@login_required()
def workout_stravaimport_view(request,message="",userid=0):
try:
thetoken = strava_open(request.user)
except NoTokenError:
return HttpResponseRedirect("/rowers/me/stravaauthorize/")
res = stravastuff.get_strava_workout_list(request.user)
r = getrequestrower(request,userid=userid)
if r.user != request.user:
messages.info(request,"You cannot import other people's workouts from Concept2")
r = getrower(request.user)
if (res.status_code != 200):
if (res.status_code == 401):
r = getrower(request.user)
if (r.stravatoken == '') or (r.stravatoken is None):
s = "Token doesn't exist. Need to authorize"
return HttpResponseRedirect("/rowers/me/stravaauthorize/")
message = "Something went wrong in workout_stravaimport_view"
messages.error(request,message)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
else:
workouts = []
r = getrower(request.user)
stravaids = [int(item['id']) for item in res.json()]
stravadata = [{
'id':int(item['id']),
'elapsed_time':item['elapsed_time'],
'start_date':item['start_date'],
} for item in res.json()]
wfailed = Workout.objects.filter(user=r,uploadedtostrava=-1)
for w in wfailed:
for item in stravadata:
elapsed_time = item['elapsed_time']
start_date = item['start_date']
stravaid = item['id']
if arrow.get(start_date) == arrow.get(w.startdatetime):
elapsed_td = datetime.timedelta(seconds=int(elapsed_time))
elapsed_time = datetime.datetime.strptime(
str(elapsed_td),
"%H:%M:%S"
)
if str(elapsed_time)[-7:] == str(w.duration)[-7:]:
w.uploadedtostrava = int(stravaid)
w.save()
knownstravaids = uniqify([
w.uploadedtostrava for w in Workout.objects.filter(user=r)
])
newids = [stravaid for stravaid in stravaids if not stravaid in knownstravaids]
for item in res.json():
d = int(float(item['distance']))
i = item['id']
if i in knownstravaids:
nnn = ''
else:
nnn = 'NEW'
n = item['name']
ttot = str(datetime.timedelta(seconds=int(float(item['elapsed_time']))))
s = item['start_date']
r = item['type']
keys = ['id','distance','duration','starttime','type','name','new']
values = [i,d,ttot,s,r,n,nnn]
res = dict(zip(keys,values))
workouts.append(res)
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':reverse(workout_stravaimport_view),
'name':'Strava'
},
]
r = getrower(request.user)
return render(request,'strava_list_import.html',
{'workouts':workouts,
'rower':r,
'active':'nav-workouts',
'breadcrumbs':breadcrumbs,
'teams':get_my_teams(request.user),
})
return HttpResponse(res)
# The page where you select which RunKeeper workout to import
@login_required()
def workout_runkeeperimport_view(request,message="",userid=0):
res = runkeeperstuff.get_runkeeper_workout_list(request.user)
if (res.status_code != 200):
if (res.status_code == 401):
r = getrower(request.user)
if (r.runkeepertoken == '') or (r.runkeepertoken is None):
s = "Token doesn't exist. Need to authorize"
return HttpResponseRedirect("/rowers/me/runkeeperauthorize/")
message = "Something went wrong in workout_runkeeperimport_view"
messages.error(request,message)
if settings.DEBUG:
return HttpResponse(res)
else:
url = reverse(workouts_view)
return HttpResponseRedirect(url)
workouts = []
for item in res.json()['items']:
d = int(float(item['total_distance']))
i = getidfromuri(item['uri'])
ttot = str(datetime.timedelta(seconds=int(float(item['duration']))))
s = item['start_time']
r = item['type']
keys = ['id','distance','duration','starttime','type']
values = [i,d,ttot,s,r]
res = dict(zip(keys,values))
workouts.append(res)
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':reverse(workout_runkeeperimport_view),
'name':'Runkeeper'
}
]
r = getrower(request.user)
return render(request,'runkeeper_list_import.html',
{'workouts':workouts,
'rower':r,
'active':'nav-workouts',
'breadcrumbs':breadcrumbs,
'teams':get_my_teams(request.user),
})
return HttpResponse(res)
# The page where you select which RunKeeper workout to import
@login_required()
def workout_underarmourimport_view(request,message="",userid=0):
res = underarmourstuff.get_underarmour_workout_list(request.user)
if (res.status_code != 200):
return HttpResponseRedirect("/rowers/me/underarmourauthorize/")
workouts = []
items = res.json()['_embedded']['workouts']
for item in items:
s = item['start_datetime']
i,r = underarmourstuff.get_idfromuri(request.user,item['_links'])
n = item['name']
try:
d = item['aggregates']['distance_total']
except KeyError:
d = 0
try:
ttot = item['aggregates']['active_time_total']
except KeyError:
ttot = 0
keys = ['id','distance','duration','starttime','type']
values = [i,d,ttot,s,r]
thedict = dict(zip(keys,values))
workouts.append(thedict)
rower = getrower(request.user)
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':reverse(workout_c2import_view),
'name':'Concept2'
},
]
return render(request,'underarmour_list_import.html',
{'workouts':workouts,
'breadcrumbs':breadcrumbs,
'rower':rower,
'active':'nav-workouts',
'teams':get_my_teams(request.user),
})
return HttpResponse(res)
# the page where you select which Polar workout to Import
@login_required()
def workout_polarimport_view(request,userid=0):
exercises = polarstuff.get_polar_workouts(request.user)
workouts = []
try:
a = exercises.status_code
if a == 401:
messages.error(request,'Not authorized. You need to connect to Polar first')
url = reverse(workouts_view)
return HttpResponseRedirect(url)
except:
pass
for exercise in exercises:
try:
d = exercise['distance']
except KeyError:
d = 0
i = exercise['id']
transactionid = exercise['transaction-id']
starttime = exercise['start-time']
rowtype = exercise['sport']
durationstring = exercise['duration']
duration = isodate.parse_duration(durationstring)
keys = ['id','distance','duration','starttime','type','transactionid']
values = [i,d,duration,starttime,rowtype,transactionid]
res = dict(zip(keys,values))
workouts.append(res)
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':reverse(workout_polarimport_view),
'name':'Polar'
},
]
r = getrower(request.user)
return render(request, 'polar_list_import.html',
{
'workouts':workouts,
'active':'nav-workouts',
'rower':r,
'breadcrumbs':breadcrumbs,
'teams':get_my_teams(request.user),
})
# The page where you select which SportTracks workout to import
@login_required()
def workout_sporttracksimport_view(request,message="",userid=0):
res = sporttracksstuff.get_sporttracks_workout_list(request.user)
if (res.status_code != 200):
if (res.status_code == 401):
r = getrower(request.user)
if (r.sporttrackstoken == '') or (r.sporttrackstoken is None):
s = "Token doesn't exist. Need to authorize"
return HttpResponseRedirect("/rowers/me/sporttracksauthorize/")
else:
return HttpResponseRedirect("/rowers/me/sporttracksrefresh/")
message = "Something went wrong in workout_sporttracksimport_view"
messages.error(request,message)
if settings.DEBUG:
return HttpResponse(res)
else:
url = reverse(workouts_view)
return HttpResponseRedirect(url)
workouts = []
r = getrower(request.user)
stids = [int(getidfromuri(item['uri'])) for item in res.json()['items']]
knownstids = uniqify([
w.uploadedtosporttracks for w in Workout.objects.filter(user=r)
])
newids = [stid for stid in stids if not stid in knownstids]
for item in res.json()['items']:
d = int(float(item['total_distance']))
i = int(getidfromuri(item['uri']))
if i in knownstids:
nnn = ''
else:
nnn = 'NEW'
n = item['name']
ttot = str(datetime.timedelta(seconds=int(float(item['duration']))))
s = item['start_time']
r = item['type']
keys = ['id','distance','duration','starttime','type','name','new']
values = [i,d,ttot,s,r,n,nnn]
res = dict(zip(keys,values))
workouts.append(res)
r = getrower(request.user)
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':reverse(workout_sporttracksimport_view),
'name':'SportTracks'
},
]
return render(request,'sporttracks_list_import.html',
{'workouts':workouts,
'breadcrumbs':breadcrumbs,
'active':'nav-workouts',
'rower':r,
'teams':get_my_teams(request.user),
})
return HttpResponse(res)
# List of workouts on Concept2 logbook. This view only used for debugging
@login_required()
def c2listdebug_view(request,page=1,message=""):
try:
thetoken = c2_open(request.user)
except NoTokenError:
return HttpResponseRedirect("/rowers/me/c2authorize/")
r = getrower(request.user)
res = c2stuff.get_c2_workout_list(request.user,page=page)
if (res.status_code != 200):
message = "Something went wrong in workout_c2import_view (C2 token renewal)"
messages.error(request,message)
if settings.DEBUG:
return HttpResponse(res)
else:
url = reverse(workouts_view)
return HttpResponseRedirect(url)
else:
workouts = []
for item in res.json()['data']:
d = item['distance']
i = item['id']
ttot = item['time_formatted']
s = item['date']
r = item['type']
s2 = item['source']
c = item['comments']
keys = ['id','distance','duration','starttime','rowtype','source','comment']
values = [i,d,ttot,s,r,s2,c]
res = dict(zip(keys,values))
workouts.append(res)
return render(request,
'c2_list_import2.html',
{'workouts':workouts,
'teams':get_my_teams(request.user),
})
# Import all unknown workouts available on Concept2 logbook
@login_required()
def workout_getc2workout_all(request,page=1,message=""):
try:
thetoken = c2_open(request.user)
except NoTokenError:
return HttpResponseRedirect("/rowers/me/c2authorize/")
res = c2stuff.get_c2_workout_list(request.user,page=page)
if (res.status_code != 200):
message = "Something went wrong in workout_c2import_view (C2 token refresh)"
messages.error(request,message)
else:
r = getrower(request.user)
c2ids = [item['id'] for item in res.json()['data']]
alldata = {}
for item in res.json()['data']:
alldata[item['id']] = item
knownc2ids = uniqify([
w.uploadedtoc2 for w in Workout.objects.filter(user=r)
])
newids = [c2id for c2id in c2ids if not c2id in knownc2ids]
for c2id in newids:
workoutid = c2stuff.create_async_workout(alldata,
request.user,c2id)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
# List of workouts available on Concept2 logbook - for import
@login_required()
def workout_c2import_view(request,page=1,userid=0,message=""):
r = getrequestrower(request,userid=userid)
if r.user != request.user:
messages.info(request,"You cannot import other people's workouts from Concept2")
r = getrower(request.user)
try:
thetoken = c2_open(request.user)
except NoTokenError:
return HttpResponseRedirect("/rowers/me/c2authorize/")
res = c2stuff.get_c2_workout_list(request.user,page=page)
if (res.status_code != 200):
message = "Something went wrong in workout_c2import_view (C2 token refresh)"
messages.error(request,message)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
workouts = []
c2ids = [item['id'] for item in res.json()['data']]
knownc2ids = uniqify([
w.uploadedtoc2 for w in Workout.objects.filter(user=r)
])
newids = [c2id for c2id in c2ids if not c2id in knownc2ids]
for item in res.json()['data']:
d = item['distance']
i = item['id']
ttot = item['time_formatted']
s = item['date']
r = item['type']
s2 = item['source']
c = item['comments']
if i in knownc2ids:
nnn = ''
else:
nnn = 'NEW'
keys = ['id','distance','duration','starttime','rowtype','source','comment','new']
values = [i,d,ttot,s,r,s2,c,nnn]
res = dict(zip(keys,values))
workouts.append(res)
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':reverse(workout_c2import_view),
'name':'Concept2'
},
{
'url':reverse(workout_c2import_view,kwargs={'page':page}),
'name':'Page '+str(page)
}
]
r = getrower(request.user)
return render(request,
'c2_list_import2.html',
{'workouts':workouts,
'rower':r,
'active':'nav-workouts',
'breadcrumbs':breadcrumbs,
'teams':get_my_teams(request.user),
'page':page,
})
importsources = {
'c2':c2stuff,
'strava':stravastuff,
'polar':polarstuff,
'ownapi':ownapistuff,
'runkeeper':runkeeperstuff,
'sporttracks':sporttracksstuff,
'trainingpeaks':tpstuff,
'underarmour':underarmourstuff
}
@login_required()
def workout_getimportview(request,externalid,source = 'c2'):
res = importsources[source].get_workout(request.user,externalid)
if not res[0]:
messages.error(request,res[1])
url = reverse(workouts_view)
return HttpResponseRedirect(url)
strokedata = res[1]
data = res[0]
# Now works only for C2
if strokedata.empty:
distance = data['distance']
c2id = data['id']
workouttype = mytypes.c2mappinginv[data['type']]
verified = data['verified']
startdatetime = iso8601.parse_date(data['date'])
weightclass = data['weight_class']
weightcategory = 'hwt'
if weightclass == "L":
weightcategory = 'lwt'
totaltime = data['time']/10.
duration = dataprep.totaltime_sec_to_string(totaltime)
duration = datetime.datetime.strptime(duration,'%H:%M:%S.%f').time()
try:
timezone_str = data['timezone']
except:
timezone_str = 'UTC'
if timezone_str is None:
timezone_str = 'UTC'
workoutdate = startdatetime.astimezone(
pytz.timezone(timezone_str)
).strftime('%Y-%m-%d')
starttime = startdatetime.astimezone(
pytz.timezone(timezone_str)
).strftime('%H:%M:%S')
r = getrower(request.user)
id, message = dataprep.create_row_df(r,
distance,
duration,
startdatetime,
workouttype=workouttype)
w = Workout.objects.get(id=id)
w.uploadedtoc2 = c2id
w.name = 'Imported from C2'
w.workouttype = workouttype
w.save()
message = "This workout does not have any stroke data associated with it. We created synthetic stroke data."
messages.info(request,message)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':int(id),
})
return HttpResponseRedirect(url)
# strokedata not empty - continue
id,message = importsources[source].add_workout_from_data(
request.user,
externalid,data,
strokedata,
source=source,
workoutsource=source)
w = get_workout(id)
if 'workout' in data:
if 'splits' in data['workout']:
splitdata = data['workout']['splits']
if 'intervals' in data['workout']:
splitdata = data['workout']['intervals']
else:
splitdata = False
# splitdata (only for C2)
if splitdata:
w.summary,sa,results = c2stuff.summaryfromsplitdata(splitdata,data,w.csvfilename)
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 source == 'strava':
w.uploadedtostrava = externalid
elif source == 'c2':
w.uploadedtoc2 = externalid
elif source == 'polar':
w.uploadedtopolar = externalid
elif source == 'runkeeper':
w.uploadedtorunkeeper = externalid
elif source == 'sporttracks':
w.uploadedtosporttracks = externalid
elif source == 'trainingpeaks':
w.uploadedtotp = externalid
elif source == 'underarmour':
w.uploadedtounderarmour = externalid
w.save()
if message:
messages.error(request,message)
r = getrower(request.user)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':int(id)
})
return HttpResponseRedirect(url)
# Imports all new workouts from SportTracks
@login_required()
def workout_getsporttracksworkout_all(request):
res = sporttracksstuff.get_sporttracks_workout_list(request.user)
if (res.status_code == 200):
r = getrower(request.user)
stids = [int(getidfromuri(item['uri'])) for item in res.json()['items']]
knownstids = uniqify([
w.uploadedtosporttracks for w in Workout.objects.filter(user=r)
])
newids = [stid for stid in stids if not stid in knownstids]
for sporttracksid in newids:
res = sporttracksstuff.get_sporttracks_workout(
request.user,sporttracksid)
data = res.json()
id,message = add_workout_from_stdata(
request.user,sporttracksid,data
)
if id==0:
messages.error(request,message)
else:
w = Workout.objects.get(id=id)
w.uploadedtosporttracks=sporttracksid
w.save()
url = reverse(workouts_view)
return HttpResponseRedirect(url)
# Imports all new workouts from SportTracks
@login_required()
def workout_getstravaworkout_all(request):
r = getrower(request.user)
res = stravastuff.get_strava_workouts(r)
if res == 1:
messages.info(request,"Your workouts are being imported and should appear on the site in the next 15 minutes")
else:
messages.error(request,"Couldn't import Strava workouts ")
url = reverse(workouts_view)
return HttpResponseRedirect(url)
# Imports all new workouts from SportTracks
@login_required()
def workout_getstravaworkout_next(request):
r = Rower.objects.get(user=request.user)
res = stravastuff.get_strava_workout_list(r.user)
if (res.status_code != 200):
return 0
else:
stravaids = [int(item['id']) for item in res.json()]
alldata = {}
for item in res.json():
alldata[item['id']] = item
knownstravaids = uniqify([
w.uploadedtostrava for w in Workout.objects.filter(user=r)
])
newids = [stravaid for stravaid in stravaids if not stravaid in knownstravaids]
theid = newids[0]
workoutid = stravastuff.create_async_workout(alldata,r.user,stravaid,debug=True)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
@login_required
def workout_toggle_ranking(request,id=0):
is_ajax = False
if request.is_ajax():
is_ajax = True
row = get_workout_permitted(request.user,id)
row.rankingpiece = not row.rankingpiece
row.save()
if is_ajax:
return JSONResponse({'result':row.rankingpiece})
else:
url = reverse(workouts_view)
response = HttpResponseRedirect(url)
return response
# This is the main view for processing uploaded files
@login_required()
def workout_upload_view(request,
uploadoptions={
'makeprivate':False,
'make_plot':False,
'upload_to_C2':False,
'plottype':'timeplot',
'landingpage':'workout_edit_view',
},
docformoptions={
'workouttype':'rower',
}):
is_ajax = False
if request.is_ajax():
is_ajax = True
r = getrower(request.user)
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url': reverse(workout_upload_view),
'name': 'Upload'
}
]
if 'uploadoptions' in request.session:
uploadoptions = request.session['uploadoptions']
try:
defaultlandingpage = uploadoptions['landingpage']
except KeyError:
uploadoptions['landingpage'] = r.defaultlandingpage
defaultlandingpage = r.defaultlandingpage
else:
request.session['uploadoptions'] = uploadoptions
if 'docformoptions' in request.session:
docformoptions = request.session['docformoptions']
else:
request.session['docformoptions'] = docformoptions
try:
makeprivate = uploadoptions['makeprivate']
except KeyError:
makeprivate = False
try:
make_plot = uploadoptions['make_plot']
except KeyError:
make_plot = False
try:
workouttype = docformoptions['workouttype']
except KeyError:
workouttype = 'rower'
try:
boattype = docformoptions['boattype']
except KeyError:
boattype = '1x'
try:
workoutsource = uploadoptions['workoutsource']
except KeyError:
workoutsource = None
try:
plottype = uploadoptions['plottype']
except KeyError:
plottype = 'timeplot'
try:
landingpage = uploadoptions['landingpage']
except KeyError:
landingpage = r.defaultlandingpage
uploadoptions['landingpage'] = landingpage
try:
upload_to_c2 = uploadoptions['upload_to_C2']
except KeyError:
upload_to_c2 = False
try:
upload_to_strava = uploadoptions['upload_to_Strava']
except KeyError:
upload_to_strava = False
try:
upload_to_st = uploadoptions['upload_to_SportTracks']
except KeyError:
upload_to_st = False
try:
upload_to_rk = uploadoptions['upload_to_RunKeeper']
except KeyError:
upload_to_rk = False
try:
upload_to_ua = uploadoptions['upload_to_MapMyFitness']
except KeyError:
upload_to_ua = False
try:
upload_to_tp = uploadoptions['upload_to_TrainingPeaks']
except KeyError:
upload_to_tp = False
response = {}
if request.method == 'POST':
form = DocumentsForm(request.POST,request.FILES)
optionsform = UploadOptionsForm(request.POST)
if form.is_valid():
# f = request.FILES['file']
f = form.cleaned_data['file']
if f is not None:
res = handle_uploaded_file(f)
else:
messages.error(request,
"Something went wrong - no file attached")
url = reverse(workout_upload_view)
if is_ajax:
return JSONResponse({'result':0,'url':0})
else:
return HttpResponseRedirect(url)
t = form.cleaned_data['title']
workouttype = form.cleaned_data['workouttype']
boattype = form.cleaned_data['boattype']
request.session['docformoptions'] = {
'workouttype':workouttype,
'boattype': boattype,
}
notes = form.cleaned_data['notes']
offline = form.cleaned_data['offline']
if optionsform.is_valid():
make_plot = optionsform.cleaned_data['make_plot']
plottype = optionsform.cleaned_data['plottype']
upload_to_c2 = optionsform.cleaned_data['upload_to_C2']
upload_to_strava = optionsform.cleaned_data['upload_to_Strava']
upload_to_st = optionsform.cleaned_data['upload_to_SportTracks']
upload_to_rk = optionsform.cleaned_data['upload_to_RunKeeper']
upload_to_ua = optionsform.cleaned_data['upload_to_MapMyFitness']
upload_to_tp = optionsform.cleaned_data['upload_to_TrainingPeaks']
makeprivate = optionsform.cleaned_data['makeprivate']
landingpage = optionsform.cleaned_data['landingpage']
uploadoptions = {
'makeprivate':makeprivate,
'make_plot':make_plot,
'plottype':plottype,
'upload_to_C2':upload_to_c2,
'upload_to_Strava':upload_to_strava,
'upload_to_SportTracks':upload_to_st,
'upload_to_RunKeeper':upload_to_rk,
'upload_to_MapMyFitness':upload_to_ua,
'upload_to_TrainingPeaks':upload_to_tp,
'landingpage':landingpage,
'boattype': boattype,
'workouttype': workouttype,
}
request.session['uploadoptions'] = uploadoptions
f1 = res[0] # file name
f2 = res[1] # file name incl media directory
if not offline:
id,message,f2 = dataprep.new_workout_from_file(
r,f2,
workouttype=workouttype,
workoutsource=workoutsource,
boattype=boattype,
makeprivate=makeprivate,
title = t,
notes=''
)
else:
workoutsbox = Mailbox.objects.filter(name='workouts')[0]
uploadoptions['fromuploadform'] = True
bodyyaml = yaml.safe_dump(
uploadoptions,
default_flow_style=False
)
msg = Message(mailbox=workoutsbox,
from_header=r.user.email,
subject = t,body=bodyyaml)
msg.save()
f3 = 'media/mailbox_attachments/'+f2[6:]
copyfile(f2,f3)
f3 = f3[6:]
a = MessageAttachment(message=msg,document=f3)
a.save()
os.remove(f2)
messages.info(
request,
"The file was too large to process in real time. It will be processed in a background process. You will receive an email when it is ready")
url = reverse(workout_upload_view)
if is_ajax:
return JSONResponse({'result':1,'url':url})
else:
response = HttpResponseRedirect(url)
return response
if not id:
messages.error(request,message)
url = reverse(workout_upload_view)
if is_ajax:
return JSONResponse({'result':0,'url':url})
else:
response = HttpResponseRedirect(url)
return response
elif id == -1:
message = 'The zip archive will be processed in the background. The files in the archive will only be uploaded without the extra actions. You will receive email when the workouts are ready.'
messages.info(request,message)
url = reverse(workout_upload_view)
if is_ajax:
return JSONResponse({'result':1,'url':url})
else:
response = HttpResponseRedirect(url)
return response
else:
if message:
messages.error(request,message)
url = reverse(workout_edit_view,
kwargs = {
'id':int(id),
})
if is_ajax:
response = {'result': 1,'url':url}
else:
response = HttpResponseRedirect(url)
w = Workout.objects.get(id=id)
r = getrower(request.user)
if (make_plot):
res,jobid = uploads.make_plot(r,w,f1,f2,plottype,t)
if res == 0:
messages.error(request,jobid)
else:
try:
request.session['async_tasks'] += [(jobid,'make_plot')]
except KeyError:
request.session['async_tasks'] = [(jobid,'make_plot')]
# upload to C2
if (upload_to_c2):
try:
message,id = c2stuff.workout_c2_upload(request.user,w)
except NoTokenError:
id = 0
message = "Something went wrong with the Concept2 sync"
if id>1:
messages.info(request,message)
else:
messages.error(request,message)
if (upload_to_strava):
try:
message,id = stravastuff.workout_strava_upload(
request.user,w
)
except NoTokenError:
id = 0
message = "Please connect to Strava first"
if id>1:
messages.info(request,message)
else:
messages.error(request,message)
if (upload_to_st):
try:
message,id = sporttracksstuff.workout_sporttracks_upload(
request.user,w
)
except NoTokenError:
message = "Please connect to SportTracks first"
id = 0
if id>1:
messages.info(request,message)
else:
messages.error(request,message)
if (upload_to_rk):
try:
message,id = runkeeperstuff.workout_runkeeper_upload(
request.user,w
)
except NoTokenError:
message = "Please connect to Runkeeper first"
id = 0
if id>1:
messages.info(request,message)
else:
messages.error(request,message)
if (upload_to_ua):
try:
message,id = underarmourstuff.workout_ua_upload(
request.user,w
)
except NoTokenError:
message = "Please connect to MapMyFitness first"
id = 0
if id>1:
messages.info(request,message)
else:
messages.error(request,message)
if (upload_to_tp):
try:
message,id = tpstuff.workout_tp_upload(
request.user,w
)
except NoTokenError:
message = "Please connect to TrainingPeaks first"
id = 0
if id>1:
messages.info(request,message)
else:
messages.error(request,message)
if landingpage != 'workout_upload_view':
url = reverse(landingpage,
kwargs = {
'id':w.id,
})
else:
url = reverse(landingpage)
if is_ajax:
response = {'result':1,'url':url}
else:
response = HttpResponseRedirect(url)
else:
if not is_ajax:
response = render(request,
'document_form.html',
{'form':form,
'teams':get_my_teams(request.user),
'optionsform': optionsform,
})
if is_ajax:
return JSONResponse(response)
else:
return response
else:
if not is_ajax:
if r.c2_auto_export and isprorower(r):
uploadoptions['upload_to_C2'] = True
if r.strava_auto_export and isprorower(r):
uploadoptions['upload_to_Strava'] = True
if r.sporttracks_auto_export and isprorower(r):
uploadoptions['upload_to_SportTracks'] = True
if r.runkeeper_auto_export and isprorower(r):
uploadoptions['upload_to_RunKeeper'] = True
if r.trainingpeaks_auto_export and isprorower(r):
uploadoptions['upload_to_TrainingPeaks'] = True
if r.mapmyfitness_auto_export and isprorower(r):
uploadoptions['upload_to_MapMyFitness'] = True
form = DocumentsForm(initial=docformoptions)
optionsform = UploadOptionsForm(initial=uploadoptions)
return render(request, 'document_form.html',
{'form':form,
'active':'nav-workouts',
'breadcrumbs':breadcrumbs,
'teams':get_my_teams(request.user),
'optionsform': optionsform,
})
else:
return {'result':0}
# This is the main view for processing uploaded files
@user_passes_test(iscoachmember,login_url="/rowers/promembership",redirect_field_name=None)
def team_workout_upload_view(request,message="",
successmessage="",
uploadoptions={
'make_plot':False,
'plottype':'timeplot',
}):
if 'uploadoptions' in request.session:
uploadoptions = request.session['uploadoptions']
else:
request.session['uploadoptions'] = uploadoptions
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url': reverse(team_workout_upload_view),
'name': 'Team Upload'
}
]
myteams = Team.objects.filter(manager=request.user)
make_plot = uploadoptions['make_plot']
plottype = uploadoptions['plottype']
r = getrower(request.user)
if request.method == 'POST':
form = DocumentsForm(request.POST,request.FILES)
optionsform = TeamUploadOptionsForm(request.POST)
rowerform = TeamInviteForm(request.POST)
rowerform.fields.pop('email')
rowerform.fields['user'].queryset = User.objects.filter(rower__isnull=False,rower__team__in=myteams).distinct()
if form.is_valid():
f = request.FILES['file']
res = handle_uploaded_file(f)
t = form.cleaned_data['title']
offline = form.cleaned_data['offline']
boattype = form.cleaned_data['boattype']
if rowerform.is_valid():
u = rowerform.cleaned_data['user']
if u:
r = getrower(u)
else:
message = 'Please select a rower'
messages.error(request,message)
messages.info(request,successmessage)
response = render(request,
'team_document_form.html',
{'form':form,
'teams':get_my_teams(request.user),
'optionsform': optionsform,
'rowerform': rowerform,
})
return response
workouttype = form.cleaned_data['workouttype']
notes = form.cleaned_data['notes']
if optionsform.is_valid():
make_plot = optionsform.cleaned_data['make_plot']
plottype = optionsform.cleaned_data['plottype']
uploadoptions = {
'makeprivate':False,
'make_plot':make_plot,
'plottype':plottype,
'upload_to_C2':False,
}
request.session['uploadoptions'] = uploadoptions
f1 = res[0] # file name
f2 = res[1] # file name incl media directory
if not offline:
id,message,f2 = dataprep.new_workout_from_file(
r,f2,
workouttype=workouttype,
boattype=boattype,
makeprivate=False,
title = t,
notes=''
)
else:
job = myqueue(
queuehigh,
handle_zip_file,
r.user.email,
t,
f2,
emailbounced = r.emailbounced
)
messages.info(
request,
"The file was too large to process in real time. It will be processed in a background process. The user will receive an email when it is ready"
)
url = reverse(team_workout_upload_view)
response = HttpResponseRedirect(url)
return response
if not id:
messages.error(request,message)
url = reverse(team_workout_upload_view)
response = HttpResponseRedirect(url)
return response
elif id == -1:
message = 'The zip archive will be processed in the background. The files in the archive will only be uploaded without the extra actions. You will receive email when the workouts are ready.'
messages.info(request,message)
url = reverse(team_workout_upload_view)
response = HttpResponseRedirect(url)
return response
else:
successmessage = "The workout was added to the user's account"
messages.info(request,successmessage)
url = reverse(team_workout_upload_view)
response = HttpResponseRedirect(url)
w = Workout.objects.get(id=id)
r = getrower(request.user)
if (make_plot):
id,jobid = uploads.make_plot(r,w,f1,f2,plottype,t)
else:
response = render(request,
'team_document_form.html',
{'form':form,
'teams':get_my_teams(request.user),
'active': 'nav-workouts',
'breadcrumbs':breadcrumbs,
'optionsform': optionsform,
'rowerform': rowerform,
})
return response
else:
form = DocumentsForm()
optionsform = TeamUploadOptionsForm(initial=uploadoptions)
rowerform = TeamInviteForm()
rowerform.fields.pop('email')
rowerform.fields['user'].queryset = User.objects.filter(rower__isnull=False,rower__team__in=myteams).distinct()
return render(request, 'team_document_form.html',
{'form':form,
'teams':get_my_teams(request.user),
'optionsform': optionsform,
'active': 'nav-workouts',
'breadcrumbs':breadcrumbs,
'rower':r,
'rowerform':rowerform,
})
# Ask the user if he really wants to delete the workout
@login_required()
def workout_delete_confirm_view(request, id=0):
try:
row = Workout.objects.get(id=id)
if (checkworkoutuser(request.user,row)==False):
raise PermissionDenied("You are not allowed to delete this workout")
else:
url = request.META.get('HTTP_REFERER','/')
return render(request,'workout_delete_confirm.html',
{'id':int(id),
'teams':get_my_teams(request.user),
'workout':row,
'url':url})
except Workout.DoesNotExist:
raise Http404("Workout doesn't exist")
# Really deleting the workout
@login_required()
def workout_delete_view(request,id=0):
try:
row = Workout.objects.get(id=id)
if (checkworkoutuser(request.user,row)==False):
raise PermissionDenied("You are not allowed to delete this workout")
else:
# files are removed by pre-delete in models.py
row.delete()
messages.info(request,'Workout deleted')
try:
url = request.session['referer']
if 'rowers/workout/' in url:
url = reverse(workouts_view)
except KeyError:
url = reverse(workouts_view)
return HttpResponseRedirect(url)
except Workout.DoesNotExist:
raise Http404("Workout doesn't exist")
# Ask the user to confirm that he wants to delete a chart
@login_required()
def graph_delete_confirm_view(request, id=0):
try:
img = GraphImage.objects.get(id=id)
row = Workout.objects.get(id=img.workout.id)
if (checkworkoutuser(request.user,row)==False):
raise PermissionDenied("You are not allowed to delete this workout")
else:
try:
url = request.session['referer']
except KeyError:
url = '/rowers/list-graphs'
request.session['referer'] = url
return render(request,'graphimage_delete_confirm.html',
{'id':int(id),
'teams':get_my_teams(request.user),
'graph':img,
'url':url})
except Workout.DoesNotExist:
raise Http404("Workout doesn't exist")
except GraphImage.DoesNotExist:
raise Http404("The image doesn't exist")
# Really deleting the chart
@login_required()
def graph_delete_view(request,id=0):
try:
img = GraphImage.objects.get(id=id)
row = Workout.objects.get(id=img.workout.id)
if (checkworkoutuser(request.user,row)==False):
raise PermissionDenied("You are not allowed to delete this graph")
else:
img.delete()
messages.info(request,'Graph deleted')
try:
url = request.session['referer']
except KeyError:
url = reverse(graphs_view)
return HttpResponseRedirect(url)
except GraphImage.DoesNotExist:
raise Http404("Graph Image doesn't exist")
except Workout.DoesNotExist:
raise Http404("Workout doesn't exist")
# A page with all the recent graphs (searchable on workout name)
@login_required()
def graphs_view(request):
request.session['referer'] = reverse(graphs_view)
try:
r = getrower(request.user)
workouts = Workout.objects.filter(user=r).order_by("-date", "-starttime")
query = request.GET.get('q')
if query:
query_list = query.split()
workouts = workouts.filter(
reduce(operator.and_,
(Q(name__icontains=q) for q in query_list)) |
reduce(operator.and_,
(Q(notes__icontains=q) for q in query_list))
)
g = GraphImage.objects.filter(workout__in=workouts).order_by("-creationdatetime")
paginator = Paginator(g,8)
page = request.GET.get('page')
try:
g = paginator.page(page)
except PageNotAnInteger:
g = paginator.page(1)
except EmptyPage:
g = paginator.page(paginator.num_pages)
return render(request, 'list_graphs.html',
{'graphs': g,
'active':'nav-workouts',
'teams':get_my_teams(request.user),
})
except Rower.DoesNotExist:
raise Http404("User has no rower instance")
# Show the chart (png image)
def graph_show_view(request,id):
try:
g = GraphImage.objects.get(id=id)
try:
width,height = Image.open(g.filename).size
g.width = width
g.height = height
g.save()
except:
pass
w = Workout.objects.get(id=g.workout.id)
r = Rower.objects.get(id=w.user.id)
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,w.id),
'name': w.name
},
{
'url':reverse(graph_show_view,kwargs={'id':id}),
'name': 'Chart'
}
]
return render(request,'show_graph.html',
{'graph':g,
'teams':get_my_teams(request.user),
'workout':w,
'breadcrumbs':breadcrumbs,
'active':'nav-workouts',
'rower':r,})
except GraphImage.DoesNotExist:
raise Http404("This graph doesn't exist")
except Workout.DoesNotExist:
raise Http404("This workout doesn't exist")
# Restore original stroke data and summary
@login_required()
def workout_summary_restore_view(request,id,message="",successmessage=""):
try:
row = Workout.objects.get(id=id)
if (checkworkoutuser(request.user,row)==False):
raise PermissionDenied("You are not allowed to edit this workout")
except Workout.DoesNotExist:
raise Http404("Workout doesn't exist")
s = ""
# still here - this is a workout we may edit
f1 = row.csvfilename
u = row.user.user
r = getrower(u)
powerperc = 100*np.array([r.pw_ut2,
r.pw_ut1,
r.pw_at,
r.pw_tr,r.pw_an])/r.ftp
ftp = float(r.ftp)
if row.workouttype in mytypes.otwtypes:
ftp = ftp*(100.-r.otwslack)/100.
rr = rrower(hrmax=r.max,hrut2=r.ut2,
hrut1=r.ut1,hrat=r.at,
hrtr=r.tr,hran=r.an,ftp=ftp,
powerperc=powerperc,powerzones=r.powerzones)
rowdata = rdata(f1,rower=rr)
if rowdata == 0:
raise Http404("Error: CSV Data File Not Found")
rowdata.restoreintervaldata()
rowdata.write_csv(f1,gzip=True)
dataprep.update_strokedata(id,rowdata.df)
intervalstats = rowdata.allstats()
row.summary = intervalstats
row.save()
# create interactive plot
try:
res = interactive_chart(id,promember=1)
script = res[0]
div = res[1]
except ValueError:
pass
messages.info(request,'Original Interval Data Restored')
url = reverse(workout_summary_edit_view,
kwargs={
'id':int(id),
}
)
return HttpResponseRedirect(url)
# Split a workout
@user_passes_test(ispromember,login_url="/rowers/promembership",message="This functionality requires a Pro plan or higher",redirect_field_name=None)
def workout_split_view(request,id=id):
row = get_workout_permitted(request.user,id)
r = row.user
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,row.id),
'name': row.name
},
{
'url':reverse(graph_show_view,kwargs={'id':id}),
'name': 'Chart'
}
]
if request.method == 'POST':
form = WorkoutSplitForm(request.POST)
if form.is_valid():
splittime = form.cleaned_data['splittime']
splitsecond = splittime.hour*3600
splitsecond += splittime.minute*60
splitsecond += splittime.second
splitsecond += splittime.microsecond/1.e6
splitmode = form.cleaned_data['splitmode']
try:
ids,mesgs = dataprep.split_workout(
r,row,splitsecond,splitmode
)
except IndexError:
messages.error(request,"Something went wrong in Split")
for message in mesgs:
messages.info(request,message)
if request.user == r:
url = reverse(workouts_view)
else:
mgrids = [team.id for team in Team.objects.filter(manager=request.user)]
rwrids = [team.id for team in r.team.all()]
teamids = list(set(mgrids) & set(rwrids))
if len(teamids) > 0:
teamid = teamids[0]
url = reverse(workouts_view,
kwargs={
'teamid':int(teamid),
}
)
else:
url = reverse(workouts_view)
rowname = row.name
if isinstance(rowname,unicode):
rowname = rowname.encode('utf8')
elif isinstance(rowname, str):
rowname = rowname.decode('utf8')
qdict = {'q':rowname}
url+='?'+urllib.urlencode(qdict)
return HttpResponseRedirect(url)
form = WorkoutSplitForm()
# create interactive plot
try:
res = interactive_chart(id,promember=1)
script = res[0]
div = res[1]
except ValueError:
pass
return render(request, 'splitworkout.html',
{'form':form,
'rower':r,
'breadcrumbs':breadcrumbs,
'active':'nav-workouts',
'workout':row,
'thediv':script,
'thescript':div,
})
# Fuse two workouts
@user_passes_test(ispromember,login_url="/rowers/promembership",message="This functionality requires a Pro plan or higher",redirect_field_name=None)
def workout_fusion_view(request,id1=0,id2=1):
r = getrower(request.user)
try:
w1 = Workout.objects.get(id=id1)
w2 = Workout.objects.get(id=id2)
r = w1.user
if (checkworkoutuser(request.user,w1)==False) or \
(checkworkoutuser(request.user,w2)==False):
raise PermissionDenied("You are not allowed to use these workouts")
except Workout.DoesNotExist:
raise Http404("One of the workouts doesn't exist")
if request.method == 'POST':
form = FusionMetricChoiceForm(request.POST,instance=w2)
if form.is_valid():
cd = form.cleaned_data
columns = cd['columns']
timeoffset = cd['offset']
posneg = cd['posneg']
if posneg == 'neg':
timeoffset = -timeoffset
# Create DataFrame
df,forceunit = dataprep.datafusion(id1,id2,columns,timeoffset)
idnew,message = dataprep.new_workout_from_df(r,df,
title='Fused data',
parent=w1,
forceunit=forceunit)
if message != None:
messages.error(request,message)
else:
successmessage = 'Data fused'
messages.info(request,message)
url = reverse(workout_edit_view,
kwargs={
'id':idnew,
})
return HttpResponseRedirect(url)
else:
form = FusionMetricChoiceForm(instance=w2)
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,w1.id),
'name': str(w1.id)
},
{
'url':reverse(workout_fusion_list,kwargs={'id':id1}),
'name': 'Sensor Fusion'
},
{
'url':reverse(workout_fusion_view,kwargs={'id1':id1,'id2':id2}),
'name': str(w2.id)
}
]
return render(request, 'fusion.html',
{'form':form,
'teams':get_my_teams(request.user),
'workout':w1,
'rower':r,
'breadcrumbs':breadcrumbs,
'active':'nav-workouts',
'workout1':w1,
'workout2':w2,
})
# Edit the splits/summary
@login_required()
def workout_summary_edit_view(request,id,message="",successmessage=""
):
row = get_workout_permitted(request.user,id)
r = getrower(request.user)
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':get_workout_default_page(request,row.id),
'name': row.name
},
{
'url':reverse(workout_summary_edit_view,kwargs={'id':id}),
'name': 'Edit Intervals'
}
]
s = ""
# still here - this is a workout we may edit
f1 = row.csvfilename
u = row.user.user
r = getrower(u)
powerperc = 100*np.array([r.pw_ut2,
r.pw_ut1,
r.pw_at,
r.pw_tr,r.pw_an])/r.ftp
ftp = float(r.ftp)
if row.workouttype in mytypes.otwtypes:
ftp = ftp*(100.-r.otwslack)/100.
rr = rrower(hrmax=r.max,hrut2=r.ut2,
hrut1=r.ut1,hrat=r.at,
hrtr=r.tr,hran=r.an,ftp=ftp,
powerperc=powerperc,powerzones=r.powerzones)
rowdata = rdata(f1,rower=rr)
if rowdata == 0:
return HttpResponse("Error: CSV Data File Not Found")
intervalstats = rowdata.allstats()
try:
itime,idist,itype = rowdata.intervalstats_values()
except TypeError:
return HttpResponse("Error: CSV Data File Not Found")
nrintervals = len(idist)
savebutton = 'nosavebutton'
formvalues = {}
form = SummaryStringForm()
tss,normp = dataprep.workout_rscore(row)
normv,normw = dataprep.workout_normv(row,pp=8.0)
work = int(normw)
power = int(normp)
try:
pace_secs = int(500./normv)
except (OverflowError, ZeroDivisionError):
pace_secs = 140.
try:
avpace = datetime.timedelta(seconds=int(500./normv))
except (OverflowError, ZeroDivisionError):
avpace = datetime.timedelta(seconds=130)
data = {
'power': int(normp),
'pace': avpace,
'selector': 'power',
'work': int(normw)
}
powerorpace = 'power'
if normp == 0:
data['selector'] = 'pace'
powerorpace = 'pace'
powerupdateform = PowerIntervalUpdateForm(initial=data)
# We have submitted the mini language interpreter
if request.method == 'POST' and "intervalstring" in request.POST:
form = SummaryStringForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
s = cd["intervalstring"]
try:
rowdata.updateinterval_string(s)
except ParseException,err:
messages.error(request,'Parsing error in column '+str(err.col))
intervalstats = rowdata.allstats()
itime,idist,itype = rowdata.intervalstats_values()
nrintervals = len(idist)
savebutton = 'savestringform'
powerupdateform = PowerIntervalUpdateForm(initial=data)
# we are saving the results obtained from the split by power/pace interpreter
elif request.method == 'POST' and "savepowerpaceform" in request.POST:
powerorpace = request.POST['powerorpace']
value_pace = request.POST['value_pace']
value_power = request.POST['value_power']
value_work = request.POST['value_work']
if powerorpace == 'power':
power = int(value_power)
elif powerorpace == 'pace':
try:
pace_secs = float(value_pace)
except ValueError:
pace_secs = float(value_pace.replace(',','.'))
elif powerorpace == 'work':
work = int(value_work)
if powerorpace == 'power' and power is not None:
try:
rowdata.updateinterval_metric(
' Power (watts)',power,mode='larger',
debug=False,smoothwindow=15.)
except:
messages.error(request,'Error updating power')
elif powerorpace == 'pace':
try:
velo = 500./pace_secs
rowdata.updateinterval_metric(
' AverageBoatSpeed (m/s)',velo,mode='larger',
debug=False,smoothwindow=15.)
except:
messages.error(request,'Error updating pace')
elif powerorpace == 'work':
try:
rowdata.updateinterval_metric(
'driveenergy',work,mode='larger',
debug=False,smoothwindow=15.)
except:
messages.error(request,'Error updating Work per Stroke')
intervalstats = rowdata.allstats()
itime,idist,itype = rowdata.intervalstats_values()
nrintervals = len(idist)
row.summary = intervalstats
row.save()
rowdata.write_csv(f1,gzip=True)
dataprep.update_strokedata(id,rowdata.df)
messages.info(request,"Updated interval data saved")
data = {
'power': power,
'pace': datetime.timedelta(seconds=int(pace_secs)),
'work': work,
'selector': powerorpace
}
form = SummaryStringForm()
powerupdateform = PowerIntervalUpdateForm(initial=data)
savebutton = 'savepowerpaceform'
formvalues = {
'powerorpace': powerorpace,
'value_power': power,
'value_pace': pace_secs,
'value_work':work,
}
# we are saving the results obtained from the mini language interpreter
elif request.method == 'POST' and "savestringform" in request.POST:
s = request.POST["savestringform"]
try:
rowdata.updateinterval_string(s)
except ParseException,err:
messages.error(request,'Parsing error in column '+str(err.col))
intervalstats = rowdata.allstats()
itime,idist,itype = rowdata.intervalstats_values()
nrintervals = len(idist)
row.summary = intervalstats
#intervalstats = rowdata.allstats()
if s:
try:
row.notes = u'{n} \n {s}'.format(
n = row.notes,
s = s
)
except TypeError:
pass
row.save()
rowdata.write_csv(f1,gzip=True)
dataprep.update_strokedata(id,rowdata.df)
messages.info(request,"Updated interval data saved")
data = {'intervalstring':s}
form = SummaryStringForm(initial=data)
powerupdateform = PowerIntervalUpdateForm(initial={
'power': int(normp),
'pace': avpace,
'selector': 'power',
'work': int(normw)
})
savebutton = 'savestringform'
# we are saving the results obtained from the power update form
elif request.method == 'POST' and 'selector' in request.POST:
powerupdateform = PowerIntervalUpdateForm(request.POST)
if powerupdateform.is_valid():
cd = powerupdateform.cleaned_data
powerorpace = cd['selector']
power = cd['power']
pace = cd['pace']
work = cd['work']
try:
pace_secs = pace.seconds+pace.microseconds/1.0e6
except AttributeError:
pace_secs = 120.
if powerorpace == 'power' and power is not None:
try:
rowdata.updateinterval_metric(' Power (watts)',power,mode='larger',
debug=False,smoothwindow=15)
except:
messages.error(request,'Error updating power')
elif powerorpace == 'pace':
try:
velo = 500./pace_secs
rowdata.updateinterval_metric(' AverageBoatSpeed (m/s)',velo,mode='larger',
debug=False,smoothwindow=15)
except:
messages.error(request,'Error updating pace')
elif powerorpace == 'work':
try:
rowdata.updateinterval_metric(
'driveenergy',work,mode='larger',
debug=False,smoothwindow=15.)
except:
messages.error(request,'Error updating Work per Stroke')
intervalstats = rowdata.allstats()
itime,idist,itype = rowdata.intervalstats_values()
nrintervals = len(idist)
savebutton = 'savepowerpaceform'
formvalues = {
'powerorpace': powerorpace,
'value_power': power,
'value_pace': pace_secs,
'value_work': work,
}
powerupdateform = PowerIntervalUpdateForm(initial=cd)
form = SummaryStringForm()
form = SummaryStringForm()
# we are saving the results obtained from the detailed form
elif request.method == 'POST' and "savedetailform" in request.POST:
savebutton = 'savedetailform'
form = SummaryStringForm()
nrintervals = int(request.POST['nrintervals'])
detailform = IntervalUpdateForm(request.POST,aantal=nrintervals)
itime = []
idist = []
itype = []
ivalues = []
iunits = []
itypes = []
iresults = []
for i in xrange(nrintervals):
try:
t = datetime.datetime.strptime(request.POST['intervalt_%s' % i],"%H:%M:%S.%f")
except ValueError:
t = datetime.datetime.strptime(request.POST['intervalt_%s' % i],"%H:%M:%S")
timesecs = 3600*t.hour+60*t.minute+t.second+t.microsecond/1.e6
itime += [timesecs]
idist += [int(request.POST['intervald_%s' % i])]
itype += [int(request.POST['type_%s' % i])]
if itype[i] == 3: # rest
itypes += ['rest']
ivalues += [timesecs]
iresults += [idist[i]]
iunits += ['seconds']
if itype[i] == 5 or itype[i] == 2: # distance based work
itypes += ['work']
ivalues += [idist[i]]
iresults += [timesecs]
iunits += ['meters']
if itype[i] == 4 or itype[i] == 1: # time based work
itypes += ['work']
ivalues += [timesecs]
iresults += [idist[i]]
iunits += ['seconds']
rowdata.updateintervaldata(ivalues,iunits,itypes,iresults=iresults)
intervalstats = rowdata.allstats()
row.summary = intervalstats
try:
row.notes += "\n"+s
except TypeError:
pass
row.save()
rowdata.write_csv(f1,gzip=True)
dataprep.update_strokedata(id,rowdata.df)
messages.info(request,"Updated interval data saved")
form = SummaryStringForm()
powerupdateform = PowerIntervalUpdateForm(initial={
'power': int(normp),
'pace': avpace,
'selector': 'power',
'work': int(normw)
})
# we are processing the details form
elif request.method == 'POST' and "nrintervals" in request.POST:
savebutton = 'savedetailform'
nrintervals = int(request.POST['nrintervals'])
detailform = IntervalUpdateForm(request.POST,aantal=nrintervals)
if detailform.is_valid():
cd = detailform.cleaned_data
itime = []
idist = []
itype = []
ivalues = []
iunits = []
itypes = []
iresults = []
for i in xrange(nrintervals):
t = cd['intervalt_%s' % i]
timesecs = t.total_seconds()
itime += [timesecs]
idist += [cd['intervald_%s' % i]]
itype += [cd['type_%s' % i]]
if itype[i] == '3': # rest
itypes += ['rest']
ivalues += [timesecs]
iresults += [idist[i]]
iunits += ['seconds']
if itype[i] == '5' or itype[i] == '2': # distance based work
itypes += ['work']
ivalues += [idist[i]]
iresults += [timesecs]
iunits += ['meters']
if itype[i] == '4' or itype[i] == '1': # time based work
itypes += ['work']
ivalues += [timesecs]
iresults += [idist[i]]
iunits += ['seconds']
rowdata.updateintervaldata(ivalues,iunits,
itypes,iresults=iresults)
intervalstats = rowdata.allstats()
form = SummaryStringForm()
powerupdateform = PowerIntervalUpdateForm()
initial = {}
for i in xrange(nrintervals):
try:
initial['intervald_%s' % i] = idist[i]
initial['intervalt_%s' % i] = get_time(itime[i])
initial['type_%s' % i] = itype[i]
except IndexError:
pass
detailform = IntervalUpdateForm(aantal=nrintervals,initial=initial)
# create interactive plot
try:
intervaldata = {
'itime':itime,
'idist':idist,
'itype':itype,
'selector': powerorpace,
'normp': normp,
'normv': normv,
}
res = interactive_chart(id,promember=1,intervaldata=intervaldata)
script = res[0]
div = res[1]
except ValueError:
pass
# render page
return render(request, 'summary_edit.html',
{'form':form,
'detailform':detailform,
'powerupdateform':powerupdateform,
'workout':row,
'rower':r,
'breadcrumbs':breadcrumbs,
'active':'nav-workouts',
'teams':get_my_teams(request.user),
'intervalstats':intervalstats,
'nrintervals':nrintervals,
'interactiveplot':script,
'the_div':div,
'intervalstring':s,
'savebutton':savebutton,
'formvalues':formvalues,
})
# Page where user can manage his favorite charts
@login_required()
def rower_favoritecharts_view(request,userid=0):
message = ''
successmessage = ''
r = getrequestrower(request,userid=userid,notpermanent=True)
favorites = FavoriteChart.objects.filter(user=r).order_by('id')
aantal = len(favorites)
favorites_data = [{'yparam1':f.yparam1,
'yparam2':f.yparam2,
'xparam':f.xparam,
'plottype':f.plottype,
'workouttype':f.workouttype,
'reststrokes':f.reststrokes,
'notes':f.notes,}
for f in favorites]
FavoriteChartFormSet = formset_factory(FavoriteForm,formset=BaseFavoriteFormSet,extra=0)
if aantal==0:
FavoriteChartFormSet = formset_factory(FavoriteForm,formset=BaseFavoriteFormSet,extra=1)
if request.method == 'POST':
favorites_formset = FavoriteChartFormSet(request.POST)
if favorites_formset.is_valid():
new_instances = []
for favorites_form in favorites_formset:
yparam1 = favorites_form.cleaned_data.get('yparam1')
yparam2 = favorites_form.cleaned_data.get('yparam2')
xparam = favorites_form.cleaned_data.get('xparam')
plottype = favorites_form.cleaned_data.get('plottype')
workouttype = favorites_form.cleaned_data.get('workouttype')
reststrokes = favorites_form.cleaned_data.get('reststrokes')
notes = favorites_form.cleaned_data.get('notes')
new_instances.append(FavoriteChart(user=r,
yparam1=yparam1,
yparam2=yparam2,
xparam=xparam,
plottype=plottype,
notes=notes,
workouttype=workouttype,
reststrokes=reststrokes))
try:
with transaction.atomic():
FavoriteChart.objects.filter(user=r).delete()
FavoriteChart.objects.bulk_create(new_instances)
successmessage = "You have updated your favorites"
messages.info(request,message)
if len(new_instances)==0:
FavoriteChartFormSet=formset_factory(FavoriteForm,formset=BaseFavoriteFormSet,extra=1)
favorites_formset = FavoriteChartFormSet()
except IntegrityError:
message = "something went wrong"
messages.error(request,message)
else:
favorites_formset = FavoriteChartFormSet(initial=favorites_data)
context = {
'favorites_formset':favorites_formset,
'teams':get_my_teams(request.user),
'rower':r,
}
return render(request,'favoritecharts.html',context)
# page where user sets his export settings
@login_required()
def rower_exportsettings_view(request,userid=0):
r = getrequestrower(request,userid=userid)
if request.method == 'POST':
form = RowerExportForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
for attr, value in cd.items():
setattr(r, attr, value)
r.save()
messages.info(request,'Settings saved')
else:
form = RowerExportForm(instance=r)
breadcrumbs = [
{
'url':'/rowers/me',
'name': 'Profile'
},
{
'url': reverse(rower_exportsettings_view),
'name': 'Export Settings'
}
]
return render(request, 'rower_exportsettings.html',
{'form':form,
'rower':r,
'breadcrumbs': breadcrumbs,
})
# Page where user can set his details
# Add email address to form so user can change his email address
@login_required()
def rower_edit_view(request,rowerid=0,userid=0,message=""):
r = getrequestrower(request,rowerid=rowerid,userid=userid,notpermanent=True)
rowerid = r.id
breadcrumbs = [
{
'url':'/rowers/me/edit',
'name': 'Profile'
},
{
'url': reverse(rower_edit_view),
'name': 'Account Settings'
}
]
if request.method == 'POST' and "ut2" in request.POST:
form = RowerForm(request.POST)
if form.is_valid():
# something
cd = form.cleaned_data
hrmax = cd['max']
ut2 = cd['ut2']
ut1 = cd['ut1']
at = cd['at']
tr = cd['tr']
an = cd['an']
rest = cd['rest']
try:
r.max = max(min(hrmax,250),10)
r.ut2 = max(min(ut2,250),10)
r.ut1 = max(min(ut1,250),10)
r.at = max(min(at,250),10)
r.tr = max(min(tr,250),10)
r.an = max(min(an,250),10)
r.rest = max(min(rest,250),10)
r.save()
successmessage = "Your Heart Rate data were changed"
messages.info(request,successmessage)
form = RowerForm(instance=r)
powerform = RowerPowerForm(instance=r)
powerzonesform = RowerPowerZonesForm(instance=r)
accountform = AccountRowerForm(instance=r)
userform = UserForm(instance=r.user)
return render(request, 'rower_form.html',
{'form':form,
'powerzonesform':powerzonesform,
'teams':get_my_teams(request.user),
'powerform':powerform,
'rower':r,
'breadcrumbs':breadcrumbs,
'accountform':accountform,
'userform':userform,
})
except Rower.DoesNotExist:
message = "Funny. This user doesn't exist."
messages.error(request,message)
url = reverse(workouts_view)
response = HttpResponseRedirect(url)
else:
message = HttpResponse("invalid form")
#form = RowerForm(instance=r)
powerform = RowerPowerForm(instance=r)
powerzonesform = RowerPowerZonesForm(instance=r)
userform = UserForm(instance=r.user)
accountform = AccountRowerForm(instance=r)
return render(request, 'rower_form.html',
{'form':form,
'teams':get_my_teams(request.user),
'breadcrumbs':breadcrumbs,
'powerzonesform':powerzonesform,
'userform':userform,
'accountform':accountform,
'powerform':powerform,
'rower':r,
})
return response
elif request.method == 'POST' and "ftp" in request.POST:
print request.POST
powerform = RowerPowerForm(request.POST)
if powerform.is_valid():
cd = powerform.cleaned_data
hrftp = cd['hrftp']
if hrftp == 0:
hrftp = int((r.an+r.tr)/2.)
ftp = cd['ftp']
otwslack = cd['otwslack']
try:
powerfrac = 100*np.array([r.pw_ut2,
r.pw_ut1,
r.pw_at,
r.pw_tr,r.pw_an])/r.ftp
r.ftp = max(min(ftp,650),50)
r.otwslack = max(min(otwslack,50),0)
ut2,ut1,at,tr,an = (r.ftp*powerfrac/100.).astype(int)
r.pw_ut2 = ut2
r.pw_ut1 = ut1
r.pw_at = at
r.pw_tr = tr
r.pw_an = an
r.hrftp = hrftp
r.save()
message = "FTP and/or OTW slack values changed."
messages.info(request,message)
url = reverse(rower_edit_view,
kwargs = {
'userid':r.user.id,
})
response = HttpResponseRedirect(url)
except Rower.DoesNotExist:
message = "Funny. This user doesn't exist."
messages.error(request,message)
url = reverse(rower_edit_view)
response = HttpResponseRedirect(url)
else:
message = HttpResponse("invalid form")
form = RowerForm(instance=r)
#powerform = RowerPowerForm(instance=r)
powerzonesform = RowerPowerZonesForm(instance=r)
userform = UserForm(instance=r.user)
accountform = AccountRowerForm(instance=r)
return render(request, 'rower_form.html',
{'form':form,
'teams':get_my_teams(request.user),
'powerform':powerform,
'rower':r,
'breadcrumbs':breadcrumbs,
'userform':userform,
'accountform':accountform,
})
return response
elif request.method == 'POST' and "ut3name" in request.POST:
powerzonesform = RowerPowerZonesForm(request.POST)
if powerzonesform.is_valid():
cd = powerzonesform.cleaned_data
pw_ut2 = cd['pw_ut2']
pw_ut1 = cd['pw_ut1']
pw_at = cd['pw_at']
pw_tr = cd['pw_tr']
pw_an = cd['pw_an']
ut3name = cd['ut3name']
ut2name = cd['ut2name']
ut1name = cd['ut1name']
atname = cd['atname']
trname = cd['trname']
anname = cd['anname']
powerzones = [ut3name,ut2name,ut1name,atname,trname,anname]
try:
r.pw_ut2 = pw_ut2
r.pw_ut1 = pw_ut1
r.pw_at = pw_at
r.pw_tr = pw_tr
r.pw_an = pw_an
r.powerzones = powerzones
r.save()
successmessage = "Your Power Zone data were changed"
messages.info(request,successmessage)
form = RowerForm(instance=r)
accountform = AccountRowerForm(instance=r)
userform = UserForm(instance=r.user)
powerform = RowerPowerForm(instance=r)
powerzonesform = RowerPowerZonesForm(instance=r)
return render(request, 'rower_form.html',
{'form':form,
'teams':get_my_teams(request.user),
'powerzonesform':powerzonesform,
'powerform':powerform,
'breadcrumbs':breadcrumbs,
'userform':userform,
'accountform':accountform,
'rower':r,
})
except Rower.DoesNotExist:
message = "Funny. This user doesn't exist."
messages.error(request,message)
url = reverse(workouts_view)
response = HttpResponseRedirect(url)
return response
else:
form = RowerForm(instance=r)
powerform = RowerPowerForm(instance=r)
accountform = AccountRowerForm(instance=r)
userform = UserForm(instance=r.user)
#powerzonesform = RowerPowerZonesForm(instance=r)
message = HttpResponse("invalid form")
return render(request, 'rower_form.html',
{'form':form,
'teams':get_my_teams(request.user),
'powerform':powerform,
'powerzonesform':powerzonesform,
'breadcrumbs':breadcrumbs,
'accountform':accountform,
'userform':userform,
'rower':r,
})
elif request.method == 'POST' and "weightcategory" in request.POST:
accountform = AccountRowerForm(request.POST)
userform = UserForm(request.POST,instance=r.user)
if accountform.is_valid() and userform.is_valid():
# process
cd = accountform.cleaned_data
ucd = userform.cleaned_data
first_name = ucd['first_name']
last_name = ucd['last_name']
email = ucd['email']
sex = cd['sex']
defaultlandingpage = cd['defaultlandingpage']
weightcategory = cd['weightcategory']
birthdate = cd['birthdate']
showfavoritechartnotes = cd['showfavoritechartnotes']
getemailnotifications = cd['getemailnotifications']
getimportantemails = cd['getimportantemails']
defaulttimezone=cd['defaulttimezone']
u = r.user
if u.email != email and len(email):
resetbounce = True
else:
resetbounce = False
if len(first_name):
u.first_name = first_name
u.last_name = last_name
if len(email): ## and check_email_freeforuse(u,email):
u.email = email
resetbounce = True
u.save()
r.defaulttimezone=defaulttimezone
r.weightcategory = weightcategory
r.getemailnotifications = getemailnotifications
r.getimportantemails = getimportantemails
r.defaultlandingpage = defaultlandingpage
r.showfavoritechartnotes = showfavoritechartnotes
r.sex = sex
r.birthdate = birthdate
if resetbounce and r.emailbounced:
r.emailbounced = False
r.save()
form = RowerForm(instance=r)
powerform = RowerPowerForm(instance=r)
powerzonesform = RowerPowerZonesForm(instance=r)
accountform = AccountRowerForm(instance=r)
userform = UserForm(instance=u)
successmessage = 'Account Information changed'
messages.info(request,successmessage)
return render(request, 'rower_form.html',
{'form':form,
'teams':get_my_teams(request.user),
'powerzonesform':powerzonesform,
'breadcrumbs':breadcrumbs,
'powerform':powerform,
'accountform':accountform,
'userform':userform,
'rower':r,
})
else:
form = RowerForm(instance=r)
powerform = RowerPowerForm(instance=r)
powerzonesform = RowerPowerZonesForm(instance=r)
return render(request, 'rower_form.html',
{'form':form,
'teams':get_my_teams(request.user),
'powerzonesform':powerzonesform,
'breadcrumbs':breadcrumbs,
'powerform':powerform,
'accountform':accountform,
'userform':userform,
'rower':r,
})
else:
try:
form = RowerForm(instance=r)
powerform = RowerPowerForm(instance=r)
powerzonesform = RowerPowerZonesForm(instance=r)
accountform = AccountRowerForm(instance=r)
userform = UserForm(instance=r.user)
grants = AccessToken.objects.filter(user=request.user)
return render(request, 'rower_form.html',
{
'form':form,
'teams':get_my_teams(request.user),
'powerform':powerform,
'breadcrumbs':breadcrumbs,
'powerzonesform':powerzonesform,
'userform':userform,
'accountform':accountform,
'grants':grants,
'rower':r,
})
except Rower.DoesNotExist:
raise Http404("This user doesn't exist")
# Page where user can set his details
# Add email address to form so user can change his email address
@login_required()
def rower_prefs_view(request,userid=0,message=""):
r = getrequestrower(request,userid=userid,notpermanent=True)
rowerid = r.id
breadcrumbs = [
{
'url':'/rowers/me/edit',
'name': 'Profile'
},
{
'url': reverse(rower_prefs_view),
'name': 'Zones'
}
]
if request.method == 'POST' and "ut2" in request.POST:
form = RowerForm(request.POST)
if form.is_valid():
# something
cd = form.cleaned_data
hrmax = cd['max']
ut2 = cd['ut2']
ut1 = cd['ut1']
at = cd['at']
tr = cd['tr']
an = cd['an']
rest = cd['rest']
try:
r.max = max(min(hrmax,250),10)
r.ut2 = max(min(ut2,250),10)
r.ut1 = max(min(ut1,250),10)
r.at = max(min(at,250),10)
r.tr = max(min(tr,250),10)
r.an = max(min(an,250),10)
r.rest = max(min(rest,250),10)
r.save()
successmessage = "Your Heart Rate data were changed"
messages.info(request,successmessage)
form = RowerForm(instance=r)
powerform = RowerPowerForm(instance=r)
powerzonesform = RowerPowerZonesForm(instance=r)
accountform = AccountRowerForm(instance=r)
userform = UserForm(instance=r.user)
return render(request, 'rower_preferences.html',
{'form':form,
'powerzonesform':powerzonesform,
'teams':get_my_teams(request.user),
'breadcrumbs':breadcrumbs,
'powerform':powerform,
'rower':r,
'accountform':accountform,
'userform':userform,
})
except Rower.DoesNotExist:
message = "Funny. This user doesn't exist."
messages.error(request,message)
url = reverse(workouts_view)
response = HttpResponseRedirect(url)
else:
message = HttpResponse("invalid form")
#form = RowerForm(instance=r)
powerform = RowerPowerForm(instance=r)
powerzonesform = RowerPowerZonesForm(instance=r)
userform = UserForm(instance=r.user)
accountform = AccountRowerForm(instance=r)
return render(request, 'rower_preferences.html',
{'form':form,
'teams':get_my_teams(request.user),
'powerzonesform':powerzonesform,
'breadcrumbs':breadcrumbs,
'userform':userform,
'accountform':accountform,
'powerform':powerform,
'rower':r,
})
return response
elif request.method == 'POST' and "ftp" in request.POST:
powerform = RowerPowerForm(request.POST)
if powerform.is_valid():
cd = powerform.cleaned_data
hrftp = cd['hrftp']
if hrftp == 0:
hrftp = int((r.an+r.tr)/2.)
ftp = cd['ftp']
otwslack = cd['otwslack']
try:
powerfrac = 100*np.array([r.pw_ut2,
r.pw_ut1,
r.pw_at,
r.pw_tr,r.pw_an])/r.ftp
r.ftp = max(min(ftp,650),50)
r.otwslack = max(min(otwslack,50),0)
ut2,ut1,at,tr,an = (r.ftp*powerfrac/100.).astype(int)
r.pw_ut2 = ut2
r.pw_ut1 = ut1
r.pw_at = at
r.pw_tr = tr
r.pw_an = an
r.hrftp = hrftp
r.save()
message = "FTP and/or OTW slack values changed."
messages.info(request,message)
url = reverse(rower_prefs_view,
kwargs = {
'userid':r.user.id,
})
response = HttpResponseRedirect(url)
except Rower.DoesNotExist:
message = "Funny. This user doesn't exist."
messages.error(request,message)
url = reverse(rower_edit_view)
response = HttpResponseRedirect(url)
else:
message = HttpResponse("invalid form")
form = RowerForm(instance=r)
#powerform = RowerPowerForm(instance=r)
powerzonesform = RowerPowerZonesForm(instance=r)
userform = UserForm(instance=r.user)
accountform = AccountRowerForm(instance=r)
return render(request, 'rower_preferences.html',
{'form':form,
'teams':get_my_teams(request.user),
'breadcrumbs':breadcrumbs,
'powerform':powerform,
'rower':r,
'userform':userform,
'accountform':accountform,
})
return response
elif request.method == 'POST' and "ut3name" in request.POST:
powerzonesform = RowerPowerZonesForm(request.POST)
if powerzonesform.is_valid():
cd = powerzonesform.cleaned_data
pw_ut2 = cd['pw_ut2']
pw_ut1 = cd['pw_ut1']
pw_at = cd['pw_at']
pw_tr = cd['pw_tr']
pw_an = cd['pw_an']
ut3name = cd['ut3name']
ut2name = cd['ut2name']
ut1name = cd['ut1name']
atname = cd['atname']
trname = cd['trname']
anname = cd['anname']
powerzones = [ut3name,ut2name,ut1name,atname,trname,anname]
try:
r.pw_ut2 = pw_ut2
r.pw_ut1 = pw_ut1
r.pw_at = pw_at
r.pw_tr = pw_tr
r.pw_an = pw_an
r.powerzones = powerzones
r.save()
successmessage = "Your Power Zone data were changed"
messages.info(request,successmessage)
form = RowerForm(instance=r)
accountform = AccountRowerForm(instance=r)
userform = UserForm(instance=r.user)
powerform = RowerPowerForm(instance=r)
powerzonesform = RowerPowerZonesForm(instance=r)
return render(request, 'rower_preferences.html',
{'form':form,
'teams':get_my_teams(request.user),
'powerzonesform':powerzonesform,
'powerform':powerform,
'userform':userform,
'breadcrumbs':breadcrumbs,
'accountform':accountform,
'rower':r,
})
except Rower.DoesNotExist:
message = "Funny. This user doesn't exist."
messages.error(request,message)
url = reverse(workouts_view)
response = HttpResponseRedirect(url)
return response
else:
form = RowerForm(instance=r)
powerform = RowerPowerForm(instance=r)
accountform = AccountRowerForm(instance=r)
userform = UserForm(instance=r.user)
#powerzonesform = RowerPowerZonesForm(instance=r)
message = HttpResponse("invalid form")
return render(request, 'rower_preferences.html',
{'form':form,
'teams':get_my_teams(request.user),
'powerform':powerform,
'breadcrumbs':breadcrumbs,
'powerzonesform':powerzonesform,
'accountform':accountform,
'userform':userform,
'rower':r,
})
elif request.method == 'POST' and "weightcategory" in request.POST:
accountform = AccountRowerForm(request.POST)
userform = UserForm(request.POST,instance=r.user)
if accountform.is_valid() and userform.is_valid():
# process
cd = accountform.cleaned_data
ucd = userform.cleaned_data
first_name = ucd['first_name']
last_name = ucd['last_name']
email = ucd['email']
sex = cd['sex']
defaultlandingpage = cd['defaultlandingpage']
weightcategory = cd['weightcategory']
birthdate = cd['birthdate']
showfavoritechartnotes = cd['showfavoritechartnotes']
getemailnotifications = cd['getemailnotifications']
getimportantemails = cd['getimportantemails']
defaulttimezone=cd['defaulttimezone']
u = r.user
if u.email != email and len(email):
resetbounce = True
else:
resetbounce = False
if len(first_name):
u.first_name = first_name
u.last_name = last_name
if len(email): ## and check_email_freeforuse(u,email):
u.email = email
resetbounce = True
u.save()
r.defaulttimezone=defaulttimezone
r.weightcategory = weightcategory
r.getemailnotifications = getemailnotifications
r.getimportantemails = getimportantemails
r.defaultlandingpage = defaultlandingpage
r.showfavoritechartnotes = showfavoritechartnotes
r.sex = sex
r.birthdate = birthdate
if resetbounce and r.emailbounced:
r.emailbounced = False
r.save()
form = RowerForm(instance=r)
powerform = RowerPowerForm(instance=r)
powerzonesform = RowerPowerZonesForm(instance=r)
accountform = AccountRowerForm(instance=r)
userform = UserForm(instance=u)
successmessage = 'Account Information changed'
messages.info(request,successmessage)
return render(request, 'rower_preferences.html',
{'form':form,
'teams':get_my_teams(request.user),
'powerzonesform':powerzonesform,
'breadcrumbs':breadcrumbs,
'powerform':powerform,
'accountform':accountform,
'userform':userform,
'rower':r,
})
else:
form = RowerForm(instance=r)
powerform = RowerPowerForm(instance=r)
powerzonesform = RowerPowerZonesForm(instance=r)
return render(request, 'rower_preferences.html',
{'form':form,
'teams':get_my_teams(request.user),
'powerzonesform':powerzonesform,
'powerform':powerform,
'breadcrumbs':breadcrumbs,
'accountform':accountform,
'userform':userform,
'rower':r,
})
else:
try:
form = RowerForm(instance=r)
powerform = RowerPowerForm(instance=r)
powerzonesform = RowerPowerZonesForm(instance=r)
accountform = AccountRowerForm(instance=r)
userform = UserForm(instance=r.user)
grants = AccessToken.objects.filter(user=request.user)
return render(request, 'rower_preferences.html',
{
'form':form,
'teams':get_my_teams(request.user),
'powerform':powerform,
'powerzonesform':powerzonesform,
'breadcrumbs':breadcrumbs,
'userform':userform,
'accountform':accountform,
'grants':grants,
'rower':r,
})
except Rower.DoesNotExist:
raise Http404("This user doesn't exist")
# Revoke an app that you granted access through the API.
# this views is called when you press a button on the User edit page
# the button is only there when you have granted access to an app
@login_required()
def rower_revokeapp_view(request,id=0):
try:
tokens = AccessToken.objects.filter(user=request.user,application=id)
refreshtokens = AccessToken.objects.filter(user=request.user,application=id)
for token in tokens:
token.revoke()
for token in refreshtokens:
token.revoke()
r = getrower(request.user)
form = RowerForm(instance=r)
powerform = RowerPowerForm(instance=r)
grants = AccessToken.objects.filter(user=request.user)
url = reverse(rower_edit_view)
return HttpResponseRedirect(url)
except AccessToken.DoesNotExist:
raise Http404("Access token doesn't exist")
# Creates unix time stamp from a datetime object
def totimestamp(dt, epoch=datetime.datetime(1970,1,1,tzinfo=tz('UTC'))):
td = dt - epoch
# return td.total_seconds()
return (td.microseconds + (td.seconds + td.days * 86400) * 10**6) / 10**6
# Check if a column of a dataframe has the required (aantal)
# number of elements. Also checks if the column is a numerical type
# Replaces any faulty columns with zeros
def trydf(df,aantal,column):
try:
s = df[column]
if len(s) != aantal:
return np.zeros(aantal)
if not np.issubdtype(s,np.number):
return np.zeros(aantal)
except KeyError:
s = np.zeros(aantal)
return s
# Stroke data form to test API upload
@login_required()
def strokedataform(request,id=0):
try:
id=int(id)
except ValueError:
id = 0
try:
w = Workout.objects.get(id=id)
except Workout.DoesNotExist:
raise Http404("Workout doesn't exist")
if request.method == 'GET':
form = StrokeDataForm()
return render(request, 'strokedata_form.html',
{
'form':form,
'teams':get_my_teams(request.user),
'id':id,
'workout':w,
})
elif request.method == 'POST':
form = StrokeDataForm()
return render(request, 'strokedata_form.html',
{
'form':form,
'teams':get_my_teams(request.user),
'id':id,
'workout':w,
})
# Process the POSTed stroke data according to the API definition
# Return the GET stroke data according to the API definition
from rest_framework_swagger.renderers import OpenAPIRenderer, SwaggerUIRenderer
@csrf_exempt
@login_required()
@api_view(['GET','POST'])
def strokedatajson(request,id):
"""
POST: Add Stroke data to workout
GET: Get stroke data of workout
"""
row = get_workout_permitted(request.user,id)
try:
id = int(id)
except ValueError:
return HttpResponse("Not a valid workout number",status=400)
if request.method == 'GET':
# currently only returns a subset.
columns = ['spm','time','hr','pace','power','distance']
datadf = dataprep.getsmallrowdata_db(columns,ids=[id])
with open('media/apilog.log','a') as logfile:
logfile.write(str(timezone.now())+": ")
logfile.write(request.user.username+"(GET) \n")
return JSONResponse(datadf)
if request.method == 'POST':
checkdata,r = dataprep.getrowdata_db(id=row.id)
if not checkdata.empty:
return HttpResponse("Duplicate Error",409)
# strokedata = request.POST['strokedata']
# checking/validating and cleaning
try:
strokedata = json.loads(request.POST['strokedata'])
except:
return HttpResponse("No JSON object could be decoded",400)
df = pd.DataFrame(strokedata)
df.index = df.index.astype(int)
df.sort_index(inplace=True)
# time, hr, pace, spm, power, drivelength, distance, drivespeed, dragfactor, strokerecoverytime, averagedriveforce, peakdriveforce, lapidx
try:
time = df['time']/1.e3
except KeyError:
return HttpResponse("There must be time values",status=400)
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')
wash = trydf(df,aantal,'wash')
catch = trydf(df,aantal,'catch')
finish = trydf(df,aantal,'finish')
peakforceangle = trydf(df,aantal,'peakforceangle')
driveenergy = trydf(df,aantal,'driveenergy')
slip = trydf(df,aantal,'slip')
lapidx = trydf(df,aantal,'lapidx')
hr = trydf(df,aantal,'hr')
starttime = totimestamp(row.startdatetime)+time[0]
unixtime = starttime+time
with open('media/apilog.log','a') as logfile:
logfile.write(str(starttime)+": ")
logfile.write(request.user.username+"(POST) \r\n")
data = pd.DataFrame({'TimeStamp (sec)':unixtime,
' Horizontal (meters)': distance,
' Cadence (stokes/min)':spm,
' HRCur (bpm)':hr,
' DragFactor':dragfactor,
' Stroke500mPace (sec/500m)':pace,
' Power (watts)':power,
' DriveLength (meters)':drivelength,
' DriveTime (ms)':drivetime,
' StrokeRecoveryTime (ms)':strokerecoverytime,
' AverageDriveForce (lbs)':averagedriveforce,
' PeakDriveForce (lbs)':peakdriveforce,
' lapIdx':lapidx,
' ElapsedTime (sec)':time,
'catch':catch,
'slip':slip,
'finish':finish,
'wash':wash,
'driveenergy':driveenergy,
'peakforceangle':peakforceangle,
})
# Following part should be replaced with dataprep.new_workout_from_df
r = getrower(request.user)
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()
powerperc = 100*np.array([r.pw_ut2,
r.pw_ut1,
r.pw_at,
r.pw_tr,r.pw_an])/r.ftp
ftp = float(r.ftp)
if row.workouttype in mytypes.otwtypes:
ftp = ftp*(100.-r.otwslack)/100.
rr = rrower(hrmax=r.max,hrut2=r.ut2,
hrut1=r.ut1,hrat=r.at,
hrtr=r.tr,hran=r.an,ftp=ftp,
powerperc=powerperc,powerzones=r.powerzones)
rowdata = rdata(row.csvfilename,rower=rr).df
datadf = dataprep.dataprep(rowdata,id=row.id,bands=True,barchart=True,otwpower=True,empower=True)
# mangling
#
return HttpResponse(row.id,status=201)
#Method not supported
return HttpResponseNotAllowed("Method not supported")
# Teams related views
import teams
@login_required()
def team_view(request,id=0,userid=0):
ismember = 0
hasrequested = 0
r = getrequestrower(request,userid=userid)
myteams, memberteams, otherteams = get_teams(request)
teams.remove_expired_invites()
try:
t = Team.objects.get(id=id)
except Team.DoesNotExist:
raise Http404("Team doesn't exist")
if request.method == 'POST' and request.user == t.manager:
inviteform = TeamInviteForm(request.POST)
inviteform.fields['user'].queryset = User.objects.filter(rower__isnull=False,rower__team__in=myteams).distinct().exclude(rower__team__name=t.name)
if inviteform.is_valid():
cd = inviteform.cleaned_data
newmember = cd['user']
email = cd['email']
inviteid,text = teams.create_invite(t,t.manager,
user=newmember,
email=email)
if inviteid:
teams.send_invite_email(inviteid)
successmessage = text
messages.info(request,successmessage)
else:
message = text
messages.error(request,message)
elif request.user == t.manager:
inviteform = TeamInviteForm()
inviteform.fields['user'].queryset = User.objects.filter(rower__isnull=False,rower__team__in=myteams).distinct().exclude(rower__team__name=t.name)
else:
inviteform = ''
members = Rower.objects.filter(team=t).order_by('user__last_name','user__first_name')
thisteammyrequests = TeamRequest.objects.filter(team=t,user=request.user)
if len(thisteammyrequests):
hasrequested = 1
if r in members:
ismember = 1
breadcrumbs = [
{
'url':reverse(rower_teams_view),
'name': 'Teams'
},
{
'url':reverse(team_view,kwargs={'id':id}),
'name': t.name
}
]
return render(request, 'team.html',
{
'team':t,
'teams':get_my_teams(request.user),
'myteams':myteams,
'memberteams':memberteams,
'members':members,
'breadcrumbs':breadcrumbs,
'active':'nav-teams',
'inviteform':inviteform,
'ismember':ismember,
'hasrequested':hasrequested,
})
@login_required()
def team_leaveconfirm_view(request,id=0):
try:
t = Team.objects.get(id=id)
except Team.DoesNotExist:
raise Http404("Team doesn't exist")
myteams, memberteams, otherteams = get_teams(request)
breadcrumbs = [
{
'url':reverse(rower_teams_view),
'name': 'Teams'
},
{
'url':reverse(team_view,kwargs={'id':id}),
'name': t.name
},
{
'url':reverse(team_leaveconfirm_view,kwargs={'id':id}),
'name': 'Leave'
}
]
return render(request,'teamleaveconfirm.html',
{
'team':t,
'teams':get_my_teams(request.user),
'myteams':myteams,
'memberteams':memberteams,
'otherteams':otherteams,
'active':'nav-teams',
'breadcrumbs':breadcrumbs,
})
@login_required()
def rower_calcdps_view(request):
r = getrower(request.user)
ws = [(w.id,w.csvfilename) for w in Workout.objects.filter(user=r)]
res = myqueue(queue,handle_updatedps,r.user.email,ws,debug=False,
emailbounced=r.emailbounced)
messages.info(request,"Your workouts are being updated in the background. You will receive email when this is done.")
url = reverse(workouts_view)
return HttpResponseRedirect(url)
@login_required()
def rower_update_empower_view(
request,
startdate=timezone.now()-datetime.timedelta(days=365),
enddate=timezone.now()
):
try:
r = getrower(request.user)
except Rower.DoesNotExist:
raise Http404("Rower doesn't exist")
# if 'startdate' in request.session:
# startdate = iso8601.parse_date(request.session['startdate'])
# if 'enddate' in request.session:
# enddate = iso8601.parse_date(request.session['enddate'])
if request.method == 'POST' and 'daterange' in request.POST:
dateform = DateRangeForm(request.POST)
if dateform.is_valid():
startdate = dateform.cleaned_data['startdate']
enddate = dateform.cleaned_data['enddate']
startdatestring = startdate.strftime('%Y-%m-%d')
enddatestring = enddate.strftime('%Y-%m-%d')
request.session['startdate'] = startdatestring
request.session['enddate'] = enddatestring
else:
dateform = DateRangeForm(initial={
'startdate':startdate,
'enddate':enddate,
})
if request.method == 'POST' and 'workouts' in request.POST:
form = WorkoutMultipleCompareForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
workouts = cd['workouts']
workoutdicts = []
for w in workouts:
if w.user != r:
message = "You can only alter your own workouts"
messages.error(request,message)
if 'x' in w.boattype and w.oarlength is not None and w.oarlength > 3.30:
message = "Oarlength and boat type mismatch for workout "+str(w.id)+". Skipping workout"
messages.error(request,message)
elif 'x' not in w.boattype and w.oarlength is not None and w.oarlength <= 3.30:
message = "Oarlength and boat type mismatch for workout "+str(w.id)+". Skipping workout"
messages.error(request,message)
elif w.oarlength is None:
message = "Incorrect oarlength in workout "+str(w.id)+". Skipping workout"
messages.error(request,message)
else:
workoutdict = {
'id':w.id,
'boattype':w.boattype,
'filename':w.csvfilename,
'inboard':w.inboard,
'oarlength':w.oarlength
}
workoutdicts.append(workoutdict)
w.workoutsource = 'speedcoach2corrected'
w.save()
job = myqueue(queuelow,handle_update_empower,
request.user.email,workoutdicts,
debug=False,
emailbounced=r.emailbounced)
try:
request.session['async_tasks'] += [(job.id,'update_empower')]
except KeyError:
request.session['async_tasks'] = [(job.id,'update_empower')]
successmessage = 'Your workouts are being updated in the background. You will receive email when this is done. You can check the status of your calculations <a href="/rowers/jobs-status" target="_blank">here</a>'
messages.info(request,successmessage)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
else:
workouts = Workout.objects.filter(
startdatetime__gte=startdate,
startdatetime__lte=enddate,
workoutsource='speedcoach2',
user=r,
).order_by("-date","-starttime")
form = WorkoutMultipleCompareForm()
form.fields["workouts"].queryset = workouts
# GET request = prepare form
return render(request, 'empower_fix.html',
{'workouts':workouts,
'active': 'nav-workouts',
'dateform':dateform,
'form':form,
'rower':r
})
@login_required()
def team_leave_view(request,id=0):
r = getrower(request.user)
teams.remove_member(id,r)
url = reverse(rower_teams_view)
response = HttpResponseRedirect(url)
return response
from rowers.forms import TeamInviteCodeForm
def get_teams(request):
r = Rower.objects.get(user=request.user)
myteams = Team.objects.filter(
manager=request.user).order_by('name')
memberteams = Team.objects.filter(
rower=r).exclude(manager=request.user).order_by('name')
otherteams = Team.objects.filter(
private='open').exclude(
rower=r).exclude(manager=request.user).order_by('name')
return myteams, memberteams, otherteams
@login_required()
def rower_teams_view(request,message='',successmessage=''):
if request.method == 'POST':
form = TeamInviteCodeForm(request.POST)
if form.is_valid():
code = form.cleaned_data['code']
res,text = teams.process_invite_code(request.user,code)
if res:
successmessage = text
else:
message = text
else:
form = TeamInviteCodeForm()
r = getrower(request.user)
ts = Team.objects.filter(rower=r)
myteams, memberteams, otherteams = get_teams(request)
teams.remove_expired_invites()
invites = TeamInvite.objects.filter(user=request.user)
requests = TeamRequest.objects.filter(user=request.user)
myrequests = TeamRequest.objects.filter(team__in=myteams)
myinvites = TeamInvite.objects.filter(team__in=myteams)
clubsize = teams.count_invites(request.user)+teams.count_club_members(request.user)
max_clubsize = r.clubsize
messages.info(request,successmessage)
messages.error(request,message)
breadcrumbs = [
{
'url':reverse(rower_teams_view),
'name': 'Teams'
}
]
return render(request, 'teams.html',
{
'teams':ts,
'active':'nav-teams',
'breadcrumbs':breadcrumbs,
'clubsize':clubsize,
'max_clubsize':max_clubsize,
'myteams':myteams,
'memberteams':memberteams,
'invites':invites,
'otherteams':otherteams,
'requests':requests,
'myrequests':myrequests,
'form':form,
'myinvites':myinvites,
})
@user_passes_test(iscoachmember,login_url="/rowers/promembership",redirect_field_name=None)
def invitation_revoke_view(request,id):
res,text = teams.revoke_invite(request.user,id)
if res:
messages.info(request,text)
successmessage = text
else:
message = text
messages.error(request,text)
url = reverse(rower_teams_view)
return HttpResponseRedirect(url)
@user_passes_test(iscoachmember,login_url="/rowers/promembership",redirect_field_name=None)
def manager_member_drop_view(request,teamid,userid,
message='',successmessage=''):
rower = Rower.objects.get(user__id=userid)
res, text = teams.mgr_remove_member(teamid,request.user,rower)
if res:
messages.info(request,text)
else:
messages.error(request,text)
url = reverse(rower_teams_view)
return HttpResponseRedirect(url)
@user_passes_test(iscoachmember,login_url="/rowers/promembership",redirect_field_name=None)
def manager_requests_view(request,code=None,message='',successmessage=''):
if code:
res,text = teams.process_request_code(request.user,code)
if res:
successmessage = text
message = ''
else:
message = text
successmessage = ''
messages.info(request,successmessage)
messages.error(request,message)
url = reverse(rower_teams_view,kwargs={
})
return HttpResponseRedirect(url)
@login_required()
def team_requestmembership_view(request,teamid,userid):
try:
t = Team.objects.get(id=teamid)
except Team.DoesNotExist:
raise Http404("Team doesn't exist")
res,text = teams.create_request(t,userid)
if res:
messages.info(request,text)
else:
messages.error(request,text)
url = reverse(team_view,kwargs={
'id':int(teamid),
})
return HttpResponseRedirect(url)
@login_required()
def request_revoke_view(request,id=0):
res,text = teams.revoke_request(request.user,id)
if res:
messages.info(request,text)
else:
messages.error(request,text)
url = reverse(rower_teams_view)
return HttpResponseRedirect(url)
@user_passes_test(iscoachmember,login_url="/rowers/promembership",redirect_field_name=None)
def request_reject_view(request,id=0):
res,text = teams.reject_request(request.user,id)
if res:
messages.info(request,text)
else:
messages.error(request,text)
url = reverse(rower_teams_view)
return HttpResponseRedirect(url)
@user_passes_test(iscoachmember,login_url="/rowers/promembership",redirect_field_name=None)
def invitation_reject_view(request,id=0):
res,text = teams.reject_invitation(request.user,id)
if res:
messages.info(request,text)
else:
messages.error(request,text)
url = reverse(rower_teams_view)
return HttpResponseRedirect(url)
@login_required()
def rower_invitations_view(request,code=None,message='',successmessage=''):
if code:
teams.remove_expired_invites()
res,text = teams.process_invite_code(request.user,code)
if res:
messages.info(request,text)
teamid=res
url = reverse(team_view,kwargs={
'id':teamid,
})
else:
messages.error(request,text)
url = reverse(rower_teams_view)
return HttpResponseRedirect(url)
url = reverse(rower_teams_view,kwargs={
})
return HttpResponseRedirect(url)
@user_passes_test(iscoachmember,login_url="/rowers/promembership",redirect_field_name=None)
def team_edit_view(request,id=0):
try:
t = Team.objects.get(id=id)
except Team.DoesNotExist:
raise Http404("Team does not exist")
if request.method == 'POST':
teamcreateform = TeamForm(request.POST,instance=t)
if teamcreateform.is_valid():
cd = teamcreateform.cleaned_data
name = cd['name']
notes = cd['notes']
manager = request.user
private = cd['private']
viewing = cd['viewing']
res,message=teams.update_team(t,name,manager,private,notes,
viewing)
if res:
messages.info(request,message)
else:
messages.error(request,message)
url = reverse(team_view,
kwargs={
'id':int(id),
}
)
response = HttpResponseRedirect(url)
return response
else:
teamcreateform = TeamForm(instance=t)
myteams, memberteams, otherteams = get_teams(request)
breadcrumbs = [
{
'url':reverse(rower_teams_view),
'name': 'Teams'
},
{
'url':reverse(team_view,kwargs={'id':id}),
'name': t.name
},
{
'url':reverse(team_edit_view,kwargs={'id':id}),
'name': 'Edit'
}
]
return render(request,'teamedit.html',
{
'form':teamcreateform,
'teams':get_my_teams(request.user),
'myteams':myteams,
'memberteams':memberteams,
'otherteams':otherteams,
'active':'nav-teams',
'breadcrumbs':breadcrumbs,
'team':t,
})
@user_passes_test(iscoachmember,login_url="/rowers/promembership",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()
myteams, memberteams, otherteams = get_teams(request)
breadcrumbs = [
{
'url':reverse(rower_teams_view),
'name': 'Teams'
},
{
'url':reverse(team_create_view),
'name': "New Team"
},
]
return render(request,'teamcreate.html',
{
'teams':get_my_teams(request.user),
'form':teamcreateform,
'myteams':myteams,
'memberteams':memberteams,
'otherteams':otherteams,
'active':'nav-teams',
'breadcrumbs':breadcrumbs,
})
@user_passes_test(iscoachmember,login_url="/rowers/promembership",redirect_field_name=None)
def team_deleteconfirm_view(request,id):
r = getrower(request.user)
try:
t = Team.objects.get(id=id)
except Team.DoesNotExist:
raise Http404("This team doesn't exist")
if t.manager != request.user:
raise PermissionDenied("You are not allowed to delete this team")
myteams, memberteams, otherteams = get_teams(request)
breadcrumbs = [
{
'url':reverse(rower_teams_view),
'name': 'Teams'
},
{
'url':reverse(team_view,kwargs={'id':id}),
'name': t.name
},
{
'url':reverse(team_deleteconfirm_view,kwargs={'id':id}),
'name': 'Leave'
}
]
return render(request,'teamdeleteconfirm.html',
{
'teams':get_my_teams(request.user),
'team':t,
'myteams':myteams,
'memberteams':memberteams,
'otherteams':otherteams,
'active':'nav-teams',
})
@user_passes_test(iscoachmember,login_url="/rowers/promembership",redirect_field_name=None)
def team_delete_view(request,id):
r = getrower(request.user)
try:
t = Team.objects.get(id=id)
except Team.DoesNotExist:
raise Http404("This team doesn't exist")
if t.manager != request.user:
raise PermissionDenied("You are not allowed to delete this team")
teams.remove_team(t.id)
url = reverse(rower_teams_view)
response = HttpResponseRedirect(url)
return response
@user_passes_test(iscoachmember,login_url="/rowers/promembership",redirect_field_name=None)
def team_members_stats_view(request,id):
r = getrower(request.user)
try:
t = Team.objects.get(id=id)
except Team.DoesNotExist:
raise Http404("This team doesn't exist")
if t.manager != request.user:
raise PermissionDenied("You are not allowed to see this page")
members = Rower.objects.filter(team=t).order_by("user__last_name","user__first_name")
theusers = [member.user for member in members]
myteams, memberteams, otherteams = get_teams(request)
breadcrumbs = [
{
'url':reverse(rower_teams_view),
'name': 'Teams'
},
{
'url':reverse(team_view,kwargs={'id':id}),
'name': t.name
},
{
'url':reverse(team_members_stats_view,kwargs={'id':id}),
'name': 'Members Stats'
}
]
response = render(request,'teamstats.html',
{
'teams':get_my_teams(request.user),
'myteams':myteams,
'memberteams':memberteams,
'otherteams':otherteams,
'active':'nav-teams',
'breadcrumbs':breadcrumbs,
'team':t,
'theusers':theusers,
})
return response
from rowers.models import C2WorldClassAgePerformance
def agegroupcpview(request,age,normalize=0):
script,div = interactive_agegroupcpchart(age,normalized=normalize)
response = render(request,'agegroupcp.html',
{
'active': 'nav-analysis',
'interactiveplot':script,
'the_div':div,
}
)
return response
def agegrouprecordview(request,sex='male',weightcategory='hwt',
distance=2000,duration=None):
if not duration:
df = pd.DataFrame(
list(
C2WorldClassAgePerformance.objects.filter(
distance=distance,
sex=sex,
weightcategory=weightcategory
).values()
)
)
else:
duration = int(duration)*60
df = pd.DataFrame(
list(
C2WorldClassAgePerformance.objects.filter(
duration=duration,
sex=sex,
weightcategory=weightcategory
).values()
)
)
script,div = interactive_agegroup_plot(df,sex=sex,distance=distance,
duration=duration,
weightcategory=weightcategory)
return render(request, 'agegroupchart.html',
{
'interactiveplot':script,
'active':'nav-analysis',
'the_div':div,
})
# Cloning sessions
@user_passes_test(hasplannedsessions,login_url="/rowers/promembership/",
message="This functionality requires a Coach or Self-Coach plan",
redirect_field_name=None)
def plannedsession_multiclone_view(
request,
userid=0,):
r = getrequestrower(request,userid=userid)
startdate,enddate = get_dates_timeperiod(request)
if request.method == 'POST' and 'daterange' in request.POST:
dateform = DateRangeForm(request.POST)
if dateform.is_valid():
startdate = dateform.cleaned_data['startdate']
enddate = dateform.cleaned_data['enddate']
startdatestring = startdate.strftime('%Y-%m-%d')
enddatestring = enddate.strftime('%Y-%m-%d')
request.session['startdate'] = startdatestring
request.session['enddate'] = enddatestring
else:
dateform = DateRangeForm(initial={
'startdate':startdate,
'enddate':enddate,
})
if request.method == 'POST' and 'plannedsessions' in request.POST:
form = PlannedSessionMultipleCloneForm(request.POST)
dateshiftform = SessionDateShiftForm(request.POST)
if form.is_valid() and dateshiftform.is_valid():
cd = form.cleaned_data
sps = cd['plannedsessions']
std = min([ps.startdate for ps in sps])
shiftstartdate = dateshiftform.cleaned_data['shiftstartdate']
delta = shiftstartdate-std
lastdate = shiftstartdate
for ps in sps:
rowers = ps.rower.all()
teams = ps.team.all()
ps.pk = None
ps.startdate += delta
ps.preferreddate += delta
ps.enddate += delta
if ps.enddate > lastdate:
lastdate = ps.enddate
ps.save()
for rower in rowers:
add_rower_session(rower,ps)
for team in teams:
add_team_session(team,ps)
startdatestring = shiftstartdate.strftime('%Y-%m-%d')
enddatestring = lastdate.strftime('%Y-%m-%d')
url = reverse(plannedsessions_view,
kwargs = {
'userid':r.user.id,
})
url+='?when='+startdatestring+'/'+enddatestring
return HttpResponseRedirect(url)
sps = PlannedSession.objects.filter(
manager=request.user,
rower__in=[r],
startdate__lte=enddate,
enddate__gte=startdate).order_by(
"startdate","preferreddate","enddate").exclude(
sessiontype='race')
query = request.GET.get('q')
if query:
query_list = query.split()
sps = sps.filter(
reduce(operator.and_,
(Q(name__icontains=q) for q in query_list)) |
reduce(operator.and_,
(Q(comment__icontains=q) for q in query_list))
)
form = PlannedSessionMultipleCloneForm()
form.fields["plannedsessions"].queryset = sps
dateshiftform = SessionDateShiftForm()
try:
trainingplan = TrainingPlan.objects.filter(
startdate__lte = startdate,
rowers = r,
enddate__gte = enddate)[0]
except IndexError:
trainingplan = None
timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d')
breadcrumbs = [
{
'url': reverse(plannedsessions_view),
'name': 'Planned Sessions'
},
{
'url': reverse(plannedsession_multiclone_view),
'name': 'Clone Multiple Sessions'
}
]
dateform = DateRangeForm(initial={
'startdate':startdate,
'enddate':enddate,
})
return render(request, 'plannedsessions_multiclone_select.html',
{'plannedsessions':sps,
'breadcrumbs':breadcrumbs,
'plan':trainingplan,
'dateform':dateform,
'startdate':startdate,
'enddate':enddate,
'form':form,
'dateshiftform':dateshiftform,
'rower':r,
'active':'nav-plan',
'timeperiod':timeperiod,
}
)
# Individual user creates training for himself
@user_passes_test(hasplannedsessions,login_url="/rowers/promembership/",
message="This functionality requires a Coach or Self-Coach plan",
redirect_field_name=None)
def plannedsession_create_view(request,
userid=0,
startdatestring='',
enddatestring=''):
r = getrequestrower(request,userid=userid)
startdate,enddate = get_dates_timeperiod(request,startdatestring=startdatestring,
enddatestring=enddatestring)
if request.method == 'POST':
sessioncreateform = PlannedSessionForm(request.POST)
if sessioncreateform.is_valid():
cd = sessioncreateform.cleaned_data
startdate = cd['startdate']
enddate = cd['enddate']
preferreddate = cd['preferreddate']
sessiontype = cd['sessiontype']
sessionmode = cd['sessionmode']
criterium = cd['criterium']
sessionvalue = cd['sessionvalue']
sessionunit = cd['sessionunit']
comment = cd['comment']
course = cd['course']
name = cd['name']
if sessionunit == 'min':
sessionmode = 'time'
elif sessionunit in ['km','m']:
sessionmode = 'distance'
ps = PlannedSession(
name=name,
startdate=startdate,
enddate=enddate,
preferreddate=preferreddate,
course=course,
sessiontype=sessiontype,
sessionmode=sessionmode,
sessionvalue=sessionvalue,
sessionunit=sessionunit,
comment=comment,
criterium=criterium,
manager=request.user)
ps.save()
add_rower_session(r,ps)
request.session['fstartdate'] = str(arrow.get(startdate))
request.session['fenddate'] = str(arrow.get(enddate))
request.session['fprefdate'] = str(arrow.get(preferreddate))
else:
if 'fstartdate' in request.session:
try:
fstartdate = arrow.get(request.session['fstartdate']).date()
except KeyError:
fstartdate = timezone.now().date()
if fstartdate < startdate:
fstartdate = startdate
try:
fenddate = arrow.get(request.session['fenddate']).date()
except KeyError:
fenddate = timezone.now().date()
if fenddate > enddate:
fenddate = enddate
try:
fprefdate = arrow.get(request.session['fprefdate']).date()
except KeyError:
fprefdate = timezone.now().date()
if fprefdate < startdate:
fprefdate = startdate
if fprefdate > enddate:
fprefdate = enddate
forminitial = {
'startdate':fstartdate,
'enddate':fenddate,
'preferreddate':fprefdate
}
else:
preferreddate = startdate
if preferreddate < timezone.now().date():
preferreddate = timezone.now().date()
if preferreddate > enddate:
preferreddate = enddate
forminitial = {
'startdate':startdate,
'enddate':enddate,
'preferreddate':preferreddate,
}
sessioncreateform = PlannedSessionForm(initial=forminitial)
sps = get_sessions(r,startdate=startdate,enddate=enddate).exclude(
sessiontype='race')
try:
trainingplan = TrainingPlan.objects.filter(
startdate__lte = startdate,
rowers = r,
enddate__gte = enddate)[0]
except IndexError:
trainingplan = None
timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d')
dateform = DateRangeForm(initial={
'startdate':startdate,
'enddate':enddate,
})
return render(request,'plannedsessioncreate.html',
{
'teams':get_my_teams(request.user),
'plan':trainingplan,
'dateform':dateform,
'form':sessioncreateform,
'active':'nav-plan',
'plannedsessions':sps,
'rower':r,
'timeperiod':timeperiod,
})
@user_passes_test(hasplannedsessions,login_url="/rowers/promembership/",
message="This functionality requires a Coach or Self-Coach plan",
redirect_field_name=None)
def plannedsession_multicreate_view(request,
teamid=0,userid=0,extrasessions=0):
extrasessions=int(extrasessions)
r = getrequestrower(request,userid=userid)
startdate,enddate = get_dates_timeperiod(request)
try:
trainingplan = TrainingPlan.objects.filter(
startdate__lte = startdate,
rowers = r,
enddate__gte = enddate)[0]
except IndexError:
trainingplan = None
m = Rower.objects.get(user=request.user)
qset = PlannedSession.objects.filter(
rower__in=[r],
manager = request.user,
startdate__lte=enddate,
enddate__gte=startdate,
).order_by("startdate","preferreddate","enddate").exclude(
sessiontype='race')
initial = {
'startdate':startdate,
'enddate':enddate,
'sessionvalue':60,
'manager':request.user,
'name': 'NEW SESSION'
}
initials = [initial for i in range(extrasessions)]
PlannedSessionFormSet = modelformset_factory(
PlannedSession,
form=PlannedSessionFormSmall,
can_delete=True,
extra=extrasessions,
)
if request.method == "POST":
ps_formset = PlannedSessionFormSet(queryset = qset,
data = request.POST)
if ps_formset.is_valid():
instances = ps_formset.save(commit=False)
for ps in instances:
ps.save()
add_rower_session(r,ps)
messages.info(request,"Saved changes for Planned Session "+str(ps))
for obj in ps_formset.deleted_objects:
messages.info(request,"Deleted Planned Session "+str(obj))
obj.delete()
else:
print ps_formset.errors
url = reverse(plannedsession_multicreate_view,
kwargs = {
'userid':r.user.id,
}
)
startdatestring = startdate.strftime('%Y-%m-%d')
enddatestring = enddate.strftime('%Y-%m-%d')
url += '?when='+startdatestring+'/'+enddatestring
return HttpResponseRedirect(url)
ps_formset = PlannedSessionFormSet(queryset = qset,
initial=initials)
timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d')
breadcrumbs = [
{
'url': reverse(plannedsessions_view),
'name': 'Planned Sessions'
},
{
'url': reverse(plannedsession_multicreate_view),
'name': 'Plan MicroCycle'
}
]
dateform = DateRangeForm(initial={
'startdate':startdate,
'enddate':enddate
})
context = {
'ps_formset':ps_formset,
'breadcrumbs':breadcrumbs,
'rower':r,
'active':'nav-plan',
'dateform':dateform,
'plan':trainingplan,
'timeperiod':timeperiod,
'teams':get_my_teams(request.user),
'extrasessions': extrasessions+1
}
return render(request,'plannedsession_multicreate.html',context)
# Manager creates sessions for entire team
@user_passes_test(iscoachmember,login_url="/rowers/promembership/",
redirect_field_name=None)
def plannedsession_teamcreate_view(request,
teamid=0,userid=0):
therower = getrequestrower(request,userid=userid)
teams = Team.objects.filter(manager=request.user)
if len(teams)>0:
teamchoices = [(team.id, team.name) for team in teams]
teaminitial = [str(teams[0].id)]
else:
messages.info(request,"You have no teams established yet. We are redirecting you to the Team Management page.")
url = reverse(rower_teams_view)
return HttpResponseRedirect(url)
startdate,enddate = get_dates_timeperiod(request)
trainingplan = None
sps = []
for team in teams:
res = get_sessions_manager(request.user,startdate=startdate,enddate=enddate)
sps += res
sps = list(set(sps))
ids = [ps.id for ps in sps]
sps = PlannedSession.objects.filter(id__in=ids).order_by(
"preferreddate","startdate","enddate")
if request.method == 'POST':
sessioncreateform = PlannedSessionForm(request.POST)
sessionteamselectform = PlannedSessionTeamForm(
request.user,request.POST
)
if sessioncreateform.is_valid() and sessionteamselectform.is_valid():
cd = sessioncreateform.cleaned_data
startdate = cd['startdate']
enddate = cd['enddate']
preferreddate = cd['preferreddate']
sessiontype = cd['sessiontype']
sessionmode = cd['sessionmode']
criterium = cd['criterium']
sessionvalue = cd['sessionvalue']
sessionunit = cd['sessionunit']
comment = cd['comment']
course = cd['course']
name = cd['name']
if sessionunit == 'min':
sessionmode = 'time'
elif sessionunit in ['km','m']:
sessionmode = 'distance'
ps = PlannedSession(
name=name,
startdate=startdate,
enddate=enddate,
preferreddate=preferreddate,
sessiontype=sessiontype,
sessionmode=sessionmode,
sessionvalue=sessionvalue,
sessionunit=sessionunit,
comment=comment,
criterium=criterium,
course=course,
manager=request.user)
ps.save()
cd = sessionteamselectform.cleaned_data
teams = cd['team']
request.session['teams'] = [team.id for team in teams]
for team in teams:
add_team_session(team,ps)
rs = Rower.objects.filter(team__in=[team])
for r in rs:
add_rower_session(r,ps)
url = reverse(plannedsession_teamcreate_view)
startdatestring = startdate.strftime('%Y-%m-%d')
enddatestring = enddate.strftime('%Y-%m-%d')
url += '?when='+startdatestring+'/'+enddatestring
return HttpResponseRedirect(url)
else:
timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d')
breadcrumbs = [
{
'url': reverse(plannedsessions_view),
'name': 'Planned Sessions'
},
{
'url': reverse(plannedsession_teamcreate_view),
'name': 'Add Team Session'
}
]
return render(request,'plannedsessionteamcreate.html',
{
'teams':get_my_teams(request.user),
'plan':trainingplan,
'breadcrumbs':breadcrumbs,
'form':sessioncreateform,
'teamform':sessionteamselectform,
'timeperiod':timeperiod,
'plannedsessions':sps,
'rower':therower,
'active':'nav-plan'
})
else:
initial = {
'startdate':startdate,
'enddate':enddate,
'preferreddate':startdate,
}
if 'teams' in request.session:
teams = request.session['teams']
theteams = Team.objects.filter(id__in=teams)
initialteam = {
'team':theteams
}
else:
initialteam = {}
sessioncreateform = PlannedSessionForm(initial=initial)
sessionteamselectform = PlannedSessionTeamForm(
request.user,initial=initialteam
)
timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d')
breadcrumbs = [
{
'url': reverse(plannedsessions_view),
'name': 'Planned Sessions'
},
{
'url': reverse(plannedsession_teamcreate_view),
'name': 'Add Team Session'
}
]
dateform = DateRangeForm(initial={
'startdate':startdate,
'enddate':enddate,
})
return render(request,'plannedsessionteamcreate.html',
{
'teams':get_my_teams(request.user),
'plan':trainingplan,
'dateform':dateform,
'breadcrumbs':breadcrumbs,
'form':sessioncreateform,
'teamform':sessionteamselectform,
'timeperiod':timeperiod,
'plannedsessions':sps,
'rower':therower,
'active':'nav-plan'
})
# Manager edits sessions for entire team
@user_passes_test(iscoachmember,login_url="/rowers/promembership/",
redirect_field_name=None)
def plannedsession_teamedit_view(request,
sessionid=0,userid=0):
r = getrequestrower(request,userid=userid)
try:
ps = PlannedSession.objects.get(id=sessionid)
except PlannedSession.DoesNotExist:
raise Http404("This session doesn't exist")
if not ps.manager == request.user:
raise PermissionDenied("You are not the manager of this session")
teams = Team.objects.filter(manager=request.user)
teamchoices = [(team.id, team.name) for team in teams]
teaminitial = ps.team.all()
startdate,enddate = get_dates_timeperiod(request)
try:
trainingplan = TrainingPlan.objects.filter(
startdate__lte = startdate,
rowers = r,
enddate__gte = enddate)[0]
except IndexError:
trainingplan = None
sps = []
rowers = []
for team in teams:
res = get_sessions_manager(request.user,startdate=startdate,enddate=enddate)
sps += res
rowers += Rower.objects.filter(team__in=[team])
rowers = list(set(rowers))
sps = list(set(sps))
ids = [pps.id for pps in sps]
sps = PlannedSession.objects.filter(id__in=ids).order_by(
"preferreddate","startdate","enddate")
if request.method == 'POST':
sessioncreateform = PlannedSessionForm(request.POST,instance=ps)
sessionteamselectform = PlannedSessionTeamForm(
request.user,request.POST
)
sessionrowerform = PlannedSessionTeamMemberForm(ps,request.POST)
if sessioncreateform.is_valid():
cd = sessioncreateform.cleaned_data
if cd['sessionunit'] == 'min':
cd['sessionmode'] = 'time'
elif cd['sessionunit'] in ['km','m']:
cd['sessionmode'] = 'distance'
res,message = update_plannedsession(ps,cd)
if res:
messages.info(request,message)
else:
messages.error(request,message)
# some logic when to add all selected rowers
if sessionteamselectform.is_valid():
cd = sessionteamselectform.cleaned_data
selectedteams = cd['team']
for team in teams:
if team in selectedteams:
add_team_session(team,ps)
rs = Rower.objects.filter(team__in=[team])
for r in rs:
add_rower_session(r,ps)
else:
remove_team_session(team,ps)
else:
selectedteams = []
for team in teams:
remove_team_session(team,ps)
if sessionrowerform.is_valid():
cd = sessionrowerform.cleaned_data
selectedrowers = cd['members']
for r in rowers:
if r in selectedrowers:
add_rower_session(r,ps)
else:
remove_rower_session(r,ps)
for t in selectedteams:
if t in r.team.all():
add_rower_session(r,ps)
url = reverse(plannedsession_teamedit_view,
kwargs = {
'sessionid':sessionid,
})
startdatestring = startdate.strftime('%Y-%m-%d')
enddatestring = enddate.strftime('%Y-%m-%d')
url += '?when='+startdatestring+'/'+enddatestring
return HttpResponseRedirect(url)
else:
sessioncreateform = PlannedSessionForm(instance=ps)
sessionteamselectform = PlannedSessionTeamForm(
request.user
)
sessionteamselectform.fields['team'].initial = teaminitial
sessionrowerform = PlannedSessionTeamMemberForm(
ps
)
sessionrowerform.fields['members'].initial = ps.rower.all()
timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d')
breadcrumbs = [
{
'url': reverse(plannedsessions_view),
'name': 'Planned Sessions'
},
{
'url': reverse(plannedsession_teamcreate_view),
'name': 'Add Team Session'
}
]
dateform = DateRangeForm(initial={
'startdate':startdate,
'enddate':enddate,
})
return render(request,'plannedsessionteamedit.html',
{
'plannedsession':ps,
'plan':trainingplan,
'dateform':dateform,
'breadcrumbs':breadcrumbs,
'rower':r,
'active':'nav-plan',
'teams':get_my_teams(request.user),
'form':sessioncreateform,
'teamform':sessionteamselectform,
'rowersform':sessionrowerform,
'timeperiod':timeperiod,
'plannedsessions':sps,
})
#@user_passes_test(iscoachmember,login_url="/rowers/promembership/",
# redirect_field_name=None)
def plannedsessions_coach_view(request,
teamid=0,userid=0):
therower = getrower(request.user)
startdate,enddate = get_dates_timeperiod(request)
trainingplan = None
if teamid != 0:
try:
theteam = Team.objects.get(id=teamid)
except Team.DoesNotExist:
theteam = False
else:
theteam = False
if request.user.rower.rowerplan == 'coach':
sps = get_sessions_manager(request.user,teamid=teamid,
enddate=enddate,
startdate=startdate)
else:
rteams = therower.team.filter(viewing='allmembers')
sps = get_sessions(therower,startdate=startdate,enddate=enddate)
rowers = [therower]
for ps in sps:
if request.user.rower.rowerplan == 'coach':
rowers += ps.rower.all()
else:
rowers += ps.rower.filter(team__in=rteams)
rowers = list(set(rowers))
statusdict = []
for ps in sps:
rowerstatus = {}
rowercolor = {}
for r in rowers:
ratio, status,completiondate = is_session_complete(r,ps)
rowerstatus[r.id] = status
rowercolor[r.id] = cratiocolors[status]
sessiondict = {
'id': ps.id,
'results':rowerstatus,
'name': ps.name,
'startdate': ps.startdate,
'color': rowercolor,
'preferreddate': ps.preferreddate,
'enddate': ps.enddate,
}
statusdict.append(sessiondict)
unmatchedworkouts = []
for r in rowers:
unmatchedworkouts += Workout.objects.filter(
user=r,
plannedsession=None,
date__gte=startdate,date__lte=enddate)
myteams = Team.objects.filter(manager=request.user)
timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d')
breadcrumbs = [
{
'url': reverse(plannedsessions_view),
'name': 'Planned Sessions'
},
{
'url': reverse(plannedsessions_coach_view),
'name': 'Coach View'
}
]
dateform = DateRangeForm(initial={
'startdate':startdate,
'enddate':enddate,
})
return render(request,'plannedsessionscoach.html',
{
'myteams':myteams,
'plannedsessions':sps,
'breadcrumbs':breadcrumbs,
'plan':trainingplan,
'statusdict':statusdict,
'dateform':dateform,
'timeperiod':timeperiod,
'rowers':rowers,
'rower':therower,
'active':'nav-plan',
'theteam':theteam,
'unmatchedworkouts':unmatchedworkouts,
'rower':getrower(request.user)
}
)
from rowers.plannedsessions import cratiocolors
@login_required()
def plannedsessions_view(request,
userid=0,startdatestring='',enddatestring=''):
r = getrequestrower(request,userid=userid)
if startdatestring:
try:
startdate = iso8601.parse_date(startdatestring)
except ParseError:
pass
if enddatestring:
try:
enddate = iso8601.parse_date(enddatestring)
except ParseError:
pass
startdate,enddate = get_dates_timeperiod(
request,
startdatestring=startdatestring,
enddatestring=enddatestring)
try:
trainingplan = TrainingPlan.objects.filter(
startdate__lte = startdate,
rowers = r,
enddate__gte = enddate)[0]
except IndexError:
trainingplan = None
sps = get_sessions(r,startdate=startdate,enddate=enddate)
completeness = {}
actualvalue = {}
completiondate = {}
sessioncolor = {}
if not sps and request.user.rower.rowerplan == 'basic':
messages.error(request,
"You must purchase Coach or Self-coach plans or be part of a team to get planned sessions")
for ps in sps:
ratio,status,cdate = is_session_complete(r,ps)
actualvalue[ps.id] = int(ps.sessionvalue*ratio)
completeness[ps.id] = status
sessioncolor[ps.id] = cratiocolors[status]
ws = Workout.objects.filter(user=r,plannedsession=ps)
completiondate[ps.id] = cdate
unmatchedworkouts = Workout.objects.filter(
user=r,
plannedsession=None,
date__gte=startdate,date__lte=enddate)
timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d')
breadcrumbs = [
{
'url': reverse(plannedsessions_view),
'name': 'Planned Sessions'
},
]
initial = {
'startdate':startdate,
'enddate':enddate,
}
dateform = DateRangeForm(initial=initial)
return render(request,'plannedsessions.html',
{
'teams':get_my_teams(request.user),
'breadcrumbs':breadcrumbs,
'plannedsessions':sps,
'plan':trainingplan,
'active': 'nav-plan',
'dateform':dateform,
'rower':r,
'timeperiod':timeperiod,
'completeness':completeness,
'sessioncolor':sessioncolor,
'actualvalue':actualvalue,
'completiondate':completiondate,
'unmatchedworkouts':unmatchedworkouts,
})
@login_required()
def plannedsessions_print_view(request,userid=0):
r = getrequestrower(request,userid=userid)
startdate,enddate = get_dates_timeperiod(request)
try:
trainingplan = TrainingPlan.objects.filter(
startdate__lte = startdate,
rowers = r,
enddate__gte = enddate)[0]
except IndexError:
trainingplan = None
sps = get_sessions(r,startdate=startdate,enddate=enddate)
completeness = {}
actualvalue = {}
completiondate = {}
timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d')
return render(request,'plannedsessions_print.html',
{
'teams':get_my_teams(request.user),
'plan':trainingplan,
'plannedsessions':sps,
'rower':r,
'active':'nav-plan',
'startdate':startdate,
'enddate':enddate,
'timeperiod':timeperiod,
})
@login_required()
def plannedsessions_manage_view(request,userid=0,
initialsession=0):
is_ajax = False
if request.is_ajax():
is_ajax = True
r = getrequestrower(request,userid=userid)
startdate,enddate = get_dates_timeperiod(request)
try:
trainingplan = TrainingPlan.objects.filter(
startdate__lte = startdate,
rowers = r,
enddate__gte = enddate)[0]
except IndexError:
trainingplan = None
sps = get_sessions(r,startdate=startdate,enddate=enddate)
if initialsession==0:
try:
initialsession=sps[0].id
except IndexError:
initialsession=0
if initialsession:
ps0 = PlannedSession.objects.get(id=initialsession)
else:
ps0 = None
ws = Workout.objects.filter(
user=r,date__gte=startdate,
date__lte=enddate
).order_by(
"date","startdatetime","id"
)
initialworkouts = [w.id for w in Workout.objects.filter(user=r,plannedsession=ps0)]
linkedworkouts = []
for w in ws:
if w.plannedsession is not None:
linkedworkouts.append(w.id)
plannedsessionstuple = []
for ps in sps:
sessiontpl = (ps.id,ps.__unicode__())
plannedsessionstuple.append(sessiontpl)
plannedsessionstuple = tuple(plannedsessionstuple)
workoutdata = {}
workoutdata['initial'] = []
choices = []
for w in ws:
wtpl = (w.id, w.__unicode__())
choices.append(wtpl)
if w.id in initialworkouts:
workoutdata['initial'].append(w.id)
workoutdata['choices'] = tuple(choices)
if request.method == 'POST':
ps_form = PlannedSessionSelectForm(plannedsessionstuple,request.POST)
w_form = WorkoutSessionSelectForm(workoutdata,request.POST)
if ps_form.is_valid():
ps = PlannedSession.objects.get(id=ps_form.cleaned_data['plannedsession'])
if w_form.is_valid():
selectedworkouts = w_form.cleaned_data['workouts']
else:
selectedworkouts = []
if len(selectedworkouts)==0:
for w in ws:
remove_workout_plannedsession(w,ps)
if selectedworkouts:
workouts = Workout.objects.filter(user=r,id__in=selectedworkouts)
for w in ws:
if w.id not in selectedworkouts:
remove_workout_plannedsession(w,ps)
result,comments,errors = add_workouts_plannedsession(workouts,ps,r)
for c in comments:
messages.info(request,c)
for er in errors:
messages.error(request,er)
ps_form = PlannedSessionSelectForm(plannedsessionstuple,
initialsession=initialsession)
w_form = WorkoutSessionSelectForm(workoutdata=workoutdata)
if is_ajax:
ajax_workouts = []
for id,name in workoutdata['choices']:
ininitial = id in initialworkouts
inlinked = id in linkedworkouts
ajax_workouts.append((id,name,ininitial,inlinked))
ajax_response = {
'workouts':ajax_workouts,
'plannedsessionstuple':plannedsessionstuple,
}
return JSONResponse(ajax_response)
breadcrumbs = [
{
'url':reverse(plannedsessions_view,
kwargs={'userid':userid}),
'name': 'Plan'
},
{
'url':reverse(plannedsessions_manage_view,
kwargs={
'userid':userid,
'initialsession':initialsession,
}
),
'name': 'Link Sessions to Workouts'
},
]
timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d')
dateform = DateRangeForm(initial={
'startdate':startdate,
'enddate':enddate,
})
return render(request,'plannedsessionsmanage.html',
{
'teams':get_my_teams(request.user),
'plan':trainingplan,
'dateform':dateform,
'plannedsessions':sps,
'workouts':ws,
'active':'nav-plan',
'breadcrumbs':breadcrumbs,
'timeperiod':timeperiod,
'rower':r,
'ps_form':ps_form,
'w_form':w_form,
})
# Clone an existing planned session
# need clarity on cloning behavior time shift
@user_passes_test(hasplannedsessions,login_url="/rowers/promembership/",
message="This functionality requires a Coach or Self-Coach plan",
redirect_field_name=None)
def plannedsession_clone_view(request,id=0,userid=0):
r = getrequestrower(request,userid=userid)
startdate,enddate = get_dates_timeperiod(request)
try:
trainingplan = TrainingPlan.objects.filter(
startdate__lte = startdate,
rowers = r,
enddate__gte = enddate)[0]
except IndexError:
trainingplan = None
try:
ps = PlannedSession.objects.get(id=id)
except PlannedSession.DoesNotExist:
raise Http404("Planned Session does not exist")
if ps.manager != request.user:
raise PermissionDenied("You are not allowed to clone this planned session")
rowers = ps.rower.all()
teams = ps.team.all()
ps.pk = None
deltadays = ps.enddate-ps.startdate
ps.startdate = timezone.now().date()
ps.enddate = (timezone.now()+deltadays).date()
ps.preferreddate = ps.preferreddate+deltadays
ps.name += ' (copy)'
ps.save()
for rower in rowers:
add_rower_session(rower,ps)
for team in teams:
add_team_session(team,ps)
url = reverse(plannedsession_edit_view,
kwargs = {
'id':ps.id,
'userid':r.user.id,
}
)
startdatestring = startdate.strftime('%Y-%m-%d')
enddatestring = enddate.strftime('%Y-%m-%d')
url += '?when='+startdatestring+'/'+enddatestring
return HttpResponseRedirect(url)
# Edit an existing planned session
@user_passes_test(hasplannedsessions,login_url="/rowers/promembership/",
message="This functionality requires a Coach or Self-Coach plan",
redirect_field_name=None)
def plannedsession_edit_view(request,id=0,userid=0):
r = getrequestrower(request,userid=userid)
startdate,enddate = get_dates_timeperiod(request)
try:
trainingplan = TrainingPlan.objects.filter(
startdate__lte = startdate,
rowers = r,
enddate__gte = enddate)[0]
except IndexError:
trainingplan = None
try:
ps = PlannedSession.objects.get(id=id)
except PlannedSession.DoesNotExist:
raise Http404("Planned Session does not exist")
if ps.manager != request.user:
raise PermissionDenied("You are not allowed to edit this planned session")
if ps.team.all() or len(ps.rower.all())>1:
url = reverse(plannedsession_teamedit_view,
kwargs={
'sessionid':id,
})
return HttpResponseRedirect(url)
if request.method == 'POST':
sessioncreateform = PlannedSessionForm(request.POST,instance=ps)
if sessioncreateform.is_valid():
cd = sessioncreateform.cleaned_data
if cd['sessionunit'] == 'min':
cd['sessionmode'] = 'time'
elif cd['sessionunit'] in ['km','m']:
cd['sessionmode'] = 'distance'
res,message = update_plannedsession(ps,cd)
if res:
messages.info(request,message)
else:
messages.error(request,message)
url = reverse(plannedsession_edit_view,
kwargs={
'id':int(ps.id),
'userid':r.user.id,
})
startdatestring = startdate.strftime('%Y-%m-%d')
enddatestring = enddate.strftime('%Y-%m-%d')
url += '?when='+startdatestring+'/'+enddatestring
return HttpResponseRedirect(url)
else:
sessioncreateform = PlannedSessionForm(instance=ps)
sps = get_sessions(r,startdate=startdate,enddate=enddate)
breadcrumbs = [
{
'url':reverse(plannedsessions_view,
kwargs={'userid':userid}),
'name': 'Plan'
},
{
'url': reverse(plannedsessions_view,
kwargs={'userid':userid}),
'name': 'Sessions'
},
{
'url':reverse(plannedsession_view,
kwargs={
'userid':userid,
'id':id,
}
),
'name': ps.id
},
{
'url':reverse(plannedsession_edit_view,
kwargs={
'userid':userid,
'id':id,
}
),
'name': 'Edit'
}
]
dateform = DateRangeForm(initial={
'startdate':startdate,
'enddate':enddate,
})
timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d')
return render(request,'plannedsessionedit.html',
{
'teams':get_my_teams(request.user),
'plan':trainingplan,
'breadcrumbs':breadcrumbs,
'form':sessioncreateform,
'active':'nav-plan',
'plannedsessions':sps,
'thesession':ps,
'dateform':dateform,
'rower':r,
'timeperiod':timeperiod,
})
@login_required()
def plannedsession_detach_view(request,id=0,psid=0):
r = getrequestrower(request)
try:
ps = PlannedSession.objects.get(id=psid)
except PlannedSession.DoesNotExist:
raise Http404("Planned Session does not exist")
w = get_workout(id)
if (checkworkoutuser(request.user,w)==False):
return HttpResponseForbidden("Permission Error")
remove_workout_plannedsession(w,ps)
url = reverse(plannedsession_view,kwargs={'id':psid})
return HttpResponseRedirect(url)
@login_required()
def plannedsession_view(request,id=0,userid=0):
r = getrequestrower(request,userid=userid)
try:
ps = PlannedSession.objects.get(id=id)
except PlannedSession.DoesNotExist:
raise Http404("Planned Session does not exist")
if ps.course:
coursescript,coursediv = course_map(ps.course)
else:
coursescript = ''
coursediv = ''
m = ps.manager
mm = Rower.objects.get(user=m)
if ps.manager != request.user:
if r.rowerplan == 'coach':
teams = Team.objects.filter(manager=request.user)
members = Rower.objects.filter(team__in=teams).distinct()
teamusers = [m.user for m in members]
if ps.manager not in teamusers:
raise PermissionDenied("You do not have access to this session")
elif r not in ps.rower.all():
raise PermissionDenied("You do not have access to this session")
resultsdict = get_session_metrics(ps)
resultsdict = pd.DataFrame(resultsdict).transpose().to_dict()
psdict = my_dict_from_instance(ps,PlannedSession)
ws = get_workouts_session(r,ps)
ratio,status,completiondate = is_session_complete(r,ps)
ratio = int(100.*ratio)
# ranking for test
ranking = []
if ps.sessiontype in ['test','coursetest']:
if ps.sessionmode == 'distance':
rankws = Workout.objects.filter(
plannedsession=ps).order_by("duration")
else:
rankws = Workout.objects.filter(
plannedsession=ps).order_by("-distance")
for w in rankws:
dd = w.duration
dddelta = datetime.timedelta(hours=dd.hour,
minutes=dd.minute,
seconds=dd.second,
microseconds=dd.microsecond)
wdict = {
'name': w.user.user.first_name+' '+w.user.user.last_name,
'date': w.date,
'distance': w.distance,
'time': dddelta,
'type': w.workouttype,
'coursecompleted':True,
}
if ps.sessiontype == 'coursetest':
vs = CourseTestResult.objects.filter(plannedsession=ps,
workoutid=w.id)
if vs:
for record in vs:
if record.workoutid == w.id:
coursemeters = record.distance
coursecompleted = record.coursecompleted
t = record.duration
wdict['time'] = datetime.timedelta(
hours=t.hour,
seconds=t.second,
minutes=t.minute,
microseconds=t.microsecond
)
wdict['distance'] = int(round(coursemeters))
wdict['coursecompleted'] = coursecompleted
else:
record = CourseTestResult(
userid=w.user.id,
workoutid=w.id,
plannedsession=ps,
duration=w.duration,
coursecompleted=False,
)
record.save()
job = myqueue(queue,handle_check_race_course,
w.csvfilename,w.id,ps.course.id,
record.id,mode='coursetest')
intsecs = 0
microsecs = 0
# taking workout duration plus 1 minute penalty
wdict['time'] = w.duration
wdict['distance'] = ps.course.distance
wdict['coursecompleted'] = False
ranking.append(wdict)
if ps.sessiontype == 'coursetest':
ranking = sorted(ranking, key=lambda k: k['time'])
# if coursetest, need to reorder the ranking
startdate,enddate = get_dates_timeperiod(request)
try:
trainingplan = TrainingPlan.objects.filter(
startdate__lte = startdate,
rowers = r,
enddate__gte = enddate)[0]
except IndexError:
trainingplan = None
timeperiod = startdate.strftime('%Y-%m-%d')+'/'+enddate.strftime('%Y-%m-%d')
breadcrumbs = [
{
'url':reverse(plannedsessions_view,
kwargs={'userid':userid}),
'name': 'Plan'
},
{
'url': reverse(plannedsessions_view,
kwargs={'userid':userid}),
'name': 'Sessions'
},
{
'url':reverse(plannedsession_view,
kwargs={
'userid':userid,
'id':id,
}
),
'name': ps.id
}
]
return render(request,'plannedsessionview.html',
{
'psdict': psdict,
'attrs':[
'name','startdate','enddate','preferreddate',
'sessiontype',
'sessionmode','criterium',
'sessionvalue','sessionunit','comment',
],
'workouts': ws,
'active':'nav-plan',
'breadcrumbs':breadcrumbs,
'manager':mm,
'rower':r,
'ratio':ratio,
'plan':trainingplan,
'status':status,
'results':resultsdict,
'plannedsession':ps,
'timeperiod':timeperiod,
'ranking':ranking,
'coursescript': coursescript,
'coursediv': coursediv
}
)
class PlannedSessionDelete(DeleteView):
model = PlannedSession
template_name = 'plannedsessiondeleteconfirm.html'
# extra parameters
def get_context_data(self, **kwargs):
context = super(PlannedSessionDelete,self).get_context_data(**kwargs)
if 'userid' in kwargs:
userid = kwargs['userid']
else:
userid = 0
context['active']= 'nav-plan'
context['rower'] = getrequestrower(self.request,userid=userid)
context['ps'] = self.object
psdict = my_dict_from_instance(self.object,PlannedSession)
context['psdict'] = psdict
context['attrs'] = ['name','startdate','enddate','sessiontype']
breadcrumbs = [
{
'url':reverse(plannedsessions_view,
kwargs={'userid':userid}),
'name': 'Plan'
},
{
'url': reverse(plannedsessions_view,
kwargs={'userid':userid}),
'name': 'Sessions'
},
{
'url':reverse(plannedsession_view,
kwargs={
'userid':userid,
'id':self.object.pk,
}
),
'name': self.object.pk
},
{
'url':reverse('plannedsession_delete_view',
kwargs={'pk':self.object.pk}),
'name': 'Delete'
}
]
context['breadcrumbs'] = breadcrumbs
return context
def get_success_url(self):
ws = Workout.objects.filter(plannedsession=self.object)
for w in ws:
w.plannedsession = None
w.save()
url = reverse(plannedsessions_view)
return url
def get_object(self, *args, **kwargs):
obj = super(PlannedSessionDelete, self).get_object(*args, **kwargs)
m = Rower.objects.get(user=obj.manager)
if not checkaccessuser(self.request.user,m):
raise PermissionDenied('You are not allowed to delete this planned session')
return obj
def virtualevents_view(request):
is_ajax = False
if request.is_ajax():
is_ajax = True
# default races
races1 = VirtualRace.objects.filter(
startdate__gte=datetime.date.today(),
)
races2 = VirtualRace.objects.filter(
startdate__lte=datetime.date.today(),
evaluation_closure__gte=timezone.now(),
)
races = (races1 | races2).order_by("startdate","start_time")
if not request.user.is_anonymous():
r = getrower(request.user)
else:
r = None
if request.method == 'POST':
# process form
form = VirtualRaceSelectForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
country = cd['country']
regattatype = cd['regattatype']
if country == 'All':
countries = VirtualRace.objects.order_by('country').values_list('country').distinct()
else:
countries = [country,
'Indoor']
if regattatype == 'upcoming':
races1 = VirtualRace.objects.filter(
startdate__gte=datetime.date.today(),
country__in=countries
)
races2 = VirtualRace.objects.filter(
startdate__lte=datetime.date.today(),
evaluation_closure__gte=timezone.now(),
country__in=countries
)
races = (races1 | races2).order_by("startdate","start_time")
elif regattatype == 'previous':
races = VirtualRace.objects.filter(
evaluation_closure__lt=timezone.now(),
country__in=countries
).order_by("-startdate","-start_time")
elif regattatype == 'ongoing':
races = VirtualRace.objects.filter(
startdate__lte=datetime.date.today(),
evaluation_closure__gte=timezone.now(),
country__in=countries
).order_by("startdate","start_time")
elif regattatype == 'my':
mysessions = get_my_session_ids(r)
races = VirtualRace.objects.filter(
id__in=mysessions,
country__in=countries
).order_by("startdate","start_time")
elif regattatype == 'all':
races = VirtualRace.objects.filter(
country__in=countries
).order_by("startdate","start_time")
else:
form = VirtualRaceSelectForm()
if is_ajax:
return render(request,'racelist.html',
{ 'races':races,
'rower':r,
})
breadcrumbs = [
{
'url':reverse(virtualevents_view),
'name': 'Racing'
},
]
return render(request,'virtualevents.html',
{ 'races':races,
'form':form,
'breadcrumbs':breadcrumbs,
'active':'nav-racing',
'rower':r,
}
)
@login_required()
def virtualevent_disqualify_view(request,raceid=0,recordid=0):
r = getrower(request.user)
try:
race = VirtualRace.objects.get(id=raceid)
except VirtualRace.DoesNotExist:
raise Http404("Virtual Race does not exist")
if r.user != race.manager:
raise PermissionDenied("Access denied")
if race.sessiontype == 'race':
recordobj = VirtualRaceResult
else:
recordobj = IndoorVirtualRaceResult
# datum moet voor race evaluation date zijn (ook in template controleren)
try:
record = recordobj.objects.get(id=recordid)
except recordobj.DoesNotExist:
messages.error(request,"We couldn't find the record")
if timezone.now() > race.evaluation_closure+datetime.timedelta(hours=1):
messages.error(request,"The evaluation is already closed and the results are official")
url = reverse(virtualevent_view,kwargs={'id':raceid})
return HttpResponseRedirect(url)
if request.method == 'POST':
form = DisqualificationForm(request.POST)
if form.is_valid():
message = form.cleaned_data['message']
reason = form.cleaned_data['reason']
disqualifier = disqualifiers[reason]
u = User.objects.get(id=record.userid)
name = record.username
job = myqueue(queue,handle_send_disqualification_email,
u.email, name,
disqualifier,message,race.name)
messages.info(request,"We have invalidated the result for: "+str(record))
record.coursecompleted = False
record.save()
url = reverse(virtualevent_view,kwargs={'id':raceid})
return HttpResponseRedirect(url)
else:
form = DisqualificationForm(request.POST)
workout = Workout.objects.get(id=record.workoutid)
g = GraphImage.objects.filter(workout=workout).order_by("-creationdatetime")
for i in g:
try:
width,height = Image.open(i.filename).size
i.width = width
i.height = height
i.save()
except:
pass
script, div = interactive_chart(record.workoutid)
f1 = workout.csvfilename
rowdata = rdata(f1)
hascoordinates = 1
if rowdata != 0:
try:
latitude = rowdata.df[' latitude']
if not latitude.std():
hascoordinates = 0
except KeyError, AttributeError:
hascoordinates = 0
else:
hascoordinates = 0
if hascoordinates:
mapscript, mapdiv = leaflet_chart(rowdata.df[' latitude'],
rowdata.df[' longitude'],
workout.name)
else:
mapscript = ""
mapdiv = ""
breadcrumbs = [
{
'url':reverse(virtualevents_view),
'name': 'Racing'
},
{
'url':reverse(virtualevent_view,
kwargs={'id':race.id}),
'name': race.name
},
{
'url':reverse(virtualevent_disqualify_view,
kwargs={'raceid':raceid,
'recordid':recordid}),
'name': 'Disqualify Entry'
},
]
return render(request,"disqualification_view.html",
{'workout':workout,
'active':'nav-racing',
'graphs':g,
'interactiveplot':script,
'the_div':div,
'mapscript':mapscript,
'mapdiv':mapdiv,
'form':form,
'race':race,
'record':record,
})
def virtualevent_view(request,id=0):
results = []
if not request.user.is_anonymous():
r = getrower(request.user)
else:
r = None
try:
race = VirtualRace.objects.get(id=id)
except VirtualRace.DoesNotExist:
raise Http404("Virtual Race does not exist")
if race.sessiontype == 'race':
script,div = course_map(race.course)
resultobj = VirtualRaceResult
else:
script = ''
div = ''
resultobj = IndoorVirtualRaceResult
records = resultobj.objects.filter(race=race)
buttons = []
if not request.user.is_anonymous():
if race_can_register(r,race):
buttons += ['registerbutton']
if race_can_adddiscipline(r,race):
buttons += ['adddisciplinebutton']
if race_can_submit(r,race):
buttons += ['submitbutton']
if race_can_resubmit(r,race):
buttons += ['resubmitbutton']
if race_can_withdraw(r,race):
buttons += ['withdrawbutton']
if race_can_edit(r,race):
buttons += ['editbutton']
if request.method == 'POST':
form = RaceResultFilterForm(request.POST,records=records)
if form.is_valid():
cd = form.cleaned_data
try:
sex = cd['sex']
except KeyError:
sex = ['female','male','mixed']
try:
boattype = cd['boattype']
except KeyError:
boattype = mytypes.waterboattype
try:
boatclass = cd['boatclass']
except KeyError:
if race.sessiontype == 'race':
boatclass = [t for t in mytypes.otwtypes]
else:
boatclass = [t for t in mytypes.otetypes]
age_min = cd['age_min']
age_max = cd['age_max']
try:
weightcategory = cd['weightcategory']
except KeyError:
weightcategory = ['hwt','lwt']
if race.sessiontype == 'race':
results = resultobj.objects.filter(
race=race,
workoutid__isnull=False,
boatclass__in=boatclass,
boattype__in=boattype,
sex__in=sex,
weightcategory__in=weightcategory,
age__gte=age_min,
age__lte=age_max
).order_by("duration")
else:
results = resultobj.objects.filter(
race=race,
workoutid__isnull=False,
boatclass__in=boatclass,
sex__in=sex,
weightcategory__in=weightcategory,
age__gte=age_min,
age__lte=age_max
).order_by("duration","-distance")
# to-do - add DNS
dns = []
if timezone.now() > race.evaluation_closure:
dns = resultobj.objects.filter(
race=race,
workoutid__isnull=True,
boatclass__in=boatclass,
sex__in=sex,
weightcategory__in=weightcategory,
age__gte=age_min,
age__lte=age_max
)
else:
results = resultobj.objects.filter(
race=race,
workoutid__isnull=False,
coursecompleted=True,
).order_by("duration","-distance")
if results:
form = RaceResultFilterForm(records=records)
else:
form = None
# to-do - add DNS
dns = []
if timezone.now() > race.evaluation_closure:
dns = resultobj.objects.filter(
race=race,
workoutid__isnull=True,
)
breadcrumbs = [
{
'url':reverse(virtualevents_view),
'name': 'Racing'
},
{
'url':reverse(virtualevent_view,
kwargs={'id':race.id}
),
'name': race.name
}
]
racelogos = race.logos.all()
if racelogos:
racelogo = racelogos[0]
else:
racelogo = None
return render(request,'virtualevent.html',
{
'coursescript':script,
'coursediv':div,
'breadcrumbs':breadcrumbs,
'race':race,
'rower':r,
'results':results,
'buttons':buttons,
'dns':dns,
'records':records,
'racelogo':racelogo,
'form':form,
'active':'nav-racing',
})
@login_required()
def virtualevent_withdraw_view(request,id=0,recordid=None):
r = getrower(request.user)
try:
race = VirtualRace.objects.get(id=id)
except VirtualRace.DoesNotExist:
raise Http404("Virtual Race does not exist")
if race_can_withdraw(r,race):
remove_rower_race(r,race,recordid=recordid)
messages.info(request,
"You have successfully withdrawn from this race.")
else:
messages.error(request,"You cannot withdraw from this race")
url = reverse(virtualevent_view,
kwargs = {
'id':race.id
})
return HttpResponseRedirect(url)
@login_required()
def virtualevent_addboat_view(request,id=0):
r = getrower(request.user)
try:
race = VirtualRace.objects.get(id=id)
except VirtualRace.DoesNotExist:
raise Http404("Virtual Race does not exist")
if not race_can_adddiscipline(r,race):
messages.error(request,"You cannot register for this race")
url = reverse(virtualevent_view,
kwargs = {
'id':race.id
})
return HttpResponseRedirect(url)
records = VirtualRaceResult.objects.filter(
userid = r.id,
race = race
)
boattypes = [record.boattype for record in records]
boatclasses = [record.boatclass for record in records]
allowedboats = tuple([ type for type in mytypes.boattypes if type[0] not in boattypes] )
# we're still here
if request.method == 'POST':
# process form
form = VirtualRaceResultForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
teamname = cd['teamname']
boattype = cd['boattype']
boatclass = cd['boatclass']
weightcategory = cd['weightcategory']
age = cd['age']
mix = cd['mix']
sex = r.sex
if mix:
sex = 'mixed'
if boattype == '1x' and r.birthdate:
age = calculate_age(r.birthdate)
sex = r.sex
if sex == 'not specified':
sex = 'male'
if boattype in boattypes and boatclass in boatclasses:
# check if different sexes
therecords = records.filter(
boattype=boattype,
boatclass=boatclass)
thesexes = [record.sex for record in therecords]
if sex in thesexes:
messages.error(
request,
"You have already registered in that boat class/type"
)
url = reverse(virtualevent_view,
kwargs = {
'id': race.id
}
)
return HttpResponseRedirect(url)
record = VirtualRaceResult(
userid=r.id,
teamname=teamname,
race=race,
username = u'{f} {l}'.format(
f = r.user.first_name,
l = r.user.last_name
),
weightcategory=weightcategory,
duration=datetime.time(0,0),
boattype=boattype,
boatclass=boatclass,
coursecompleted=False,
sex=sex,
age=age
)
record.save()
add_rower_race(r,race)
messages.info(
request,
"You have successfully registered for this race. Good luck!"
)
url = reverse(virtualevent_view,
kwargs = {
'id':race.id
})
return HttpResponseRedirect(url)
else:
initial = {
'age': calculate_age(r.birthdate),
'weightcategory': r.weightcategory,
}
form = VirtualRaceResultForm(initial=initial)
breadcrumbs = [
{
'url':reverse(virtualevents_view),
'name': 'Racing'
},
{
'url':reverse(virtualevent_view,
kwargs={'id':race.id}
),
'name': race.name
},
{
'url': reverse(virtualevent_addboat_view,
kwargs = {'id':race.id}
),
'name': 'Add Discipline'
}
]
return render(request,'virtualeventregister.html',
{
'form':form,
'breadcrumbs':breadcrumbs,
'race':race,
'userid':r.user.id,
'active': 'nav-racing',
})
@login_required()
def virtualevent_register_view(request,id=0):
r = getrower(request.user)
try:
race = VirtualRace.objects.get(id=id)
except VirtualRace.DoesNotExist:
raise Http404("Virtual Race does not exist")
if not race_can_register(r,race):
messages.error(request,"You cannot register for this race")
url = reverse(virtualevent_view,
kwargs = {
'id':race.id
})
return HttpResponseRedirect(url)
# we're still here
if request.method == 'POST':
# process form
form = VirtualRaceResultForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
teamname = cd['teamname']
boattype = cd['boattype']
boatclass = cd['boatclass']
weightcategory = cd['weightcategory']
age = cd['age']
mix = cd['mix']
sex = r.sex
if mix:
sex = 'mixed'
if boattype == '1x' and r.birthdate:
age = calculate_age(r.birthdate)
sex = r.sex
if sex == 'not specified':
sex = 'male'
record = VirtualRaceResult(
userid=r.id,
teamname=teamname,
race=race,
username = u'{f} {l}'.format(
f = r.user.first_name,
l = r.user.last_name
),
weightcategory=weightcategory,
duration=datetime.time(0,0),
boatclass=boatclass,
boattype=boattype,
coursecompleted=False,
sex=sex,
age=age
)
record.save()
add_rower_race(r,race)
otherrecords = IndoorVirtualRaceResult.objects.filter(
race = race).exclude(userid = r.id)
for otherrecord in otherrecords:
otheruser = Rower.objects.get(id=otherrecord.userid)
othername = otheruser.user.first_name+' '+otheruser.user.last_name
registeredname = r.user.first_name+' '+r.user.last_name
if otherrecord.emailnotifications:
job = myqueue(
queue,
handle_sendemail_raceregistration,
otheruser.user.email, othername,
registeredname,
race.name,
race.id
)
messages.info(
request,
"You have successfully registered for this race. Good luck!"
)
url = reverse(virtualevent_view,
kwargs = {
'id':race.id
})
return HttpResponseRedirect(url)
else:
initial = {
'age': calculate_age(r.birthdate),
'weightcategory': r.weightcategory,
}
form = VirtualRaceResultForm(initial=initial)
breadcrumbs = [
{
'url':reverse(virtualevents_view),
'name': 'Racing'
},
{
'url':reverse(virtualevent_view,
kwargs={'id':race.id}
),
'name': race.name
},
{
'url': reverse(virtualevent_register_view,
kwargs = {'id':race.id}
),
'name': 'Register'
}
]
return render(request,'virtualeventregister.html',
{
'form':form,
'breadcrumbs':breadcrumbs,
'race':race,
'userid':r.user.id,
})
@login_required()
def indoorvirtualevent_register_view(request,id=0):
r = getrower(request.user)
try:
race = VirtualRace.objects.get(id=id)
except VirtualRace.DoesNotExist:
raise Http404("Virtual Race does not exist")
if not race_can_register(r,race):
messages.error(request,"You cannot register for this race")
url = reverse(virtualevent_view,
kwargs = {
'id':race.id
})
return HttpResponseRedirect(url)
# we're still here
if request.method == 'POST':
# process form
form = IndoorVirtualRaceResultForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
teamname = cd['teamname']
weightcategory = cd['weightcategory']
age = cd['age']
boatclass = cd['boatclass']
sex = r.sex
if r.birthdate:
age = calculate_age(r.birthdate)
sex = r.sex
if sex == 'not specified':
sex = 'male'
record = IndoorVirtualRaceResult(
userid=r.id,
teamname=teamname,
race=race,
username = u'{f} {l}'.format(
f = r.user.first_name,
l = r.user.last_name
),
weightcategory=weightcategory,
duration=datetime.time(0,0),
boatclass=boatclass,
coursecompleted=False,
sex=sex,
age=age
)
record.save()
add_rower_race(r,race)
otherrecords = IndoorVirtualRaceResult.objects.filter(
race = race).exclude(userid = r.id)
for otherrecord in otherrecords:
otheruser = Rower.objects.get(id=otherrecord.userid)
othername = otheruser.user.first_name+' '+otheruser.user.last_name
registeredname = r.user.first_name+' '+r.user.last_name
if otherrecord.emailnotifications:
job = myqueue(
queue,
handle_sendemail_raceregistration,
otheruser.user.email, othername,
registeredname,
race.name,
race.id
)
messages.info(
request,
"You have successfully registered for this race. Good luck!"
)
url = reverse(virtualevent_view,
kwargs = {
'id':race.id
})
return HttpResponseRedirect(url)
else:
initial = {
'age': calculate_age(r.birthdate),
'weightcategory': r.weightcategory,
}
form = IndoorVirtualRaceResultForm(initial=initial)
breadcrumbs = [
{
'url':reverse(virtualevents_view),
'name': 'Racing'
},
{
'url':reverse(virtualevent_view,
kwargs={'id':race.id}
),
'name': race.name
},
{
'url': reverse(indoorvirtualevent_register_view,
kwargs = {'id':race.id}
),
'name': 'Register'
}
]
return render(request,'virtualeventregister.html',
{
'form':form,
'race':race,
'breadcrumbs':breadcrumbs,
'userid':r.user.id,
})
@login_required()
def indoorvirtualevent_create_view(request):
r = getrower(request.user)
if request.method == 'POST':
racecreateform = IndoorVirtualRaceForm(request.POST)
if racecreateform.is_valid():
cd = racecreateform.cleaned_data
startdate = cd['startdate']
start_time = cd['start_time']
enddate = cd['enddate']
end_time = cd['end_time']
comment = cd['comment']
sessionunit = cd['sessionunit']
sessionvalue = cd['sessionvalue']
name = cd['name']
registration_form = cd['registration_form']
registration_closure = cd['registration_closure']
evaluation_closure = cd['evaluation_closure']
contact_phone = cd['contact_phone']
contact_email = cd['contact_email']
# correct times
timezone_str = cd['timezone']
startdatetime = datetime.datetime.combine(startdate,start_time)
enddatetime = datetime.datetime.combine(enddate,end_time)
startdatetime = pytz.timezone(timezone_str).localize(
startdatetime
)
enddatetime = pytz.timezone(timezone_str).localize(
enddatetime
)
evaluation_closure = pytz.timezone(timezone_str).localize(
evaluation_closure.replace(tzinfo=None)
)
if registration_form == 'manual':
try:
registration_closure = pytz.timezone(
timezone_str
).localize(
registration_closure.replace(tzinfo=None)
)
except AttributeError:
registration_closure = startdatetime
elif registration_form == 'windowstart':
registration_closure = startdatetime
elif registration_form == 'windowend':
registration_closure = enddatetime
else:
registration_closure = evaluation_closure
if sessionunit == 'min':
sessionmode = 'time'
else:
sessionmode = 'distance'
vs = VirtualRace(
name=name,
startdate=startdate,
preferreddate = startdate,
start_time = start_time,
enddate=enddate,
end_time=end_time,
comment=comment,
sessiontype = 'indoorrace',
sessionunit = sessionunit,
sessionmode = sessionmode,
sessionvalue = sessionvalue,
course=None,
timezone=timezone_str,
evaluation_closure=evaluation_closure,
registration_closure=registration_closure,
contact_phone=contact_phone,
contact_email=contact_email,
country = 'Indoor',
manager=request.user,
)
vs.save()
# create Site Announcement & Tweet
if settings.DEBUG:
dotweet = False
elif 'dev' in settings.SITE_URL:
dotweet = False
else:
dotweet = True
try:
sa = SiteAnnouncement(
announcement = "New Virtual Indoor Race on rowsandall.com: {name}".format(
name = name.encode('utf8'),
),
dotweet = dotweet
)
sa.save()
except UnicodeEncodeError:
sa = SiteAnnouncement(
announcement = "New Virtual Indoor Race on rowsandall.com: {name}".format(
name = name,
),
dotweet = dotweet
)
sa.save()
url = reverse(virtualevents_view)
return HttpResponseRedirect(url)
else:
racecreateform = IndoorVirtualRaceForm()
breadcrumbs = [
{
'url':reverse(virtualevents_view),
'name': 'Racing'
},
{
'url':reverse(indoorvirtualevent_create_view,
),
'name': 'New Indoor Virtual Regatta'
},
]
return render(request,'indoorvirtualeventcreate.html',
{
'form':racecreateform,
'breadcrumbs':breadcrumbs,
'rower':r,
'active':'nav-racing',
})
@login_required()
def virtualevent_create_view(request):
r = getrower(request.user)
if request.method == 'POST':
racecreateform = VirtualRaceForm(request.POST)
if racecreateform.is_valid():
cd = racecreateform.cleaned_data
startdate = cd['startdate']
start_time = cd['start_time']
enddate = cd['enddate']
end_time = cd['end_time']
comment = cd['comment']
course = cd['course']
name = cd['name']
registration_form = cd['registration_form']
registration_closure = cd['registration_closure']
evaluation_closure = cd['evaluation_closure']
contact_phone = cd['contact_phone']
contact_email = cd['contact_email']
# correct times
geocourse = GeoCourse.objects.get(id= course.id)
timezone_str = get_course_timezone(geocourse)
startdatetime = datetime.datetime.combine(startdate,start_time)
enddatetime = datetime.datetime.combine(enddate,end_time)
startdatetime = pytz.timezone(timezone_str).localize(
startdatetime
)
enddatetime = pytz.timezone(timezone_str).localize(
enddatetime
)
evaluation_closure = pytz.timezone(timezone_str).localize(
evaluation_closure.replace(tzinfo=None)
)
if registration_form == 'manual':
try:
registration_closure = pytz.timezone(
timezone_str
).localize(
registration_closure.replace(tzinfo=None)
)
except AttributeError:
registration_closure = startdatetime
elif registration_form == 'windowstart':
registration_closure = startdatetime
elif registration_form == 'windowend':
registration_closure = enddatetime
else:
registration_closure = evaluation_closure
vs = VirtualRace(
name=name,
startdate=startdate,
preferreddate = startdate,
start_time = start_time,
enddate=enddate,
end_time=end_time,
course=geocourse,
comment=comment,
sessiontype = 'race',
timezone=timezone_str,
evaluation_closure=evaluation_closure,
registration_closure=registration_closure,
contact_phone=contact_phone,
contact_email=contact_email,
country = course.country,
manager=request.user,
)
vs.save()
# create Site Announcement & Tweet
if settings.DEBUG:
dotweet = False
elif 'dev' in settings.SITE_URL:
dotweet = False
else:
dotweet = True
try:
sa = SiteAnnouncement(
announcement = "New Virtual Race on rowsandall.com: {name} on course {course}".format(
name = name.encode('utf8'),
course = course.name.encode('utf8')
),
dotweet = dotweet
)
sa.save()
except UnicodeEncodeError:
sa = SiteAnnouncement(
announcement = "New Virtual Race on rowsandall.com: {name} on course {course}".format(
name = name,
course = str(course.name.encode('utf8','ignore'))
),
dotweet = dotweet
)
sa.save()
url = reverse(virtualevents_view)
return HttpResponseRedirect(url)
else:
racecreateform = VirtualRaceForm()
breadcrumbs = [
{
'url':reverse(virtualevents_view),
'name': 'Racing'
},
{
'url':reverse(virtualevent_create_view,
),
'name': 'New Virtual Regatta'
},
]
return render(request,'virtualeventcreate.html',
{
'form':racecreateform,
'breadcrumbs':breadcrumbs,
'rower':r,
'active':'nav-racing',
})
@login_required()
def virtualevent_edit_view(request,id=0):
r = getrower(request.user)
try:
race = VirtualRace.objects.get(id=id)
if race.manager != request.user:
raise PermissionDenied("Access denied")
except VirtualRace.DoesNotExist:
raise Http404("Virtual Race does not exist")
start_time = race.start_time
start_date = race.startdate
startdatetime = datetime.datetime.combine(start_date,start_time)
startdatetime = pytz.timezone(race.timezone).localize(
startdatetime
)
if timezone.now() > startdatetime:
messages.error(request,"You cannot edit a race after the start of the race window")
url = reverse(virtualevent_view,
kwargs={
'id':race.id,
})
if request.method == 'POST':
racecreateform = VirtualRaceForm(request.POST,instance=race)
if racecreateform.is_valid():
cd = racecreateform.cleaned_data
res, message = update_virtualrace(race,cd)
if res:
messages.info(request,message)
else:
messages.error(request,message)
url = reverse(virtualevent_view,
kwargs = {
'id':race.id
})
return HttpResponseRedirect(url)
else:
racecreateform = VirtualRaceForm(instance=race)
breadcrumbs = [
{
'url':reverse(virtualevents_view),
'name': 'Racing'
},
{
'url':reverse(virtualevent_view,
kwargs={'id':race.id}
),
'name': race.name
},
{
'url': reverse(virtualevent_edit_view,
kwargs = {'id':race.id}
),
'name': 'Edit'
}
]
return render(request,'virtualeventedit.html',
{
'form':racecreateform,
'breadcrumbs':breadcrumbs,
'rower':r,
'race':race,
})
@login_required()
def indoorvirtualevent_edit_view(request,id=0):
r = getrower(request.user)
try:
race = VirtualRace.objects.get(id=id)
if race.manager != request.user:
raise PermissionDenied("Access denied")
except VirtualRace.DoesNotExist:
raise Http404("Virtual Race does not exist")
start_time = race.start_time
start_date = race.startdate
startdatetime = datetime.datetime.combine(start_date,start_time)
startdatetime = pytz.timezone(race.timezone).localize(
startdatetime
)
if timezone.now() > startdatetime:
messages.error(request,"You cannot edit a race after the start of the race window")
url = reverse(virtualevent_view,
kwargs={
'id':race.id,
})
if request.method == 'POST':
racecreateform = IndoorVirtualRaceForm(request.POST,instance=race)
if racecreateform.is_valid():
cd = racecreateform.cleaned_data
res, message = update_indoorvirtualrace(race,cd)
if res:
messages.info(request,message)
else:
messages.error(request,message)
url = reverse(virtualevent_view,
kwargs = {
'id':race.id
})
return HttpResponseRedirect(url)
else:
racecreateform = IndoorVirtualRaceForm(instance=race)
breadcrumbs = [
{
'url':reverse(virtualevents_view),
'name': 'Racing'
},
{
'url':reverse(virtualevent_view,
kwargs={'id':race.id}
),
'name': race.name
},
{
'url': reverse(indoorvirtualevent_edit_view,
kwargs = {'id':race.id}
),
'name': 'Edit'
}
]
return render(request,'virtualeventedit.html',
{
'form':racecreateform,
'breadcrumbs':breadcrumbs,
'rower':r,
'race':race,
})
@login_required()
def virtualevent_submit_result_view(request,id=0):
r = getrower(request.user)
try:
race = VirtualRace.objects.get(id=id)
except VirtualRace.DoesNotExist:
raise Http404("Virtual Race does not exist")
start_time = race.start_time
start_date = race.startdate
startdatetime = datetime.datetime.combine(start_date, start_time)
startdatetime = pytz.timezone(race.timezone).localize(startdatetime)
end_time = race.end_time
end_date = race.enddate
enddatetime = datetime.datetime.combine(end_date, end_time)
enddatetime = pytz.timezone(race.timezone).localize(enddatetime)
can_submit = race_can_submit(r,race) or race_can_resubmit(r,race)
if race.sessiontype == 'race':
resultobj = VirtualRaceResult
else:
resultobj = IndoorVirtualRaceResult
records = resultobj.objects.filter(
userid = r.id,
race=race
)
entrychoices = []
for record in records:
rtpl = (record.id, record.__unicode__())
entrychoices.append(rtpl)
entries = {}
entries['choices'] = entrychoices
entries['initial'] = [records[0].id]
if not can_submit:
messages.error(request,'You cannot submit a result to this race')
url = reverse(virtualevent_view,
kwargs = {
'id':id
}
)
return HttpResponseRedirect(url)
ws = Workout.objects.filter(
user=r,
startdatetime__gte=startdatetime,
startdatetime__lte=enddatetime,
).order_by("date","startdatetime","id")
initialworkouts = [w.id for w in Workout.objects.filter(
user=r,plannedsession=race
)]
workoutdata = {}
workoutdata['initial'] = []
choices = []
for w in ws:
wtpl = (w.id, w.__unicode__())
choices.append(wtpl)
if w.id in initialworkouts:
workoutdata['initial'].append(w.id)
workoutdata['choices'] = tuple(choices)
if request.method == 'POST':
w_form = WorkoutRaceSelectForm(workoutdata,entries,request.POST)
if w_form.is_valid():
selectedworkout = w_form.cleaned_data['workouts']
splitsecond = 0
recordid = w_form.cleaned_data['record']
# splittime = w_form.cleaned_data['evaluate_after']
# if splittime is not None:
# splitsecond = splittime.hour*3600
# splitsecond += splittime.minute*60
# splitsecond += splittime.second
# splitsecond += splittime.microsecond/1.e6
# else:
# splitsecond = 0
else:
selectedworkout = None
if selectedworkout is not None:
workouts = Workout.objects.filter(id=selectedworkout)
if race.sessiontype == 'race':
result,comments,errors,jobid = add_workout_race(
workouts,race,r,
splitsecond=splitsecond,recordid=recordid)
else:
result,comments,errors,jobid = add_workout_indoorrace(
workouts,race,r,recordid=recordid)
# if result:
# for w in ws:
# remove_workout_plannedsession(w,race)
# delete_race_result(w,race)
for c in comments:
messages.info(request,c)
for er in errors:
messages.error(request,er)
if jobid:
try:
request.session['async_tasks'] += [(jobid,'submit_race')]
except KeyError:
request.session['async_tasks'] = [(jobid,'submit_race')]
messages.info(request,"We are evaluating your result. The page will reload when we're done. Your result will show up if you adhered to the course")
if result:
otherrecords = resultobj.objects.filter(
race = race).exclude(userid = r.id)
for otherrecord in otherrecords:
otheruser = Rower.objects.get(id=otherrecord.userid)
othername = otheruser.user.first_name+' '+otheruser.user.last_name
registeredname = r.user.first_name+' '+r.user.last_name
if otherrecord.emailnotifications:
job = myqueue(
queue,
handle_sendemail_racesubmission,
otheruser.user.email, othername,
registeredname,
race.name,
race.id
)
# redirect to race page
url = reverse(virtualevent_view,
kwargs = {
'id':race.id
})
return HttpResponseRedirect(url)
else:
w_form = WorkoutRaceSelectForm(workoutdata,entries)
breadcrumbs = [
{
'url':reverse(virtualevents_view),
'name': 'Racing'
},
{
'url':reverse(virtualevent_view,
kwargs={'id':race.id}
),
'name': race.name
},
{
'url': reverse(virtualevent_submit_result_view,
kwargs = {'id':race.id}
),
'name': 'Submit Result'
}
]
return render(request,'race_submit.html',
{
'race':race,
'workouts':ws,
'breadcrumbs':breadcrumbs,
'active':'nav-racing',
'rower':r,
'w_form':w_form,
})
@user_passes_test(hasplannedsessions,login_url="/rowers/promembership",
message="This functionality requires a Coach or Self-Coach plan",
redirect_field_name=None)
def rower_create_trainingplan(request,userid=0):
therower = getrequestrower(request,userid=userid)
theuser = therower.user
themanager = getrower(request.user)
if request.method == 'POST' and 'date' in request.POST:
targetform = TrainingTargetForm(request.POST,user=request.user)
if targetform.is_valid():
name = targetform.cleaned_data['name']
date = targetform.cleaned_data['date']
notes = targetform.cleaned_data['notes']
t = TrainingTarget(
name=name,
date=date,
manager=themanager,
notes=notes)
t.save()
t.rowers.add(therower)
t.save()
elif request.method == 'POST' and 'startdate' in request.POST:
form = TrainingPlanForm(request.POST,user=request.user)
if form.is_valid():
name = form.cleaned_data['name']
try:
target = form.cleaned_data['target']
except KeyError:
try:
targetid = request.POST['target']
if targetid != '':
target = TrainingTarget.objects.get(id=int(targetid))
else:
target = None
except KeyError:
target = None
startdate = form.cleaned_data['startdate']
enddate = form.cleaned_data['enddate']
try:
athletes = form.cleaned_data['rowers']
except KeyError:
athletes = [therower]
p = TrainingPlan(
name=name,
target=target,
manager=themanager,
startdate=startdate,
enddate=enddate,
)
p.save()
for athlete in athletes:
p.rowers.add(athlete)
targets = TrainingTarget.objects.filter(
rowers=therower,
date__gte=datetime.date.today(),
).order_by("date")
targetform = TrainingTargetForm(user=request.user)
plans = TrainingPlan.objects.filter(rowers=therower).order_by("-startdate")
form = TrainingPlanForm(targets=targets,
initial={'status':False,'rowers':[therower]},
user=request.user)
breadcrumbs = [
{
'url':reverse(plannedsessions_view,
kwargs={'userid':userid}),
'name': 'Plan'
},
{
'url':reverse(rower_create_trainingplan,
kwargs={'userid':userid}),
'name': 'Manage Plans and Targets'
}
]
return render(request,'trainingplan_create.html',
{
'form':form,
'rower':therower,
'breadcrumbs':breadcrumbs,
'plans':plans,
'active':'nav-plan',
'targets':targets,
'targetform':targetform,
})
@user_passes_test(hasplannedsessions,login_url="/rowers/promembership",
message="This functionality requires a Coach or Self-Coach plan",
redirect_field_name=None)
def rower_delete_trainingtarget(request,id=0):
try:
target = TrainingTarget.objects.get(id=id)
except TrainingPlan.DoesNotExist:
raise Http404("Training Plan Does Not Exist")
if checkaccessuser(request.user,target.manager):
target.delete()
messages.info(request,"We have deleted the training target")
else:
raise PermissionDenied("Access denied")
url = reverse(rower_create_trainingplan)
return HttpResponseRedirect(url)
@user_passes_test(hasplannedsessions,login_url="/rowers/promembership",
message="This functionality requires a Coach or Self-Coach plan",
redirect_field_name=None)
def rower_delete_trainingplan(request,id=0):
try:
plan = TrainingPlan.objects.get(id=id)
except TrainingPlan.DoesNotExist:
raise Http404("Training Plan Does Not Exist")
if checkaccessuser(request.user,plan.manager):
plan.delete()
messages.info(request,"We have deleted the training plan")
else:
raise PermissionDenied("Access denied")
url = reverse(rower_create_trainingplan)
return HttpResponseRedirect(url)
class TrainingPlanDelete(DeleteView):
model = TrainingPlan
template_name = 'trainingplan_delete.html'
success_url = reverse_lazy(rower_create_trainingplan)
def get_object(self, *args, **kwargs):
obj = super(TrainingPlanDelete, self).get_object(*args, **kwargs)
if not checkaccessuser(self.request.user,obj.manager):
raise PermissionDenied('You are not allowed to delete this training plan')
return obj
class MicroCycleDelete(DeleteView):
model = TrainingMicroCycle
template_name = 'trainingplan_delete.html'
# extra parameters
def get_context_data(self, **kwargs):
context = super(MicroCycleDelete, self).get_context_data(**kwargs)
if 'userid' in kwargs:
userid = kwargs['userid']
else:
userid=0
breadcrumbs = [
{
'url':reverse(plannedsessions_view,
kwargs={'userid':userid}),
'name': 'Plan'
},
{
'url':reverse(rower_trainingplan_view,
kwargs={'userid':userid,
'id':self.object.plan.plan.plan.id}),
'name': self.object.plan.plan.plan.name
},
{
'url':reverse('macrocycle_update_view',
kwargs={'pk':self.object.plan.plan.pk}),
'name': self.object.plan.plan.name
},
{
'url':reverse('mesocycle_update_view',
kwargs={'pk':self.object.plan.pk}),
'name': self.object.plan.name
},
{
'url':reverse('microcycle_update_view',
kwargs={'pk':self.object.pk}),
'name': self.object.name
}
]
context['active'] = 'nav-plan'
context['breadcrumbs'] = breadcrumbs
context['rower'] = getrequestrower(self.request,userid=userid)
return context
def get_success_url(self):
plan = self.object.plan.plan.plan
createmacrofillers(plan)
thismesoid = self.object.plan.pk
return reverse(rower_trainingplan_view,
kwargs = {
'id':plan.id,
'thismesoid':thismesoid
})
def get_object(self, *args, **kwargs):
obj = super(MicroCycleDelete, self).get_object(*args, **kwargs)
if not checkaccessuser(self.request.user,obj.plan.plan.plan.manager):
raise PermissionDenied('You are not allowed to delete this training plan cycle')
return obj
class MesoCycleDelete(DeleteView):
model = TrainingMesoCycle
template_name = 'trainingplan_delete.html'
# extra parameters
def get_context_data(self, **kwargs):
context = super(MesoCycleDelete, self).get_context_data(**kwargs)
if 'userid' in kwargs:
userid = kwargs['userid']
else:
userid=0
breadcrumbs = [
{
'url':reverse(plannedsessions_view,
kwargs={'userid':userid}),
'name': 'Plan'
},
{
'url':reverse(rower_trainingplan_view,
kwargs={'userid':userid,
'id':self.object.plan.plan.id}),
'name': self.object.plan.plan.name
},
{
'url':reverse('macrocycle_update_view',
kwargs={'pk':self.object.plan.pk}),
'name': self.object.plan.name
},
{
'url':reverse('mesocycle_update_view',
kwargs={'pk':self.object.pk}),
'name': self.object.name
}
]
context['active'] = 'nav-plan'
context['breadcrumbs'] = breadcrumbs
context['rower'] = getrequestrower(self.request,userid=userid)
return context
def get_success_url(self):
plan = self.object.plan.plan
thismacroid = self.object.plan.pk
createmacrofillers(plan)
return reverse(rower_trainingplan_view,
kwargs = {
'id':plan.id,
'thismacroid':thismacroid,
})
def get_object(self, *args, **kwargs):
obj = super(MesoCycleDelete, self).get_object(*args, **kwargs)
if not checkaccessuser(self.request.user,obj.plan.plan.manager):
raise PermissionDenied('You are not allowed to delete this training plan cycle')
return obj
class GraphDelete(DeleteView):
login_required = True
model = GraphImage
template_name = 'graphimage_delete_confirm.html'
# extra parameters
def get_context_data(self, **kwargs):
context = super(GraphDelete, self).get_context_data(**kwargs)
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':get_workout_default_page(self.request,self.object.workout.id),
'name': str(self.object.workout.id)
},
{
'url':reverse(graph_show_view,kwargs={'id':self.object.pk}),
'name': 'Chart'
},
{ 'url':reverse('graph_delete',kwargs={'pk':str(self.object.pk)}),
'name': 'Delete'
}
]
context['active'] = 'nav-workouts'
context['rower'] = getrower(self.request.user)
context['breadcrumbs'] = breadcrumbs
return context
def get_success_url(self):
w = self.object.workout
return reverse(workout_edit_view,kwargs={'id':str(w.id)})
def get_object(self, *args, **kwargs):
obj = super(GraphDelete, self).get_object(*args, **kwargs)
if not checkaccessuser(self.request.user,obj.workout.user):
raise PermissionDenied('You are not allowed to delete this chart')
return obj
class WorkoutDelete(DeleteView):
login_required = True
model = Workout
template_name = 'workout_delete_confirm.html'
# extra parameters
def get_context_data(self, **kwargs):
context = super(WorkoutDelete, self).get_context_data(**kwargs)
breadcrumbs = [
{
'url':'/rowers/list-workouts',
'name':'Workouts'
},
{
'url':get_workout_default_page(self.request,self.object.id),
'name': str(self.object.id)
},
{ 'url':reverse('workout_delete',kwargs={'pk':str(self.object.pk)}),
'name': 'Delete'
}
]
mayedit=0
promember=0
if not self.request.user.is_anonymous():
r = getrower(self.request.user)
result = self.request.user.is_authenticated() and ispromember(self.request.user)
if result:
promember=1
if self.request.user == self.object.user.user:
mayedit=1
context['active'] = 'nav-workouts'
context['rower'] = getrower(self.request.user)
context['breadcrumbs'] = breadcrumbs
context['workout'] = self.object
context['mayedit'] = mayedit
context['promember'] = promember
return context
def get_success_url(self):
return reverse(workouts_view)
def get_object(self, *args, **kwargs):
obj = super(WorkoutDelete, self).get_object(*args, **kwargs)
if not checkaccessuser(self.request.user,obj.user):
raise PermissionDenied('You are not allowed to delete this workout')
return obj
class MacroCycleDelete(DeleteView):
model = TrainingMacroCycle
template_name = 'trainingplan_delete.html'
# extra parameters
def get_context_data(self, **kwargs):
context = super(MacroCycleDelete, self).get_context_data(**kwargs)
if 'userid' in kwargs:
userid = kwargs['userid']
else:
userid=0
breadcrumbs = [
{
'url':reverse(plannedsessions_view,
kwargs={'userid':userid}),
'name': 'Plan'
},
{
'url':reverse(rower_trainingplan_view,
kwargs={'userid':userid,
'id':self.object.plan.id}),
'name': self.object.plan.name
},
{
'url':reverse('macrocycle_update_view',
kwargs={'pk':self.object.pk}),
'name': self.object.name
}
]
context['active'] = 'nav-plan'
context['breadcrumbs'] = breadcrumbs
context['rower'] = getrequestrower(self.request,userid=userid)
return context
def get_success_url(self):
plan = self.object.plan
createmacrofillers(plan)
return reverse(rower_trainingplan_view,
kwargs = {
'id':plan.id
})
def get_object(self, *args, **kwargs):
obj = super(MacroCycleDelete, self).get_object(*args, **kwargs)
if not checkaccessuser(self.request.user,obj.plan.manager):
raise PermissionDenied('You are not allowed to delete this training plan cycle')
return obj
@user_passes_test(hasplannedsessions,login_url="/rowers/promembership",
message="This functionality requires a Coach or Self-Coach plan",
redirect_field_name=None)
def rower_trainingplan_view(request,
id=0,
userid=0,
thismicroid=0,
thismacroid=0,
thismesoid=0):
startdate,enddate = get_dates_timeperiod(request)
try:
plan = TrainingPlan.objects.get(id=id)
except TrainingPlan.DoesNotExist:
raise Http404("Training Plan Does Not Exist")
r = getrequestrower(request,userid=userid)
if not checkaccessuser(request.user,plan.manager):
if request.user.rower not in plan.rowers.all():
raise PermissionDenied("Access denied")
createmacrofillers(plan)
macrocycles = TrainingMacroCycle.objects.filter(
plan=plan,
type='userdefined').order_by("startdate")
for m in macrocycles:
createmesofillers(m)
m.plantime = 0
m.actualtime = 0
m.plandistance = 0
m.actualdistance = 0
m.planrscore = 0
m.actualrscore = 0
m.plantrimp = 0
m.actualtrimp = 0
mesocycles = TrainingMesoCycle.objects.filter(
plan=m,
type='userdefined').order_by("startdate")
for me in mesocycles:
me.plantime = 0
me.actualtime = 0
me.plandistance = 0
me.actualdistance = 0
me.planrscore = 0
me.actualrscore = 0
me.plantrimp = 0
me.actualtrimp = 0
microcycles = TrainingMicroCycle.objects.filter(
plan=me,
type='userdefined').order_by("startdate")
for mm in microcycles:
sps = PlannedSession.objects.filter(
rower = r,
startdate__lte=mm.enddate,
enddate__gte=mm.startdate)
mm.plantime = 0
mm.actualtime = 0
mm.plandistance = 0
mm.actualdistance = 0
mm.planrscore = 0
mm.actualrscore = 0
mm.plantrimp = 0
mm.actualtrimp = 0
if mm.type == 'userdefined':
for ps in sps:
ratio, status, cdate = is_session_complete(r,ps)
if ps.sessionmode == 'time':
mm.plantime += ps.sessionvalue
mm.actualtime += int(ps.sessionvalue*ratio)
elif ps.sessionmode == 'distance' and ps.sessiontype != 'race':
mm.plandistance += ps.sessionvalue
mm.actualdistance += int(ps.sessionvalue*ratio)
elif ps.sessionmode == 'rScore':
mm.planrscore += ps.sessionvalue
mm.actualrscore += int(ps.sessionvalue*ratio)
elif ps.sessionmode == 'TRIMP':
mm.plantrimp += ps.sessionvalue
mm.actualtrimp += int(ps.sessionvalue*ratio)
mm.save()
me.plantime += mm.plantime
me.actualtime += mm.actualtime
me.plandistance += mm.plandistance
me.actualdistance += mm.actualdistance
me.planrscore += mm.planrscore
me.actualrscore += mm.actualrscore
me.plantrimp += mm.plantrimp
me.actualtrimp += mm.actualtrimp
if me.type == 'userdefined':
me.save()
m.plantime += me.plantime
m.actualtime += me.actualtime
m.plandistance += me.plandistance
m.actualdistance += me.actualdistance
m.planrscore += me.planrscore
m.actualrscore += me.actualrscore
m.plantrimp += me.plantrimp
m.actualtrimp += me.actualtrimp
if m.type == 'userdefined':
m.save()
createmacrofillers(plan)
macrocycles = TrainingMacroCycle.objects.filter(plan=plan).order_by("startdate")
count = 0
cycles = {}
for m in macrocycles:
createmesofillers(m)
mesocycles = TrainingMesoCycle.objects.filter(plan=m).order_by("startdate")
mesos = {}
count2 = 0
for me in mesocycles:
createmicrofillers(me)
microcycles = TrainingMicroCycle.objects.filter(plan=me).order_by("startdate")
mesos[count2] = (me, microcycles)
count2 += 1
cycles[count] = (m,mesos)
count += 1
breadcrumbs = [
{
'url':reverse(plannedsessions_view,
kwargs={'userid':userid}),
'name': 'Plan'
},
{
'url':reverse(rower_trainingplan_view,
kwargs={'userid':userid,
'id':id}),
'name': plan.name
}
]
if not thismicroid and not thismacroid and not thismesoid:
try:
thismicro = get_todays_micro(plan,thedate=startdate)
thismicroid = thismicro.pk
except AttributeError:
thismicroid = None
return render(request,'trainingplan.html',
{
'plan':plan,
'active':'nav-plan',
'breadcrumbs':breadcrumbs,
'rower':r,
'cycles':cycles,
'thismicroid':thismicroid,
'thismacroid':thismacroid,
'thismesoid':thismesoid,
}
)
class TrainingMacroCycleUpdate(UpdateView):
model = TrainingMacroCycle
template_name = 'trainingplan_edit.html'
form_class = TrainingMacroCycleForm
# extra parameters
def get_context_data(self, **kwargs):
context = super(TrainingMacroCycleUpdate, self).get_context_data(**kwargs)
if 'userid' in kwargs:
userid = kwargs['userid']
else:
userid=0
breadcrumbs = [
{
'url':reverse(plannedsessions_view,
kwargs={'userid':userid}),
'name': 'Plan'
},
{
'url':reverse(rower_trainingplan_view,
kwargs={'userid':userid,
'id':self.object.plan.id}),
'name': self.object.plan.name
},
{
'url':reverse('macrocycle_update_view',
kwargs={'pk':self.object.pk}),
'name': self.object.name
}
]
context['active'] = 'nav-plan'
context['breadcrumbs'] = breadcrumbs
context['rower'] = getrequestrower(self.request,userid=userid)
return context
def get_success_url(self):
plan = self.object.plan
createmacrofillers(plan)
return reverse(rower_trainingplan_view,
kwargs = {
'id':plan.id,
'thismacroid':self.object.id,
}
)
def form_valid(self, form):
form.instance.user = self.request.user
form.instance.post_date = datetime.datetime.now()
macrocycle = form.save()
mesocyclecheckdates(macrocycle)
return super(TrainingMacroCycleUpdate, self).form_valid(form)
def get_object(self, *args, **kwargs):
obj = super(TrainingMacroCycleUpdate, self).get_object(*args, **kwargs)
if obj.plan.manager is not None and self.request.user.rower != obj.plan.manager:
raise PermissionDenied('You are not allowed to edit this training plan cycle')
if not checkaccessuser(self.request.user,obj.plan.manager):
raise PermissionDenied('You are not allowed to edit this training plan cycle')
else:
obj.type = 'userdefined'
obj.save()
return obj
class TrainingMesoCycleUpdate(UpdateView):
model = TrainingMesoCycle
template_name = 'trainingplan_edit.html'
form_class = TrainingMesoCycleForm
# extra parameters
def get_context_data(self, **kwargs):
context = super(TrainingMesoCycleUpdate, self).get_context_data(**kwargs)
if 'userid' in kwargs:
userid = kwargs['userid']
else:
userid=0
breadcrumbs = [
{
'url':reverse(plannedsessions_view,
kwargs={'userid':userid}),
'name': 'Plan'
},
{
'url':reverse(rower_trainingplan_view,
kwargs={'userid':userid,
'id':self.object.plan.plan.id}),
'name': self.object.plan.plan.name
},
{
'url':reverse('macrocycle_update_view',
kwargs={'pk':self.object.plan.pk}),
'name': self.object.plan.name
},
{
'url':reverse('mesocycle_update_view',
kwargs={'pk':self.object.pk}),
'name': self.object.name
}
]
context['active'] = 'nav-plan'
context['breadcrumbs'] = breadcrumbs
context['rower'] = getrequestrower(self.request,userid=userid)
return context
def get_success_url(self):
plan = self.object.plan
createmesofillers(plan)
return reverse(rower_trainingplan_view,
kwargs = {
'id':plan.plan.id,
'thismesoid':self.object.id,
}
)
def form_valid(self, form):
form.instance.user = self.request.user
form.instance.post_date = datetime.datetime.now()
mesocycle = form.save()
microcyclecheckdates(mesocycle)
return super(TrainingMesoCycleUpdate, self).form_valid(form)
def get_object(self, *args, **kwargs):
obj = super(TrainingMesoCycleUpdate, self).get_object(*args, **kwargs)
if obj.plan.plan.manager is not None and self.request.user.rower != obj.plan.plan.manager:
raise PermissionDenied('You are not allowed to edit this training plan cycle')
else:
obj.type = 'userdefined'
obj.save()
obj.plan.type = 'userdefined'
obj.plan.save()
return obj
class TrainingMicroCycleUpdate(UpdateView):
model = TrainingMicroCycle
template_name = 'trainingplan_edit.html'
form_class = TrainingMicroCycleForm
# extra parameters
def get_context_data(self, **kwargs):
context = super(TrainingMicroCycleUpdate, self).get_context_data(**kwargs)
if 'userid' in kwargs:
userid = kwargs['userid']
else:
userid=0
breadcrumbs = [
{
'url':reverse(plannedsessions_view,
kwargs={'userid':userid}),
'name': 'Plan'
},
{
'url':reverse(rower_trainingplan_view,
kwargs={'userid':userid,
'id':self.object.plan.plan.plan.id}),
'name': self.object.plan.plan.plan.name
},
{
'url':reverse('macrocycle_update_view',
kwargs={'pk':self.object.plan.plan.pk}),
'name': self.object.plan.plan.name
},
{
'url':reverse('mesocycle_update_view',
kwargs={'pk':self.object.plan.pk}),
'name': self.object.plan.name
},
{
'url':reverse('microcycle_update_view',
kwargs={'pk':self.object.pk}),
'name': self.object.name
}
]
context['active'] = 'nav-plan'
context['breadcrumbs'] = breadcrumbs
context['rower'] = getrequestrower(self.request,userid=userid)
return context
def get_success_url(self):
plan = self.object.plan
createmicrofillers(plan)
return reverse(rower_trainingplan_view,
kwargs = {
'id':plan.plan.plan.id,
'thismicroid':self.object.pk
}
)
def form_valid(self, form):
form.instance.user = self.request.user
form.instance.post_date = datetime.datetime.now()
microcycle = form.save()
return super(TrainingMicroCycleUpdate, self).form_valid(form)
def get_object(self, *args, **kwargs):
obj = super(TrainingMicroCycleUpdate, self).get_object(*args, **kwargs)
if obj.plan.plan.plan.manager is not None and self.request.user.rower != obj.plan.plan.plan.manager:
raise PermissionDenied('You are not allowed to edit this training plan cycle')
else:
obj.type = 'userdefined'
obj.save()
obj.plan.type = 'userdefined'
obj.plan.save()
return obj
class TrainingPlanUpdate(UpdateView):
model = TrainingPlan
template_name = 'trainingplan_edit.html'
form_class = TrainingPlanForm
# extra parameters
def get_context_data(self, **kwargs):
context = super(TrainingPlanUpdate, self).get_context_data(**kwargs)
if 'userid' in kwargs:
userid = kwargs['userid']
else:
userid=0
breadcrumbs = [
{
'url':reverse(plannedsessions_view,
kwargs={'userid':userid}),
'name': 'Plan'
},
{
'url':reverse(rower_trainingplan_view,
kwargs={'userid':userid,
'id':self.object.id}),
'name': self.object.name
},
{
'url':reverse('trainingplan_update_view',
kwargs={'pk':self.object.pk}),
'name': 'Edit'
}
]
context['active'] = 'nav-plan'
context['breadcrumbs'] = breadcrumbs
context['rower'] = getrequestrower(self.request,userid=userid)
return context
def get_success_url(self):
return reverse(rower_create_trainingplan)
def form_valid(self, form):
form.instance.user = self.request.user
form.instance.post_date = datetime.datetime.now()
plan = form.save()
plan.manager = self.request.user.rower
plan.save()
macrocyclecheckdates(plan)
return super(TrainingPlanUpdate, self).form_valid(form)
def get_object(self, *args, **kwargs):
obj = super(TrainingPlanUpdate, self).get_object(*args, **kwargs)
if obj.manager is not None and self.request.user.rower != obj.manager:
raise PermissionDenied('You are not allowed to edit this training plan cycle')
if obj.manager.rowerplan not in ['coach','plan']:
raise PermissionDenied('You are not allowed to edit this training plan')
return obj
class TrainingTargetUpdate(UpdateView):
model = TrainingTarget
template_name = 'trainingplan_edit.html'
form_class = TrainingTargetForm
# extra parameters
def get_context_data(self, **kwargs):
context = super(TrainingTargetUpdate, self).get_context_data(**kwargs)
if 'userid' in kwargs:
userid = kwargs['userid']
else:
userid=0
breadcrumbs = [
{
'url':reverse(plannedsessions_view,
kwargs={'userid':userid}),
'name': 'Plan'
},
{
'url':reverse('trainingtarget_update_view',
kwargs={'pk':self.object.pk}),
'name': 'Edit'
}
]
context['active'] = 'nav-plan'
context['breadcrumbs'] = breadcrumbs
context['rower'] = getrequestrower(self.request,userid=userid)
return context
def get_success_url(self):
return reverse(rower_create_trainingplan)
def form_valid(self, form):
form.instance.user = self.request.user
form.instance.post_date = datetime.datetime.now()
target = form.save()
return super(TrainingTargetUpdate, self).form_valid(form)
def get_object(self, *args, **kwargs):
obj = super(TrainingTargetUpdate, self).get_object(*args, **kwargs)
if obj.manager is not None and self.request.user.rower != obj.manager:
raise PermissionDenied('You are not allowed to edit this training plan cycle')
return obj
def allsundays(startdate,enddate):
d = startdate
d += timedelta(days = 6 - d.weekday()) # first Sunday
while d<=enddate:
yield d
d += timedelta(days=7)
@user_passes_test(hasplannedsessions,login_url="/rowers/promembership",
message="This functionality requires a Coach or Self-Coach plan",
redirect_field_name=None)
def planmesocyclebyweek(request,id=0,userid=0):
print 'planning by week'
try:
cycle = TrainingMesoCycle.objects.get(id=id)
except TrainingMesoCycle.DoesNotExist:
raise Http404("Training Cycle does not exist")
if not checkaccessuser(request.user,cycle.plan.plan.manager):
raise PermissionDenied("You are not allowed to do this")
micros = TrainingMicroCycle.objects.filter(plan=cycle)
for m in micros:
m.delete()
cycle.type = 'userdefined'
cycle.save()
#we're still here. We have permission
sundays = [s for s in allsundays(cycle.startdate,cycle.enddate)]
if sundays and sundays[-1] < cycle.enddate:
sundays = sundays+[cycle.enddate]
else:
sundays = [cycle.enddate]
for i in range(len(sundays)):
if i==0:
monday = cycle.startdate
else:
monday = sundays[i]-timedelta(days=6)
if monday < cycle.startdate:
monday = cycle.startdate
nextsunday = sundays[i]
micro = TrainingMicroCycle(startdate = monday,
enddate = nextsunday,
plan = cycle,
name = 'Week %s' % monday.isocalendar()[1],
type = 'userdefined')
micro.save()
micros = TrainingMicroCycle.objects.filter(plan=cycle)
url = reverse(rower_trainingplan_view,
kwargs = {'userid':str(userid),
'id':str(cycle.plan.plan.id),
'thismicroid':str(micros[0].id)})
return HttpResponseRedirect(url)
def allmonths(startdate,enddate):
d = startdate
while d<enddate:
yield d
d = datetime.date(d.year+(d.month / 12),((d.month % 12) + 1),1)
@user_passes_test(hasplannedsessions,login_url="/rowers/promembership",
message="This functionality requires a Coach or Self-Coach plan",
redirect_field_name=None)
def planmacrocyclebymonth(request,id=0,userid=0):
try:
cycle = TrainingMacroCycle.objects.get(id=id)
except TrainingMacroCycle.DoesNotExist:
raise Http404("Training Cycle does not exist")
if not checkaccessuser(request.user,cycle.plan.manager):
raise PermissionDenied("You are not allowed to do this")
mesos = TrainingMesoCycle.objects.filter(plan=cycle)
for m in mesos:
m.delete()
cycle.type = 'userdefined'
cycle.save()
#we're still here. We have permission
monthstarts = [d for d in allmonths(cycle.startdate,cycle.enddate)]
monthstarts.append(cycle.enddate)
for i in range(len(monthstarts)-1):
firstday = monthstarts[i]
lastday = monthstarts[i+1]-timedelta(days=1)
if lastday < cycle.enddate and i == len(monthstarts)-2:
lastday = cycle.enddate
meso = TrainingMesoCycle(startdate = firstday,
enddate = lastday,
plan = cycle,
name = '%s' % firstday.strftime("%B"),
type = 'userdefined')
meso.save()
mesos = TrainingMesoCycle.objects.filter(plan=cycle)
url = reverse(rower_trainingplan_view,
kwargs = {'userid':str(userid),
'id':str(cycle.plan.id),
'thismesoid':str(mesos[0].id)})
return HttpResponseRedirect(url)