import time import colorsys import timestring import zipfile import bleach import arrow import pytz import operator import warnings import urllib import yaml from PIL import Image from numbers import Number from django.views.generic.base import TemplateView from django.db.models import Q from django import template from django.db import IntegrityError, transaction from django.views.decorators.csrf import csrf_exempt from django.shortcuts import render from django.http import ( HttpResponse, HttpResponseRedirect, HttpResponseForbidden, HttpResponseNotAllowed, HttpResponseNotFound,Http404 ) from django.contrib.auth import authenticate, login, logout from rowers.forms import ( LoginForm,DocumentsForm,UploadOptionsForm, TeamUploadOptionsForm,WorkFlowLeftPanelForm,WorkFlowMiddlePanelForm, WorkFlowLeftPanelElement,WorkFlowMiddlePanelElement, LandingPageForm, ) from django.core.urlresolvers import reverse from django.core.exceptions import PermissionDenied from django.template import RequestContext from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.conf import settings from django.utils.datastructures import MultiValueDictKeyError from django.utils import timezone,translation from django.core.mail import send_mail, BadHeaderError from rowers.forms import ( SummaryStringForm,IntervalUpdateForm,StrokeDataForm, StatsOptionsForm,PredictedPieceForm,DateRangeForm,DeltaDaysForm, EmailForm, RegistrationForm, RegistrationFormTermsOfService, RegistrationFormUniqueEmail,CNsummaryForm,UpdateWindForm, UpdateStreamForm,WorkoutMultipleCompareForm,ChartParamChoiceForm, FusionMetricChoiceForm,BoxPlotChoiceForm,MultiFlexChoiceForm, TrendFlexModalForm,WorkoutSplitForm,WorkoutJoinParamForm, ) from rowers.models import Workout, User, Rower, WorkoutForm,FavoriteChart from rowers.models import ( RowerPowerForm,RowerForm,GraphImage,AdvancedWorkoutForm, RowerPowerZonesForm,AccountRowerForm,UserForm,StrokeData, Team,TeamForm,TeamInviteForm,TeamInvite,TeamRequest, WorkoutComment,WorkoutCommentForm,RowerExportForm, ) from rowers.models import FavoriteForm,BaseFavoriteFormSet,SiteAnnouncement from rowers.metrics import rowingmetrics,defaultfavoritecharts import rowers.uploads as uploads from django.forms.formsets import formset_factory import StringIO from django.contrib.auth.decorators import login_required,user_passes_test from time import strftime,strptime,mktime,time,daylight import os,sys import datetime import iso8601 import c2stuff from c2stuff import C2NoTokenError,c2_open from runkeeperstuff import RunKeeperNoTokenError,runkeeper_open from sporttracksstuff import SportTracksNoTokenError,sporttracks_open from tpstuff import TPNoTokenError,tp_open from iso8601 import ParseError import stravastuff from stravastuff import StravaNoTokenError import sporttracksstuff import underarmourstuff from underarmourstuff import UnderArmourNoTokenError,underarmour_open import tpstuff import runkeeperstuff import ownapistuff from ownapistuff import TEST_CLIENT_ID, TEST_CLIENT_SECRET, TEST_REDIRECT_URI from rowsandall_app.settings import ( C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET, STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET, SPORTTRACKS_CLIENT_ID, SPORTTRACKS_REDIRECT_URI, SPORTTRACKS_CLIENT_SECRET, UNDERARMOUR_CLIENT_ID, UNDERARMOUR_REDIRECT_URI, UNDERARMOUR_CLIENT_SECRET,UNDERARMOUR_CLIENT_KEY, RUNKEEPER_CLIENT_ID,RUNKEEPER_REDIRECT_URI,RUNKEEPER_CLIENT_SECRET, TP_CLIENT_ID,TP_REDIRECT_URI,TP_CLIENT_KEY,TP_CLIENT_SECRET, ) from rowers.tasks_standalone import addcomment2 from django.contrib import messages from async_messages import messages as a_messages import requests import json from rest_framework.renderers import JSONRenderer from rest_framework.parsers import JSONParser from rowers.rows import handle_uploaded_file from rowers.tasks import handle_makeplot,handle_otwsetpower,handle_sendemailtcx,handle_sendemailcsv from rowers.tasks import ( handle_sendemail_unrecognized,handle_sendemailnewcomment, handle_sendemailnewresponse, handle_updatedps, handle_updatecp,long_test_task,long_test_task2, handle_zip_file ) from scipy.signal import savgol_filter from django.shortcuts import render_to_response from Cookie import SimpleCookie from shutil import copyfile import types from rowingdata import rower as rrower from rowingdata import main as rmain from rowingdata import rowingdata as rrdata from rowingdata import make_cumvalues from rowingdata import summarydata import pandas as pd import numpy as np import matplotlib.pyplot as plt from pytz import timezone as tz,utc import dateutil import mpld3 from mpld3 import plugins import stravalib from stravalib.exc import ActivityUploadFailed,TimeoutExceeded from weather import get_wind_data,get_airport_code,get_metar_data from oauth2_provider.models import Application,Grant,AccessToken import django_rq queue = django_rq.get_queue('default') queuelow = django_rq.get_queue('low') queuehigh = django_rq.get_queue('low') import redis import threading from redis import StrictRedis,Redis from rq.exceptions import NoSuchJobError from rq.registry import StartedJobRegistry from rq import Queue,cancel_job from django.core.cache import cache from django_mailbox.models import Message,Mailbox,MessageAttachment # Utility to get stroke data in a JSON response class JSONResponse(HttpResponse): def __init__(self, data, **kwargs): content = JSONRenderer().render(data) kwargs['content_type'] = 'application/json' super(JSONResponse, self).__init__(content, **kwargs) def 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 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', 'make_plot': 'Create static chart', 'long_test_task': 'Long Test Task', 'long_test_task2': 'Long Test Task 2', } def get_job_status(jobid): if settings.DEBUG: job = celery_result.AsyncResult(jobid) jobresult = job.result if 'fail' in job.status.lower(): jobresult = '0' summary = { 'status': job.status, 'result': jobresult, } else: try: job = Job.fetch(jobid,connection=redis_connection) summary = { 'status':job.status, 'result':job.result, } except NoSuchJobError: summary = { 'status': 'success', 'result': 1, } 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 return summary def kill_async_job(request,id='aap'): if settings.DEBUG: job = celery_result.AsyncResult(id) job.revoke() else: try: cancel_job(id,connection=redis_connection) except NoSuchJobError: pass remove_asynctask(request,id) cache.delete(id) url = reverse(session_jobs_status) return HttpResponseRedirect(url) @login_required() def test_job_view(request,aantal=100): session_key = request.session._session_key job = myqueue(queuehigh,long_test_task,int(aantal), session_key=session_key) try: request.session['async_tasks'] += [(job.id,'long_test_task')] except KeyError: request.session['async_tasks'] = [(job.id,'long_test_task')] url = reverse(session_jobs_status) return HttpResponseRedirect(url) @login_required() def test_job_view2(request,aantal=100): job = myqueue(queuehigh,long_test_task2,int(aantal), secret=settings.PROGRESS_CACHE_SECRET) try: request.session['async_tasks'] += [(job.id,'long_test_task2')] except KeyError: request.session['async_tasks'] = [(job.id,'long_test_task2')] url = reverse(session_jobs_status) return HttpResponseRedirect(url) @csrf_exempt def post_progress(request,id=None,value=0): if request.method == 'POST': try: secret = request.POST['secret'] except KeyError: return HttpResponse('Access Denied',status=401) if secret == settings.PROGRESS_CACHE_SECRET: if not id: try: id = request.POST['id'] except KeyError: return HttpResponse('Invalid request',400) try: value = request.POST['value'] except KeyError: pass cache.set(id,value,3600) # test result = cache.get(id) return HttpResponse('progress cached '+str(result), status=201) else: # secret not given return HttpResponse('access denied',status=401) else: # request method is not POST return HttpResponse('GET method not allowed',status=405) def get_all_queued_jobs(userid=0): r = StrictRedis() jobs = [] celerykeys = r.keys('celery*') for key in celerykeys: id= key[17:] job = celery_result.AsyncResult(id) jobresult = job.result if 'fail' in job.status.lower(): jobresult = '0' jobs.append( (id,{ 'status':job.status, 'result':jobresult, 'function':'', 'meta':job.info, })) ids = [j.id for j in queue.jobs] ids += [j.id for j in queuehigh.jobs] ids += [j.id for j in queuelow.jobs] ids += [j.id for j in queuefailed.jobs] for id in ids: job = Job.fetch(id,connection=redis_connection) jobs.append( (id,{ 'status':job.get_status(), 'result':job.result, 'function':job.func_name, 'meta':job.meta, })) return jobs def get_stored_tasks_status(request): try: taskids = request.session['async_tasks'] except KeyError: taskids = [] taskstatus = [] for id,func_name in reversed(taskids): progress = 0 cached_progress = cache.get(id) finished = get_job_status(id)['finished'] if finished: cache.set(id,100) progress = 100 elif cached_progress>0: progress = cached_progress else: progress = 0 this_task_status = { 'id':id, 'status':get_job_status(id)['status'], 'failed':get_job_status(id)['failed'], 'finished':get_job_status(id)['finished'], 'func_name':func_name, 'verbose': verbose_job_status[func_name], 'progress': progress, } taskstatus.append(this_task_status) return taskstatus @login_required() def get_thumbnails(request,id): try: row = Workout.objects.get(id=id) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") if (checkworkoutuser(request.user,row)==False): raise Http404("You are not allowed to edit this workout") r = getrower(request.user) result = request.user.is_authenticated() and ispromember(request.user) if result: promember=1 if request.user == row.user.user: mayedit=1 comments = WorkoutComment.objects.filter(workout=row) aantalcomments = len(comments) workouttype = 'ote' if row.workouttype in ('water','coastal'): workouttype = 'otw' try: favorites = FavoriteChart.objects.filter(user=r, workouttype__in=[workouttype,'both']).order_by("id") maxfav = len(favorites)-1 except: favorites = None maxfav = 0 charts = [] charts = thumbnails_set(r,id,favorites) try: if charts[0]['script'] == '': charts = [] except IndexError: charts = [] return JSONResponse(charts) @login_required() def get_testscript(request,id): try: row = Workout.objects.get(id=id) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") if (checkworkoutuser(request.user,row)==False): raise Http404("You are not allowed to edit this workout") r = getrower(request.user) object = { "script":"""
No erg pieces uploaded yet.
' return render(request, 'histoall.html', {'interactiveplot':script, 'the_div':div, 'id':theuser, 'theuser':u, 'teams':get_my_teams(request.user), }) # The Flex plot for a large selection of workouts @login_required() def cum_flex_data( request, options={ 'includereststrokes':False, 'workouttypes':['rower','dynamic','slides'], 'waterboattype':['1x','2x','2-','4x','4-','8+'], 'theuser':0, 'xparam':'spm', 'yparam1':'power', 'yparam2':'None', 'enddatestring':'', 'startdatestring':'', 'deltadays':-1, }): if 'options' in request.session: options = request.session['options'] workouttypes = options['workouttypes'] includereststrokes = options['includereststrokes'] waterboattype = options['waterboattype'] workstrokesonly = not includereststrokes theuser = options['theuser'] xparam = options['xparam'] yparam1 = options['yparam1'] yparam2 = options['yparam2'] startdatestring = options['startdatestring'] enddatestring = options['enddatestring'] deltadays = options['deltadays'] try: startdate = iso8601.parse_date(startdatestring) except ParseError: startdate = timezone.now()-datetime.timedelta(days=7) try: enddate = iso8601.parse_date(enddatestring) except ParseError: enddate = timezone.now() if deltadays>0: startdate = enddate-datetime.timedelta(days=int(deltadays)) if enddate < startdate: s = enddate enddate = startdate startdate = s promember=0 if theuser == 0: theuser = request.user.id if not request.user.is_anonymous(): r = getrower(request.user) result = request.user.is_authenticated() and ispromember(request.user) if result: promember=1 r2 = getrower(theuser) allworkouts = Workout.objects.filter(user=r2, workouttype__in=workouttypes, boattype__in=waterboattype, startdatetime__gte=startdate, startdatetime__lte=enddate) if allworkouts: res = interactive_cum_flex_chart2(allworkouts,xparam=xparam, yparam1=yparam1, yparam2=yparam2, promember=promember, workstrokesonly=workstrokesonly) script = res[0] div = res[1] else: script = '' div = 'No erg pieces uploaded for this date range.
' scripta = script.split('\n')[2:-1] script = ''.join(scripta) data = { "script":script, "div":div, } return JSONResponse(data) @login_required() def cum_flex(request,theuser=0, xparam='spm', yparam1='power', yparam2='None', startdate=timezone.now()-datetime.timedelta(days=10), enddate=timezone.now()+datetime.timedelta(days=1), deltadays=-1, startdatestring="", enddatestring="", options={ 'includereststrokes':False, 'workouttypes':['rower','dynamic','slides'], 'waterboattype':['1x','2x','2-','4x','4-','8+'] }): if 'options' in request.session: options = request.session['options'] workouttypes = options['workouttypes'] includereststrokes = options['includereststrokes'] waterboattype = options['waterboattype'] workstrokesonly = not includereststrokes checktypes = ['water','rower','dynamic','slides','skierg', 'paddle','snow','coastal','other'] if deltadays>0: startdate = enddate-datetime.timedelta(days=int(deltadays)) if startdatestring != "": startdate = iso8601.parse_date(startdatestring) if enddatestring != "": enddate = iso8601.parse_date(enddatestring) if enddate < startdate: s = enddate enddate = startdate startdate = s promember=0 if theuser == 0: theuser = request.user.id if not request.user.is_anonymous(): r = getrower(request.user) result = request.user.is_authenticated() and ispromember(request.user) if result: promember=1 #if not promember: #return HttpResponseRedirect("/rowers/about/") # get all indoor rows of in date range # process form if request.method == 'POST' and "daterange" in request.POST: form = DateRangeForm(request.POST) deltaform = DeltaDaysForm(request.POST) optionsform = StatsOptionsForm() if form.is_valid(): startdate = form.cleaned_data['startdate'] enddate = form.cleaned_data['enddate'] if startdate > enddate: s = enddate enddate = startdate startdate = s elif request.method == 'POST' and "datedelta" in request.POST: deltaform = DeltaDaysForm(request.POST) optionsform = StatsOptionsForm() if deltaform.is_valid(): deltadays = deltaform.cleaned_data['deltadays'] if deltadays != 0 and isinstance(deltadays, Number): enddate = timezone.now() startdate = enddate-datetime.timedelta(days=deltadays) if startdate > enddate: s = enddate enddate = startdate startdate = s form = DateRangeForm(initial={ 'startdate': startdate, 'enddate': enddate, }) else: form = DateRangeForm() optionsform = StatsOptionsForm() elif request.method == 'POST' and 'options' in request.POST: optionsform = StatsOptionsForm(request.POST) if optionsform.is_valid(): includereststrokes = optionsform.cleaned_data['includereststrokes'] workstrokesonly = not includereststrokes waterboattype = optionsform.cleaned_data['waterboattype'] workouttypes = [] for type in checktypes: if optionsform.cleaned_data[type]: workouttypes.append(type) options = { 'includereststrokes':includereststrokes, 'workouttypes':workouttypes, 'waterboattype':waterboattype, } request.session['options'] = options form = DateRangeForm(initial={ 'startdate': startdate, 'enddate': enddate, }) deltaform = DeltaDaysForm() else: form = DateRangeForm(initial={ 'startdate': startdate, 'enddate': enddate, }) deltaform = DeltaDaysForm() else: form = DateRangeForm(initial={ 'startdate': startdate, 'enddate': enddate, }) deltaform = DeltaDaysForm() optionsform = StatsOptionsForm() script = '' div = get_call() js_resources = '' css_resources = '' axchoicesbasic = {ax[0]:ax[1] for ax in axes if ax[4]=='basic'} axchoicespro = {ax[0]:ax[1] for ax in axes if ax[4]=='pro'} noylist = ["time","distance"] axchoicesbasic.pop("cumdist") # set options form correctly initial = {} initial['includereststrokes'] = includereststrokes initial['waterboattype'] = waterboattype for wtype in checktypes: if wtype in workouttypes: initial[wtype] = True else: initial[wtype] = False try: u = User.objects.get(id=theuser) except User.DoesNotExist: u = '' optionsform = StatsOptionsForm(initial=initial) options['includereststrokes'] = includereststrokes options['workouttypes'] =workouttypes options['waterboattype'] =waterboattype options['theuser'] =theuser options['xparam'] =xparam options['yparam1'] =yparam1 options['yparam2'] =yparam2 options['startdatestring'] = startdatestring options['enddatestring'] =enddatestring options['deltadays'] =deltadays request.session['options'] = options return render(request, 'cum_flex.html', {'interactiveplot':script, 'the_div':div, 'js_res': js_resources, 'css_res':css_resources, 'id':theuser, 'theuser':u, 'startdate':startdate, 'enddate':enddate, 'form':form, 'optionsform':optionsform, 'deltaform':deltaform, 'xparam':xparam, 'yparam1':yparam1, 'yparam2':yparam2, 'promember':promember, 'teams':get_my_teams(request.user), 'axchoicesbasic':axchoicesbasic, 'axchoicespro':axchoicespro, 'noylist':noylist, }) # Show the EMpower Oarlock generated Stroke Profile @user_passes_test(ispromember,login_url="/",redirect_field_name=None) def workout_forcecurve_view(request,id=0,workstrokesonly=False): try: row = Workout.objects.get(id=id) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") promember=0 mayedit=0 if not request.user.is_anonymous(): r = getrower(request.user) result = request.user.is_authenticated() and ispromember(request.user) if result: promember=1 if request.user == row.user.user: mayedit=1 if not promember: return HttpResponseRedirect("/rowers/about/") if request.method == 'POST' and 'workstrokesonly' in request.POST: workstrokesonly = request.POST['workstrokesonly'] if workstrokesonly == 'True': workstrokesonly = True else: workstrokesonly = False script,div,js_resources,css_resources = interactive_forcecurve([row], workstrokesonly=workstrokesonly) return render(request, 'forcecurve_single.html', { 'the_script':script, 'the_div':div, 'js_res': js_resources, 'css_res':css_resources, 'id':int(id), 'mayedit':mayedit, 'workstrokesonly': not workstrokesonly, 'teams':get_my_teams(request.user), }) # Test asynchronous tasking and messaging @login_required() def workout_test_task_view(request,id=0): row = Workout.objects.get(id=id) res = myqueue(queuehigh,addcomment2,request.user.id,row.id) url = reverse(workout_edit_view, kwargs = { 'id':int(id), }) return HttpResponseRedirect(url) # Show Stroke power histogram for a workout @login_required() def workout_histo_view(request,id=0): try: row = Workout.objects.get(id=id) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") promember=0 mayedit=0 if not request.user.is_anonymous(): r = getrower(request.user) result = request.user.is_authenticated() and ispromember(request.user) if result: promember=1 if request.user == row.user.user: mayedit=1 if not promember: return HttpResponseRedirect("/rowers/about/") res = interactive_histoall([row]) script = res[0] div = res[1] return render(request, 'histo_single.html', {'interactiveplot':script, 'the_div':div, 'id':int(id), 'mayedit':mayedit, 'teams':get_my_teams(request.user), }) # Histogram for a date/time range @login_required() def histo(request,theuser=0, startdate=timezone.now()-datetime.timedelta(days=365), enddate=timezone.now(), deltadays=-1, startdatestring="", enddatestring="", options={ 'includereststrokes':False, 'workouttypes':['water','rower','dynamic','slides'], 'waterboattype':['1x','2x','2-','4x','4-','8+'] }): if 'options' in request.session: options = request.session['options'] workouttypes = options['workouttypes'] includereststrokes = options['includereststrokes'] waterboattype = options['waterboattype'] workstrokesonly = not includereststrokes checktypes = ['water','rower','dynamic','slides','skierg', 'paddle','snow','coastal','other'] if deltadays>0: startdate = enddate-datetime.timedelta(days=int(deltadays)) if startdatestring != "": startdate = iso8601.parse_date(startdatestring) if enddatestring != "": enddate = iso8601.parse_date(enddatestring) if enddate < startdate: s = enddate enddate = startdate startdate = s promember=0 if theuser == 0: theuser = request.user.id if not request.user.is_anonymous(): r = getrower(request.user) result = request.user.is_authenticated() and ispromember(request.user) if result: promember=1 if not promember: return HttpResponseRedirect("/rowers/promembership/") # get all indoor rows of in date range # process form if request.method == 'POST' and "daterange" in request.POST: form = DateRangeForm(request.POST) deltaform = DeltaDaysForm(request.POST) optionsform = StatsOptionsForm() if form.is_valid(): startdate = form.cleaned_data['startdate'] enddate = form.cleaned_data['enddate'] if startdate > enddate: s = enddate enddate = startdate startdate = s elif request.method == 'POST' and "datedelta" in request.POST: deltaform = DeltaDaysForm(request.POST) optionsform = StatsOptionsForm() if deltaform.is_valid(): deltadays = deltaform.cleaned_data['deltadays'] if deltadays != 0 and deltadays != None: enddate = timezone.now() startdate = enddate-datetime.timedelta(days=deltadays) if startdate > enddate: s = enddate enddate = startdate startdate = s form = DateRangeForm(initial={ 'startdate': startdate, 'enddate': enddate, }) else: form = DateRangeForm() optionsform = StatsOptionsForm() elif request.method == 'POST' and 'options' in request.POST: optionsform = StatsOptionsForm(request.POST) if optionsform.is_valid(): includereststrokes = optionsform.cleaned_data['includereststrokes'] workstrokesonly = not includereststrokes waterboattype = optionsform.cleaned_data['waterboattype'] workouttypes = [] for type in checktypes: if optionsform.cleaned_data[type]: workouttypes.append(type) options = { 'includereststrokes':includereststrokes, 'workouttypes':workouttypes, 'waterboattype':waterboattype, } request.session['options'] = options form = DateRangeForm(initial={ 'startdate': startdate, 'enddate': enddate, }) deltaform = DeltaDaysForm() else: form = DateRangeForm(initial={ 'startdate': startdate, 'enddate': enddate, }) deltaform = DeltaDaysForm() else: form = DateRangeForm(initial={ 'startdate': startdate, 'enddate': enddate, }) deltaform = DeltaDaysForm() optionsform = StatsOptionsForm() try: r2 = getrower(theuser) allergworkouts = Workout.objects.filter(user=r2, workouttype__in=workouttypes, boattype__in=waterboattype, startdatetime__gte=startdate, startdatetime__lte=enddate) except Rower.DoesNotExist: allergworkouts = [] r2=0 try: u = User.objects.get(id=theuser) except User.DoesNotExist: u = '' if allergworkouts: res = interactive_histoall(allergworkouts) script = res[0] div = res[1] else: script = '' typesstring = ', '.join('{}'.format(item) for i,item in enumerate(workouttypes)) if 'water' in workouttypes: boatsstring = 'water ('+', '.join('{}'.format(item) for i,item in enumerate(waterboattype))+')' typesstring = typesstring.replace('water',boatsstring) div = 'No pieces found for '+typesstring+' for this date range.
' # set options form correctly initial = {} initial['includereststrokes'] = includereststrokes initial['waterboattype'] = waterboattype for wtype in checktypes: if wtype in workouttypes: initial[wtype] = True else: initial[wtype] = False optionsform = StatsOptionsForm(initial=initial) request.session['options'] = options return render(request, 'histo.html', {'interactiveplot':script, 'the_div':div, 'id':theuser, 'theuser':u, 'startdate':startdate, 'enddate':enddate, 'form':form, 'optionsform':optionsform, 'deltaform':deltaform, 'teams':get_my_teams(request.user), }) # add a workout manually @login_required() def addmanual_view(request): r = Rower.objects.get(user=request.user) if request.method == 'POST': # Form was submitted form = WorkoutForm(request.POST) if form.is_valid(): # Get values from form name = form.cleaned_data['name'] date = form.cleaned_data['date'] starttime = form.cleaned_data['starttime'] workouttype = form.cleaned_data['workouttype'] duration = form.cleaned_data['duration'] distance = form.cleaned_data['distance'] notes = form.cleaned_data['notes'] thetimezone = form.cleaned_data['timezone'] try: boattype = request.POST['boattype'] except KeyError: boattype = '1x' try: privacy = request.POST['privacy'] except KeyError: privacy = 'visible' try: rankingpiece = form.cleaned_data['rankingpiece'] except KeyError: rankingpiece =- Workout.objects.get(id=id).rankingpiece startdatetime = (str(date) + ' ' + str(starttime)) startdatetime = datetime.datetime.strptime(startdatetime, "%Y-%m-%d %H:%M:%S") startdatetime = timezone.make_aware(startdatetime) startdatetime = startdatetime.astimezone( pytz.timezone(thetimezone) ) print name id,message = dataprep.create_row_df(r, distance, duration,startdatetime, title = name, notes=notes, workouttype=workouttype) if message: messages.error(request,message) if id: w = Workout.objects.get(id=id) w.rankingpiece = rankingpiece w.notes = notes w.save() messages.info(request,'New workout created') initial = { 'workouttype':'rower', 'date':datetime.date.today(), 'starttime':timezone.now(), 'timezone':r.defaulttimezone, 'duration':datetime.timedelta(minutes=2), 'distance':500, } form = WorkoutForm(initial=initial) return render(request,'manualadd.html', {'form':form, }) # 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) 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) rankingdistances = [100,500,1000,2000,5000,6000,10000,21097,42195,100000] rankingdurations = [] rankingdurations.append(datetime.time(minute=1)) rankingdurations.append(datetime.time(minute=4)) rankingdurations.append(datetime.time(minute=30)) rankingdurations.append(datetime.time(hour=1,minute=15)) rankingdurations.append(datetime.time(hour=1)) thedistances = [] theworkouts = [] thesecs = [] rankingdistances.sort() rankingdurations.sort() for rankingdistance in rankingdistances: workouts = Workout.objects.filter(user=r,distance=rankingdistance, workouttype__in=['rower','dynamic','slides'], startdatetime__gte=startdate, startdatetime__lte=enddate).order_by('duration') if workouts: thedistances.append(rankingdistance) theworkouts.append(workouts[0]) timesecs = 3600*workouts[0].duration.hour timesecs += 60*workouts[0].duration.minute timesecs += workouts[0].duration.second timesecs += 1.e-6*workouts[0].duration.microsecond thesecs.append(timesecs) for rankingduration in rankingdurations: workouts = Workout.objects.filter(user=r,duration=rankingduration, workouttype='rower', startdatetime__gte=startdate, startdatetime__lte=enddate).order_by('-distance') if workouts: thedistances.append(workouts[0].distance) theworkouts.append(workouts[0]) timesecs = 3600*workouts[0].duration.hour timesecs += 60*workouts[0].duration.minute timesecs += workouts[0].duration.second timesecs += 1.e-5*workouts[0].duration.microsecond thesecs.append(timesecs) thedistances = np.array(thedistances) thesecs = np.array(thesecs) thevelos = thedistances/thesecs theavpower = 2.8*(thevelos**3) # create interactive plot if len(thedistances) !=0 : res = interactive_cpchart( 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 = 'No ranking pieces found.
' paulslope = 1 paulintercept = 1 p1 = [1,1,1,1] message = "" if request.method == 'POST' and "piece" in request.POST: form = PredictedPieceForm(request.POST) if form.is_valid(): value = form.cleaned_data['value'] hourvalue,value = divmod(value,60) if hourvalue >= 24: hourvalue = 23 pieceunit = form.cleaned_data['pieceunit'] if pieceunit == 'd': rankingdistances.append(value) else: rankingdurations.append(datetime.time(minute=int(value),hour=int(hourvalue))) else: form = PredictedPieceForm() rankingdistances.sort() rankingdurations.sort() predictions = [] cpredictions = [] for rankingdistance in rankingdistances: # Paul's model p = paulslope*np.log10(rankingdistance)+paulintercept velo = 500./p t = rankingdistance/velo pwr = 2.8*(velo**3) a = {'distance':rankingdistance, 'duration':timedeltaconv(t), 'pace':timedeltaconv(p), 'power':int(pwr)} predictions.append(a) # CP model - pwr2 = p1[0]/(1+t/p1[2]) pwr2 += p1[1]/(1+t/p1[3]) if pwr2 <= 0: pwr2 = 50. velo2 = (pwr2/2.8)**(1./3.) if np.isnan(velo2) or velo2 <= 0: velo2 = 1.0 t2 = rankingdistance/velo2 pwr3 = p1[0]/(1+t2/p1[2]) pwr3 += p1[1]/(1+t2/p1[3]) if pwr3 <= 0: pwr3 = 50. velo3 = (pwr3/2.8)**(1./3.) if np.isnan(velo3) or velo3 <= 0: velo3 = 1.0 t3 = rankingdistance/velo3 p3 = 500./velo3 a = {'distance':rankingdistance, 'duration':timedeltaconv(t3), 'pace':timedeltaconv(p3), 'power':int(pwr3)} cpredictions.append(a) for rankingduration in rankingdurations: t = 3600.*rankingduration.hour t += 60.*rankingduration.minute t += rankingduration.second t += rankingduration.microsecond/1.e6 # Paul's model ratio = paulintercept/paulslope u = ((2**(2+ratio))*(5.**(3+ratio))*t*np.log(10))/paulslope d = 500*t*np.log(10.) d = d/(paulslope*lambertw(u)) d = d.real velo = d/t p = 500./velo pwr = 2.8*(velo**3) a = {'distance':int(d), 'duration':timedeltaconv(t), 'pace':timedeltaconv(p), 'power':int(pwr)} predictions.append(a) # CP model pwr = p1[0]/(1+t/p1[2]) pwr += p1[1]/(1+t/p1[3]) if pwr <= 0: pwr = 50. velo = (pwr/2.8)**(1./3.) if np.isnan(velo) or velo <=0: velo = 1.0 d = t*velo p = 500./velo a = {'distance':int(d), 'duration':timedeltaconv(t), 'pace':timedeltaconv(p), 'power':int(pwr)} cpredictions.append(a) messages.error(request,message) return render(request, 'rankings.html', {'rankingworkouts':theworkouts, 'interactiveplot':script, 'the_div':div, 'predictions':predictions, 'cpredictions':cpredictions, 'nrdata':len(thedistances), 'form':form, 'dateform':dateform, 'deltaform':deltaform, 'id': theuser, 'theuser':uu, 'startdate':startdate, 'enddate':enddate, 'teams':get_my_teams(request.user), }) @user_passes_test(ispromember,login_url="/",redirect_field_name=None) def workout_update_cp_view(request,id=0): try: row = Workout.objects.get(id=id) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") if (checkworkoutuser(request.user,row)==False): message = "You are not allowed to edit this workout" messages.error(request,message) url = reverse(workouts_view) return HttpResponseRedirect(url) row.rankingpiece = True row.save() r = getrower(request.user) dataprep.runcpupdate(r) if row.workouttype in ('water','coastal'): url = reverse(otwrankings_view) else: url = reverse(oterankings_view) return HttpResponseRedirect(url) # Show ranking distances including predicted paces @user_passes_test(ispromember,login_url="/",redirect_field_name=None) def otwrankings_view(request,theuser=0, startdate=timezone.now()-datetime.timedelta(days=365), enddate=timezone.now(), deltadays=-1, startdatestring="", enddatestring=""): if deltadays>0: startdate = enddate-datetime.timedelta(days=int(deltadays)) if startdatestring != "": startdate = iso8601.parse_date(startdatestring) if enddatestring != "": enddate = iso8601.parse_date(enddatestring) if enddate < startdate: s = enddate enddate = startdate startdate = s if theuser == 0: theuser = request.user.id promember=0 if not request.user.is_anonymous(): r = Rower.objects.get(user=request.user) result = request.user.is_authenticated() and ispromember(request.user) if result: promember=1 # get all OTW rows in date range # process form if request.method == 'POST' and "daterange" in request.POST: dateform = DateRangeForm(request.POST) deltaform = DeltaDaysForm(request.POST) if dateform.is_valid(): startdate = dateform.cleaned_data['startdate'] enddate = dateform.cleaned_data['enddate'] if startdate > enddate: s = enddate enddate = startdate startdate = s elif request.method == 'POST' and "datedelta" in request.POST: deltaform = DeltaDaysForm(request.POST) if deltaform.is_valid(): deltadays = deltaform.cleaned_data['deltadays'] if deltadays: enddate = timezone.now() startdate = enddate-datetime.timedelta(days=deltadays) if startdate > enddate: s = enddate enddate = startdate startdate = s dateform = DateRangeForm(initial={ 'startdate': startdate, 'enddate': enddate, }) else: dateform = DateRangeForm() deltaform = DeltaDaysForm() else: dateform = DateRangeForm(initial={ 'startdate': startdate, 'enddate': enddate, }) deltaform = DeltaDaysForm() # get all 2k (if any) - this rower, in date range try: r = Rower.objects.get(user=theuser) except Rower.DoesNotExist: allergworkouts = [] 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) rankingdurations = [] rankingdurations.append(datetime.time(minute=1)) rankingdurations.append(datetime.time(minute=4)) rankingdurations.append(datetime.time(minute=30)) rankingdurations.append(datetime.time(hour=1)) rankingdurations.append(datetime.time(hour=1,minute=15)) thedistances = [] theworkouts = [] thesecs = [] theworkouts = Workout.objects.filter(user=r,rankingpiece=True, workouttype='water', startdatetime__gte=startdate, startdatetime__lte=enddate) 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 remove_asynctask(request,task[id]) messages.error(request,'Oh, your task failed') 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. Refresh page or resubmit the date form to get the result') powerdf = pd.DataFrame({ 'Delta':delta, 'CP':cpvalue, }) if powerdf.empty: messages.info(request,'Your calculations are running in the background. Please reload this page.') 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) # create interactive plot if len(powerdf) !=0 : res = interactive_otwcpchart(powerdf,promember=promember) 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 = 'No ranking pieces found.
' paulslope = 1 paulintercept = 1 p1 = [1,1,1,1] message = "" if request.method == 'POST' and "piece" in request.POST: form = PredictedPieceForm(request.POST) clean = form.is_valid() value = form.cleaned_data['value'] hourvalue,value = divmod(value,60) hourvalue = int(hourvalue) minutevalue = int(value) value = int(60*(value-minutevalue)) if hourvalue >= 24: hourvalue = 23 rankingdurations.append(datetime.time(minute=minutevalue, hour=hourvalue, second=value)) else: form = PredictedPieceForm() cpredictions = [] for rankingduration in rankingdurations: t = 3600.*rankingduration.hour t += 60.*rankingduration.minute t += rankingduration.second t += rankingduration.microsecond/1.e6 # CP model pwr = p1[0]/(1+t/p1[2]) pwr += p1[1]/(1+t/p1[3]) if pwr <= 0: pwr = 50. if not np.isnan(pwr): try: pwr2 = pwr*ratio except: pwr2 = pwr a = { 'duration':timedeltaconv(t), 'power':int(pwr), 'upper':int(pwr2)} cpredictions.append(a) del form.fields["pieceunit"] messages.error(request,message) return render(request, 'otwrankings.html', {'rankingworkouts':theworkouts, 'interactiveplot':script, 'the_div':div, 'cpredictions':cpredictions, 'avgpower':avgpower, 'form':form, 'dateform':dateform, 'deltaform':deltaform, 'id': theuser, 'theuser':uu, 'startdate':startdate, 'enddate':enddate, 'teams':get_my_teams(request.user), 'workouttype':'water', }) # Show ranking distances including predicted paces @user_passes_test(ispromember,login_url="/",redirect_field_name=None) def oterankings_view(request,theuser=0, startdate=timezone.now()-datetime.timedelta(days=365), enddate=timezone.now(), deltadays=-1, startdatestring="", enddatestring=""): if deltadays>0: startdate = enddate-datetime.timedelta(days=int(deltadays)) if startdatestring != "": startdate = iso8601.parse_date(startdatestring) if enddatestring != "": enddate = iso8601.parse_date(enddatestring) if enddate < startdate: s = enddate enddate = startdate startdate = s if theuser == 0: theuser = request.user.id promember=0 if not request.user.is_anonymous(): r = Rower.objects.get(user=request.user) result = request.user.is_authenticated() and ispromember(request.user) if result: promember=1 # get all OTW rows in date range # process form if request.method == 'POST' and "daterange" in request.POST: dateform = DateRangeForm(request.POST) deltaform = DeltaDaysForm(request.POST) if dateform.is_valid(): startdate = dateform.cleaned_data['startdate'] enddate = dateform.cleaned_data['enddate'] if startdate > enddate: s = enddate enddate = startdate startdate = s elif request.method == 'POST' and "datedelta" in request.POST: deltaform = DeltaDaysForm(request.POST) if deltaform.is_valid(): deltadays = deltaform.cleaned_data['deltadays'] if deltadays: enddate = timezone.now() startdate = enddate-datetime.timedelta(days=deltadays) if startdate > enddate: s = enddate enddate = startdate startdate = s dateform = DateRangeForm(initial={ 'startdate': startdate, 'enddate': enddate, }) else: dateform = DateRangeForm() deltaform = DeltaDaysForm() else: dateform = DateRangeForm(initial={ 'startdate': startdate, 'enddate': enddate, }) deltaform = DeltaDaysForm() # get all 2k (if any) - this rower, in date range try: r = Rower.objects.get(user=theuser) except Rower.DoesNotExist: allergworkouts = [] 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) rankingdurations = [] rankingdurations.append(datetime.time(minute=1)) rankingdurations.append(datetime.time(minute=4)) rankingdurations.append(datetime.time(minute=30)) rankingdurations.append(datetime.time(hour=1)) rankingdurations.append(datetime.time(hour=1,minute=15)) rankingdistances = [100,500,1000,2000,5000,6000,10000,21097,42195,100000] thedistances = [] theworkouts = [] thesecs = [] theworkouts = Workout.objects.filter(user=r,rankingpiece=True, workouttype__in=[ 'rower', 'dynamic', 'slides' ], startdatetime__gte=startdate, startdatetime__lte=enddate) 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 remove_asynctask(request,task[id]) messages.error(request,'Oh, your task failed') 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.') powerdf = pd.DataFrame({ 'Delta':delta, 'CP':cpvalue, }) if powerdf.empty: messages.info(request,'Your calculations are running in the background. Please reload this page.') 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) # create interactive plot if len(powerdf) !=0 : res = interactive_otwcpchart(powerdf,promember=promember) 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 = 'No ranking pieces found.
' paulslope = 1 paulintercept = 1 p1 = [1,1,1,1] message = "" if request.method == 'POST' and "piece" in request.POST: form = PredictedPieceForm(request.POST) clean = form.is_valid() value = form.cleaned_data['value'] hourvalue,tvalue = divmod(value,60) hourvalue = int(hourvalue) minutevalue = int(tvalue) tvalue = int(60*(tvalue-minutevalue)) if hourvalue >= 24: hourvalue = 23 pieceunit = form.cleaned_data['pieceunit'] if pieceunit == 'd': rankingdistances.append(value) else: rankingdurations.append(datetime.time( minute=minutevalue, hour=hourvalue, second=tvalue )) else: form = PredictedPieceForm() cpredictions = [] for rankingduration in rankingdurations: t = 3600.*rankingduration.hour t += 60.*rankingduration.minute t += rankingduration.second t += rankingduration.microsecond/1.e6 # CP model pwr = p1[0]/(1+t/p1[2]) pwr += p1[1]/(1+t/p1[3]) velo = (pwr/2.8)**(1./3.) p = 500./velo d = t*velo if pwr <= 0: pwr = 50. if not np.isnan(pwr): try: pwr2 = pwr*ratio except: pwr2 = pwr a = { 'distance':int(d), 'duration':timedeltaconv(t), 'power':int(pwr), 'upper':int(pwr2), 'pace':timedeltaconv(p)} cpredictions.append(a) # initiation - get 10 min power, then use Paul's law t_10 = 600. power_10 = p1[0]/(1+t_10/p1[2]) power_10 += p1[1]/(1+t_10/p1[3]) velo_10 = (power_10/2.8)**(1./3.) pace_10 = 500./velo_10 distance_10 = t_10*velo_10 paulslope = 5. for rankingdistance in rankingdistances: delta = paulslope * np.log(rankingdistance/distance_10)/np.log(2) p = pace_10+delta velo = 500./p t = rankingdistance/velo pwr2 = p1[0]/(1+t/p1[2]) pwr2 += p1[1]/(1+t/p1[3]) try: pwr2 *= ratio except UnboundLocalError: pass if pwr2 <= 0: pwr2 = 50. velo2 = (pwr2/2.8)**(1./3.) if np.isnan(velo2) or velo2 <= 0: velo2 = 1.0 t2 = rankingdistance/velo2 pwr3 = p1[0]/(1+t2/p1[2]) pwr3 += p1[1]/(1+t2/p1[3]) pwr3 *= ratio if pwr3 <= 0: pwr3 = 50. velo3 = (pwr3/2.8)**(1./3.) if np.isnan(velo3) or velo3 <= 0: velo3 = 1.0 t3 = rankingdistance/velo3 p3 = 500./velo3 a = { 'distance':rankingdistance, 'duration':timedeltaconv(t3), 'power':'--', 'upper':int(pwr3), 'pace':timedeltaconv(p3)} cpredictions.append(a) # del form.fields["pieceunit"] messages.error(request,message) return render(request, 'oterankings.html', {'rankingworkouts':theworkouts, 'interactiveplot':script, 'the_div':div, 'cpredictions':cpredictions, 'avgpower':avgpower, 'form':form, 'dateform':dateform, 'deltaform':deltaform, 'id': theuser, 'theuser':uu, 'startdate':startdate, 'enddate':enddate, 'teams':get_my_teams(request.user), 'workouttype':'rower', }) # Reload the workout and calculate the summary from the stroke data (lapIDx) @login_required() def workout_recalcsummary_view(request,id=0): try: row = Workout.objects.get(id=id) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") if (checkworkoutuser(request.user,row)==False): message = "You are not allowed to edit this workout" 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=''): try: row = Workout.objects.get(id=id) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") if (checkworkoutuser(request.user,row)==False): message = "You are not allowed to edit this workout" 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=''): try: row = Workout.objects.get(id=id) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") if (checkworkoutuser(request.user,row)==False): message = "You are not allowed to edit this workout" messages.error(request,message) url = reverse(workouts_view) return HttpResponseRedirect(url) row.privacy = 'private' row.save() for team in row.team.all(): row.team.remove(team) message = "Workout set to private. Only you will see it" messages.info(request,message) url = reverse(workout_edit_view, kwargs = { 'id':int(id), }) return HttpResponseRedirect(url) # Joining workout @user_passes_test(ispromember,login_url="/",redirect_field_name=None) def workouts_join_view(request): promember=0 if not request.user.is_anonymous(): r = getrower(request.user) result = request.user.is_authenticated() and ispromember(request.user) if result: promember=1 if request.method == 'POST' and 'workouts' in request.POST: form = WorkoutMultipleCompareForm(request.POST) paramform = WorkoutJoinParamForm(request.POST) if form.is_valid() and paramform.is_valid(): workout_name = paramform.cleaned_data['workout_name'] set_private = paramform.cleaned_data['set_private'] cd = form.cleaned_data workouts = cd['workouts'] ids = [int(w.id) for w in workouts] request.session['ids'] = ids id,message = dataprep.join_workouts(r,ids, title=workout_name, setprivate=set_private) if message: messages.error(request,message) url = reverse(r.defaultlandingpage, kwargs = { 'id':int(id), }) return HttpResponseRedirect(url) else: return HttpResponse("form is not valid") else: url = reverse(workouts_join_select) return HttpResponseRedirect(url) @user_passes_test(ispromember,login_url="/",redirect_field_name=None) def workouts_join_select(request, startdatestring="", enddatestring="", message='', successmessage='', startdate=timezone.now()-datetime.timedelta(days=30), enddate=timezone.now()+datetime.timedelta(days=1), teamid=0): try: r = getrower(request.user) except Rower.DoesNotExist: raise Http404("Rower doesn't exist") if 'startdate' in request.session: startdate = iso8601.parse_date(request.session['startdate']) if 'enddate' in request.session: enddate = iso8601.parse_date(request.session['enddate']) if 'waterboattype' in request.session: waterboattype = request.session['waterboattype'] else: waterboattype = ['1x','2x','2-','4x','4-','8+'] if 'modalities' in request.session: modalities = request.session['modalities'] if len(modalities) > 1: modality = 'all' else: modality = modalities[0] else: modalities = [m[0] for m in types.workouttypes] modality = 'all' if request.method == 'POST' and 'daterange' in request.POST: dateform = DateRangeForm(request.POST) if dateform.is_valid(): startdate = dateform.cleaned_data['startdate'] enddate = dateform.cleaned_data['enddate'] startdatestring = startdate.strftime('%Y-%m-%d') enddatestring = enddate.strftime('%Y-%m-%d') request.session['startdate'] = startdatestring request.session['enddate'] = enddatestring else: dateform = DateRangeForm(initial={ 'startdate':startdate, 'enddate':enddate, }) if request.method == 'POST' and 'modality' in request.POST: modalityform = TrendFlexModalForm(request.POST) if modalityform.is_valid(): modality = modalityform.cleaned_data['modality'] waterboattype = modalityform.cleaned_data['waterboattype'] if modality == 'all': modalities = [m[0] for m in types.workouttypes] else: modalities = [modality] if modality != 'water': waterboattype = [b[0] for b in types.boattypes] request.session['modalities'] = modalities request.session['waterboattype'] = waterboattype negtypes = [] for b in types.boattypes: if b[0] not in waterboattype: negtypes.append(b[0]) startdate = datetime.datetime.combine(startdate,datetime.time()) enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) enddate = enddate+datetime.timedelta(days=1) if startdatestring: startdate = iso8601.parse_date(startdatestring) if enddatestring: enddate = iso8601.parse_date(enddatestring) if enddate < startdate: s = enddate enddate = startdate startdate = s try: theteam = Team.objects.get(id=teamid) except Team.DoesNotExist: theteam = 0 if r.rowerplan == 'basic' and theteam==0: raise Http404("Not allowed") if theteam and (theteam.viewing == 'allmembers' or theteam.manager == request.user): workouts = Workout.objects.filter(team=theteam, startdatetime__gte=startdate, startdatetime__lte=enddate, workouttype__in=modalities).order_by("-date", "-starttime").exclude(boattype__in=negtypes) elif theteam and theteam.viewing == 'coachonly': workouts = Workout.objects.filter(team=theteam,user=r, startdatetime__gte=startdate, startdatetime__lte=enddate, workouttype__in=modalities).order_by("-date","-starttime").exclude(boattype__in=negtypes) else: theteam = None workouts = Workout.objects.filter(user=r, startdatetime__gte=startdate, startdatetime__lte=enddate, workouttype__in=modalities).order_by("-date", "-starttime").exclude(boattype__in=negtypes) query = request.GET.get('q') if query: query_list = query.split() workouts = workouts.filter( reduce(operator.and_, (Q(name__icontains=q) for q in query_list)) | reduce(operator.and_, (Q(notes__icontains=q) for q in query_list)) ) form = WorkoutMultipleCompareForm() form.fields["workouts"].queryset = workouts if theteam: theid = theteam.id else: theid = 0 joinparamform = WorkoutJoinParamForm() modalityform = TrendFlexModalForm(initial={ 'modality':modality, 'waterboattype':waterboattype }) messages.info(request,successmessage) messages.error(request,message) return render(request, 'workout_join_select.html', {'workouts': workouts, 'dateform':dateform, 'startdate':startdate, 'enddate':enddate, 'team':theteam, 'form':form, 'joinparamform':joinparamform, 'modalityform':modalityform, 'teams':get_my_teams(request.user), }) # Team comparison @login_required() def team_comparison_select(request, startdatestring="", enddatestring="", message='', successmessage='', startdate=timezone.now()-datetime.timedelta(days=30), enddate=timezone.now()+datetime.timedelta(days=1), teamid=0): try: r = getrower(request.user) except Rower.DoesNotExist: raise Http404("Rower doesn't exist") if 'startdate' in request.session: startdate = iso8601.parse_date(request.session['startdate']) if 'enddate' in request.session: enddate = iso8601.parse_date(request.session['enddate']) if 'waterboattype' in request.session: waterboattype = request.session['waterboattype'] else: waterboattype = ['1x','2x','2-','4x','4-','8+'] if 'modalities' in request.session: modalities = request.session['modalities'] if len(modalities) > 1: modality = 'all' else: modality = modalities[0] else: modalities = [m[0] for m in types.workouttypes] modality = 'all' if request.method == 'POST' and 'daterange' in request.POST: dateform = DateRangeForm(request.POST) if dateform.is_valid(): startdate = dateform.cleaned_data['startdate'] enddate = dateform.cleaned_data['enddate'] startdatestring = startdate.strftime('%Y-%m-%d') enddatestring = enddate.strftime('%Y-%m-%d') request.session['startdate'] = startdatestring request.session['enddate'] = enddatestring else: dateform = DateRangeForm(initial={ 'startdate':startdate, 'enddate':enddate, }) if request.method == 'POST' and 'modality' in request.POST: modalityform = TrendFlexModalForm(request.POST) if modalityform.is_valid(): modality = modalityform.cleaned_data['modality'] waterboattype = modalityform.cleaned_data['waterboattype'] if modality == 'all': modalities = [m[0] for m in types.workouttypes] else: modalities = [modality] if modality != 'water': waterboattype = [b[0] for b in types.boattypes] request.session['modalities'] = modalities request.session['waterboattype'] = waterboattype negtypes = [] for b in types.boattypes: if b[0] not in waterboattype: negtypes.append(b[0]) startdate = datetime.datetime.combine(startdate,datetime.time()) enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) enddate = enddate+datetime.timedelta(days=1) if startdatestring: startdate = iso8601.parse_date(startdatestring) if enddatestring: enddate = iso8601.parse_date(enddatestring) if enddate < startdate: s = enddate enddate = startdate startdate = s try: theteam = Team.objects.get(id=teamid) except Team.DoesNotExist: theteam = 0 if r.rowerplan == 'basic' and theteam==0: raise Http404("Not allowed") if theteam and (theteam.viewing == 'allmembers' or theteam.manager == request.user): workouts = Workout.objects.filter(team=theteam, startdatetime__gte=startdate, startdatetime__lte=enddate, workouttype__in=modalities).order_by("-date", "-starttime").exclude(boattype__in=negtypes) elif theteam and theteam.viewing == 'coachonly': workouts = Workout.objects.filter(team=theteam,user=r, startdatetime__gte=startdate, startdatetime__lte=enddate, workouttype__in=modalities).order_by("-date","-starttime").exclude(boattype__in=negtypes) else: theteam = None workouts = Workout.objects.filter(user=r, startdatetime__gte=startdate, startdatetime__lte=enddate, workouttype__in=modalities).order_by("-date", "-starttime").exclude(boattype__in=negtypes) query = request.GET.get('q') if query: query_list = query.split() workouts = workouts.filter( reduce(operator.and_, (Q(name__icontains=q) for q in query_list)) | reduce(operator.and_, (Q(notes__icontains=q) for q in query_list)) ) form = WorkoutMultipleCompareForm() form.fields["workouts"].queryset = workouts if theteam: theid = theteam.id else: theid = 0 chartform = ChartParamChoiceForm(initial={'teamid':0}) modalityform = TrendFlexModalForm(initial={ 'modality':modality, 'waterboattype':waterboattype }) messages.info(request,successmessage) messages.error(request,message) return render(request, 'team_compare_select.html', {'workouts': workouts, 'dateform':dateform, 'startdate':startdate, 'enddate':enddate, 'team':theteam, 'form':form, 'chartform':chartform, 'modalityform':modalityform, 'teams':get_my_teams(request.user), }) # Team comparison @login_required() def multi_compare_view(request): promember=0 if not request.user.is_anonymous(): r = getrower(request.user) result = request.user.is_authenticated() and ispromember(request.user) if result: promember=1 if request.method == 'POST' and 'workouts' in request.POST: form = WorkoutMultipleCompareForm(request.POST) chartform = ChartParamChoiceForm(request.POST) if form.is_valid() and chartform.is_valid(): cd = form.cleaned_data workouts = cd['workouts'] xparam = chartform.cleaned_data['xparam'] yparam = chartform.cleaned_data['yparam'] plottype = chartform.cleaned_data['plottype'] teamid = chartform.cleaned_data['teamid'] ids = [int(w.id) for w in workouts] request.session['ids'] = ids labeldict = { int(w.id): w.__unicode__() for w in workouts } res = interactive_multiple_compare_chart(ids,xparam,yparam, promember=promember, plottype=plottype, labeldict=labeldict) script = res[0] div = res[1] errormessage = res[3] if errormessage != '': messages.error(request,errormessage) return render(request,'multicompare.html', {'interactiveplot':script, 'the_div':div, 'promember':promember, 'teamid':teamid, 'chartform':chartform, 'teams':get_my_teams(request.user), }) else: return HttpResponse("Form is not valid") if request.method == 'POST' and 'ids' in request.session: chartform = ChartParamChoiceForm(request.POST) if chartform.is_valid(): xparam = chartform.cleaned_data['xparam'] yparam = chartform.cleaned_data['yparam'] plottype = chartform.cleaned_data['plottype'] teamid = chartform.cleaned_data['teamid'] ids = request.session['ids'] request.session['ids'] = ids workouts = [Workout.objects.get(id=id) for id in ids] labeldict = { int(w.id): w.__unicode__() for w in workouts } res = interactive_multiple_compare_chart(ids,xparam,yparam, promember=promember, plottype=plottype, labeldict=labeldict) script = res[0] div = res[1] return render(request,'multicompare.html', {'interactiveplot':script, 'the_div':div, 'promember':promember, 'teamid':teamid, 'chartform':chartform, 'teams':get_my_teams(request.user), }) else: url = reverse(workouts_view) return HttpResponseRedirect(url) # Multi Flex Chart with Grouping @user_passes_test(ispromember,login_url="/",redirect_field_name=None) def user_multiflex_select(request, startdatestring="", enddatestring="", message='', successmessage='', startdate=timezone.now()-datetime.timedelta(days=30), enddate=timezone.now()+datetime.timedelta(days=1), userid=0): if userid == 0: user = request.user else: user = User.objects.get(id=userid) r = getrower(user) if 'options' in request.session: options = request.session['options'] else: options = {} try: palette = request.session['palette'] except KeyError: palette = 'monochrome_blue' try: includereststrokes = request.session['includereststrokes'] except KeyError: includereststrokes = False try: ploterrorbars = request.session['ploterrorbars'] except: ploterrorbars = False if 'startdate' in request.session: startdate = iso8601.parse_date(request.session['startdate']) if 'enddate' in request.session: enddate = iso8601.parse_date(request.session['enddate']) if 'waterboattype' in request.session: waterboattype = request.session['waterboattype'] else: waterboattype = ['1x','2x','2-','4x','4-','8+'] if 'modalities' in request.session: modalities = request.session['modalities'] if len(modalities) > 1: modality = 'all' else: modality = modalities[0] else: modalities = [m[0] for m in types.workouttypes] modality = 'all' if request.method == 'POST' and 'daterange' in request.POST: dateform = DateRangeForm(request.POST) if dateform.is_valid(): startdate = dateform.cleaned_data['startdate'] enddate = dateform.cleaned_data['enddate'] startdatestring = startdate.strftime('%Y-%m-%d') enddatestring = enddate.strftime('%Y-%m-%d') request.session['startdate'] = startdatestring request.session['enddate'] = enddatestring else: dateform = DateRangeForm(initial={ 'startdate':startdate, 'enddate':enddate, }) if request.method == 'POST' and 'modality' in request.POST: modalityform = TrendFlexModalForm(request.POST) if modalityform.is_valid(): modality = modalityform.cleaned_data['modality'] waterboattype = modalityform.cleaned_data['waterboattype'] if modality == 'all': modalities = [m[0] for m in types.workouttypes] else: modalities = [modality] if modality != 'water': waterboattype = [b[0] for b in types.boattypes] request.session['modalities'] = modalities request.session['waterboattype'] = waterboattype startdate = datetime.datetime.combine(startdate,datetime.time()) enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) enddate = enddate+datetime.timedelta(days=1) if startdatestring: startdate = iso8601.parse_date(startdatestring) if enddatestring: enddate = iso8601.parse_date(enddatestring) if enddate < startdate: s = enddate enddate = startdate startdate = s negtypes = [] for b in types.boattypes: if b[0] not in waterboattype: negtypes.append(b[0]) workouts = Workout.objects.filter(user=r, startdatetime__gte=startdate, startdatetime__lte=enddate, workouttype__in=modalities).order_by("-date", "-starttime").exclude(boattype__in=negtypes) query = request.GET.get('q') if query: query_list = query.split() workouts = workouts.filter( reduce(operator.and_, (Q(name__icontains=q) for q in query_list)) | reduce(operator.and_, (Q(notes__icontains=q) for q in query_list)) ) form = WorkoutMultipleCompareForm() form.fields["workouts"].queryset = workouts chartform = MultiFlexChoiceForm(initial={ 'palette':palette, 'ploterrorbars':ploterrorbars, 'includereststrokes':includereststrokes, }) modalityform = TrendFlexModalForm(initial={ 'modality':modality, 'waterboattype':waterboattype }) messages.info(request,successmessage) messages.error(request,message) startdatestring = startdate.strftime('%Y-%m-%d') enddatestring = enddate.strftime('%Y-%m-%d') request.session['startdate'] = startdatestring request.session['enddate'] = enddatestring return render(request, 'user_multiflex_select.html', {'workouts': workouts, 'dateform':dateform, 'startdate':startdate, 'enddate':enddate, 'theuser':user, 'form':form, 'chartform':chartform, 'modalityform':modalityform, 'teams':get_my_teams(request.user), }) @user_passes_test(ispromember,login_url="/",redirect_field_name=None) def multiflex_data(request,userid=0, options={ 'includereststrokes':False, 'ploterrorbars':False, 'userid':0, 'palette': 'monochrome_blue', 'groupby': 'spm', 'binsize': 1, 'xparam': 'hr', 'yparam': 'pace', 'spmmin': 15, 'spmmax': 55, 'workmin': 400, 'workmax': 1500, 'ids': [], 'ploterrorbars':False, }): if 'options' in request.session: options = request.session['options'] try: includereststrokes = options['includereststrokes'] except KeyError: includereststrokes = False try: ploterrorbars = options['ploterrorbars'] except KeyError: ploterrorbars = False try: palette = request.session['palette'] except KeyError: palette = 'monochrome_blue' workstrokesonly = not includereststrokes if userid==0: userid = request.user.id palette = options['palette'] groupby = options['groupby'] binsize = options['binsize'] xparam = options['xparam'] yparam = options['yparam'] spmmin = options['spmmin'] spmmax = options['spmmax'] workmin = options['workmin'] workmax = options['workmax'] ids = options['ids'] workouts = [Workout.objects.get(id=id) for id in ids] labeldict = { int(w.id): w.__unicode__() for w in workouts } fieldlist,fielddict = dataprep.getstatsfields() fieldlist = [xparam,yparam,groupby, 'workoutid','spm','driveenergy', 'workoutstate'] # prepare data frame datadf = dataprep.read_cols_df_sql(ids,fieldlist) datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly) datadf = dataprep.filter_df(datadf,'spm',spmmin, largerthan=True) datadf = dataprep.filter_df(datadf,'spm',spmmax, largerthan=False) datadf = dataprep.filter_df(datadf,'driveenergy',workmin, largerthan=True) datadf = dataprep.filter_df(datadf,'driveneergy',workmax, largerthan=False) datadf.dropna(axis=0,how='any',inplace=True) datemapping = { w.id:w.date for w in workouts } datadf['date'] = datadf['workoutid'] datadf['date'].replace(datemapping,inplace=True) today = datetime.date.today() datadf['days ago'] = map(lambda x : x.days, datadf.date - today) if groupby != 'date': bins = np.arange(datadf[groupby].min()-binsize, datadf[groupby].max()+binsize, binsize) groups = datadf.groupby(pd.cut(datadf[groupby],bins,labels=False)) else: bins = np.arange(datadf['days ago'].min()-binsize, datadf['days ago'].max()+binsize, binsize, ) groups = datadf.groupby(pd.cut(datadf['days ago'], bins, labels=False)) xvalues = groups.mean()[xparam] yvalues = groups.mean()[yparam] xerror = groups.std()[xparam] yerror = groups.std()[yparam] groupsize = groups.count()[xparam] mask = groupsize <= min([0.01*groupsize.sum(),0.2*groupsize.mean()]) xvalues.loc[mask] = np.nan yvalues.loc[mask] = np.nan xerror.loc[mask] = np.nan yerror.loc[mask] = np.nan groupsize.loc[mask] = np.nan xvalues.dropna(inplace=True) yvalues.dropna(inplace=True) xerror.dropna(inplace=True) yerror.dropna(inplace=True) groupsize.dropna(inplace=True) if len(groupsize) == 0: messages.error(request,'No data in selection') url = reverse(user_multiflex_select) return HttpResponseRedirect(url) else: groupsize = 30.*np.sqrt(groupsize/float(groupsize.max())) df = pd.DataFrame({ xparam:xvalues, yparam:yvalues, 'x':xvalues, 'y':yvalues, 'xerror':xerror, 'yerror':yerror, 'groupsize':groupsize, }) if yparam == 'pace': df['y'] = dataprep.paceformatsecs(df['y']/1.0e3) aantal = len(df) if groupby != 'date': try: df['groupval'] = groups.mean()[groupby] df['groupval'].loc[mask] = np.nan groupcols = df['groupval'] except ValueError: df['groupval'] = groups.mean()[groupby].fillna(value=0) df['groupval'].loc[mask] = np.nan groupcols = df['groupval'] except KeyError: messages.error(request,'Data selection error') url = reverse(user_multiflex_select) return HttpResponseRedirect(url) else: try: dates = groups.min()[groupby] dates.loc[mask] = np.nan dates.dropna(inplace=True) df['groupval'] = [x.strftime("%Y-%m-%d") for x in dates] df['groupval'].loc[mask] = np.nan groupcols = 100.*np.arange(aantal)/float(aantal) except AttributeError: df['groupval'] = groups.mean()['days ago'].fillna(value=0) groupcols = 100.*np.arange(aantal)/float(aantal) groupcols = (groupcols-groupcols.min())/(groupcols.max()-groupcols.min()) if aantal == 1: groupcols = np.array([1.]) colors = range_to_color_hex(groupcols,palette=palette) df['color'] = colors clegendx = np.arange(0,1.2,.2) legcolors = range_to_color_hex(clegendx,palette=palette) if groupby != 'date': clegendy = df['groupval'].min()+clegendx*(df['groupval'].max()-df['groupval'].min()) else: clegendy = df.index.min()+clegendx*(df.index.max()-df.index.min()) colorlegend = zip(range(6),clegendy,legcolors) if userid == 0: extratitle = '' else: u = User.objects.get(id=userid) extratitle = ' '+u.first_name+' '+u.last_name script,div = interactive_multiflex(df,xparam,yparam, groupby, extratitle=extratitle, ploterrorbars=ploterrorbars, binsize=binsize, colorlegend=colorlegend) scripta= script.split('\n')[2:-1] script = ''.join(scripta) return JSONResponse({ "script":script, "div":div, }) @user_passes_test(ispromember,login_url="/",redirect_field_name=None) def multiflex_view(request,userid=0, options={ 'includereststrokes':False, 'ploterrorbars':False, }): if 'options' in request.session: options = request.session['options'] try: includereststrokes = options['includereststrokes'] except KeyError: includereststrokes = False try: ploterrorbars = options['ploterrorbars'] except KeyError: ploterrorbars = False try: palette = request.session['palette'] except KeyError: palette = 'monochrome_blue' workstrokesonly = not includereststrokes if userid==0: userid = request.user.id if request.method == 'POST' and 'workouts' in request.POST: form = WorkoutMultipleCompareForm(request.POST) chartform = MultiFlexChoiceForm(request.POST) if form.is_valid() and chartform.is_valid(): cd = form.cleaned_data workouts = cd['workouts'] xparam = chartform.cleaned_data['xparam'] yparam = chartform.cleaned_data['yparam'] includereststrokes = chartform.cleaned_data['includereststrokes'] ploterrorbars = chartform.cleaned_data['ploterrorbars'] workstrokesonly = not includereststrokes palette = chartform.cleaned_data['palette'] groupby = chartform.cleaned_data['groupby'] binsize = chartform.cleaned_data['binsize'] if binsize <= 0: binsize = 1 if groupby == 'pace': binsize *= 1000 spmmin = chartform.cleaned_data['spmmin'] spmmax = chartform.cleaned_data['spmmax'] workmin = chartform.cleaned_data['workmin'] workmax = chartform.cleaned_data['workmax'] ids = [int(w.id) for w in workouts] request.session['ids'] = ids else: return HttpResponse("Form is not valid") elif request.method == 'POST' and 'ids' in request.session: chartform = MultiFlexChoiceForm(request.POST) if chartform.is_valid(): xparam = chartform.cleaned_data['xparam'] yparam = chartform.cleaned_data['yparam'] includereststrokes = chartform.cleaned_data['includereststrokes'] ploterrorbars = chartform.cleaned_data['ploterrorbars'] request.session['ploterrorbars'] = ploterrorbars request.session['includereststrokes'] = includereststrokes workstrokesonly = not includereststrokes palette = chartform.cleaned_data['palette'] groupby = chartform.cleaned_data['groupby'] binsize = chartform.cleaned_data['binsize'] if binsize <= 0: binsize = 1 if groupby == 'pace': binsize *= 1000. spmmin = chartform.cleaned_data['spmmin'] spmmax = chartform.cleaned_data['spmmax'] workmin = chartform.cleaned_data['workmin'] workmax = chartform.cleaned_data['workmax'] ids = request.session['ids'] request.session['ids'] = ids workouts = dataprep.get_workouts(ids,userid) if not workouts: message = 'Error: Workouts in session storage do not belong to this user.' messages.error(request,message) url = reverse(user_multiflex_select, kwargs={ 'userid':userid, } ) return HttpResponseRedirect(url) # workouts = [Workout.objects.get(id=id) for id in ids] else: return HttpResponse("invalid form") else: url = reverse(user_multiflex_select) return HttpResponseRedirect(url) div = get_call() options['includereststrokes'] = includereststrokes options['ploterrorbars'] = ploterrorbars options['userid'] = userid options['palette'] = palette options['groupby'] = groupby options['binsize'] = binsize options['xparam'] = xparam options['yparam'] = yparam options['spmmin'] = spmmin options['spmmax'] = spmmax options['workmin'] = workmin options['workmax'] = workmax options['ids'] = ids request.session['options'] = options return render(request,'multiflex.html', {'interactiveplot':'', 'the_div':div, 'chartform':chartform, 'userid':userid, 'teams':get_my_teams(request.user), }) # Box plots @user_passes_test(ispromember,login_url="/",redirect_field_name=None) def user_boxplot_select(request, startdatestring="", enddatestring="", message='', successmessage='', startdate=timezone.now()-datetime.timedelta(days=30), enddate=timezone.now()+datetime.timedelta(days=1), userid=0): if userid == 0: user = request.user else: user = User.objects.get(id=userid) r = getrower(user) if 'startdate' in request.session: startdate = iso8601.parse_date(request.session['startdate']) if 'enddate' in request.session: enddate = iso8601.parse_date(request.session['enddate']) if 'waterboattype' in request.session: waterboattype = request.session['waterboattype'] else: waterboattype = ['1x','2x','2-','4x','4-','8+'] if 'modalities' in request.session: modalities = request.session['modalities'] if len(modalities) > 1: modality = 'all' else: modality = modalities[0] else: modalities = [m[0] for m in types.workouttypes] modality = 'all' if request.method == 'POST' and 'daterange' in request.POST: dateform = DateRangeForm(request.POST) if dateform.is_valid(): startdate = dateform.cleaned_data['startdate'] enddate = dateform.cleaned_data['enddate'] startdatestring = startdate.strftime('%Y-%m-%d') enddatestring = enddate.strftime('%Y-%m-%d') request.session['startdate'] = startdatestring request.session['enddate'] = enddatestring else: dateform = DateRangeForm(initial={ 'startdate':startdate, 'enddate':enddate, }) if request.method == 'POST' and 'modality' in request.POST: modalityform = TrendFlexModalForm(request.POST) if modalityform.is_valid(): modality = modalityform.cleaned_data['modality'] waterboattype = modalityform.cleaned_data['waterboattype'] if modality == 'all': modalities = [m[0] for m in types.workouttypes] else: modalities = [modality] if modality != 'water': waterboattype = [b[0] for b in types.boattypes] request.session['modalities'] = modalities request.session['waterboattype'] = waterboattype startdate = datetime.datetime.combine(startdate,datetime.time()) enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) enddate = enddate+datetime.timedelta(days=1) if startdatestring: startdate = iso8601.parse_date(startdatestring) if enddatestring: enddate = iso8601.parse_date(enddatestring) if enddate < startdate: s = enddate enddate = startdate startdate = s negtypes = [] for b in types.boattypes: if b[0] not in waterboattype: negtypes.append(b[0]) workouts = Workout.objects.filter(user=r, startdatetime__gte=startdate, startdatetime__lte=enddate, workouttype__in=modalities).order_by("-date", "-starttime").exclude(boattype__in=negtypes) query = request.GET.get('q') if query: query_list = query.split() workouts = workouts.filter( reduce(operator.and_, (Q(name__icontains=q) for q in query_list)) | reduce(operator.and_, (Q(notes__icontains=q) for q in query_list)) ) form = WorkoutMultipleCompareForm() form.fields["workouts"].queryset = workouts chartform = BoxPlotChoiceForm() modalityform = TrendFlexModalForm(initial={ 'modality':modality, 'waterboattype':waterboattype }) messages.info(request,successmessage) messages.error(request,message) startdatestring = startdate.strftime('%Y-%m-%d') enddatestring = enddate.strftime('%Y-%m-%d') request.session['startdate'] = startdatestring request.session['enddate'] = enddatestring return render(request, 'user_boxplot_select.html', {'workouts': workouts, 'dateform':dateform, 'startdate':startdate, 'enddate':enddate, 'theuser':user, 'form':form, 'chartform':chartform, 'modalityform':modalityform, 'teams':get_my_teams(request.user), }) @user_passes_test(ispromember,login_url="/",redirect_field_name=None) def boxplot_view_data(request,userid=0, options={ 'includereststrokes':False, 'spmmin':15, 'spmmax':55, 'workmin':0, 'workmax':1500, 'ids':[], 'userid':0, 'plotfield':'spm', }): if 'options' in request.session: options = request.session['options'] includereststrokes = options['includereststrokes'] spmmin = options['spmmin'] spmmax = options['spmmax'] workmin = options['workmin'] workmax = options['workmax'] ids = options['ids'] userid = options['userid'] plotfield = options['plotfield'] workstrokesonly = not includereststrokes if userid==0: userid = request.user.id workouts = [Workout.objects.get(id=id) for id in ids] labeldict = { int(w.id): w.__unicode__() for w in workouts } datemapping = { w.id:w.date for w in workouts } fieldlist,fielddict = dataprep.getstatsfields() fieldlist = [plotfield,'workoutid','spm','driveenergy', 'workoutstate'] # prepare data frame datadf = dataprep.read_cols_df_sql(ids,fieldlist) datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly) datadf = dataprep.filter_df(datadf,'spm',spmmin, largerthan=True) datadf = dataprep.filter_df(datadf,'spm',spmmax, largerthan=False) datadf = dataprep.filter_df(datadf,'driveenergy',workmin, largerthan=True) datadf = dataprep.filter_df(datadf,'driveneergy',workmax, largerthan=False) datadf.dropna(axis=0,how='any',inplace=True) datadf['workoutid'].replace(datemapping,inplace=True) datadf.rename(columns={"workoutid":"date"},inplace=True) datadf = datadf.sort_values(['date']) if userid == 0: extratitle = '' else: u = User.objects.get(id=userid) extratitle = ' '+u.first_name+' '+u.last_name script,div = interactive_boxchart(datadf,plotfield, extratitle=extratitle) scripta = script.split('\n')[2:-1] script = ''.join(scripta) return JSONResponse({ "script":script, "div":div, }) @user_passes_test(ispromember,login_url="/",redirect_field_name=None) def boxplot_view(request,userid=0, options={ 'includereststrokes':False, }): if 'options' in request.session: options = request.session['options'] includereststrokes = options['includereststrokes'] workstrokesonly = not includereststrokes if userid==0: userid = request.user.id if request.method == 'POST' and 'workouts' in request.POST: form = WorkoutMultipleCompareForm(request.POST) chartform = BoxPlotChoiceForm(request.POST) if form.is_valid() and chartform.is_valid(): cd = form.cleaned_data workouts = cd['workouts'] plotfield = chartform.cleaned_data['yparam'] includereststrokes = chartform.cleaned_data['includereststrokes'] request.session['includereststrokes'] = includereststrokes workstrokesonly = not includereststrokes spmmin = chartform.cleaned_data['spmmin'] spmmax = chartform.cleaned_data['spmmax'] workmin = chartform.cleaned_data['workmin'] workmax = chartform.cleaned_data['workmax'] ids = [int(w.id) for w in workouts] request.session['ids'] = ids else: return HttpResponse("Form is not valid") elif request.method == 'POST' and 'ids' in request.session: chartform = BoxPlotChoiceForm(request.POST) if chartform.is_valid(): plotfield = chartform.cleaned_data['yparam'] includereststrokes = chartform.cleaned_data['includereststrokes'] spmmin = chartform.cleaned_data['spmmin'] spmmax = chartform.cleaned_data['spmmax'] workmin = chartform.cleaned_data['workmin'] workmax = chartform.cleaned_data['workmax'] request.session['includereststrokes'] = includereststrokes workstrokesonly = not includereststrokes ids = request.session['ids'] request.session['ids'] = ids else: return HttpResponse("invalid form") else: url = reverse(user_boxplot_select) return HttpResponseRedirect(url) div = get_call() options['spmmin'] = spmmin options['spmmax'] = spmmax options['workmin'] = workmin options['workmax'] = workmax options['ids'] = ids options['userid'] = userid options['plotfield'] = plotfield request.session['options'] = options return render(request,'boxplot.html', {'interactiveplot':'', 'the_div':div, 'chartform':chartform, 'userid':userid, 'teams':get_my_teams(request.user), }) # List Workouts @login_required() def workouts_view(request,message='',successmessage='', startdatestring="",enddatestring="", startdate=timezone.now()-datetime.timedelta(days=365), enddate=timezone.now()+datetime.timedelta(days=1), teamid=0,rankingonly=False): request.session['referer'] = absolute(request)['PATH'] try: r = getrower(request.user) except Rower.DoesNotExist: raise Http404("Rower doesn't exist") if request.method == 'POST': dateform = DateRangeForm(request.POST) if dateform.is_valid(): startdate = dateform.cleaned_data['startdate'] enddate = dateform.cleaned_data['enddate'] else: dateform = DateRangeForm(initial={ 'startdate':startdate, 'enddate':enddate, }) startdate = datetime.datetime.combine(startdate,datetime.time()) enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) enddate = enddate+datetime.timedelta(days=1) if startdatestring: startdate = iso8601.parse_date(startdatestring) if enddatestring: enddate = iso8601.parse_date(enddatestring) if enddate < startdate: s = enddate enddate = startdate startdate = s # start date for the small graph activity_startdate = enddate-datetime.timedelta(days=15) try: if utc.localize(enddate) > timezone.now(): activity_enddate = timezone.now() activity_startdate = activity_enddate-datetime.timedelta(days=15) else: activity_enddate = enddate except ValueError: activity_enddate = enddate if teamid: try: theteam = Team.objects.get(id=teamid) except Team.DoesNotExist: raise Http404("Team doesn't exist") if theteam.viewing == 'allmembers' or theteam.manager == request.user: workouts = Workout.objects.filter(team=theteam, startdatetime__gte=startdate, startdatetime__lte=enddate).order_by("-startdatetime") g_workouts = Workout.objects.filter(team=theteam, startdatetime__gte=activity_startdate, startdatetime__lte=activity_enddate).order_by("-date", "-starttime") elif theteam.viewing == 'coachonly': workouts = Workout.objects.filter(team=theteam,user=r, startdatetime__gte=startdate, startdatetime__lte=enddate).order_by("-startdatetime") g_workouts = Workout.objects.filter(team=theteam,user=r, startdatetime__gte=activity_startdate, enddatetime__lte=activity_enddate).order_by("-startdatetime") else: theteam = None workouts = Workout.objects.filter(user=r, startdatetime__gte=startdate, startdatetime__lte=enddate).order_by("-date", "-starttime") g_workouts = Workout.objects.filter(user=r, startdatetime__gte=activity_startdate, startdatetime__lte=activity_enddate).order_by("-startdatetime") if rankingonly: workouts = [w for w in workouts if w.rankingpiece] query = request.GET.get('q') if query: query_list = query.split() workouts = workouts.filter( reduce(operator.and_, (Q(name__icontains=q) for q in query_list)) | reduce(operator.and_, (Q(notes__icontains=q) for q in query_list)) ) paginator = Paginator(workouts,20) # show 25 workouts per page page = request.GET.get('page') try: workouts = paginator.page(page) except PageNotAnInteger: workouts = paginator.page(1) except EmptyPage: workouts = paginator.page(paginator.num_pages) today = timezone.now() announcements = SiteAnnouncement.objects.filter( expires__gte=today ).order_by( "-created", "-id" ) if theteam: stack='rower' else: stack='type' script,div = interactive_activitychart(g_workouts, activity_startdate, activity_enddate, stack=stack) messages.info(request,successmessage) messages.error(request,message) return render(request, 'list_workouts.html', {'workouts': workouts, 'rower':r, 'dateform':dateform, 'startdate':startdate, 'enddate':enddate, 'announcements':announcements[0:4], 'team':theteam, 'rankingonly':rankingonly, 'teams':get_my_teams(request.user), 'interactiveplot':script, 'the_div':div, }) # List of workouts to compare a selected workout to @user_passes_test(ispromember,login_url="/",redirect_field_name=None) def workout_comparison_list(request,id=0,message='',successmessage='', startdatestring="",enddatestring="", startdate=timezone.now()-datetime.timedelta(days=365), enddate=timezone.now()): try: r = getrower(request.user) u = User.objects.get(id=r.user.id) if request.method == 'POST': dateform = DateRangeForm(request.POST) if dateform.is_valid(): startdate = dateform.cleaned_data['startdate'] enddate = dateform.cleaned_data['enddate'] else: dateform = DateRangeForm(initial={ 'startdate':startdate, 'enddate':enddate, }) if startdatestring: startdate = iso8601.parse_date(startdatestring) if enddatestring: enddate = iso8601.parse_date(enddatestring) startdate = datetime.datetime.combine(startdate,datetime.time()) enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) enddate = enddate+datetime.timedelta(days=1) if enddate < startdate: s = enddate enddate = startdate startdate = s workouts = Workout.objects.filter(user=r, startdatetime__gte=startdate, startdatetime__lte=enddate).order_by("-date", "-starttime").exclude(id=id) query = request.GET.get('q') if query: query_list = query.split() workouts = workouts.filter( reduce(operator.and_, (Q(name__icontains=q) for q in query_list)) | reduce(operator.and_, (Q(notes__icontains=q) for q in query_list)) ) paginator = Paginator(workouts,15) # show 25 workouts per page page = request.GET.get('page') try: workouts = paginator.page(page) except PageNotAnInteger: workouts = paginator.page(1) except EmptyPage: workouts = paginator.page(paginator.num_pages) try: row = Workout.objects.get(id=id) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") messages.error(request,message) messages.info(request,successmessage) return render(request, 'comparison_list.html', {'id':int(id), 'workout':row, 'workouts': workouts, 'last_name':u.last_name, 'first_name':u.first_name, 'dateform':dateform, 'startdate':startdate, 'enddate':enddate, 'teams':get_my_teams(request.user), }) except Rower.DoesNotExist: raise Http404("User has no rower instance") # List of workouts to compare a selected workout to @user_passes_test(ispromember,login_url="/",redirect_field_name=None) def workout_fusion_list(request,id=0,message='',successmessage='', startdatestring="",enddatestring="", startdate=timezone.now()-datetime.timedelta(days=365), enddate=timezone.now()): try: r = getrower(request.user) u = User.objects.get(id=r.user.id) if request.method == 'POST': dateform = DateRangeForm(request.POST) if dateform.is_valid(): startdate = dateform.cleaned_data['startdate'] enddate = dateform.cleaned_data['enddate'] else: dateform = DateRangeForm(initial={ 'startdate':startdate, 'enddate':enddate, }) if startdatestring: startdate = iso8601.parse_date(startdatestring) if enddatestring: enddate = iso8601.parse_date(enddatestring) startdate = datetime.datetime.combine(startdate,datetime.time()) enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) enddate = enddate+datetime.timedelta(days=1) if enddate < startdate: s = enddate enddate = startdate startdate = s workouts = Workout.objects.filter(user=r, startdatetime__gte=startdate, startdatetime__lte=enddate).order_by("-date", "-starttime").exclude(id=id) query = request.GET.get('q') if query: query_list = query.split() workouts = workouts.filter( reduce(operator.and_, (Q(name__icontains=q) for q in query_list)) | reduce(operator.and_, (Q(notes__icontains=q) for q in query_list)) ) paginator = Paginator(workouts,15) # show 25 workouts per page page = request.GET.get('page') try: workouts = paginator.page(page) except PageNotAnInteger: workouts = paginator.page(1) except EmptyPage: workouts = paginator.page(paginator.num_pages) try: row = Workout.objects.get(id=id) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") messages.info(request,successmessage) messages.error(request,message) return render(request, 'fusion_list.html', {'id':int(id), 'workout':row, 'workouts': workouts, 'last_name':u.last_name, 'first_name':u.first_name, 'dateform':dateform, 'startdate':startdate, 'enddate':enddate, 'teams':get_my_teams(request.user), }) except Rower.DoesNotExist: raise Http404("User has no rower instance") # Basic 'EDIT' view of workout def workout_view(request,id=0): request.session['referer'] = absolute(request)['PATH'] try: # check if valid ID exists (workout exists) row = Workout.objects.get(id=id) comments = WorkoutComment.objects.filter(workout=row) aantalcomments = len(comments) if row.privacy == 'private': raise Http404("Not allowed to view this workout") g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime") for i in g: try: width,height = Image.open(i.filename).size i.width = width i.height = height i.save() except: pass r = Rower.objects.get(id=row.user.id) u = User.objects.get(id=r.user.id) # create interactive plot res = interactive_chart(id) script = res[0] div = res[1] # create map f1 = row.csvfilename u = row.user.user r = getrower(u) rowdata = rdata(f1) hascoordinates = 1 if rowdata != 0: try: latitude = rowdata.df[' latitude'] if not latitude.std(): hascoordinates = 0 except KeyError,AttributeError: hascoordinates = 0 else: hascoordinates = 0 if hascoordinates: mapscript,mapdiv = leaflet_chart(rowdata.df[' latitude'], rowdata.df[' longitude'], row.name) else: mapscript = "" mapdiv = "" # render page if (len(g)<=3): return render(request, 'workout_view.html', {'workout':row, 'graphs1':g[0:3], 'last_name':u.last_name, 'first_name':u.first_name, 'interactiveplot':script, 'aantalcomments':aantalcomments, 'mapscript':mapscript, 'mapdiv':mapdiv, 'teams':get_my_teams(request.user), 'the_div':div}) else: return render(request, 'workout_view.html', {'workout':row, 'graphs1':g[0:3], 'graphs2':g[3:6], 'last_name':u.last_name, 'first_name':u.first_name, 'teams':get_my_teams(request.user), 'aantalcomments':aantalcomments, 'mapscript':mapscript, 'mapdiv':mapdiv, 'interactiveplot':script, 'the_div':div}) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") # Resets stroke data to raw data (pace) @user_passes_test(ispromember,login_url="/",redirect_field_name=None) def workout_undo_smoothenpace_view(request,id=0,message="",successmessage=""): try: row = Workout.objects.get(id=id) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") if (checkworkoutuser(request.user,row)==False): message = "You are not allowed to edit this workout" messages.error(request,message) url = reverse(workouts_view) return HttpResponseRedirect(url) filename = row.csvfilename row = rdata(filename) if row == 0: return HttpResponse("Error: CSV Data File Not Found") if 'originalvelo' in row.df: velo = row.df['originalvelo'].values row.df[' Stroke500mPace (sec/500m)'] = 500./velo row.write_csv(filename,gzip=True) dataprep.update_strokedata(id,row.df) url = "/rowers/workout/"+str(id)+"/advanced" return HttpResponseRedirect(url) # Data smoothing of pace data @user_passes_test(ispromember,login_url="/",redirect_field_name=None) def workout_smoothenpace_view(request,id=0,message="",successmessage=""): try: row = Workout.objects.get(id=id) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") if (checkworkoutuser(request.user,row)==False): message = "You are not allowed to edit this workout" messages.error(request,message) url = reverse(workouts_view) return HttpResponseRedirect(url) filename = row.csvfilename row = rdata(filename) if row == 0: return HttpResponse("Error: CSV Data File Not Found") pace = row.df[' Stroke500mPace (sec/500m)'].values velo = 500./pace if not 'originalvelo' in row.df: row.df['originalvelo'] = velo velo2 = stravastuff.ewmovingaverage(velo,5) pace2 = 500./abs(velo2) row.df[' Stroke500mPace (sec/500m)'] = pace2 row.df = row.df.fillna(0) row.write_csv(filename,gzip=True) dataprep.update_strokedata(id,row.df) url = "/rowers/workout/"+str(id)+"/advanced" return HttpResponseRedirect(url) # Process CrewNerd Summary CSV and update summary @user_passes_test(ispromember,login_url="/",redirect_field_name=None) def workout_crewnerd_summary_view(request,id=0,message="",successmessage=""): try: row = Workout.objects.get(id=id) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") if request.method == 'POST': form = CNsummaryForm(request.POST,request.FILES) if form.is_valid(): f = request.FILES['file'] res = handle_uploaded_file(f) fname = res[1] try: sumd = summarydata(fname) row.summary = sumd.allstats() row.save() os.remove(fname) successmessage = "CrewNerd summary added" messages.info(request,successmessage) url = reverse(workout_edit_view, kwargs = { 'id':int(id), }) return HttpResponseRedirect(url) except: try: os.remove(fname) except: pass message = "Something went wrong (workout_crewnerd_summary_view)" messages.error(request,message) url = reverse(workout_edit_view, kwargs = { 'id':int(id), }) return HttpResponseRedirect(url) else: return render(request, "cn_form.html", {'form':form, 'teams':get_my_teams(request.user), 'id':row.id}) else: form = CNsummaryForm() return render(request, "cn_form.html", {'form':form, 'teams':get_my_teams(request.user), 'id':row.id}) # Get weather for given location and date/time @user_passes_test(ispromember,login_url="/",redirect_field_name=None) def workout_downloadwind_view(request,id=0, airportcode=None, message="",successmessage=""): try: row = Workout.objects.get(id=id) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") f1 = row.csvfilename if (checkworkoutuser(request.user,row)==False): message = "You are not allowed to edit this workout" messages.error(request,message) url = reverse(workouts_view) return HttpResponseRedirect(url) # create bearing rowdata = rdata(f1) if rowdata == 0: return HttpResponse("Error: CSV Data File Not Found") try: bearing = rowdata.df.ix[:,'bearing'].values except KeyError: rowdata.add_bearing() rowdata.write_csv(f1,gzip=True) # get wind try: avglat = rowdata.df[' latitude'].mean() avglon = rowdata.df[' longitude'].mean() avgtime = int(rowdata.df['TimeStamp (sec)'].mean()-rowdata.df.ix[0,'TimeStamp (sec)']) startdatetime = dateutil.parser.parse("{}, {}".format(row.date, row.starttime)) starttimeunix = int(arrow.get(row.startdatetime).timestamp) #starttimeunix = int(mktime(startdatetime.utctimetuple())) avgtime = starttimeunix+avgtime winddata = get_wind_data(avglat,avglon,avgtime) windspeed = winddata[0] windbearing = winddata[1] message = winddata[2] row.notes += "\n"+message row.save() rowdata.add_wind(windspeed,windbearing) rowdata.write_csv(f1,gzip=True) messages.info(request,message) kwargs = { 'id':int(id)} url = reverse(workout_wind_view,kwargs=kwargs) response = HttpResponseRedirect(url) except KeyError: message = "No latitude/longitude data" messages.error(request,message) kwargs = { 'id':int(id) } url = reverse(workout_wind_view,kwargs=kwargs) response = HttpResponseRedirect(url) return response # Get weather for given location and date/time @user_passes_test(ispromember,login_url="/",redirect_field_name=None) def workout_downloadmetar_view(request,id=0, airportcode=None, message="",successmessage=""): try: row = Workout.objects.get(id=id) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") f1 = row.csvfilename if (checkworkoutuser(request.user,row)==False): message = "You are not allowed to edit this workout" messages.error(request,message) url = reverse(workouts_view) return HttpResponseRedirect(url) # create bearing rowdata = rdata(f1) if rowdata == 0: return HttpResponse("Error: CSV Data File Not Found") try: bearing = rowdata.df.ix[:,'bearing'].values except KeyError: rowdata.add_bearing() rowdata.write_csv(f1,gzip=True) # get wind try: avglat = rowdata.df[' latitude'].mean() avglon = rowdata.df[' longitude'].mean() airportcode = get_airport_code(avglat,avglon)[0] avgtime = int(rowdata.df['TimeStamp (sec)'].mean()-rowdata.df.ix[0,'TimeStamp (sec)']) startdatetime = dateutil.parser.parse("{}, {}".format(row.date, row.starttime)) starttimeunix = arrow.get(row.startdatetime).timestamp #starttimeunix = int(mktime(startdatetime.utctimetuple())) avgtime = starttimeunix+avgtime winddata = get_metar_data(airportcode,avgtime) windspeed = winddata[0] windbearing = winddata[1] message = winddata[2] row.notes += "\n"+message row.save() rowdata.add_wind(windspeed,windbearing) rowdata.write_csv(f1,gzip=True) messages.info(request,message) kwargs = { 'id':int(id)} url = reverse(workout_wind_view,kwargs=kwargs) response = HttpResponseRedirect(url) except KeyError: message = "No latitude/longitude data" messages.error(request,message) kwargs = { 'id':int(id) } url = reverse(workout_wind_view,kwargs=kwargs) response = HttpResponseRedirect(url) return response # Show form to update wind data @user_passes_test(ispromember,login_url="/",redirect_field_name=None) def workout_wind_view(request,id=0,message="",successmessage=""): try: row = Workout.objects.get(id=id) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") if (checkworkoutuser(request.user,row)==False): message = "You are not allowed to edit this workout" messages.error(request,message) url = reverse(workouts_view) return HttpResponseRedirect(url) # get data f1 = row.csvfilename u = row.user.user r = getrower(u) # create bearing rowdata = rdata(f1) if row == 0: return HttpResponse("Error: CSV Data File Not Found") hascoordinates = 1 try: latitude = rowdata.df.ix[:,' latitude'] except KeyError: hascoordinates = 0 if hascoordinates and not latitude.std(): hascoordinates = 0 try: bearing = rowdata.df.ix[:,'bearing'].values except KeyError: rowdata.add_bearing() rowdata.write_csv(f1,gzip=True) if hascoordinates: avglat = rowdata.df[' latitude'].mean() avglon = rowdata.df[' longitude'].mean() airportcode,newlat,newlon,airportdistance = get_airport_code(avglat,avglon) airportcode = airportcode.upper() airportdistance = airportdistance[0] else: airportcode = 'UNKNOWN' airportdistance = 0 if request.method == 'POST': # process form form = UpdateWindForm(request.POST) if form.is_valid(): vwind1 = form.cleaned_data['vwind1'] vwind2 = form.cleaned_data['vwind2'] dist1 = form.cleaned_data['dist1'] dist2 = form.cleaned_data['dist2'] winddirection1 = form.cleaned_data['winddirection1'] winddirection2 = form.cleaned_data['winddirection2'] windunit = form.cleaned_data['windunit'] rowdata.update_wind(vwind1,vwind2, winddirection1, winddirection2, dist1,dist2, units=windunit) rowdata.write_csv(f1,gzip=True) else: message = "Invalid Form" messages.error(request,message) kwargs = { 'id':int(id) } url = reverse(workout_wind_view,kwargs=kwargs) response = HttpResponseRedirect(url) else: form = UpdateWindForm() # create interactive plot res = interactive_windchart(id,promember=1) script = res[0] div = res[1] if hascoordinates: gmscript,gmdiv = leaflet_chart( rowdata.df[' latitude'], rowdata.df[' longitude'], row.name) else: gmscript = "" gmdiv = "No GPS data available" messages.info(request,successmessage) messages.error(request,message) return render(request, 'windedit.html', {'workout':row, 'teams':get_my_teams(request.user), 'interactiveplot':script, 'form':form, 'airport':airportcode, 'airportdistance':airportdistance, 'the_div':div, 'gmap':gmscript, 'gmapdiv':gmdiv}) # Show form to update River stream data (for river dwellers) @user_passes_test(ispromember,login_url="/",redirect_field_name=None) def workout_stream_view(request,id=0,message="",successmessage=""): try: row = Workout.objects.get(id=id) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") if (checkworkoutuser(request.user,row)==False): message = "You are not allowed to edit this workout" messages.error(request,message) url = reverse(workouts_view) return HttpResponseRedirect(url) # create interactive plot f1 = row.csvfilename u = row.user.user r = getrower(u) rowdata = rdata(f1) if rowdata == 0: return HttpResponse("Error: CSV Data File Not Found") if request.method == 'POST': # process form form = UpdateStreamForm(request.POST) if form.is_valid(): dist1 = form.cleaned_data['dist1'] dist2 = form.cleaned_data['dist2'] stream1 = form.cleaned_data['stream1'] stream2 = form.cleaned_data['stream2'] streamunit = form.cleaned_data['streamunit'] rowdata.update_stream(stream1,stream2,dist1,dist2, units=streamunit) rowdata.write_csv(f1,gzip=True) else: message = "Invalid Form" messages.error(request,message) kwargs = { 'id':int(id)} url = reverse(workout_wind_view,kwargs=kwargs) response = HttpResponseRedirect(url) else: form = UpdateStreamForm() # create interactive plot res = interactive_streamchart(id,promember=1) script = res[0] div = res[1] messages.info(request,successmessage) messages.error(request,message) return render(request, 'streamedit.html', {'workout':row, 'teams':get_my_teams(request.user), 'interactiveplot':script, 'form':form, 'the_div':div}) # Form to set average crew weight and boat type, then run power calcs @user_passes_test(ispromember, login_url="/",redirect_field_name=None) def workout_otwsetpower_view(request,id=0,message="",successmessage=""): try: row = Workout.objects.get(id=id) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") if (checkworkoutuser(request.user,row)==False): message = "You are not allowed to edit this workout" 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(): boattype = form.cleaned_data['boattype'] weightvalue = form.cleaned_data['weightvalue'] row.boattype = boattype row.weightvalue = weightvalue row.save() # load row data & create power/wind/bearing columns if not set f1 = row.csvfilename rowdata = rdata(f1) if rowdata == 0: return HttpResponse("Error: CSV Data File Not Found") try: vstream = rowdata.df['vstream'] except KeyError: rowdata.add_stream(0) rowdata.write_csv(f1,gzip=True) try: bearing = rowdata.df['bearing'] except KeyError: rowdata.add_bearing() rowdata.write_csv(f1,gzip=True) try: vwind = rowdata.df['vwind'] except KeyError: rowdata.add_wind(0,0) rowdata.write_csv(f1,gzip=True) # do power calculation (asynchronous) r = row.user u = r.user first_name = u.first_name last_name = u.last_name emailaddress = u.email job = myqueue(queuelow, handle_otwsetpower,f1,boattype, weightvalue, first_name,last_name,emailaddress,id, ps=[r.p0,r.p1,r.p2,r.p3], ratio=r.cpratio) 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 here' messages.info(request,successmessage) kwargs = { 'id':int(id)} try: url = request.session['referer'] except KeyError: url = reverse(workout_advanced_view,kwargs=kwargs) response = HttpResponseRedirect(url) return response else: message = "Invalid Form" messages.error(request,message) kwargs = { 'id':int(id)} url = reverse(workout_otwsetpower_view,kwargs=kwargs) response = HttpResponseRedirect(url) else: form = AdvancedWorkoutForm(instance=row) messages.error(request,message) messages.info(request,successmessage) return render(request, 'otwsetpower.html', {'workout':row, 'teams':get_my_teams(request.user), 'form':form, }) # A special Edit page with all the Geeky functionality for the workout @login_required() def workout_geeky_view(request,id=0,message="",successmessage=""): try: row = Workout.objects.get(id=id) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") form = WorkoutForm(instance=row) g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime") for i in g: try: width,height = Image.open(i.filename).size i.width = width i.height = height i.save() except: pass # check if user is owner of this workout if (checkworkoutuser(request.user,row)==False): message = "You are not allowed to edit this workout" messages.error(request,message) url = reverse(workouts_view) return HttpResponseRedirect(url) # create interactive plot f1 = row.csvfilename u = row.user.user r = getrower(u) # create interactive plot try: res = interactive_chart(id,promember=1) script = res[0] div = res[1] except ValueError: pass messages.error(request,message) messages.info(request,successmessage) if row.workouttype in ('water','coastal'): return render(request, 'otwgeeky.html', {'workout':row, 'teams':get_my_teams(request.user), 'interactiveplot':script, 'the_div':div}) else: return render(request, 'advancededit.html', {'workout':row, 'teams':get_my_teams(request.user), 'interactiveplot':script, 'the_div':div}) # Cumulative stats page @login_required() def cumstats(request,theuser=0, startdate=timezone.now()-datetime.timedelta(days=30), enddate=timezone.now(), deltadays=-1, startdatestring="", enddatestring="", plotfield='spm', options={ 'includereststrokes':False, 'workouttypes':['rower','dynamic','slides'], 'waterboattype':['1x','2x','2-','4x','4-','8+'] }): if 'options' in request.session: options = request.session['options'] workouttypes = options['workouttypes'] includereststrokes = options['includereststrokes'] workstrokesonly = not includereststrokes checktypes = ['water','rower','dynamic','slides','skierg', 'paddle','snow','other','coastal'] waterboattype = ['1x','2x','2-','4x','4-','8+'] if deltadays>0: startdate = enddate-datetime.timedelta(days=int(deltadays)) if startdatestring != "": startdate = iso8601.parse_date(startdatestring) if enddatestring != "": enddate = iso8601.parse_date(enddatestring) if enddate < startdate: s = enddate enddate = startdate startdate = s promember=0 if theuser == 0: theuser = request.user.id if not request.user.is_anonymous(): r = getrower(request.user) result = request.user.is_authenticated() and ispromember(request.user) if result: promember=1 if not promember: return HttpResponseRedirect("/rowers/promembership/") # get all indoor rows of in date range # process form if request.method == 'POST' and "daterange" in request.POST: form = DateRangeForm(request.POST) deltaform = DeltaDaysForm(request.POST) optionsform = StatsOptionsForm() if form.is_valid(): startdate = form.cleaned_data['startdate'] enddate = form.cleaned_data['enddate'] if startdate > enddate: s = enddate enddate = startdate startdate = s elif request.method == 'POST' and "datedelta" in request.POST: deltaform = DeltaDaysForm(request.POST) if deltaform.is_valid(): deltadays = deltaform.cleaned_data['deltadays'] if deltadays != 0 and isinstance(deltadays, Number): enddate = timezone.now() startdate = enddate-datetime.timedelta(days=deltadays) if startdate > enddate: s = enddate enddate = startdate startdate = s form = DateRangeForm(initial={ 'startdate': startdate, 'enddate': enddate, }) optionsform = StatsOptionsForm() else: form = DateRangeForm() optionsform = StatsOptionsForm() elif request.method == 'POST' and 'options' in request.POST: optionsform = StatsOptionsForm(request.POST) if optionsform.is_valid(): includereststrokes = optionsform.cleaned_data['includereststrokes'] workstrokesonly = not includereststrokes workouttypes = [] waterboattype = optionsform.cleaned_data['waterboattype'] for type in checktypes: if optionsform.cleaned_data[type]: workouttypes.append(type) options = { 'includereststrokes':includereststrokes, 'workouttypes':workouttypes, 'waterboattype':waterboattype, } request.session['options'] = options form = DateRangeForm() deltaform = DeltaDaysForm() else: form = DateRangeForm() deltaform = DeltaDaysForm() else: form = DateRangeForm(initial={ 'startdate': startdate, 'enddate': enddate, }) deltaform = DeltaDaysForm() optionsform = StatsOptionsForm() try: r2 = getrower(theuser) allergworkouts = Workout.objects.filter(user=r2, workouttype__in=workouttypes, boattype__in=waterboattype, startdatetime__gte=startdate, startdatetime__lte=enddate) except Rower.DoesNotExist: allergworkouts = [] r2=0 try: u = User.objects.get(id=theuser) except User.DoesNotExist: u = '' ids = [int(workout.id) for workout in allergworkouts] datemapping = { w.id:w.date for w in allergworkouts } fieldlist,fielddict = dataprep.getstatsfields() # prepare data frame datadf = dataprep.read_cols_df_sql(ids,fieldlist) datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly) if datadf.empty: stats = {} plotfield = 'spm' cordict = {} div = "No Data found" script = '' response = render(request, 'cumstats.html', { 'stats':stats, 'teams':get_my_teams(request.user), 'options':options, 'id':theuser, 'theuser':u, 'startdate':startdate, 'enddate':enddate, 'form':form, 'deltaform':deltaform, 'optionsform':optionsform, 'cordict':cordict, 'plotscript':script, 'plotdiv':div, 'plotfield':plotfield, }) request.session['options'] = options return response # Create stats stats = {} fielddict.pop('workoutstate') fielddict.pop('workoutid') for field,verbosename in fielddict.iteritems(): thedict = { 'mean':datadf[field].mean(), 'min': datadf[field].min(), 'std': datadf[field].std(), 'max': datadf[field].max(), 'median': datadf[field].median(), 'firstq':datadf[field].quantile(q=0.25), 'thirdq':datadf[field].quantile(q=0.75), 'verbosename':verbosename, } stats[field] = thedict # Create a dict with correlation values cor = datadf.corr(method='spearman') cor.fillna(value=0,inplace=True) cordict = {} for field1,verbosename in fielddict.iteritems(): thedict = {} for field2,verbosename in fielddict.iteritems(): try: thedict[field2] = cor.ix[field1,field2] except KeyError: thedict[field2] = 0 cordict[field1] = thedict # interactive box plot datadf['workoutid'].replace(datemapping,inplace=True) datadf.rename(columns={"workoutid":"date"},inplace=True) datadf = datadf.sort_values(['date']) script,div = interactive_boxchart(datadf,plotfield) # set options form correctly initial = {} initial['includereststrokes'] = includereststrokes initial['waterboattype'] = waterboattype for wtype in checktypes: if wtype in workouttypes: initial[wtype] = True else: initial[wtype] = False optionsform = StatsOptionsForm(initial=initial) response = render(request, 'cumstats.html', { 'stats':stats, 'teams':get_my_teams(request.user), 'options':options, 'id':theuser, 'theuser':u, 'startdate':startdate, 'enddate':enddate, 'form':form, 'deltaform':deltaform, 'optionsform':optionsform, 'cordict':cordict, 'plotscript':script, 'plotdiv':div, 'plotfield':plotfield, }) request.session['options'] = options return response # Stats page @login_required() def workout_stats_view(request,id=0,message="",successmessage=""): r = getrower(request.user) try: w = Workout.objects.get(id=id) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") workstrokesonly = True if request.method == 'POST' and 'workstrokesonly' in request.POST: workstrokesonly = str2bool(request.POST['workstrokesonly']) # prepare data frame datadf,row = dataprep.getrowdata_db(id=id) if (checkworkoutuser(request.user,row)==False): message = "You are not allowed to see the stats of this workout" messages.error(request,message) url = reverse(workouts_view) return HttpResponseRedirect(url) datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly) if datadf.empty: return HttpResponse("CSV data file not found") workoutstateswork = [1,4,5,8,9,6,7] workoutstatesrest = [3] workoutstatetransition = [0,2,10,11,12,13] # Create stats stats = {} fieldlist,fielddict = dataprep.getstatsfields() fielddict.pop('workoutstate') 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 # additional non-automated stats otherstats = {} # Normalized power & TSS duration = datadf['time'].max()-datadf['time'].min() duration /= 1.0e3 pwr4 = datadf['power']**(4) normp = (pwr4.mean())**(0.25) if not np.isnan(normp): ftp = float(r.ftp) if w.workouttype in ('water','coastal'): ftp = ftp*(100.-r.otwslack)/100. intensityfactor = datadf['power'].mean()/float(ftp) intensityfactor = normp/float(ftp) tss = 100.*((duration*normp*intensityfactor)/(3600.*ftp)) if not np.isnan(tss): 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 return render(request, 'workoutstats.html', { 'stats':stats, 'teams':get_my_teams(request.user), 'workout':row, 'workstrokesonly':workstrokesonly, 'cordict':cordict, 'otherstats':otherstats, }) # The Advanced edit page @login_required() def workout_advanced_view(request,id=0,message="",successmessage=""): try: row = Workout.objects.get(id=id) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") form = WorkoutForm(instance=row) g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime") for i in g: try: width,height = Image.open(i.filename).size i.width = width i.height = height i.save() except: pass # check if user is owner of this workout if (checkworkoutuser(request.user,row)==False): message = "You are not allowed to edit this workout" messages.error(request,message) url = reverse(workouts_view) return HttpResponseRedirect(url) # create interactive plot f1 = row.csvfilename u = row.user.user r = getrower(u) # create interactive plot try: res = interactive_chart(id,promember=1) script = res[0] div = res[1] except ValueError: pass messages.error(request,message) messages.info(request,successmessage) if row.workouttype in ('water','coastal'): return render(request, 'advancedotw.html', {'workout':row, 'teams':get_my_teams(request.user), 'interactiveplot':script, 'the_div':div}) else: return render(request, 'advancededit.html', {'workout':row, 'teams':get_my_teams(request.user), 'interactiveplot':script, 'the_div':div}) # The interactive plot comparing two workouts (obsolete version) def workout_comparison_view(request,id1=0,id2=0,xparam='distance',yparam='spm'): promember=0 if not request.user.is_anonymous(): r = getrower(request.user) result = request.user.is_authenticated() and ispromember(request.user) if result: promember=1 # create interactive plot res = interactive_comparison_chart(id1,id2,xparam=xparam,yparam=yparam, promember=promember) script = res[0] div = res[1] axchoicesbasic = {ax[0]:ax[1] for ax in axes if ax[4]=='basic'} axchoicespro = {ax[0]:ax[1] for ax in axes if ax[4]=='pro'} noylist = ["time","distance"] axchoicesbasic.pop("cumdist") return render(request, 'comparisonchart.html', {'interactiveplot':script, 'the_div':div, 'teams':get_my_teams(request.user), 'id1':id1, 'id2':id2, 'axchoicesbasic':axchoicesbasic, 'axchoicespro':axchoicespro, 'noylist':noylist, 'xparam':xparam, 'yparam':yparam, }) # Updated version of comparison plot def workout_comparison_view2(request,id1=0,id2=0,xparam='distance', yparam='spm',plottype='line'): promember=0 if not request.user.is_anonymous(): r = getrower(request.user) result = request.user.is_authenticated() and ispromember(request.user) if result: promember=1 if not Workout.objects.filter(id=id1).exists() or not Workout.objects.filter(id=id2).exists(): raise Http404("Workout doesn't exist") # create interactive plot res = interactive_comparison_chart(id1,id2,xparam=xparam,yparam=yparam, promember=promember,plottype=plottype) script = res[0] div = res[1] axchoicesbasic = {ax[0]:ax[1] for ax in axes if ax[4]=='basic'} axchoicespro = {ax[0]:ax[1] for ax in axes if ax[4]=='pro'} noylist = ["time","distance"] axchoicesbasic.pop("cumdist") row1 = Workout.objects.get(id=id1) row2 = Workout.objects.get(id=id2) 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, 'axchoicesbasic':axchoicesbasic, 'axchoicespro':axchoicespro, 'noylist':noylist, 'xparam':xparam, 'yparam':yparam, 'plottype':plottype, 'promember':promember, }) # Change default landing page @login_required() def workflow_default_view(request): r = getrower(request.user) if r.defaultlandingpage == 'workout_edit_view': r.defaultlandingpage = 'workout_workflow_view' else: r.defaultlandingpage = 'workout_edit_view' r.save() url = reverse(workout_workflow_config2_view) return HttpResponseRedirect(url) # Workflow Configuration @login_required() def workout_workflow_config_view(request): request.session['referer'] = absolute(request)['PATH'] request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE try: workoutid = request.session['lastworkout'] except KeyError: workoutid = 0 r = getrower(request.user) if request.method == 'POST' and 'leftpanel' in request.POST: formleft = WorkFlowLeftPanelForm(request.POST) if formleft.is_valid(): leftpanel = formleft.cleaned_data['leftpanel'] r.workflowleftpanel = leftpanel r.save() if request.method == 'POST' and 'middlepanel' in request.POST: formmiddle = WorkFlowMiddlePanelForm(request.POST) if formmiddle.is_valid(): middlepanel = formmiddle.cleaned_data['middlepanel'] r.workflowmiddlepanel = middlepanel r.save() formleft = WorkFlowLeftPanelForm(instance=r) formmiddle = WorkFlowMiddlePanelForm(instance=r) tmplt = 'workflowconfig.html' return render(request,tmplt, { 'rower':r, 'formleft':formleft, 'formmiddle':formmiddle, 'workoutid': workoutid, }) @login_required() def workout_workflow_config2_view(request): request.session['referer'] = absolute(request)['PATH'] request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE try: workoutid = request.session['lastworkout'] except KeyError: workoutid = 0 r = getrower(request.user) MiddlePanelFormSet = formset_factory(WorkFlowMiddlePanelElement,extra=1) LeftPanelFormSet = formset_factory(WorkFlowLeftPanelElement,extra=1) if request.method == 'POST': wasmiddle = [1 for key,value in request.POST.items() if 'middlepanel' in key.lower()] wasleft = [1 for key,valye in request.POST.items() if 'leftpanel' in key.lower()] if wasmiddle: middlepanel_formset = MiddlePanelFormSet(request.POST, prefix='middlepanel') newmiddlepanel = [] if middlepanel_formset.is_valid(): for form in middlepanel_formset: value = form.cleaned_data.get('panel') if value != 'None': newmiddlepanel.append(value) newmiddlepanel = [i for i in newmiddlepanel if i != None] r.workflowmiddlepanel = newmiddlepanel try: r.save() except IntegrityError: messages.error(request,'Something went wrong') if wasleft: leftpanel_formset = LeftPanelFormSet(request.POST, prefix='leftpanel') newleftpanel = [] if leftpanel_formset.is_valid(): for form in leftpanel_formset: value = form.cleaned_data.get('panel') if value != 'None': newleftpanel.append(value) newleftpanel = [i for i in newleftpanel if i != None] r.workflowleftpanel = newleftpanel try: r.save() except IntegrityError: messages.error(request,'Something went wrong') leftpanelform_data = [{'panel':panel} for panel in r.workflowleftpanel] middlepanelform_data = [{'panel':panel} for panel in r.workflowmiddlepanel] leftpanel_formset = LeftPanelFormSet(initial=leftpanelform_data, prefix='leftpanel') middlepanel_formset = MiddlePanelFormSet(initial=middlepanelform_data, prefix='middlepanel') tmplt = 'workflowconfig2.html' return render(request,tmplt, { 'rower':getrower(request.user), 'leftpanel_formset':leftpanel_formset, 'middlepanel_formset':middlepanel_formset, 'workoutid': workoutid, }) # Workflow View @login_required() def workout_workflow_view(request,id): request.session['referer'] = absolute(request)['PATH'] request.session['lastworkout'] = id request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE try: row = Workout.objects.get(id=id) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") if (checkworkoutuser(request.user,row)==False): raise Http404("You are not allowed to edit this workout") r = getrower(request.user) result = request.user.is_authenticated() and ispromember(request.user) if result: promember=1 if request.user == row.user.user: mayedit=1 comments = WorkoutComment.objects.filter(workout=row) aantalcomments = len(comments) workouttype = 'ote' if row.workouttype in ('water','coastal'): workouttype = 'otw' try: favorites = FavoriteChart.objects.filter(user=r, workouttype__in=[workouttype,'both']).order_by("id") maxfav = len(favorites)-1 except: favorites = None maxfav = 0 charts = get_call() if 'panel_map.html' in r.workflowmiddlepanel and rowhascoordinates(row): rowdata = rdata(row.csvfilename) mapscript,mapdiv = leaflet_chart2(rowdata.df[' latitude'], rowdata.df[' longitude'], row.name) else: mapscript = '' mapdiv = '' statcharts = GraphImage.objects.filter(workout=row) middleTemplates = [] for t in r.workflowmiddlepanel: try: template.loader.get_template(t) middleTemplates.append(t) except template.TemplateDoesNotExist: pass leftTemplates = [] for t in r.workflowleftpanel: try: template.loader.get_template(t) leftTemplates.append(t) except template.TemplateDoesNotExist: pass return render(request, 'workflow.html', { 'middleTemplates':middleTemplates, 'leftTemplates':leftTemplates, 'charts':charts, 'workout':row, 'mapscript':mapscript, 'mapdiv':mapdiv, 'statcharts':statcharts, 'rower':r, 'aantalcomments':aantalcomments, }) # The famous flex chart def workout_flexchart3_view(request,*args,**kwargs): try: id = kwargs['id'] except KeyError: return HttpResponse("Invalid workout number") if 'promember' in kwargs: promember = kwargs['promember'] else: promember = 0 try: favoritenr = int(request.GET['favoritechart']) except: favoritenr = -1 try: row = Workout.objects.get(id=id) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") promember=0 mayedit=0 if not request.user.is_anonymous(): r = getrower(request.user) result = request.user.is_authenticated() and ispromember(request.user) if result: promember=1 if request.user == row.user.user: mayedit=1 workouttype = 'ote' if row.workouttype in ('water','coastal'): workouttype = 'otw' try: favorites = FavoriteChart.objects.filter(user=r, workouttype__in=[workouttype,'both']).order_by("id") maxfav = len(favorites)-1 except: favorites = None maxfav = 0 # check if favoritenr is not out of range if favorites: try: t = favorites[favoritenr].xparam except IndexError: favoritenr=0 except AssertionError: favoritenr=0 if 'xparam' in kwargs: xparam = kwargs['xparam'] else: if favorites: xparam = favorites[favoritenr].xparam else: xparam = 'distance' if 'yparam1' in kwargs: yparam1 = kwargs['yparam1'] else: if favorites: yparam1 = favorites[favoritenr].yparam1 else: yparam1 = 'pace' if 'yparam2' in kwargs: yparam2 = kwargs['yparam2'] if yparam2 == '': yparam2 = 'None' else: if favorites: yparam2 = favorites[favoritenr].yparam2 if yparam2 == '': yparam2 = 'None' else: yparam2 = 'hr' if not request.user.is_anonymous(): r = getrower(request.user) if favoritenr>=0 and r.showfavoritechartnotes: favoritechartnotes = favorites[favoritenr].notes else: favoritechartnotes = '' else: favoritechartnotes = '' favoritenr = 0 if 'plottype' in kwargs: plottype = kwargs['plottype'] else: if favorites: plottype = favorites[favoritenr].plottype else: plottype = 'line' if 'workstrokesonly' in kwargs: workstrokesonly = kwargs['workstrokesonly'] else: if favorites: workstrokesonly = not favorites[favoritenr].reststrokes else: workstrokesonly = False if request.method == 'POST' and 'savefavorite' in request.POST: if not request.user.is_anonymous(): workstrokesonly = request.POST['workstrokesonlysave'] reststrokes = not workstrokesonly r = getrower(request.user) f = FavoriteChart(user=r,xparam=xparam, yparam1=yparam1,yparam2=yparam2, plottype=plottype,workouttype=workouttype, reststrokes=reststrokes) f.save() if request.method == 'POST' and 'workstrokesonly' in request.POST: workstrokesonly = request.POST['workstrokesonly'] if workstrokesonly == 'True': workstrokesonly = True else: workstrokesonly = False if not promember: for name,d in rowingmetrics: if d['type'] != 'basic': if xparam == name: xparam = 'time' messages.info(request,'To use '+d['verbose_name']+', you have to be Pro member') if yparam1 == name: yparam1 = 'pace' messages.info(request,'To use '+d['verbose_name']+', you have to be Pro member') if yparam2 == name: yparam2 = 'spm' messages.info(request,'To use '+d['verbose_name']+', you have to be Pro member') # 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 = interactive_flex_chart2(id,xparam=xparam,yparam1=yparam1, yparam2=yparam2, promember=promember,plottype=plottype, workstrokesonly=workstrokesonly) js_resources = "" css_resources = "" axchoicesbasic = {ax[0]:ax[1] for ax in axes if ax[4]=='basic'} axchoicespro = {ax[0]:ax[1] for ax in axes if ax[4]=='pro'} noylist = ["time","distance"] axchoicesbasic.pop("cumdist") if row.workouttype in ('water','coastal'): for name,d in rowingmetrics: if d['mode'] == 'erg': axchoicespro.pop(name) return render(request, 'flexchart3otw.html', {'the_script':script, 'the_div':div, 'js_res': js_resources, 'css_res':css_resources, 'id':int(id), 'teams':get_my_teams(request.user), 'xparam':xparam, 'yparam1':yparam1, 'yparam2':yparam2, 'plottype':plottype, 'favoritechartnotes':favoritechartnotes, 'mayedit':mayedit, 'promember':promember, 'axchoicesbasic':axchoicesbasic, 'axchoicespro':axchoicespro, 'noylist':noylist, 'workstrokesonly': not workstrokesonly, 'favoritenr':favoritenr, 'maxfav':maxfav, }) else: for name,d in rowingmetrics: if d['mode'] == 'water': axchoicespro.pop(name) return render(request, 'flexchart3otw.html', {'the_script':script, 'the_div':div, 'js_res': js_resources, 'css_res':css_resources, 'teams':get_my_teams(request.user), 'id':int(id), 'xparam':xparam, 'yparam1':yparam1, 'yparam2':yparam2, 'plottype':plottype, 'axchoicesbasic':axchoicesbasic, 'axchoicespro':axchoicespro, '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=""): try: row = Workout.objects.get(id=id) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") # check if user is owner of this workout # create interactive plot f1 = row.csvfilename u = row.user.user # r = 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=""): try: row = Workout.objects.get(id=id) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") # check if user is owner of this workout # create interactive plot f1 = row.csvfilename u = row.user.user # r = getrower(u) promember=0 mayedit=0 if not request.user.is_anonymous(): r = getrower(request.user) result = request.user.is_authenticated() and ispromember(request.user) if result: promember=1 if request.user == row.user.user: mayedit=1 # create interactive plot res = interactive_otw_advanced_pace_chart(id,promember=promember) script = res[0] div = res[1] messages.error(request,message) messages.info(request,successmessage) return render(request, 'otwinteractive.html', {'workout':row, 'teams':get_my_teams(request.user), 'interactiveplot':script, 'the_div':div, 'mayedit':mayedit}) # the page where you can choose where to export this workout @login_required() def workout_export_view(request,id=0, message="", successmessage=""): request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE try: row = Workout.objects.get(id=id) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") try: thetoken = c2_open(request.user) except C2NoTokenError: thetoken = 0 if (checkworkoutuser(request.user,row)) and thetoken: c2userid = c2stuff.get_userid(thetoken) else: c2userid = 0 try: rktoken = runkeeper_open(request.user) except RunKeeperNoTokenError: rktoken = 0 if (checkworkoutuser(request.user,row)) and rktoken: rkuserid = runkeeperstuff.get_userid(rktoken) else: rkuserid = 0 form = WorkoutForm(instance=row) g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime") for i in g: try: width,height = Image.open(i.filename).size i.width = width i.height = height i.save() except: pass # check if user is owner of this workout if (checkworkoutuser(request.user,row)==False): message = "You are not allowed to edit this workout" messages.error(request,message) url = reverse(workouts_view) return HttpResponseRedirect(url) messages.error(request,message) messages.info(request,successmessage) return render(request, 'export.html', {'workout':row, 'teams':get_my_teams(request.user), 'c2userid':c2userid, 'rkuserid':rkuserid, }) # @login_required() def workout_unsubscribe_view(request,id=0): try: w = Workout.objects.get(id=id) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") if w.privacy == 'private' and w.user.user != request.user: return HttpResponseForbidden("Permission error") comments = WorkoutComment.objects.filter(workout=w, user=request.user).order_by("created") for c in comments: c.notification = False c.save() form = WorkoutCommentForm() 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): try: w = Workout.objects.get(id=id) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") if w.privacy == 'private' and w.user.user != request.user: return HttpResponseForbidden("Permission error") comments = WorkoutComment.objects.filter(workout=w).order_by("created") # ok we're permitted if request.method == 'POST': r = w.user form = WorkoutCommentForm(request.POST) if form.is_valid(): cd = form.cleaned_data comment = cd['comment'] comment = bleach.clean(comment) notification = cd['notification'] c = WorkoutComment(workout=w,user=request.user,comment=comment, notification=notification) c.save() url = reverse(workout_comment_view, kwargs={ 'id':id }) message = '{name} says: {comment}'.format( name = request.user.first_name, comment = comment, url = url, ) if request.user != r.user: a_messages.info(r.user,message) res = myqueue(queuehigh, handle_sendemailnewcomment,r.user.first_name, r.user.last_name, r.user.email, request.user.first_name, request.user.last_name, comment,w.name,w.id) commenters = {oc.user for oc in comments if oc.notification} for u in commenters: a_messages.info(u,message) if u != request.user and u != r.user: res = myqueue(queuelow, handle_sendemailnewresponse, u.first_name, u.last_name, u.email, request.user.first_name, request.user.last_name, comment, w.name, w.id, c.id) url = reverse(workout_comment_view,kwargs = { 'id':id}) return HttpResponseRedirect(url) form = WorkoutCommentForm() g = GraphImage.objects.filter(workout=w).order_by("-creationdatetime") for i in g: try: width,height = Image.open(i.filename).size i.width = width i.height = height i.save() except: pass if (len(g)<=3): return render(request, 'workout_comments.html', {'workout':w, 'teams':get_my_teams(request.user), 'graphs1':g[0:3], 'comments':comments, 'form':form, }) else: return render(request, 'workout_comments.html', {'workout':w, 'teams':get_my_teams(request.user), 'graphs1':g[0:3], 'graphs1':g[3:6], 'comments':comments, 'form':form, }) # 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'] try: # check if valid ID exists (workout exists) row = Workout.objects.get(id=id) form = WorkoutForm(instance=row) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") if request.method == 'POST': # Form was submitted form = WorkoutForm(request.POST) if form.is_valid(): # Get values from form name = form.cleaned_data['name'] date = form.cleaned_data['date'] starttime = form.cleaned_data['starttime'] workouttype = form.cleaned_data['workouttype'] duration = form.cleaned_data['duration'] distance = form.cleaned_data['distance'] notes = form.cleaned_data['notes'] thetimezone = form.cleaned_data['timezone'] try: boattype = request.POST['boattype'] except KeyError: boattype = Workout.objects.get(id=id).boattype try: privacy = request.POST['privacy'] except KeyError: privacy = Workout.objects.get(id=id).privacy try: rankingpiece = form.cleaned_data['rankingpiece'] except KeyError: rankingpiece =- Workout.objects.get(id=id).rankingpiece startdatetime = (str(date) + ' ' + str(starttime)) startdatetime = datetime.datetime.strptime(startdatetime, "%Y-%m-%d %H:%M:%S") startdatetime = timezone.make_aware(startdatetime) startdatetime = startdatetime.astimezone(pytz.timezone(thetimezone)) # check if user is owner of this workout if checkworkoutuser(request.user,row): row.name = name row.date = date row.starttime = starttime row.startdatetime = startdatetime row.workouttype = workouttype row.notes = notes row.duration = duration row.distance = distance row.boattype = boattype row.privacy = privacy row.rankingpiece = rankingpiece row.timezone = thetimezone try: row.save() except IntegrityError: pass # change data in csv file r = rdata(row.csvfilename) if r == 0: return HttpResponse("Error: CSV Data File Not Found") r.rowdatetime = startdatetime r.write_csv(row.csvfilename,gzip=True) dataprep.update_strokedata(id,r.df) successmessage = "Changes saved" if rankingpiece: dataprep.runcpupdate(row.user,type=row.workouttype) messages.info(request,successmessage) url = reverse(workout_edit_view, kwargs = { 'id':str(row.id), }) response = HttpResponseRedirect(url) else: message = "You are not allowed to change this workout" messages.error(request,message) url = reverse(workouts_view) response = HttpResponseRedirect(url) #else: # form not POSTed form = WorkoutForm(instance=row) try: row = Workout.objects.get(id=id) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime") for i in g: try: width,height = Image.open(i.filename).size i.width = width i.height = height i.save() except: pass # check if user is owner of this workout comments = WorkoutComment.objects.filter(workout=row) aantalcomments = len(comments) if (checkworkoutuser(request.user,row)==False): raise Http404("You are not allowed to edit this workout") # create interactive plot f1 = row.csvfilename u = row.user.user r = getrower(u) rowdata = rdata(f1) hascoordinates = 1 if rowdata != 0: try: latitude = rowdata.df[' latitude'] if not latitude.std(): hascoordinates = 0 except KeyError,AttributeError: hascoordinates = 0 else: hascoordinates = 0 if hascoordinates: mapscript,mapdiv = leaflet_chart(rowdata.df[' latitude'], rowdata.df[' longitude'], row.name) #res = googlemap_chart(rowdata.df[' latitude'], # rowdata.df[' longitude'], # row.name) #gmscript = res[0] #gmdiv = res[1] else: mapscript = "" mapdiv = "" # render page if (len(g)<=3): return render(request, 'workout_form.html', {'form':form, 'workout':row, 'teams':get_my_teams(request.user), 'graphs1':g[0:3], 'mapscript':mapscript, 'aantalcomments':aantalcomments, 'mapdiv':mapdiv, }) else: return render(request, 'workout_form.html', {'form':form, 'teams':get_my_teams(request.user), 'workout':row, 'graphs1':g[0:3], 'graphs2':g[3:6], 'mapscript':mapscript, 'aantalcomments':aantalcomments, 'mapdiv':mapdiv, }) return HttpResponseRedirect(url) @login_required() def workout_map_view(request,id=0): request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE request.session['referer'] = absolute(request)['PATH'] try: # check if valid ID exists (workout exists) row = Workout.objects.get(id=id) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") # create interactive plot f1 = row.csvfilename u = row.user.user r = getrower(u) rowdata = rdata(f1) hascoordinates = 1 if rowdata != 0: try: latitude = rowdata.df[' latitude'] if not latitude.std(): hascoordinates = 0 except KeyError,AttributeError: hascoordinates = 0 else: hascoordinates = 0 if hascoordinates: mapscript,mapdiv = leaflet_chart2(rowdata.df[' latitude'], rowdata.df[' longitude'], row.name) else: mapscript = "" mapdiv = "" mayedit=0 if not request.user.is_anonymous(): r = getrower(request.user) result = request.user.is_authenticated() and ispromember(request.user) if result: promember=1 if request.user == row.user.user: mayedit=1 return render(request, 'map_view.html', {'mapscript':mapscript, 'workout':row, 'mapdiv':mapdiv, 'mayedit':mayedit, }) # The basic edit page @login_required() def workout_edit_view_navionics(request,id=0,message="",successmessage=""): request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE request.session['referer'] = absolute(request)['PATH'] try: # check if valid ID exists (workout exists) row = Workout.objects.get(id=id) form = WorkoutForm(instance=row) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") if request.method == 'POST': # Form was submitted form = WorkoutForm(request.POST) if form.is_valid(): # Get values from form name = form.cleaned_data['name'] date = form.cleaned_data['date'] starttime = form.cleaned_data['starttime'] workouttype = form.cleaned_data['workouttype'] duration = form.cleaned_data['duration'] distance = form.cleaned_data['distance'] notes = form.cleaned_data['notes'] thetimezone = form.cleaned_data['timezone'] try: boattype = request.POST['boattype'] except KeyError: boattype = Workout.objects.get(id=id).boattype try: privacy = request.POST['privacy'] except KeyError: privacy = Workout.objects.get(id=id).privacy try: rankingpiece = form.cleaned_data['rankingpiece'] except KeyError: rankingpiece =- Workout.objects.get(id=id).rankingpiece startdatetime = (str(date) + ' ' + str(starttime)) startdatetime = datetime.datetime.strptime(startdatetime, "%Y-%m-%d %H:%M:%S") startdatetime = timezone.make_aware(startdatetime) startdatetime = startdatetime.astimezone(pytz.timezone(thetimezone)) # check if user is owner of this workout if checkworkoutuser(request.user,row): row.name = name row.date = date row.starttime = starttime row.startdatetime = startdatetime row.workouttype = workouttype row.notes = notes row.duration = duration row.distance = distance row.boattype = boattype row.privacy = privacy row.rankingpiece = rankingpiece row.timezone = thetimezone try: row.save() except IntegrityError: pass # change data in csv file r = rdata(row.csvfilename) if r == 0: return HttpResponse("Error: CSV Data File Not Found") r.rowdatetime = startdatetime r.write_csv(row.csvfilename,gzip=True) dataprep.update_strokedata(id,r.df) successmessage = "Changes saved" messages.info(request,successmessage) url = reverse(workout_edit_view, kwargs = { 'id':str(row.id), }) response = HttpResponseRedirect(url) else: message = "You are not allowed to change this workout" messages.error(request,message) url = reverse(workouts_view) response = HttpResponseRedirect(url) #else: # form not POSTed form = WorkoutForm(instance=row) try: row = Workout.objects.get(id=id) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime") for i in g: try: width,height = Image.open(i.filename).size i.width = width i.height = height i.save() except: pass # check if user is owner of this workout comments = WorkoutComment.objects.filter(workout=row) aantalcomments = len(comments) if (checkworkoutuser(request.user,row)==False): raise Http404("You are not allowed to edit this workout") # create interactive plot f1 = row.csvfilename u = row.user.user r = getrower(u) rowdata = rdata(f1) hascoordinates = 1 if rowdata != 0: try: latitude = rowdata.df[' latitude'] if not latitude.std(): hascoordinates = 0 except KeyError,AttributeError: hascoordinates = 0 else: hascoordinates = 0 if hascoordinates: mapscript,mapdiv = leaflet_chart2(rowdata.df[' latitude'], rowdata.df[' longitude'], row.name) else: mapscript = "" mapdiv = "" # render page if (len(g)<=3): return render(request, 'workout_form.html', {'form':form, 'workout':row, 'teams':get_my_teams(request.user), 'graphs1':g[0:3], 'mapscript':mapscript, 'aantalcomments':aantalcomments, 'mapdiv':mapdiv, }) else: return render(request, 'workout_form.html', {'form':form, 'teams':get_my_teams(request.user), 'workout':row, 'graphs1':g[0:3], 'graphs2':g[3:6], 'mapscript':mapscript, 'aantalcomments':aantalcomments, 'mapdiv':mapdiv, }) return HttpResponseRedirect(url) # Generic chart creation @login_required() def workout_add_chart_view(request,id,plotnr=1): try: w = Workout.objects.get(id=id) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") 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 ) try: request.session['async_tasks'] += [(jobid,'make_plot')] except KeyError: request.session['async_tasks'] = [(jobid,'make_plot')] try: url = request.session['referer'] except KeyError: url = "/rowers/workout/"+str(w.id)+"/edit" return HttpResponseRedirect(url) # The page where you select which Strava workout to import @login_required() def workout_stravaimport_view(request,message=""): res = stravastuff.get_strava_workout_list(request.user) if (res.status_code != 200): if (res.status_code == 401): r = getrower(request.user) if (r.stravatoken == '') or (r.stravatoken is None): s = "Token doesn't exist. Need to authorize" return HttpResponseRedirect("/rowers/me/stravaauthorize/") message = "Something went wrong in workout_stravaimport_view" messages.error(request,message) if settings.DEBUG: return HttpResponse(res) else: url = reverse(workouts_view) return HttpResponseRedirect(url) else: workouts = [] r = getrower(request.user) stravaids = [int(item['id']) for item in res.json()] knownstravaids = uniqify([ w.uploadedtostrava for w in Workout.objects.filter(user=r) ]) newids = [stravaid for stravaid in stravaids if not stravaid in knownstravaids] for item in res.json(): d = int(float(item['distance'])) i = item['id'] if i in knownstravaids: nnn = '' else: nnn = 'NEW' n = item['name'] ttot = str(datetime.timedelta(seconds=int(float(item['elapsed_time'])))) s = item['start_date'] r = item['type'] keys = ['id','distance','duration','starttime','type','name','new'] values = [i,d,ttot,s,r,n,nnn] res = dict(zip(keys,values)) workouts.append(res) return render(request,'strava_list_import.html', {'workouts':workouts, 'teams':get_my_teams(request.user), }) return HttpResponse(res) # The page where you select which RunKeeper workout to import @login_required() def workout_runkeeperimport_view(request,message=""): res = runkeeperstuff.get_runkeeper_workout_list(request.user) if (res.status_code != 200): if (res.status_code == 401): r = getrower(request.user) if (r.runkeepertoken == '') or (r.runkeepertoken is None): s = "Token doesn't exist. Need to authorize" return HttpResponseRedirect("/rowers/me/runkeeperauthorize/") message = "Something went wrong in workout_runkeeperimport_view" messages.error(request,message) if settings.DEBUG: return HttpResponse(res) else: url = reverse(workouts_view) return HttpResponseRedirect(url) else: workouts = [] for item in res.json()['items']: d = int(float(item['total_distance'])) i = getidfromsturi(item['uri'],length=9) ttot = str(datetime.timedelta(seconds=int(float(item['duration'])))) s = item['start_time'] r = item['type'] keys = ['id','distance','duration','starttime','type'] values = [i,d,ttot,s,r] res = dict(zip(keys,values)) workouts.append(res) return render(request,'runkeeper_list_import.html', {'workouts':workouts, 'teams':get_my_teams(request.user), }) return HttpResponse(res) # The page where you select which RunKeeper workout to import @login_required() def workout_underarmourimport_view(request,message=""): res = underarmourstuff.get_underarmour_workout_list(request.user) if (res.status_code != 200): if (res.status_code == 401): r = getrower(request.user) if (r.underarmourtoken == '') or (r.underarmourtoken is None): s = "Token doesn't exist. Need to authorize" return HttpResponseRedirect("/rowers/me/underarmourauthorize/") message = "Something went wrong in workout_underarmourimport_view" messages.error(request,message) if settings.DEBUG: return HttpResponse(res) else: url = reverse(workouts_view) return HttpResponseRedirect(url) else: workouts = [] items = res.json()['_embedded']['workouts'] for item in items: if 'has_time_series' in item: if item['has_time_series']: s = item['start_datetime'] i,r = underarmourstuff.get_idfromuri(request.user,item['_links']) n = item['name'] try: d = item['aggregates']['distance_total'] except KeyError: d = 0 try: ttot = item['aggregates']['active_time_total'] except KeyError: ttot = 0 keys = ['id','distance','duration','starttime','type'] values = [i,d,ttot,s,r] thedict = dict(zip(keys,values)) workouts.append(thedict) return render(request,'underarmour_list_import.html', {'workouts':workouts, 'teams':get_my_teams(request.user), }) return HttpResponse(res) # The page where you select which SportTracks workout to import @login_required() def workout_sporttracksimport_view(request,message=""): res = sporttracksstuff.get_sporttracks_workout_list(request.user) if (res.status_code != 200): if (res.status_code == 401): r = getrower(request.user) if (r.sporttrackstoken == '') or (r.sporttrackstoken is None): s = "Token doesn't exist. Need to authorize" return HttpResponseRedirect("/rowers/me/sporttracksauthorize/") message = "Something went wrong in workout_sporttracksimport_view" messages.error(request,message) if settings.DEBUG: return HttpResponse(res) else: url = reverse(workouts_view) return HttpResponseRedirect(url) else: workouts = [] r = getrower(request.user) stids = [int(getidfromsturi(item['uri'])) for item in res.json()['items']] knownstids = uniqify([ w.uploadedtosporttracks for w in Workout.objects.filter(user=r) ]) newids = [stid for stid in stids if not stid in knownstids] for item in res.json()['items']: d = int(float(item['total_distance'])) i = int(getidfromsturi(item['uri'])) if i in knownstids: nnn = '' else: nnn = 'NEW' n = item['name'] ttot = str(datetime.timedelta(seconds=int(float(item['duration'])))) s = item['start_time'] r = item['type'] keys = ['id','distance','duration','starttime','type','name','new'] values = [i,d,ttot,s,r,n,nnn] res = dict(zip(keys,values)) workouts.append(res) return render(request,'sporttracks_list_import.html', {'workouts':workouts, 'teams':get_my_teams(request.user), }) return HttpResponse(res) # List of workouts on Concept2 logbook. This view only used for debugging @login_required() def c2listdebug_view(request,page=1,message=""): try: thetoken = c2_open(request.user) except C2NoTokenError: return HttpResponseRedirect("/rowers/me/c2authorize/") r = getrower(request.user) res = c2stuff.get_c2_workout_list(request.user,page=page) if (res.status_code != 200): message = "Something went wrong in workout_c2import_view (C2 token renewal)" messages.error(request,message) if settings.DEBUG: return HttpResponse(res) else: url = reverse(workouts_view) return HttpResponseRedirect(url) else: workouts = [] print res.json()['meta'] for item in res.json()['data']: d = item['distance'] i = item['id'] ttot = item['time_formatted'] s = item['date'] r = item['type'] s2 = item['source'] c = item['comments'] keys = ['id','distance','duration','starttime','rowtype','source','comment'] values = [i,d,ttot,s,r,s2,c] res = dict(zip(keys,values)) workouts.append(res) return render(request, 'c2_list_import2.html', {'workouts':workouts, 'teams':get_my_teams(request.user), }) # Import all unknown workouts available on Concept2 logbook @login_required() def workout_getc2workout_all(request,page=1,message=""): try: thetoken = c2_open(request.user) except C2NoTokenError: return HttpResponseRedirect("/rowers/me/c2authorize/") res = c2stuff.get_c2_workout_list(request.user,page=page) if (res.status_code != 200): message = "Something went wrong in workout_c2import_view (C2 token refresh)" messages.error(request,message) else: r = getrower(request.user) c2ids = [item['id'] for item in res.json()['data'] if item['source'] != 'Web'] 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: res = c2stuff.get_c2_workout(request.user,c2id) if (res.status_code == 200): data = res.json()['data'] splitdata = None if 'workout' in data: if 'splits' in data['workout']: splitdata = data['workout']['splits'] if 'intervals' in data['workout']: splitdata = data['workout']['intervals'] # Check if workout has stroke data, and get the stroke data if data['stroke_data']: res2 = c2stuff.get_c2_workout_strokes(request.user,c2id) # We have stroke data if res2.status_code == 200: strokedata = pd.DataFrame.from_dict(res2.json()['data']) # create the workout id,message = add_workout_from_strokedata( request.user,c2id,data,strokedata, source='c2') w = Workout.objects.get(id=id) w.uploadedtoc2=c2id w.save() if message: messages.error(request,message) url = reverse(workouts_view) return HttpResponseRedirect(url) # List of workouts available on Concept2 logbook - for import @login_required() def workout_c2import_view(request,page=1,message=""): try: thetoken = c2_open(request.user) except C2NoTokenError: return HttpResponseRedirect("/rowers/me/c2authorize/") res = c2stuff.get_c2_workout_list(request.user,page=page) if (res.status_code != 200): message = "Something went wrong in workout_c2import_view (C2 token refresh)" messages.error(request,message) if settings.DEBUG: return HttpResponse(res) else: url = reverse(workouts_view) return HttpResponseRedirect(url) else: workouts = [] r = getrower(request.user) c2ids = [item['id'] for item in res.json()['data']] knownc2ids = uniqify([ w.uploadedtoc2 for w in Workout.objects.filter(user=r) ]) newids = [c2id for c2id in c2ids if not c2id in knownc2ids] for item in res.json()['data']: d = item['distance'] i = item['id'] ttot = item['time_formatted'] s = item['date'] r = item['type'] s2 = item['source'] c = item['comments'] if i in knownc2ids: nnn = '' else: nnn = 'NEW' keys = ['id','distance','duration','starttime','rowtype','source','comment','new'] values = [i,d,ttot,s,r,s2,c,nnn] res = dict(zip(keys,values)) workouts.append(res) return render(request, 'c2_list_import2.html', {'workouts':workouts, 'teams':get_my_teams(request.user), 'page':page, }) # Import a workout from Strava @login_required() def workout_getstravaworkout_view(request,stravaid): res = stravastuff.get_strava_workout(request.user,stravaid) if not res[0]: messages.error(request,res[1]) return imports_view(request) strokedata = res[1] data = res[0] id,message = add_workout_from_strokedata(request.user,stravaid,data,strokedata, source='strava', workoutsource='strava') try: w = Workout.objects.get(id=id) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") w.uploadedtostrava=stravaid w.save() if message: messages.error(request,message) r = getrower(request.user) url = reverse(r.defaultlandingpage, kwargs = { 'id':int(id), }) return HttpResponseRedirect(url) # Imports a workout from Runkeeper @login_required() def workout_getrunkeeperworkout_view(request,runkeeperid): res = runkeeperstuff.get_runkeeper_workout(request.user,runkeeperid) data = res.json() id,message = add_workout_from_runkeeperdata(request.user,runkeeperid,data) w = Workout.objects.get(id=id) w.uploadedtorunkeeper=runkeeperid thetoken = runkeeper_open(request.user) w.save() if message: messages.error(request,message) r = getrower(request.user) url = reverse(r.defaultlandingpage, kwargs = { 'id':int(id), }) return HttpResponseRedirect(url) # Imports a workout from Underarmour @login_required() def workout_getunderarmourworkout_view(request,underarmourid): res = underarmourstuff.get_underarmour_workout(request.user,underarmourid) data = res.json() id,message = add_workout_from_underarmourdata(request.user,underarmourid,data) w = Workout.objects.get(id=id) w.uploadedtounderarmour=underarmourid w.save() if message: messages.error(request,message) r = getrower(request.user) url = reverse(r.defaultlandingpage, kwargs = { 'id':int(id), }) return HttpResponseRedirect(url) # Imports a workout from SportTracks @login_required() def workout_getsporttracksworkout_view(request,sporttracksid): res = sporttracksstuff.get_sporttracks_workout(request.user,sporttracksid) data = res.json() id,message = add_workout_from_stdata(request.user,sporttracksid,data) if id==0: messages.error(request,message) url = reverse(workouts_view) return HttpResponseRedirect(url) w = Workout.objects.get(id=id) w.uploadedtosporttracks=sporttracksid w.save() if message: messages.error(request,message) r = getrower(request.user) url = reverse(r.defaultlandingpage, kwargs = { 'id':int(id), }) return HttpResponseRedirect(url) # Imports all new workouts from SportTracks @login_required() def workout_getsporttracksworkout_all(request): res = sporttracksstuff.get_sporttracks_workout_list(request.user) if (res.status_code == 200): r = getrower(request.user) stids = [int(getidfromsturi(item['uri'])) for item in res.json()['items']] knownstids = uniqify([ w.uploadedtosporttracks for w in Workout.objects.filter(user=r) ]) newids = [stid for stid in stids if not stid in knownstids] for sporttracksid in newids: res = sporttracksstuff.get_sporttracks_workout( request.user,sporttracksid) data = res.json() id,message = add_workout_from_stdata(request.user,sporttracksid,data) if id==0: messages.error(request,message) else: w = Workout.objects.get(id=id) w.uploadedtosporttracks=sporttracksid w.save() url = reverse(workouts_view) return HttpResponseRedirect(url) # Imports all new workouts from SportTracks @login_required() def workout_getstravaworkout_all(request): res = stravastuff.get_strava_workout_list(request.user) if (res.status_code == 200): r = getrower(request.user) stravaids = [int(item['id']) for item in res.json()] knownstravaids = uniqify([ w.uploadedtostrava for w in Workout.objects.filter(user=r) ]) newids = [stravaid for stravaid in stravaids if not stravaid in knownstravaids] for stravaid in newids: res = stravastuff.get_strava_workout(request.user,stravaid) strokedata = res[1] data = res[0] if data: id,message = add_workout_from_strokedata( request.user,stravaid,data,strokedata, source='strava', workoutsource='strava') if id==0: messages.error(request,message) else: messages.info(request,"imported Strava workout "+str(stravaid)) w = Workout.objects.get(id=id) w.uploadedtostrava=stravaid w.save() else: messages.error(request,"Couldn't import Strava workout "+str(stravaid)) url = reverse(workouts_view) return HttpResponseRedirect(url) # Imports a workout from Concept2 @login_required() def workout_getc2workout_view(request,c2id): try: thetoken = c2_open(request.user) except C2NoTokenError: return HttpResponseRedirect("/rowers/me/c2authorize/") res = c2stuff.get_c2_workout(request.user,c2id) if (res.status_code == 200): data = res.json()['data'] splitdata = None if 'workout' in data: if 'splits' in data['workout']: splitdata = data['workout']['splits'] if 'intervals' in data['workout']: splitdata = data['workout']['intervals'] # Check if workout has stroke data, and get the stroke data if data['stroke_data']: res2 = c2stuff.get_c2_workout_strokes(request.user,c2id) else: message = "This workout does not have any stroke data associated with it" messages.error(request,message) url = reverse(workout_c2import_view) return HttpResponseRedirect(url) # We have stroke data if res2.status_code == 200: strokedata = pd.DataFrame.from_dict(res2.json()['data']) # create the workout id,message = add_workout_from_strokedata(request.user,c2id,data,strokedata, source='c2') w = Workout.objects.get(id=id) w.uploadedtoc2=c2id # If we have split data, update the stroke data so they # match exactly (some users are anal about this) if splitdata: try: w.summary,sa,results = c2stuff.summaryfromsplitdata(splitdata,data,w.csvfilename) except: sa = [] results = [] with open("media/c2splitdata.log","a") as errorlog: errorstring = str(sys.exc_info()[0]) timestr = strftime("%Y%m%d-%H%M%S") errorlog.write(timestr+errorstring+"\r\n") errorlog.write("views.py line 952\r\n") w.save() from rowingdata.trainingparser import getlist # set stroke data in CSV file if sa: values = getlist(sa) units = getlist(sa,sel='unit') types = getlist(sa,sel='type') rowdata = rdata(w.csvfilename) if rowdata: rowdata.updateintervaldata(values, units,types,results) rowdata.write_csv(w.csvfilename,gzip=True) dataprep.update_strokedata(w.id,rowdata.df) if message: messages.error(request,message) r = getrower(request.user) url = reverse(r.defaultlandingpage, kwargs = { 'id':int(id), }) return HttpResponseRedirect(url) else: # message = json.loads(s.text)['message'] message = json.loads(res2.text)['message'] messages.error(request,message) url = reverse(workout_c2import_view) return HttpResponseRedirect(url) else: message = "Received error code from Concept2" messages.error(request,message) if settings.DEBUG: return HttpResponse(res) else: url = reverse(workout_c2import_view) return HttpResponseRedirect(url) # This is the main view for processing uploaded files @login_required() def workout_upload_view(request, uploadoptions={ 'makeprivate':False, 'make_plot':False, 'upload_to_C2':False, 'plottype':'timeplot', 'landingpage':'workout_edit_view', }, docformoptions={ 'workouttype':'rower', }): is_ajax = False if request.is_ajax(): is_ajax = True r = getrower(request.user) if 'uploadoptions' in request.session: uploadoptions = request.session['uploadoptions'] try: defaultlandingpage = uploadoptions['landingpage'] except KeyError: uploadoptions['landingpage'] = r.defaultlandingpage defaultlandingpage = r.defaultlandingpage else: request.session['uploadoptions'] = uploadoptions if 'docformoptions' in request.session: docformoptions = request.session['docformoptions'] else: request.session['docformoptions'] = docformoptions try: makeprivate = uploadoptions['makeprivate'] except KeyError: makeprivate = False try: make_plot = uploadoptions['make_plot'] except KeyError: make_plot = False try: workouttype = docformoptions['workouttype'] except KeyError: workouttype = 'rower' try: plottype = uploadoptions['plottype'] except KeyError: plottype = 'timeplot' try: landingpage = uploadoptions['landingpage'] except KeyError: landingpage = r.defaultlandingpage uploadoptions['landingpage'] = landingpage try: upload_to_c2 = uploadoptions['upload_to_C2'] except KeyError: upload_to_c2 = False try: upload_to_strava = uploadoptions['upload_to_Strava'] except KeyError: upload_to_strava = False try: upload_to_st = uploadoptions['upload_to_SportTracks'] except KeyError: upload_to_st = False try: upload_to_rk = uploadoptions['upload_to_RunKeeper'] except KeyError: upload_to_rk = False try: upload_to_ua = uploadoptions['upload_to_MapMyFitness'] except KeyError: upload_to_ua = False try: upload_to_tp = uploadoptions['upload_to_TrainingPeaks'] except KeyError: upload_to_tp = False response = {} if request.method == 'POST': form = DocumentsForm(request.POST,request.FILES) optionsform = UploadOptionsForm(request.POST) if form.is_valid(): # f = request.FILES['file'] f = form.cleaned_data['file'] res = handle_uploaded_file(f) t = form.cleaned_data['title'] workouttype = form.cleaned_data['workouttype'] request.session['docformoptions'] = { 'workouttype':workouttype, } notes = form.cleaned_data['notes'] offline = form.cleaned_data['offline'] if optionsform.is_valid(): make_plot = optionsform.cleaned_data['make_plot'] plottype = optionsform.cleaned_data['plottype'] upload_to_c2 = optionsform.cleaned_data['upload_to_C2'] upload_to_strava = optionsform.cleaned_data['upload_to_Strava'] upload_to_st = optionsform.cleaned_data['upload_to_SportTracks'] upload_to_rk = optionsform.cleaned_data['upload_to_RunKeeper'] upload_to_ua = optionsform.cleaned_data['upload_to_MapMyFitness'] upload_to_tp = optionsform.cleaned_data['upload_to_TrainingPeaks'] makeprivate = optionsform.cleaned_data['makeprivate'] landingpage = optionsform.cleaned_data['landingpage'] uploadoptions = { 'makeprivate':makeprivate, 'make_plot':make_plot, 'plottype':plottype, 'upload_to_C2':upload_to_c2, 'upload_to_Strava':upload_to_strava, 'upload_to_SportTracks':upload_to_st, 'upload_to_RunKeeper':upload_to_rk, 'upload_to_MapMyFitness':upload_to_ua, 'upload_to_TrainingPeaks':upload_to_tp, 'landingpage':landingpage, } request.session['uploadoptions'] = uploadoptions f1 = res[0] # file name f2 = res[1] # file name incl media directory if not offline: id,message,f2 = dataprep.new_workout_from_file( r,f2, workouttype=workouttype, makeprivate=makeprivate, title = t, notes='' ) else: workoutsbox = Mailbox.objects.filter(name='workouts')[0] uploadoptions['fromuploadform'] = True bodyyaml = yaml.safe_dump( uploadoptions, default_flow_style=False ) msg = Message(mailbox=workoutsbox, from_header=r.user.email, subject = t,body=bodyyaml) msg.save() f3 = 'media/mailbox_attachments/'+f2[6:] copyfile(f2,f3) f3 = f3[6:] a = MessageAttachment(message=msg,document=f3) a.save() os.remove(f2) # job = myqueue( # queuehigh, # handle_zip_file, # r.user.email, # t, # f2) messages.info( request, "The file was too large to process in real time. It will be processed in a background process. 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) try: request.session['async_tasks'] += [(jobid,'make_plot')] except KeyError: request.session['async_tasks'] = [(jobid,'make_plot')] # upload to C2 if (upload_to_c2): try: message,id = c2stuff.workout_c2_upload(request.user,w) except C2NoTokenError: id = 0 message = "Something went wrong with the Concept2 sync" if id>1: messages.info(request,message) else: messages.error(request,message) if (upload_to_strava): try: message,id = stravastuff.workout_strava_upload( request.user,w ) except StravaNoTokenError: id = 0 message = "Please connect to Strava first" if id>1: messages.info(request,message) else: messages.error(request,message) if (upload_to_st): try: message,id = sporttracksstuff.workout_sporttracks_upload( request.user,w ) except SportTracksNoTokenError: message = "Please connect to SportTracks first" id = 0 if id>1: messages.info(request,message) else: messages.error(request,message) if (upload_to_rk): try: message,id = runkeeperstuff.workout_runkeeper_upload( request.user,w ) except RunKeeperNoTokenError: message = "Please connect to Runkeeper first" id = 0 if id>1: messages.info(request,message) else: messages.error(request,message) if (upload_to_ua): try: message,id = underarmourstuff.workout_ua_upload( request.user,w ) except UnderArmourNoTokenError: message = "Please connect to MapMyFitness first" id = 0 if id>1: messages.info(request,message) else: messages.error(request,message) if (upload_to_tp): try: message,id = tpstuff.workout_tp_upload( request.user,w ) except TPNoTokenError: message = "Please connect to TrainingPeaks first" id = 0 if id>1: messages.info(request,message) else: messages.error(request,message) if landingpage != 'workout_upload_view': url = reverse(landingpage, kwargs = { 'id':w.id, }) else: url = reverse(landingpage) if is_ajax: response = {'result':1,'url':url} else: response = HttpResponseRedirect(url) else: if not is_ajax: response = render(request, 'document_form.html', {'form':form, 'teams':get_my_teams(request.user), 'optionsform': optionsform, }) if is_ajax: return JSONResponse(response) else: return response else: if not is_ajax: form = DocumentsForm(initial=docformoptions) optionsform = UploadOptionsForm(initial=uploadoptions) return render(request, 'document_form.html', {'form':form, 'teams':get_my_teams(request.user), 'optionsform': optionsform, }) else: return {'result':0} # This is the main view for processing uploaded files @user_passes_test(iscoachmember,login_url="/",redirect_field_name=None) def team_workout_upload_view(request,message="", successmessage="", uploadoptions={ 'make_plot':False, 'plottype':'timeplot', }): if 'uploadoptions' in request.session: uploadoptions = request.session['uploadoptions'] else: request.session['uploadoptions'] = uploadoptions myteams = Team.objects.filter(manager=request.user) make_plot = uploadoptions['make_plot'] plottype = uploadoptions['plottype'] r = getrower(request.user) if request.method == 'POST': form = DocumentsForm(request.POST,request.FILES) optionsform = TeamUploadOptionsForm(request.POST) rowerform = TeamInviteForm(request.POST) rowerform.fields.pop('email') rowerform.fields['user'].queryset = User.objects.filter(rower__isnull=False,rower__team__in=myteams).distinct() if form.is_valid(): f = request.FILES['file'] res = handle_uploaded_file(f) t = form.cleaned_data['title'] offline = form.cleaned_data['offline'] if rowerform.is_valid(): u = rowerform.cleaned_data['user'] if u: r = getrower(u) else: message = 'Please select a rower' messages.error(request,message) messages.info(request,successmessage) response = render(request, 'team_document_form.html', {'form':form, 'teams':get_my_teams(request.user), 'optionsform': optionsform, 'rowerform': rowerform, }) return response workouttype = form.cleaned_data['workouttype'] notes = form.cleaned_data['notes'] if optionsform.is_valid(): make_plot = optionsform.cleaned_data['make_plot'] plottype = optionsform.cleaned_data['plottype'] uploadoptions = { 'makeprivate':False, 'make_plot':make_plot, 'plottype':plottype, 'upload_to_C2':False, } request.session['uploadoptions'] = uploadoptions f1 = res[0] # file name f2 = res[1] # file name incl media directory if not offline: id,message,f2 = dataprep.new_workout_from_file( r,f2, workouttype=workouttype, makeprivate=False, title = t, notes='' ) else: job = myqueue( queuehigh, handle_zip_file, r.user.email, t, f2) messages.info( request, "The file was too large to process in real time. It will be processed in a background process. The user will receive an email when it is ready" ) url = reverse(team_workout_upload_view) response = HttpResponseRedirect(url) return response if not id: messages.error(request,message) url = reverse(team_workout_upload_view) response = HttpResponseRedirect(url) return response elif id == -1: message = 'The zip archive will be processed in the background. The files in the archive will only be uploaded without the extra actions. You will receive email when the workouts are ready.' messages.info(request,message) url = reverse(team_workout_upload_view) response = HttpResponseRedirect(url) return response else: successmessage = "The workout was added to the user's account" messages.info(request,successmessage) url = reverse(team_workout_upload_view) response = HttpResponseRedirect(url) w = Workout.objects.get(id=id) r = getrower(request.user) if (make_plot): id = uploads.make_plot(r,w,f1,f2,plottype,t) else: response = render(request, 'team_document_form.html', {'form':form, 'teams':get_my_teams(request.user), 'optionsform': optionsform, 'rowerform': rowerform, }) return response else: form = DocumentsForm() optionsform = TeamUploadOptionsForm(initial=uploadoptions) rowerform = TeamInviteForm() rowerform.fields.pop('email') rowerform.fields['user'].queryset = User.objects.filter(rower__isnull=False,rower__team__in=myteams).distinct() return render(request, 'team_document_form.html', {'form':form, 'teams':get_my_teams(request.user), 'optionsform': optionsform, 'rowerform':rowerform, }) # Ask the user if he really wants to delete the workout @login_required() def workout_delete_confirm_view(request, id=0): try: row = Workout.objects.get(id=id) if (checkworkoutuser(request.user,row)==False): raise PermissionDenied("You are not allowed to delete this workout") else: url = request.META.get('HTTP_REFERER','/') return render(request,'workout_delete_confirm.html', {'id':int(id), 'teams':get_my_teams(request.user), 'workout':row, 'url':url}) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") # Really deleting the workout @login_required() def workout_delete_view(request,id=0): try: row = Workout.objects.get(id=id) if (checkworkoutuser(request.user,row)==False): raise PermissionDenied("You are not allowed to delete this workout") else: # files are removed by pre-delete in models.py row.delete() messages.info(request,'Workout deleted') try: url = request.session['referer'] if 'rowers/workout/' in url: url = reverse(workouts_view) except KeyError: url = reverse(workouts_view) return HttpResponseRedirect(url) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") # Ask the user to confirm that he wants to delete a chart @login_required() def graph_delete_confirm_view(request, id=0): try: img = GraphImage.objects.get(id=id) row = Workout.objects.get(id=img.workout.id) if (checkworkoutuser(request.user,row)==False): raise PermissionDenied("You are not allowed to delete this workout") else: try: url = request.session['referer'] except KeyError: url = '/rowers/list-graphs' request.session['referer'] = url return render(request,'graphimage_delete_confirm.html', {'id':int(id), 'teams':get_my_teams(request.user), 'graph':img, 'url':url}) except Workout.DoesNotExist: raise Http404("Workout doesn't exist") except GraphImage.DoesNotExist: raise Http404("The image doesn't exist") # Really deleting the chart @login_required() def graph_delete_view(request,id=0): try: img = GraphImage.objects.get(id=id) row = Workout.objects.get(id=img.workout.id) if (checkworkoutuser(request.user,row)==False): raise PermissionDenied("You are not allowed to delete this graph") else: img.delete() messages.info(request,'Graph deleted') try: url = request.session['referer'] except KeyError: url = reverse(graphs_view) return HttpResponseRedirect(url) except GraphImage.DoesNotExist: raise Http404("Graph Image doesn't exist") except Workout.DoesNotExist: raise Http404("Workout doesn't exist") # A page with all the recent graphs (searchable on workout name) @login_required() def graphs_view(request): request.session['referer'] = reverse(graphs_view) try: r = getrower(request.user) workouts = Workout.objects.filter(user=r).order_by("-date", "-starttime") query = request.GET.get('q') if query: query_list = query.split() workouts = workouts.filter( reduce(operator.and_, (Q(name__icontains=q) for q in query_list)) | reduce(operator.and_, (Q(notes__icontains=q) for q in query_list)) ) g = GraphImage.objects.filter(workout__in=workouts).order_by("-creationdatetime") if (len(g)<=5): return render(request, 'list_graphs.html', {'graphs1': g[0:4], 'teams':get_my_teams(request.user), }) else: return render(request, 'list_graphs.html', {'graphs1': g[0:5], 'teams':get_my_teams(request.user), 'graphs2': g[5:10]}) except Rower.DoesNotExist: raise Http404("User has no rower instance") # Show the chart (png image) def graph_show_view(request,id): try: g = GraphImage.objects.get(id=id) try: width,height = Image.open(g.filename).size g.width = width g.height = height g.save() except: pass w = Workout.objects.get(id=g.workout.id) r = Rower.objects.get(id=w.user.id) return render(request,'show_graph.html', {'graph':g, 'teams':get_my_teams(request.user), 'workout':w, 'rower':r,}) except GraphImage.DoesNotExist: raise Http404("This graph doesn't exist") except Workout.DoesNotExist: raise Http404("This workout doesn't exist") # Restore original stroke data and summary @login_required() def workout_summary_restore_view(request,id,message="",successmessage=""): try: row = Workout.objects.get(id=id) if (checkworkoutuser(request.user,row)==False): raise PermissionDenied("You are not allowed to edit this workout") except Workout.DoesNotExist: raise Http404("Workout doesn't exist") s = "" # still here - this is a workout we may edit f1 = row.csvfilename u = row.user.user r = getrower(u) powerperc = 100*np.array([r.pw_ut2, r.pw_ut1, r.pw_at, r.pw_tr,r.pw_an])/r.ftp ftp = float(r.ftp) if row.workouttype in ('water','coastal'): ftp = ftp*(100.-r.otwslack)/100. rr = rrower(hrmax=r.max,hrut2=r.ut2, hrut1=r.ut1,hrat=r.at, hrtr=r.tr,hran=r.an,ftp=ftp, powerperc=powerperc,powerzones=r.powerzones) rowdata = rdata(f1,rower=rr) if rowdata == 0: raise Http404("Error: CSV Data File Not Found") rowdata.restoreintervaldata() rowdata.write_csv(f1,gzip=True) dataprep.update_strokedata(id,rowdata.df) intervalstats = rowdata.allstats() row.summary = intervalstats row.save() itime,idist,itype = rowdata.intervalstats_values() nrintervals = len(idist) # create interactive plot try: res = interactive_chart(id,promember=1) script = res[0] div = res[1] except ValueError: pass messages.info(request,'Original Interval Data Restored') url = reverse(workout_summary_edit_view, kwargs={ 'id':int(id), } ) return HttpResponseRedirect(url) # Split a workout @user_passes_test(ispromember,login_url="/",redirect_field_name=None) def workout_split_view(request,id=id): 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") r = row.user if request.method == 'POST': form = WorkoutSplitForm(request.POST) if form.is_valid(): splittime = form.cleaned_data['splittime'] splitsecond = splittime.hour*3600 splitsecond += splittime.minute*60 splitsecond += splittime.second splitsecond += splittime.microsecond/1.e6 splitmode = form.cleaned_data['splitmode'] ids,mesgs = dataprep.split_workout(r,row,splitsecond,splitmode) for message in mesgs: messages.info(request,message) if request.user == r: url = reverse(workouts_view) else: mgrids = [team.id for team in Team.objects.filter(manager=request.user)] rwrids = [team.id for team in r.team.all()] teamids = list(set(mgrids) & set(rwrids)) if len(teamids) > 0: teamid = teamids[0] url = reverse(workouts_view, kwargs={ 'teamid':int(teamid), } ) else: url = reverse(workouts_view) qdict = {'q':row.name} url+='?'+urllib.urlencode(qdict) return HttpResponseRedirect(url) form = WorkoutSplitForm() # create interactive plot try: res = interactive_chart(id,promember=1) script = res[0] div = res[1] except ValueError: pass return render(request, 'splitworkout.html', {'form':form, 'workout':row, 'thediv':script, 'thescript':div, }) # Fuse two workouts @user_passes_test(ispromember,login_url="/",redirect_field_name=None) def workout_fusion_view(request,id1=0,id2=1): try: w1 = Workout.objects.get(id=id1) w2 = Workout.objects.get(id=id2) r = w1.user if (checkworkoutuser(request.user,w1)==False) or \ (checkworkoutuser(request.user,w2)==False): raise PermissionDenied("You are not allowed to use these workouts") except Workout.DoesNotExist: raise Http404("One of the workouts doesn't exist") if request.method == 'POST': form = FusionMetricChoiceForm(request.POST,instance=w2) if form.is_valid(): cd = form.cleaned_data columns = cd['columns'] timeoffset = cd['offset'] posneg = cd['posneg'] if posneg == 'neg': timeoffset = -timeoffset # Create DataFrame df,forceunit = dataprep.datafusion(id1,id2,columns,timeoffset) idnew,message = dataprep.new_workout_from_df(r,df, title='Fused data', parent=w1, forceunit=forceunit) if message != None: messages.error(request,message) else: successmessage = 'Data fused' messages.info(request,message) url = reverse(workout_edit_view, kwargs={ 'id':idnew, }) return HttpResponseRedirect(url) else: return render(request, 'fusion.html', {'form':form, 'teams':get_my_teams(request.user), 'workout1':w1, 'workout2':w2, }) form = FusionMetricChoiceForm(instance=w2) return render(request, 'fusion.html', {'form':form, 'teams':get_my_teams(request.user), 'workout1':w1, 'workout2':w2, }) # Edit the splits/summary @login_required() def workout_summary_edit_view(request,id,message="",successmessage="" ): try: row = Workout.objects.get(id=id) if (checkworkoutuser(request.user,row)==False): raise PermissionDenied("You are not allowed to edit this workout") except Workout.DoesNotExist: raise Http404("Workout doesn't exist") s = "" # still here - this is a workout we may edit f1 = row.csvfilename u = row.user.user r = getrower(u) powerperc = 100*np.array([r.pw_ut2, r.pw_ut1, r.pw_at, r.pw_tr,r.pw_an])/r.ftp ftp = float(r.ftp) if row.workouttype in ('water','coastal'): ftp = ftp*(100.-r.otwslack)/100. rr = rrower(hrmax=r.max,hrut2=r.ut2, hrut1=r.ut1,hrat=r.at, hrtr=r.tr,hran=r.an,ftp=ftp, powerperc=powerperc,powerzones=r.powerzones) rowdata = rdata(f1,rower=rr) if rowdata == 0: return HttpResponse("Error: CSV Data File Not Found") intervalstats = rowdata.allstats() itime,idist,itype = rowdata.intervalstats_values() nrintervals = len(idist) # create interactive plot try: res = interactive_chart(id,promember=1) script = res[0] div = res[1] except ValueError: pass savebutton = 'nosavebutton' # We have submitted the mini language interpreter if request.method == 'POST' and "intervalstring" in request.POST: form = SummaryStringForm(request.POST) if form.is_valid(): cd = form.cleaned_data s = cd["intervalstring"] try: rowdata.updateinterval_string(s) except IndexError: messages.error(request,'Interval string parse error') intervalstats = rowdata.allstats() itime,idist,itype = rowdata.intervalstats_values() nrintervals = len(idist) savebutton = 'savestringform' # we are saving the results obtained from the mini language interpreter elif request.method == 'POST' and "savestringform" in request.POST: s = request.POST["savestringform"] rowdata.updateinterval_string(s) intervalstats = rowdata.allstats() itime,idist,itype = rowdata.intervalstats_values() nrintervals = len(idist) row.summary = intervalstats #intervalstats = rowdata.allstats() if s: try: row.notes = u'{n} \n {s}'.format( n = row.notes, s = s ) except TypeError: pass row.save() rowdata.write_csv(f1,gzip=True) dataprep.update_strokedata(id,rowdata.df) messages.info(request,"Updated interval data saved") data = {'intervalstring':s} form = SummaryStringForm(initial=data) savebutton = 'savestringform' # we are saving the results obtained from the detailed form elif request.method == 'POST' and "savedetailform" in request.POST: savebutton = 'savedetailform' form = SummaryStringForm() nrintervals = int(request.POST['nrintervals']) detailform = IntervalUpdateForm(request.POST,aantal=nrintervals) itime = [] idist = [] itype = [] ivalues = [] iunits = [] itypes = [] iresults = [] for i in xrange(nrintervals): try: t = datetime.datetime.strptime(request.POST['intervalt_%s' % i],"%H:%M:%S.%f") except ValueError: t = datetime.datetime.strptime(request.POST['intervalt_%s' % i],"%H:%M:%S") timesecs = 3600*t.hour+60*t.minute+t.second+t.microsecond/1.e6 itime += [timesecs] idist += [int(request.POST['intervald_%s' % i])] itype += [int(request.POST['type_%s' % i])] if itype[i] == 3: # rest itypes += ['rest'] ivalues += [timesecs] iresults += [idist[i]] iunits += ['seconds'] if itype[i] == 5 or itype[i] == 2: # distance based work itypes += ['work'] ivalues += [idist[i]] iresults += [timesecs] iunits += ['meters'] if itype[i] == 4 or itype[i] == 1: # time based work itypes += ['work'] ivalues += [timesecs] iresults += [idist[i]] iunits += ['seconds'] rowdata.updateintervaldata(ivalues,iunits,itypes,iresults=iresults) intervalstats = rowdata.allstats() row.summary = intervalstats row.notes += "\n"+s row.save() rowdata.write_csv(f1,gzip=True) dataprep.update_strokedata(id,rowdata.df) messages.info(request,"Updated interval data saved") form = SummaryStringForm() # we are processing the details form elif request.method == 'POST' and "nrintervals" in request.POST: savebutton = 'savedetailform' nrintervals = int(request.POST['nrintervals']) detailform = IntervalUpdateForm(request.POST,aantal=nrintervals) if detailform.is_valid(): cd = detailform.cleaned_data itime = [] idist = [] itype = [] ivalues = [] iunits = [] itypes = [] iresults = [] for i in xrange(nrintervals): t = cd['intervalt_%s' % i] timesecs = t.total_seconds() itime += [timesecs] idist += [cd['intervald_%s' % i]] itype += [cd['type_%s' % i]] if itype[i] == '3': # rest itypes += ['rest'] ivalues += [timesecs] iresults += [idist[i]] iunits += ['seconds'] if itype[i] == '5' or itype[i] == '2': # distance based work itypes += ['work'] ivalues += [idist[i]] iresults += [timesecs] iunits += ['meters'] if itype[i] == '4' or itype[i] == '1': # time based work itypes += ['work'] ivalues += [timesecs] iresults += [idist[i]] iunits += ['seconds'] rowdata.updateintervaldata(ivalues,iunits, itypes,iresults=iresults) intervalstats = rowdata.allstats() form = SummaryStringForm() else: form = SummaryStringForm() initial = {} for i in xrange(nrintervals): initial['intervald_%s' % i] = idist[i] initial['intervalt_%s' % i] = get_time(itime[i]) initial['type_%s' % i] = itype[i] detailform = IntervalUpdateForm(aantal=nrintervals,initial=initial) # render page return render(request, 'summary_edit.html', {'form':form, 'detailform':detailform, 'workout':row, 'teams':get_my_teams(request.user), 'intervalstats':intervalstats, 'nrintervals':nrintervals, 'interactiveplot':script, 'the_div':div, 'intervalstring':s, 'savebutton':savebutton, }) # Page where user can manage his favorite charts @login_required() def rower_favoritecharts_view(request): message = '' successmessage = '' r = getrower(request.user) favorites = FavoriteChart.objects.filter(user=r).order_by('id') aantal = len(favorites) favorites_data = [{'yparam1':f.yparam1, 'yparam2':f.yparam2, 'xparam':f.xparam, 'plottype':f.plottype, 'workouttype':f.workouttype, 'reststrokes':f.reststrokes, 'notes':f.notes,} for f in favorites] FavoriteChartFormSet = formset_factory(FavoriteForm,formset=BaseFavoriteFormSet,extra=0) if aantal==0: FavoriteChartFormSet = formset_factory(FavoriteForm,formset=BaseFavoriteFormSet,extra=1) if request.method == 'POST': favorites_formset = FavoriteChartFormSet(request.POST) if favorites_formset.is_valid(): new_instances = [] for favorites_form in favorites_formset: yparam1 = favorites_form.cleaned_data.get('yparam1') yparam2 = favorites_form.cleaned_data.get('yparam2') xparam = favorites_form.cleaned_data.get('xparam') plottype = favorites_form.cleaned_data.get('plottype') workouttype = favorites_form.cleaned_data.get('workouttype') reststrokes = favorites_form.cleaned_data.get('reststrokes') notes = favorites_form.cleaned_data.get('notes') new_instances.append(FavoriteChart(user=r, yparam1=yparam1, yparam2=yparam2, xparam=xparam, plottype=plottype, notes=notes, workouttype=workouttype, reststrokes=reststrokes)) try: with transaction.atomic(): FavoriteChart.objects.filter(user=r).delete() FavoriteChart.objects.bulk_create(new_instances) successmessage = "You have updated your favorites" messages.info(request,message) if len(new_instances)==0: FavoriteChartFormSet=formset_factory(FavoriteForm,formset=BaseFavoriteFormSet,extra=1) favorites_formset = FavoriteChartFormSet() except IntegrityError: message = "something went wrong" messages.error(request,message) else: favorites_formset = FavoriteChartFormSet(initial=favorites_data) context = { 'favorites_formset':favorites_formset, 'teams':get_my_teams(request.user), } return render(request,'favoritecharts.html',context) # page where user sets his export settings @login_required() def rower_exportsettings_view(request): r = getrower(request.user) if request.method == 'POST': form = RowerExportForm(request.POST) if form.is_valid(): stravaexportas = form.cleaned_data['stravaexportas'] r.stravaexportas = stravaexportas r.save() else: form = RowerExportForm(instance=r) return render(request, 'rower_exportsettings.html', {'form':form, 'rower':r, }) # Page where user can set his details # Add email address to form so user can change his email address @login_required() def rower_edit_view(request,message=""): r = getrower(request.user) if request.method == 'POST' and "ut2" in request.POST: form = RowerForm(request.POST) if form.is_valid(): # something cd = form.cleaned_data hrmax = cd['max'] ut2 = cd['ut2'] ut1 = cd['ut1'] at = cd['at'] tr = cd['tr'] an = cd['an'] rest = cd['rest'] try: r = getrower(request.user) r.max = max(min(hrmax,250),10) r.ut2 = max(min(ut2,250),10) r.ut1 = max(min(ut1,250),10) r.at = max(min(at,250),10) r.tr = max(min(tr,250),10) r.an = max(min(an,250),10) r.rest = max(min(rest,250),10) r.save() successmessage = "Your Heart Rate data were changed" messages.info(request,successmessage) form = RowerForm(instance=r) powerform = RowerPowerForm(instance=r) powerzonesform = RowerPowerZonesForm(instance=r) accountform = AccountRowerForm(instance=r) userform = UserForm(instance=request.user) return render(request, 'rower_form.html', {'form':form, 'powerzonesform':powerzonesform, 'teams':get_my_teams(request.user), 'powerform':powerform, 'rower':r, 'accountform':accountform, 'userform':userform, }) except Rower.DoesNotExist: message = "Funny. This user doesn't exist." messages.error(request,message) url = reverse(workouts_view) response = HttpResponseRedirect(url) else: message = HttpResponse("invalid form") #form = RowerForm(instance=r) powerform = RowerPowerForm(instance=r) powerzonesform = RowerPowerZonesForm(instance=r) userform = UserForm(instance=request.user) accountform = AccountRowerForm(instance=r) return render(request, 'rower_form.html', {'form':form, 'teams':get_my_teams(request.user), 'powerzonesform':powerzonesform, 'userform':userform, 'accountform':accountform, 'powerform':powerform, 'rower':r, }) return response elif request.method == 'POST' and "ftp" in request.POST: powerform = RowerPowerForm(request.POST) if powerform.is_valid(): cd = powerform.cleaned_data ftp = cd['ftp'] otwslack = cd['otwslack'] try: r = getrower(request.user) powerfrac = 100*np.array([r.pw_ut2, r.pw_ut1, r.pw_at, r.pw_tr,r.pw_an])/r.ftp r.ftp = max(min(ftp,650),50) r.otwslack = max(min(otwslack,50),0) ut2,ut1,at,tr,an = (r.ftp*powerfrac/100.).astype(int) r.pw_ut2 = ut2 r.pw_ut1 = ut1 r.pw_at = at r.pw_tr = tr r.pw_an = an r.save() message = "FTP and/or OTW slack values changed." messages.info(request,message) url = reverse(rower_edit_view) 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=request.user) accountform = AccountRowerForm(instance=r) return render(request, 'rower_form.html', {'form':form, 'teams':get_my_teams(request.user), 'powerform':powerform, 'rower':r, 'userform':userform, 'accountform':accountform, }) return response elif request.method == 'POST' and "ut3name" in request.POST: powerzonesform = RowerPowerZonesForm(request.POST) if powerzonesform.is_valid(): cd = powerzonesform.cleaned_data pw_ut2 = cd['pw_ut2'] pw_ut1 = cd['pw_ut1'] pw_at = cd['pw_at'] pw_tr = cd['pw_tr'] pw_an = cd['pw_an'] ut3name = cd['ut3name'] ut2name = cd['ut2name'] ut1name = cd['ut1name'] atname = cd['atname'] trname = cd['trname'] anname = cd['anname'] powerzones = [ut3name,ut2name,ut1name,atname,trname,anname] try: r = getrower(request.user) r.pw_ut2 = pw_ut2 r.pw_ut1 = pw_ut1 r.pw_at = pw_at r.pw_tr = pw_tr r.pw_an = pw_an r.powerzones = powerzones r.save() successmessage = "Your Power Zone data were changed" messages.info(request,successmessage) form = RowerForm(instance=r) accountform = AccountRowerForm(instance=r) userform = UserForm(instance=request.user) powerform = RowerPowerForm(instance=r) powerzonesform = RowerPowerZonesForm(instance=r) return render(request, 'rower_form.html', {'form':form, 'teams':get_my_teams(request.user), 'powerzonesform':powerzonesform, 'powerform':powerform, 'userform':userform, 'accountform':accountform, 'rower':r, }) except Rower.DoesNotExist: message = "Funny. This user doesn't exist." messages.error(request,message) url = reverse(workouts_view) response = HttpResponseRedirect(url) return response else: form = RowerForm(instance=r) powerform = RowerPowerForm(instance=r) accountform = AccountRowerForm(instance=r) userform = UserForm(instance=request.user) #powerzonesform = RowerPowerZonesForm(instance=r) message = HttpResponse("invalid form") return render(request, 'rower_form.html', {'form':form, 'teams':get_my_teams(request.user), 'powerform':powerform, 'powerzonesform':powerzonesform, 'accountform':accountform, 'userform':userform, 'rower':r, }) elif request.method == 'POST' and "weightcategory" in request.POST: accountform = AccountRowerForm(request.POST) userform = UserForm(request.POST,instance=request.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'] defaultlandingpage = cd['defaultlandingpage'] weightcategory = cd['weightcategory'] showfavoritechartnotes = cd['showfavoritechartnotes'] getemailnotifications = cd['getemailnotifications'] defaulttimezone=cd['defaulttimezone'] u = request.user if len(first_name): u.first_name = first_name u.last_name = last_name if len(email): ## and check_email_freeforuse(u,email): u.email = email u.save() r = getrower(u) r.defaulttimezone=defaulttimezone r.weightcategory = weightcategory r.getemailnotifications = getemailnotifications r.defaultlandingpage = defaultlandingpage r.showfavoritechartnotes = showfavoritechartnotes r.save() form = RowerForm(instance=r) powerform = RowerPowerForm(instance=r) powerzonesform = RowerPowerZonesForm(instance=r) accountform = AccountRowerForm(instance=r) userform = UserForm(instance=u) successmessage = 'Account Information changed' messages.info(request,successmessage) return render(request, 'rower_form.html', {'form':form, 'teams':get_my_teams(request.user), 'powerzonesform':powerzonesform, 'powerform':powerform, 'accountform':accountform, 'userform':userform, 'rower':r, }) else: form = RowerForm(instance=r) powerform = RowerPowerForm(instance=r) powerzonesform = RowerPowerZonesForm(instance=r) return render(request, 'rower_form.html', {'form':form, 'teams':get_my_teams(request.user), 'powerzonesform':powerzonesform, 'powerform':powerform, 'accountform':accountform, 'userform':userform, 'rower':r, }) else: try: r = getrower(request.user) form = RowerForm(instance=r) powerform = RowerPowerForm(instance=r) powerzonesform = RowerPowerZonesForm(instance=r) accountform = AccountRowerForm(instance=r) userform = UserForm(instance=request.user) grants = AccessToken.objects.filter(user=request.user) return render(request, 'rower_form.html', { 'form':form, 'teams':get_my_teams(request.user), 'powerform':powerform, 'powerzonesform':powerzonesform, 'userform':userform, 'accountform':accountform, 'grants':grants, 'rower':r, }) except Rower.DoesNotExist: raise Http404("This user doesn't exist") # Revoke an app that you granted access through the API. # this views is called when you press a button on the User edit page # the button is only there when you have granted access to an app @login_required() def rower_revokeapp_view(request,id=0): try: tokens = AccessToken.objects.filter(user=request.user,application=id) refreshtokens = AccessToken.objects.filter(user=request.user,application=id) for token in tokens: token.revoke() for token in refreshtokens: token.revoke() r = getrower(request.user) form = RowerForm(instance=r) powerform = RowerPowerForm(instance=r) grants = AccessToken.objects.filter(user=request.user) return render(request, 'rower_form.html', { 'form':form, 'teams':get_my_teams(request.user), 'powerform':powerform, 'grants':grants, }) except AccessToken.DoesNotExist: raise Http404("Access token doesn't exist") # Creates unix time stamp from a datetime object def totimestamp(dt, epoch=datetime.datetime(1970,1,1,tzinfo=tz('UTC'))): td = dt - epoch # return td.total_seconds() return (td.microseconds + (td.seconds + td.days * 86400) * 10**6) / 10**6 # Check if a column of a dataframe has the required (aantal) # number of elements. Also checks if the column is a numerical type # Replaces any faulty columns with zeros def trydf(df,aantal,column): try: s = df[column] if len(s) != aantal: return np.zeros(aantal) if not np.issubdtype(s,np.number): return np.zeros(aantal) except KeyError: s = np.zeros(aantal) return s # Stroke data form to test API upload @login_required() def strokedataform(request,id=0): if request.method == 'GET': form = StrokeDataForm() return render(request, 'strokedata_form.html', { 'form':form, 'teams':get_my_teams(request.user), 'id':int(id), }) elif request.method == 'POST': form = StrokeDataForm() return render(request, 'strokedata_form.html', { 'form':form, 'teams':get_my_teams(request.user), 'id':int(id), }) # Process the POSTed stroke data according to the API definition # Return the GET stroke data according to the API definition from rest_framework_swagger.renderers import OpenAPIRenderer, SwaggerUIRenderer @csrf_exempt @login_required() @api_view(['GET','POST']) def strokedatajson(request,id): """ POST: Add Stroke data to workout GET: Get stroke data of workout """ try: row = Workout.objects.get(id=id) if (checkworkoutuser(request.user,row)==False): return HttpResponseForbidden("Permission error") except Workout.DoesNotExist: raise Http404("Workout doesn't exist") try: id = int(id) except ValueError: return HttpResponse("Not a valid workout number",status=400) if request.method == 'GET': # currently only returns a subset. columns = ['spm','time','hr','pace','power','distance'] datadf = dataprep.getsmallrowdata_db(columns,ids=[id]) with open('media/apilog.log','a') as logfile: logfile.write(str(timezone.now())+": ") logfile.write(request.user.username+"(GET) \n") return JSONResponse(datadf) if request.method == 'POST': checkdata,r = dataprep.getrowdata_db(id=row.id) if not checkdata.empty: return HttpResponse("Duplicate Error",409) # strokedata = request.POST['strokedata'] # checking/validating and cleaning try: strokedata = json.loads(request.POST['strokedata']) except: return HttpResponse("No JSON object could be decoded",400) df = pd.DataFrame(strokedata) df.index = df.index.astype(int) df.sort_index(inplace=True) # time, hr, pace, spm, power, drivelength, distance, drivespeed, dragfactor, strokerecoverytime, averagedriveforce, peakdriveforce, lapidx time = df['time']/1.e3 aantal = len(time) pace = df['pace']/1.e3 if len(pace) != aantal: return HttpResponse("Pace array has incorrect length",status=400) distance = df['distance'] if len(distance) != aantal: return HttpResponse("Distance array has incorrect length",status=400) spm = df['spm'] if len(spm) != aantal: return HttpResponse("SPM array has incorrect length",status=400) res = dataprep.testdata(time,distance,pace,spm) if not res: return HttpResponse("Data are not numerical",status=400) power = trydf(df,aantal,'power') drivelength = trydf(df,aantal,'drivelength') drivespeed = trydf(df,aantal,'drivespeed') dragfactor = trydf(df,aantal,'dragfactor') drivetime = trydf(df,aantal,'drivetime') strokerecoverytime = trydf(df,aantal,'strokerecoverytime') averagedriveforce = trydf(df,aantal,'averagedriveforce') peakdriveforce = trydf(df,aantal,'peakdriveforce') lapidx = trydf(df,aantal,'lapidx') hr = trydf(df,aantal,'hr') starttime = totimestamp(row.startdatetime)+time unixtime = starttime+time with open('media/apilog.log','a') as logfile: logfile.write(starttime+": ") logfile.write(request.user.username+"(POST) \r\n") data = pd.DataFrame({'TimeStamp (sec)':unixtime, ' Horizontal (meters)': distance, ' Cadence (stokes/min)':spm, ' HRCur (bpm)':hr, ' DragFactor':dragfactor, ' Stroke500mPace (sec/500m)':pace, ' Power (watts)':power, ' DriveLength (meters)':drivelength, ' DriveTime (ms)':drivetime, ' StrokeRecoveryTime (ms)':strokerecoverytime, ' AverageDriveForce (lbs)':averagedriveforce, ' PeakDriveForce (lbs)':peakdriveforce, ' lapIdx':lapidx, ' ElapsedTime (sec)':time, }) # Following part should be replaced with dataprep.new_workout_from_df timestr = row.startdatetime.strftime("%Y%m%d-%H%M%S") csvfilename ='media/Import_'+timestr+'.csv' res = data.to_csv(csvfilename+'.gz',index_label='index', compression='gzip') row.csvfilename = csvfilename row.save() r = getrower(request.user) powerperc = 100*np.array([r.pw_ut2, r.pw_ut1, r.pw_at, r.pw_tr,r.pw_an])/r.ftp ftp = float(r.ftp) if row.workouttype in ('water','coastal'): ftp = ftp*(100.-r.otwslack)/100. rr = rrower(hrmax=r.max,hrut2=r.ut2, hrut1=r.ut1,hrat=r.at, hrtr=r.tr,hran=r.an,ftp=ftp, powerperc=powerperc,powerzones=r.powerzones) rowdata = rdata(row.csvfilename,rower=rr).df datadf = dataprep.dataprep(rowdata,id=row.id,bands=True,barchart=True,otwpower=True,empower=True) # mangling # return HttpResponse(row.id,status=201) #Method not supported return HttpResponseNotAllowed("Method not supported") # Teams related views import teams @login_required() def team_view(request,id=0): ismember = 0 hasrequested = 0 r = getrower(request.user) myteams = Team.objects.filter(manager=request.user) try: t = Team.objects.get(id=id) except Team.DoesNotExist: raise Http404("Team doesn't exist") if request.method == 'POST' and request.user == t.manager: inviteform = TeamInviteForm(request.POST) inviteform.fields['user'].queryset = User.objects.filter(rower__isnull=False,rower__team__in=myteams).distinct().exclude(rower__team__name=t.name) if inviteform.is_valid(): cd = inviteform.cleaned_data newmember = cd['user'] email = cd['email'] inviteid,text = teams.create_invite(t,t.manager, user=newmember, email=email) if inviteid: teams.send_invite_email(inviteid) successmessage = text messages.info(request,successmessage) else: message = text messages.error(request,message) elif request.user == t.manager: inviteform = TeamInviteForm() inviteform.fields['user'].queryset = User.objects.filter(rower__isnull=False,rower__team__in=myteams).distinct().exclude(rower__team__name=t.name) else: inviteform = '' members = Rower.objects.filter(team=t).order_by('user__last_name','user__first_name') thisteammyrequests = TeamRequest.objects.filter(team=t,user=request.user) if len(thisteammyrequests): hasrequested = 1 if r in members: ismember = 1 return render(request, 'team.html', { 'team':t, 'teams':get_my_teams(request.user), 'members':members, 'inviteform':inviteform, 'ismember':ismember, 'hasrequested':hasrequested, }) @login_required() def team_leaveconfirm_view(request,id=0): try: t = Team.objects.get(id=id) except Team.DoesNotExist: raise Http404("Team doesn't exist") return render(request,'teamleaveconfirm.html', { 'team':t, 'teams':get_my_teams(request.user), }) @login_required() def rower_calcdps_view(request): r = getrower(request.user) ws = [(w.id,w.csvfilename) for w in Workout.objects.filter(user=r)] res = myqueue(queue,handle_updatedps,r.user.email,ws,debug=False) messages.info(request,"Your workouts are being updated in the background. You will receive email when this is done.") url = reverse(workouts_view) return HttpResponseRedirect(url) @login_required() def team_leave_view(request,id=0): r = getrower(request.user) teams.remove_member(id,r) url = reverse(rower_teams_view) response = HttpResponseRedirect(url) return response from rowers.forms import TeamInviteCodeForm @login_required() def rower_teams_view(request,message='',successmessage=''): if request.method == 'POST': form = TeamInviteCodeForm(request.POST) if form.is_valid(): code = form.cleaned_data['code'] res,text = teams.process_invite_code(request.user,code) if res: successmessage = text else: message = text else: form = TeamInviteCodeForm() r = getrower(request.user) ts = Team.objects.filter(rower=r) myteams = Team.objects.filter(manager=request.user) otherteams = Team.objects.filter(private='open').exclude(rower=r).exclude(manager=request.user).order_by('name') teams.remove_expired_invites() invites = TeamInvite.objects.filter(user=request.user) requests = TeamRequest.objects.filter(user=request.user) myrequests = TeamRequest.objects.filter(team__in=myteams) myinvites = TeamInvite.objects.filter(team__in=myteams) clubsize = teams.count_invites(request.user)+teams.count_club_members(request.user) max_clubsize = r.clubsize messages.info(request,successmessage) messages.error(request,message) return render(request, 'teams.html', { 'teams':ts, 'clubsize':clubsize, 'max_clubsize':max_clubsize, 'myteams':myteams, 'invites':invites, 'otherteams':otherteams, 'requests':requests, 'myrequests':myrequests, 'form':form, 'myinvites':myinvites, }) @user_passes_test(iscoachmember,login_url="/",redirect_field_name=None) def invitation_revoke_view(request,id): res,text = teams.revoke_invite(request.user,id) if res: messages.info(request,text) successmessage = text else: message = text messages.error(request,text) url = reverse(rower_teams_view) return HttpResponseRedirect(url) @user_passes_test(iscoachmember,login_url="/",redirect_field_name=None) def manager_member_drop_view(request,teamid,userid, message='',successmessage=''): rower = Rower.objects.get(user__id=userid) res, text = teams.mgr_remove_member(teamid,request.user,rower) if res: messages.info(request,text) else: messages.error(request,text) url = reverse(rower_teams_view) return HttpResponseRedirect(url) @user_passes_test(iscoachmember,login_url="/",redirect_field_name=None) def manager_requests_view(request,code=None,message='',successmessage=''): if code: res,text = teams.process_request_code(request.user,code) if res: successmessage = text message = '' else: message = text successmessage = '' messages.info(request,successmessage) messages.error(request,message) url = reverse(rower_teams_view,kwargs={ }) return HttpResponseRedirect(url) @login_required() def team_requestmembership_view(request,teamid,userid): try: t = Team.objects.get(id=teamid) except Team.DoesNotExist: raise Http404("Team doesn't exist") res,text = teams.create_request(t,userid) if res: messages.info(request,text) else: messages.error(request,text) url = reverse(team_view,kwargs={ 'id':int(teamid), }) return HttpResponseRedirect(url) @login_required() def request_revoke_view(request,id=0): res,text = teams.revoke_request(request.user,id) if res: messages.info(request,text) else: messages.error(request,text) url = reverse(rower_teams_view) return HttpResponseRedirect(url) @user_passes_test(iscoachmember,login_url="/",redirect_field_name=None) def request_reject_view(request,id=0): res,text = teams.reject_request(request.user,id) if res: messages.info(request,text) else: messages.error(request,text) url = reverse(rower_teams_view) return HttpResponseRedirect(url) @user_passes_test(iscoachmember,login_url="/",redirect_field_name=None) def invitation_reject_view(request,id=0): res,text = teams.reject_invitation(request.user,id) if res: messages.info(request,text) else: messages.error(request,text) url = reverse(rower_teams_view) return HttpResponseRedirect(url) @login_required() def rower_invitations_view(request,code=None,message='',successmessage=''): if code: teams.remove_expired_invites() res,text = teams.process_invite_code(request.user,code) if res: messages.info(request,text) teamid=res url = reverse(team_view,kwargs={ 'id':teamid, }) else: messages.error(request,text) url = reverse(rower_teams_view) return HttpResponseRedirect(url) url = reverse(rower_teams_view,kwargs={ }) return HttpResponseRedirect(url) @user_passes_test(iscoachmember,login_url="/",redirect_field_name=None) def team_edit_view(request,id=0): try: t = Team.objects.get(id=id) except Team.DoesNotExist: raise Http404("Team does not exist") if request.method == 'POST': teamcreateform = TeamForm(request.POST,instance=t) if teamcreateform.is_valid(): cd = teamcreateform.cleaned_data name = cd['name'] notes = cd['notes'] manager = request.user private = cd['private'] viewing = cd['viewing'] res,message=teams.update_team(t,name,manager,private,notes, viewing) if res: messages.info(request,message) else: messages.error(request,message) url = reverse(team_view, kwargs={ 'id':int(id), } ) response = HttpResponseRedirect(url) return response else: teamcreateform = TeamForm(instance=t) return render(request,'teamedit.html', { 'form':teamcreateform, 'teams':get_my_teams(request.user), 'team':t, }) @user_passes_test(iscoachmember,login_url="/",redirect_field_name=None) def team_create_view(request): if request.method == 'POST': teamcreateform = TeamForm(request.POST) if teamcreateform.is_valid(): cd = teamcreateform.cleaned_data name = cd['name'] notes = cd['notes'] manager = request.user private = cd['private'] viewing = cd['viewing'] res,message=teams.create_team(name,manager,private,notes, viewing) url = reverse(rower_teams_view) response = HttpResponseRedirect(url) return response else: teamcreateform = TeamForm() return render(request,'teamcreate.html', { 'teams':get_my_teams(request.user), 'form':teamcreateform, }) @user_passes_test(iscoachmember,login_url="/",redirect_field_name=None) def team_deleteconfirm_view(request,id): r = getrower(request.user) try: t = Team.objects.get(id=id) except Team.DoesNotExist: raise Http404("This team doesn't exist") if t.manager != request.user: raise PermissionDenied("You are not allowed to delete this team") return render(request,'teamdeleteconfirm.html', { 'teams':get_my_teams(request.user), 'team':t }) @user_passes_test(iscoachmember,login_url="/",redirect_field_name=None) def team_delete_view(request,id): r = getrower(request.user) try: t = Team.objects.get(id=id) except Team.DoesNotExist: raise Http404("This team doesn't exist") if t.manager != request.user: raise PermissionDenied("You are not allowed to delete this team") teams.remove_team(t.id) url = reverse(rower_teams_view) response = HttpResponseRedirect(url) return response @user_passes_test(iscoachmember,login_url="/",redirect_field_name=None) def team_members_stats_view(request,id): r = getrower(request.user) try: t = Team.objects.get(id=id) except Team.DoesNotExist: raise Http404("This team doesn't exist") if t.manager != request.user: raise PermissionDenied("You are not allowed to see this page") members = Rower.objects.filter(team=t).order_by("user__last_name","user__first_name") theusers = [member.user for member in members] response = render(request,'teamstats.html', { 'teams':get_my_teams(request.user), 'team':t, 'theusers':theusers, }) return response