1306 lines
37 KiB
Python
1306 lines
37 KiB
Python
|
|
import time
|
|
import colorsys
|
|
|
|
import zipfile
|
|
import bleach
|
|
import arrow
|
|
import pytz
|
|
from pytz import UnknownTimeZoneError
|
|
import operator
|
|
import warnings
|
|
import urllib
|
|
import yaml
|
|
from PIL import Image
|
|
from numbers import Number
|
|
from django.views.generic.base import TemplateView
|
|
from django.contrib.auth import views as auth_views
|
|
from django.db.models import Q
|
|
from django import template
|
|
from django.db import IntegrityError, transaction
|
|
from django.views.decorators.csrf import csrf_exempt
|
|
from matplotlib.backends.backend_agg import FigureCanvas
|
|
import gc
|
|
from pyparsing import ParseException
|
|
from uuid import uuid4
|
|
import codecs
|
|
import isodate
|
|
import re
|
|
import cgi
|
|
from icalendar import Calendar, Event
|
|
|
|
from functools import reduce
|
|
|
|
import rowers.braintreestuff as braintreestuff
|
|
import rowers.payments as payments
|
|
from rowers.opaque import encoder
|
|
|
|
from django.shortcuts import render
|
|
from django.template.loader import render_to_string
|
|
|
|
from django.views.generic.edit import UpdateView,DeleteView
|
|
|
|
from django.http import (
|
|
HttpResponse, HttpResponseRedirect,
|
|
JsonResponse,
|
|
HttpResponseForbidden, HttpResponseNotAllowed,
|
|
HttpResponseNotFound,Http404
|
|
)
|
|
from django.contrib.auth import authenticate, login, logout
|
|
from rowers.forms import (
|
|
ForceCurveOptionsForm,HistoForm,TeamMessageForm,
|
|
LoginForm,DocumentsForm,UploadOptionsForm,ImageForm,CourseForm,
|
|
TeamUploadOptionsForm,WorkFlowLeftPanelForm,WorkFlowMiddlePanelForm,
|
|
WorkFlowLeftPanelElement,WorkFlowMiddlePanelElement,
|
|
LandingPageForm,PlannedSessionSelectForm,WorkoutSessionSelectForm,
|
|
PlannedSessionTeamForm,PlannedSessionTeamMemberForm,
|
|
VirtualRaceSelectForm,WorkoutRaceSelectForm,CourseSelectForm,
|
|
RaceResultFilterForm,PowerIntervalUpdateForm,FlexAxesForm,
|
|
FlexOptionsForm,DataFrameColumnsForm,OteWorkoutTypeForm,
|
|
MetricsForm,DisqualificationForm,disqualificationreasons,
|
|
disqualifiers,SearchForm,BillingForm,PlanSelectForm,
|
|
VideoAnalysisCreateForm,WorkoutSingleSelectForm,
|
|
VideoAnalysisMetricsForm,
|
|
)
|
|
|
|
from django.urls import reverse, reverse_lazy
|
|
|
|
from django.core.exceptions import PermissionDenied
|
|
from django.template import RequestContext
|
|
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
|
from django.conf import settings
|
|
from django.utils.datastructures import MultiValueDictKeyError
|
|
from django.utils import timezone,translation
|
|
from django.core.mail import send_mail, BadHeaderError
|
|
from rowers.forms import (
|
|
SummaryStringForm,IntervalUpdateForm,StrokeDataForm,
|
|
StatsOptionsForm,PredictedPieceForm,DateRangeForm,DeltaDaysForm,
|
|
FitnessMetricForm,PredictedPieceFormNoDistance,
|
|
EmailForm, RegistrationForm, RegistrationFormTermsOfService,
|
|
RegistrationFormUniqueEmail,RegistrationFormSex,
|
|
CNsummaryForm,UpdateWindForm,
|
|
UpdateStreamForm,WorkoutMultipleCompareForm,ChartParamChoiceForm,
|
|
FusionMetricChoiceForm,BoxPlotChoiceForm,MultiFlexChoiceForm,
|
|
TrendFlexModalForm,WorkoutSplitForm,WorkoutJoinParamForm,
|
|
AnalysisOptionsForm, AnalysisChoiceForm,
|
|
PlannedSessionMultipleCloneForm,SessionDateShiftForm,RowerTeamForm,
|
|
)
|
|
from rowers.models import (
|
|
Workout, User, Rower, WorkoutForm,FavoriteChart,
|
|
PlannedSession, DeactivateUserForm,DeleteUserForm,
|
|
TrainingPlan,TrainingPlanForm,TrainingTarget,TrainingTargetForm,
|
|
TrainingMacroCycle,TrainingMesoCycle,TrainingMicroCycle,
|
|
TrainingTarget,TrainingTargetForm,
|
|
TrainingMacroCycleForm,createmacrofillers,
|
|
createmicrofillers, createmesofillers,
|
|
microcyclecheckdates,mesocyclecheckdates,macrocyclecheckdates,
|
|
TrainingMesoCycleForm, TrainingMicroCycleForm,
|
|
RaceLogo,RowerBillingAddressForm,PaidPlan,
|
|
AlertEditForm, ConditionEditForm,
|
|
PlannedSessionComment,CoachRequest,CoachOffer,checkaccessplanuser,
|
|
VideoAnalysis
|
|
)
|
|
from rowers.models import (
|
|
RowerPowerForm,RowerForm,GraphImage,AdvancedWorkoutForm,
|
|
RowerPowerZonesForm,AccountRowerForm,UserForm,
|
|
Team,TeamForm,TeamInviteForm,TeamInvite,TeamRequest,
|
|
WorkoutComment,WorkoutCommentForm,RowerExportForm,
|
|
CalcAgePerformance,
|
|
PowerTimeFitnessMetric,BlogPost,
|
|
PlannedSessionForm,
|
|
PlannedSessionFormSmall,GeoCourseEditForm,VirtualRace,
|
|
VirtualRaceForm,VirtualRaceResultForm,RowerImportExportForm,
|
|
IndoorVirtualRaceResultForm,IndoorVirtualRaceResult,
|
|
IndoorVirtualRaceForm,PlannedSessionCommentForm,
|
|
Alert, Condition
|
|
)
|
|
from rowers.models import (
|
|
FavoriteForm,BaseFavoriteFormSet,SiteAnnouncement,BasePlannedSessionFormSet,
|
|
get_course_timezone,BaseConditionFormSet,
|
|
)
|
|
from rowers.metrics import rowingmetrics,defaultfavoritecharts,nometrics,metricsgroups
|
|
from rowers import metrics as metrics
|
|
from rowers import courses as courses
|
|
import rowers.uploads as uploads
|
|
from django.forms.formsets import formset_factory
|
|
from django.forms import modelformset_factory
|
|
|
|
from django.contrib.auth.decorators import login_required #,user_passes_test
|
|
from rowers.decorators import user_passes_test
|
|
from time import strftime,strptime,mktime,time,daylight
|
|
import os,sys
|
|
import datetime
|
|
import iso8601
|
|
import rowers.c2stuff as c2stuff
|
|
from rowers.c2stuff import c2_open
|
|
from rowers.runkeeperstuff import runkeeper_open
|
|
from rowers.sporttracksstuff import sporttracks_open
|
|
from rowers.tpstuff import tp_open
|
|
from iso8601 import ParseError
|
|
import rowers.stravastuff as stravastuff
|
|
from rowers.stravastuff import strava_open
|
|
import rowers.polarstuff as polarstuff
|
|
import rowers.sporttracksstuff as sporttracksstuff
|
|
import rowers.underarmourstuff as underarmourstuff
|
|
from rowers.underarmourstuff import underarmour_open
|
|
import rowers.tpstuff as tpstuff
|
|
import rowers.runkeeperstuff as runkeeperstuff
|
|
import rowers.ownapistuff as ownapistuff
|
|
from rowers.ownapistuff import TEST_CLIENT_ID, TEST_CLIENT_SECRET, TEST_REDIRECT_URI
|
|
from rowsandall_app.settings import (
|
|
C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET,
|
|
STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET,
|
|
POLAR_CLIENT_ID, POLAR_REDIRECT_URI, POLAR_CLIENT_SECRET,
|
|
SPORTTRACKS_CLIENT_ID, SPORTTRACKS_REDIRECT_URI,
|
|
SPORTTRACKS_CLIENT_SECRET,
|
|
UNDERARMOUR_CLIENT_ID, UNDERARMOUR_REDIRECT_URI,
|
|
UNDERARMOUR_CLIENT_SECRET,UNDERARMOUR_CLIENT_KEY,
|
|
RUNKEEPER_CLIENT_ID,RUNKEEPER_REDIRECT_URI,RUNKEEPER_CLIENT_SECRET,
|
|
TP_CLIENT_ID,TP_REDIRECT_URI,TP_CLIENT_KEY,TP_CLIENT_SECRET,
|
|
BRAINTREE_MERCHANT_ID,BRAINTREE_PUBLIC_KEY,BRAINTREE_PRIVATE_KEY,
|
|
PAYMENT_PROCESSING_ON,
|
|
RECAPTCHA_SITE_KEY, RECAPTCHA_SITE_SECRET
|
|
)
|
|
|
|
#from rowers.tasks_standalone import addcomment2
|
|
from django.contrib import messages
|
|
from async_messages import messages as a_messages
|
|
|
|
from django.contrib.admin.widgets import AdminDateWidget,AdminTimeWidget,AdminSplitDateTime
|
|
|
|
|
|
import requests
|
|
import json
|
|
from rest_framework.renderers import JSONRenderer
|
|
from rest_framework.parsers import JSONParser
|
|
from rowers.rows import handle_uploaded_file,handle_uploaded_image
|
|
from rowers.plannedsessions import *
|
|
from rowers.tasks import handle_makeplot,handle_otwsetpower,handle_sendemailtcx,handle_sendemailcsv
|
|
from rowers.tasks import (
|
|
handle_sendemail_unrecognized,handle_sendemailnewcomment,
|
|
handle_sendemailsummary,
|
|
handle_send_disqualification_email,
|
|
handle_sendemailfile,
|
|
handle_sendemailkml,
|
|
handle_sendemailnewresponse, handle_updatedps,
|
|
handle_updatecp,long_test_task,long_test_task2,
|
|
handle_zip_file,handle_getagegrouprecords,
|
|
handle_updatefitnessmetric,
|
|
handle_update_empower,
|
|
handle_sendemailics,
|
|
handle_sendemail_userdeleted,
|
|
handle_sendemail_raceregistration,
|
|
handle_sendemail_racesubmission,
|
|
handle_sendemail_ical,
|
|
)
|
|
|
|
from scipy.signal import savgol_filter
|
|
#from django.shortcuts import render_to_response
|
|
try:
|
|
from Cookie import SimpleCookie
|
|
except ModuleNotFoundError:
|
|
from http.cookies import SimpleCookie
|
|
from shutil import copyfile,move
|
|
import rowers.mytypes as mytypes
|
|
from rowingdata import rower as rrower
|
|
from rowingdata import main as rmain
|
|
from rowingdata import rowingdata as rrdata
|
|
from rowingdata import make_cumvalues
|
|
from rowingdata import summarydata
|
|
import pandas as pd
|
|
import numpy as np
|
|
import matplotlib.pyplot as plt
|
|
|
|
from rowers.emails import send_template_email,htmlstrip
|
|
from rowers.alerts import *
|
|
|
|
from pytz import timezone as tz,utc
|
|
from timezonefinder import TimezoneFinder
|
|
import dateutil
|
|
import mpld3
|
|
from mpld3 import plugins
|
|
import stravalib
|
|
from stravalib.exc import ActivityUploadFailed,TimeoutExceeded
|
|
from rowers.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 getfavorites(r,row):
|
|
workouttype = 'ote'
|
|
if row.workouttype in mytypes.otwtypes:
|
|
workouttype = 'otw'
|
|
|
|
matchworkouttypes = [workouttype,'all']
|
|
|
|
workoutsource = row.workoutsource
|
|
if 'speedcoach2' in row.workoutsource:
|
|
workoutsource = 'speedcoach2'
|
|
|
|
favorites = FavoriteChart.objects.filter(user=r,
|
|
workouttype__in=matchworkouttypes).order_by("id")
|
|
favorites2 = FavoriteChart.objects.filter(user=r,
|
|
workouttype__in=[workoutsource]).order_by("id")
|
|
|
|
|
|
|
|
favorites = favorites | favorites2
|
|
|
|
maxfav = len(favorites)-1
|
|
|
|
|
|
return favorites,maxfav
|
|
|
|
def get_workout_default_page(request,id):
|
|
if request.user.is_anonymous:
|
|
return reverse('workout_view',kwargs={'id':id})
|
|
else:
|
|
r = Rower.objects.get(user=request.user)
|
|
if r.defaultlandingpage == 'workout_edit_view':
|
|
return reverse('workout_edit_view',kwargs={'id':id})
|
|
else:
|
|
return reverse('workout_workflow_view',kwargs={'id':id})
|
|
|
|
def getrequestrower(request,rowerid=0,userid=0,notpermanent=False):
|
|
|
|
userid = int(userid)
|
|
rowerid = int(rowerid)
|
|
|
|
if notpermanent == False:
|
|
if rowerid == 0 and 'rowerid' in request.session:
|
|
rowerid = request.session['rowerid']
|
|
|
|
if userid != 0:
|
|
rowerid = 0
|
|
|
|
try:
|
|
|
|
if rowerid != 0:
|
|
r = Rower.objects.get(id=rowerid)
|
|
elif userid != 0:
|
|
u = User.objects.get(id=userid)
|
|
r = getrower(u)
|
|
else:
|
|
r = getrower(request.user)
|
|
|
|
except Rower.DoesNotExist:
|
|
raise Http404("Rower doesn't exist")
|
|
|
|
if not checkaccessuser(request.user,r):
|
|
raise PermissionDenied("You have no access to this user")
|
|
|
|
if notpermanent == False:
|
|
request.session['rowerid'] = r.id
|
|
|
|
return r
|
|
|
|
def getrequestplanrower(request,rowerid=0,userid=0,notpermanent=False):
|
|
|
|
userid = int(userid)
|
|
rowerid = int(rowerid)
|
|
|
|
if notpermanent == False:
|
|
if rowerid == 0 and 'rowerid' in request.session:
|
|
rowerid = request.session['rowerid']
|
|
|
|
if userid != 0:
|
|
rowerid = 0
|
|
|
|
try:
|
|
|
|
if rowerid != 0:
|
|
r = Rower.objects.get(id=rowerid)
|
|
elif userid != 0:
|
|
u = User.objects.get(id=userid)
|
|
r = getrower(u)
|
|
else:
|
|
r = getrower(request.user)
|
|
|
|
except Rower.DoesNotExist:
|
|
raise Http404("Rower doesn't exist")
|
|
|
|
if not checkaccessplanuser(request.user,r):
|
|
raise PermissionDenied("You have no access to this user")
|
|
|
|
if notpermanent == False:
|
|
request.session['rowerid'] = r.id
|
|
|
|
return r
|
|
|
|
|
|
def getrower(user):
|
|
try:
|
|
if user.is_anonymous:
|
|
return None
|
|
except AttributeError:
|
|
if User.objects.get(id=user).is_anonymous:
|
|
return None
|
|
try:
|
|
r = Rower.objects.get(user=user)
|
|
except Rower.DoesNotExist:
|
|
r = Rower(user=user)
|
|
r.save()
|
|
|
|
return r
|
|
|
|
|
|
def get_workout(id):
|
|
try:
|
|
id = encoder.decode_hex(id)
|
|
w = Workout.objects.get(id=id)
|
|
except Workout.DoesNotExist:
|
|
raise Http404("Workout doesn't exist")
|
|
|
|
return w
|
|
|
|
def get_workout_permitted(user,id):
|
|
w = get_workout(id)
|
|
|
|
if (checkworkoutuser(user,w)==False):
|
|
raise PermissionDenied("Access denied")
|
|
|
|
return w
|
|
|
|
def get_workout_permittedview(user,id):
|
|
w = get_workout(id)
|
|
|
|
if (checkworkoutuserview(user,w)==False):
|
|
raise PermissionDenied("Access denied")
|
|
|
|
return w
|
|
|
|
def getvalue(data):
|
|
perc = 0
|
|
total = 1
|
|
done = 0
|
|
id = 0
|
|
session_key = 'noot'
|
|
for i in data.iteritems():
|
|
if i[0] == 'total':
|
|
total = float(i[1])
|
|
if i[0] == 'done':
|
|
done = float(i[1])
|
|
if i[0] == 'id':
|
|
id = i[1]
|
|
if i[0] == 'session_key':
|
|
session_key = i[1]
|
|
|
|
return total,done,id,session_key
|
|
|
|
class SessionTaskListener(threading.Thread):
|
|
def __init__(self, r, channels):
|
|
threading.Thread.__init__(self)
|
|
self.redis = r
|
|
self.pubsub = self.redis.pubsub()
|
|
self.pubsub.subscribe(channels)
|
|
|
|
def work(self, item):
|
|
|
|
try:
|
|
data = json.loads(item['data'])
|
|
total,done,id,session_key = getvalue(data)
|
|
perc = int(100.*done/total)
|
|
cache.set(id,perc,3600)
|
|
|
|
except TypeError:
|
|
pass
|
|
|
|
def run(self):
|
|
for item in self.pubsub.listen():
|
|
if item['data'] == "KILL":
|
|
self.pubsub.unsubscribe()
|
|
print(self, "unsubscribed and finished")
|
|
break
|
|
else:
|
|
self.work(item)
|
|
|
|
|
|
queuefailed = Queue("failed",connection=Redis())
|
|
redis_connection = StrictRedis()
|
|
r = Redis()
|
|
|
|
# this doesn't yet work on production
|
|
#if settings.DEBUG:
|
|
# client = SessionTaskListener(r,['tasks'])
|
|
# client.start()
|
|
|
|
|
|
rq_registry = StartedJobRegistry(queue.name,connection=redis_connection)
|
|
rq_registryhigh = StartedJobRegistry(queuehigh.name,connection=redis_connection)
|
|
rq_registrylow = StartedJobRegistry(queuelow.name,connection=redis_connection)
|
|
|
|
from rq.job import Job
|
|
|
|
try:
|
|
from rest_framework_swagger.views import get_swagger_view
|
|
except ImportError:
|
|
pass
|
|
|
|
from rest_framework.renderers import JSONRenderer
|
|
from rest_framework.parsers import JSONParser
|
|
from rest_framework.response import Response
|
|
from rowers.serializers import RowerSerializer,WorkoutSerializer
|
|
try:
|
|
from rest_framework import status,permissions,generics
|
|
except ImportError:
|
|
pass
|
|
|
|
from rest_framework.decorators import api_view, renderer_classes
|
|
|
|
|
|
from rowers.permissions import IsOwnerOrNot
|
|
|
|
import rowers.plots as plots
|
|
import rowers.mailprocessing as mailprocessing
|
|
|
|
from io import BytesIO
|
|
from scipy.special import lambertw
|
|
|
|
from rowers.dataprep import timedeltaconv
|
|
from rowers.dataprep import getsmallrowdata_db
|
|
|
|
from scipy.interpolate import griddata
|
|
|
|
#LOCALTIMEZONE = tz('Etc/UTC')
|
|
USER_LANGUAGE = 'en-US'
|
|
|
|
from rowers.interactiveplots import *
|
|
from rowers.celery import result as celery_result
|
|
|
|
# Define the API documentation
|
|
try:
|
|
schema_view = get_swagger_view(title='Rowsandall API')
|
|
except NameError:
|
|
pass
|
|
|
|
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.TESTING:
|
|
return None
|
|
elif settings.CELERY:
|
|
result = celery_result.AsyncResult(jobid).result
|
|
else:
|
|
running_job_ids = rq_registry.get_job_ids()
|
|
if len(running_job_ids) and jobid in running_job_ids:
|
|
# job is running
|
|
return None
|
|
else:
|
|
# job is ready
|
|
try:
|
|
job = Job.fetch(jobid,connection=redis_connection)
|
|
result = job.result
|
|
except NoSuchJobError:
|
|
return None
|
|
|
|
return result
|
|
|
|
verbose_job_status = {
|
|
'updatecp': 'Critical Power Calculation for Ergometer Workouts',
|
|
'updatecpwater': 'Critical Power Calculation for OTW Workouts',
|
|
'otwsetpower': 'Rowing Physics OTW Power Calculation',
|
|
'agegrouprecords': 'Calculate age group records',
|
|
'make_plot': 'Create static chart',
|
|
'long_test_task': 'Long Test Task',
|
|
'long_test_task2': 'Long Test Task 2',
|
|
'update_empower': 'Correct Empower Inflated Power Bug',
|
|
'submit_race': 'Checking Race Course Result',
|
|
}
|
|
|
|
def get_job_status(jobid):
|
|
if settings.TESTING:
|
|
summary = {
|
|
'status': 'failed',
|
|
'result': 0,
|
|
'finished': True,
|
|
'failed': True,
|
|
'started_at':None,
|
|
}
|
|
return summary
|
|
elif settings.CELERY:
|
|
job = celery_result.AsyncResult(jobid)
|
|
jobresult = job.result
|
|
|
|
|
|
if 'fail' in job.status.lower():
|
|
jobresult = '0'
|
|
summary = {
|
|
'status': job.status,
|
|
'result': jobresult,
|
|
'started_at': None
|
|
}
|
|
else:
|
|
try:
|
|
job = Job.fetch(jobid,connection=redis_connection)
|
|
try:
|
|
status = job.status
|
|
except AttributeError:
|
|
status = job.get_status()
|
|
|
|
summary = {
|
|
'status':status,
|
|
'result':job.result,
|
|
'started_at':job.started_at
|
|
}
|
|
except NoSuchJobError:
|
|
summary = {
|
|
'status': 'success',
|
|
'result': 1,
|
|
'started_at':None,
|
|
}
|
|
|
|
try:
|
|
if 'fail' in summary['status'].lower():
|
|
summary['failed'] = True
|
|
else:
|
|
summary['failed'] = False
|
|
|
|
if 'success' in summary['status'].lower():
|
|
summary['finished'] = True
|
|
elif 'finished' in summary['status'].lower():
|
|
summary['finished'] = True
|
|
else:
|
|
summary['finished'] = False
|
|
except AttributeError:
|
|
summary = {
|
|
'status': 'failed',
|
|
'result': 0,
|
|
'finished': True,
|
|
'failed': True,
|
|
'started_at':None,
|
|
}
|
|
|
|
return summary
|
|
|
|
def kill_async_job(request,id='aap'):
|
|
if settings.CELERY:
|
|
job = celery_result.AsyncResult(id)
|
|
job.revoke()
|
|
else:
|
|
try:
|
|
cancel_job(id,connection=redis_connection)
|
|
except NoSuchJobError:
|
|
pass
|
|
|
|
remove_asynctask(request,id)
|
|
cache.delete(id)
|
|
url = reverse(session_jobs_status)
|
|
|
|
return HttpResponseRedirect(url)
|
|
|
|
@login_required()
|
|
def raise_500(request):
|
|
if request.user.is_superuser:
|
|
raise ValueError
|
|
else:
|
|
return HttpResponse("invalid")
|
|
|
|
#@login_required()
|
|
#def test_job_view(request,aantal=100):
|
|
#
|
|
# session_key = request.session._session_key
|
|
#
|
|
# job = myqueue(queuehigh,long_test_task,int(aantal),
|
|
# session_key=session_key)
|
|
#
|
|
#
|
|
# try:
|
|
# request.session['async_tasks'] += [(job.id,'long_test_task')]
|
|
# except KeyError:
|
|
# request.session['async_tasks'] = [(job.id,'long_test_task')]
|
|
#
|
|
# url = reverse(session_jobs_status)
|
|
#
|
|
# return HttpResponseRedirect(url)
|
|
|
|
#@login_required()
|
|
#def test_job_view2(request,aantal=100):
|
|
#
|
|
#
|
|
# job = myqueue(queuehigh,long_test_task2,int(aantal),
|
|
# secret=settings.PROGRESS_CACHE_SECRET)#
|
|
#
|
|
#
|
|
# try:
|
|
# request.session['async_tasks'] += [(job.id,'long_test_task2')]
|
|
# except KeyError:
|
|
# request.session['async_tasks'] = [(job.id,'long_test_task2')]
|
|
#
|
|
# url = reverse(session_jobs_status)
|
|
#
|
|
# return HttpResponseRedirect(url)
|
|
|
|
@csrf_exempt
|
|
def post_progress(request,id=None,value=0):
|
|
if request.method == 'POST':
|
|
try:
|
|
secret = request.POST['secret']
|
|
except KeyError:
|
|
return HttpResponse('Access Denied',status=401)
|
|
if secret == settings.PROGRESS_CACHE_SECRET:
|
|
if not id:
|
|
try:
|
|
id = request.POST['id']
|
|
except KeyError:
|
|
return HttpResponse('Invalid request',400)
|
|
try:
|
|
value = request.POST['value']
|
|
except KeyError:
|
|
pass
|
|
|
|
cache.set(id,value,3600)
|
|
# test
|
|
result = cache.get(id)
|
|
|
|
return HttpResponse('progress cached '+str(result),
|
|
status=201)
|
|
else: # secret not given
|
|
return HttpResponse('access denied',status=401)
|
|
|
|
else: # request method is not POST
|
|
return HttpResponse('GET method not allowed',status=405)
|
|
|
|
def get_all_queued_jobs(userid=0):
|
|
r = StrictRedis()
|
|
|
|
jobs = []
|
|
|
|
celerykeys = r.keys('celery*')
|
|
for key in celerykeys:
|
|
id= key[17:]
|
|
job = celery_result.AsyncResult(id)
|
|
jobresult = job.result
|
|
if 'fail' in job.status.lower():
|
|
jobresult = '0'
|
|
jobs.append(
|
|
(id,{
|
|
'status':job.status,
|
|
'result':jobresult,
|
|
'function':'',
|
|
'meta':job.info,
|
|
}))
|
|
|
|
ids = [j.id for j in queue.jobs]
|
|
ids += [j.id for j in queuehigh.jobs]
|
|
ids += [j.id for j in queuelow.jobs]
|
|
ids += [j.id for j in queuefailed.jobs]
|
|
|
|
|
|
for id in ids:
|
|
job = Job.fetch(id,connection=redis_connection)
|
|
jobs.append(
|
|
(id,{
|
|
'status':job.get_status(),
|
|
'result':job.result,
|
|
'function':job.func_name,
|
|
'meta':job.meta,
|
|
}))
|
|
|
|
return jobs
|
|
|
|
def get_stored_tasks_status(request):
|
|
try:
|
|
taskids = request.session['async_tasks']
|
|
except KeyError:
|
|
taskids = []
|
|
|
|
taskstatus = []
|
|
for id,func_name in reversed(taskids):
|
|
progress = 0
|
|
try:
|
|
cached_progress = cache.get(id)
|
|
if cached_progress is not None:
|
|
cached_progress = int(cached_progress)
|
|
else:
|
|
cached_progress = 0
|
|
except ValueError:
|
|
cached_progress = 0
|
|
finished = get_job_status(id)['finished']
|
|
if finished:
|
|
cache.set(id,100)
|
|
progress = 100
|
|
elif cached_progress is not None and cached_progress>0:
|
|
progress = cached_progress
|
|
else:
|
|
progress = 0
|
|
|
|
this_task_status = {
|
|
'id':id,
|
|
'status':get_job_status(id)['status'],
|
|
'failed':get_job_status(id)['failed'],
|
|
'finished':get_job_status(id)['finished'],
|
|
'func_name':func_name,
|
|
'verbose': verbose_job_status[func_name],
|
|
'progress': progress,
|
|
}
|
|
|
|
taskstatus.append(this_task_status)
|
|
|
|
|
|
return taskstatus
|
|
|
|
@login_required()
|
|
def get_thumbnails(request,id):
|
|
row = get_workout_permitted(request.user,id)
|
|
|
|
|
|
r = getrower(request.user)
|
|
result = request.user.is_authenticated and ispromember(request.user)
|
|
if result:
|
|
promember=1
|
|
if request.user == row.user.user:
|
|
mayedit=1
|
|
|
|
comments = WorkoutComment.objects.filter(workout=row)
|
|
|
|
aantalcomments = len(comments)
|
|
|
|
favorites,maxfav = getfavorites(r,row)
|
|
|
|
charts = []
|
|
|
|
charts = thumbnails_set(r,encoder.decode_hex(id),favorites)
|
|
try:
|
|
if charts[0]['script'] == '':
|
|
charts = []
|
|
except IndexError:
|
|
charts = []
|
|
|
|
|
|
return JSONResponse(charts)
|
|
|
|
def get_blog_posts(request):
|
|
blogposts = BlogPost.objects.all().order_by("-date")
|
|
|
|
jsondata = []
|
|
|
|
if blogposts:
|
|
for blogpost in blogposts[0:3]:
|
|
thedict = {
|
|
'title':blogpost.title,
|
|
'link':blogpost.link,
|
|
}
|
|
|
|
jsondata.append(thedict)
|
|
|
|
return JSONResponse(jsondata)
|
|
|
|
def get_blog_posts_old(request):
|
|
try:
|
|
response = requests.get(
|
|
'https://analytics.rowsandall.com/wp-json/wp/v2/posts?per_page=3')
|
|
if response.status_code == 200:
|
|
blogs_json = response.json()
|
|
# with open('blogs.txt','w') as o:
|
|
# o.write(json.dumps(blogs_json,indent=2,sort_keys=True))
|
|
else:
|
|
blogs_json = []
|
|
except ConnectionError:
|
|
pass
|
|
|
|
blogposts = []
|
|
|
|
|
|
for postdata in blogs_json[0:3]:
|
|
|
|
try:
|
|
title = postdata['title']['rendered'].encode(
|
|
'ascii','xmlcharrefreplace')
|
|
|
|
except TypeError:
|
|
title = postdata['title']['rendered'].encode(
|
|
'ascii','xmlcharrefreplace').decode('utf-8')
|
|
|
|
|
|
thedict = {
|
|
'title': title,
|
|
# 'author': '',
|
|
# 'image': image_url,
|
|
# 'excerpt': excerpt_first,
|
|
'link': postdata['link'],
|
|
}
|
|
|
|
blogposts.append(thedict)
|
|
|
|
return JSONResponse(blogposts)
|
|
|
|
|
|
|
|
@login_required()
|
|
def get_testscript(request,id):
|
|
row = get_workout_permitted(request.user,id)
|
|
r = getrower(request.user)
|
|
|
|
object = {
|
|
"script":"""
|
|
<div id="id_script">
|
|
<script>alert("hi")</script>
|
|
</div>
|
|
""",
|
|
"div":"""
|
|
<div id="id_div">
|
|
Hoi
|
|
</div>
|
|
"""
|
|
}
|
|
|
|
|
|
return JSONResponse([object,object])
|
|
|
|
@login_required()
|
|
def session_jobs_view(request):
|
|
taskstatus = get_stored_tasks_status(request)
|
|
|
|
return HttpResponse(json.dumps(taskstatus))
|
|
|
|
@login_required()
|
|
def session_jobs_status(request):
|
|
taskstatus = get_stored_tasks_status(request)
|
|
|
|
return render(request,
|
|
'async_tasks.html',
|
|
{'taskstatus':taskstatus})
|
|
|
|
# Test if row data include candidates
|
|
def rowhascoordinates(row):
|
|
# create interactive plot
|
|
f1 = row.csvfilename
|
|
u = row.user.user
|
|
r = getrower(u)
|
|
rowdata = rdata(f1)
|
|
hascoordinates = 1
|
|
if rowdata != 0:
|
|
try:
|
|
latitude = rowdata.df[' latitude']
|
|
|
|
if not latitude.std():
|
|
hascoordinates = 0
|
|
except (KeyError,AttributeError):
|
|
hascoordinates = 0
|
|
|
|
else:
|
|
hascoordinates = 0
|
|
|
|
return hascoordinates
|
|
|
|
|
|
# Wrapper around the rowingdata call to catch some exceptions
|
|
# Checks for CSV file, then for gzipped CSV file, and if all fails, returns 0
|
|
def rdata(file,rower=rrower()):
|
|
try:
|
|
res = rrdata(csvfile=file,rower=rower)
|
|
except (IOError, IndexError, EOFError):
|
|
try:
|
|
res = rrdata(csvfile=file+'.gz',rower=rower)
|
|
except (IOError, IndexError, EOFError):
|
|
res = 0
|
|
|
|
return res
|
|
|
|
# Query to get teams managed and member of
|
|
def get_my_teams(user):
|
|
try:
|
|
therower = Rower.objects.get(user=user)
|
|
try:
|
|
teams1 = therower.team.all()
|
|
except AttributeError:
|
|
teams1 = []
|
|
|
|
teams2 = Team.objects.filter(manager=user)
|
|
teams = list(set(teams1).union(set(teams2)))
|
|
except TypeError:
|
|
teams = []
|
|
|
|
return teams
|
|
|
|
# Used for the interval editor - translates seconds to a time object
|
|
def get_time(second):
|
|
if (second<=0) or (second>1e9):
|
|
hours = 0
|
|
minutes=0
|
|
sec=0
|
|
microsecond = 0
|
|
elif math.isnan(second):
|
|
hours = 0
|
|
minutes=0
|
|
sec=0
|
|
microsecond = 0
|
|
else:
|
|
days = int(second/(24.*3600.)) % (24*3600)
|
|
hours = int((second-24.*3600.*days)/3600.) % 24
|
|
minutes = int((second-3600.*(hours+24.*days))/60.) % 60
|
|
sec = int(second-3600.*(hours+24.*days)-60.*minutes) % 60
|
|
microsecond = int(1.0e6*(second-3600.*(hours+24.*days)-60.*minutes-sec))
|
|
return datetime.time(hours,minutes,sec,microsecond)
|
|
|
|
|
|
# get the workout ID from the SportTracks URI
|
|
def getidfromsturi(uri,length=8):
|
|
return uri[len(uri)-length:]
|
|
|
|
import re
|
|
|
|
def getidfromuri(uri):
|
|
m = re.search('/(\w.*)\/(\d+)',uri)
|
|
return m.group(2)
|
|
|
|
|
|
|
|
from rowers.utils import (
|
|
geo_distance,serialize_list,deserialize_list,uniqify,
|
|
str2bool,range_to_color_hex,absolute,myqueue,get_call,
|
|
calculate_age,rankingdistances,rankingdurations,
|
|
is_ranking_piece,my_dict_from_instance,wavg,NoTokenError
|
|
)
|
|
|
|
import rowers.datautils as datautils
|
|
|
|
from rowers.models import (
|
|
checkworkoutuser,checkaccessuser,checkviewworkouts,checkworkoutuserview
|
|
)
|
|
|
|
# Check if a user is a Coach member
|
|
def iscoachmember(user):
|
|
if not user.is_anonymous:
|
|
try:
|
|
r = Rower.objects.get(user=user)
|
|
except Rower.DoesNotExist:
|
|
r = Rower(user=user)
|
|
r.save()
|
|
|
|
result = user.is_authenticated and ('coach' in r.rowerplan)
|
|
else:
|
|
result = False
|
|
|
|
return result
|
|
|
|
def cancreateteam(user):
|
|
if user.is_anonymous:
|
|
return False
|
|
|
|
try:
|
|
r = Rower.objects.get(user=user)
|
|
except Rower.DoesNotExist:
|
|
r = Rower(user=user)
|
|
r.save()
|
|
|
|
if user.is_authenticated and ('coach' in r.rowerplan):
|
|
return True
|
|
elif user.is_athenticated() and r.rowerplan in ['plan','pro']:
|
|
ts = Team.objects.filter(manager=user)
|
|
if len(otherteams) >= 1:
|
|
return False
|
|
|
|
# Check if a user can create planned sessions
|
|
def hasplannedsessions(user):
|
|
if not user.is_anonymous:
|
|
try:
|
|
r = Rower.objects.get(user=user)
|
|
except Rower.DoesNotExist:
|
|
r = Rower(user=user)
|
|
r.save()
|
|
|
|
result = user.is_authenticated and (r.rowerplan=='coach' or r.rowerplan=='freecoach' or r.rowerplan=='plan')
|
|
if not result and r.plantrialexpires:
|
|
result = user.is_authenticated and r.plantrialexpires >= datetime.date.today()
|
|
else:
|
|
result = False
|
|
|
|
return result
|
|
|
|
from rowers.utils import ProcessorCustomerError
|
|
from rowers.utils import isprorower
|
|
|
|
# Check if a user is a Pro member
|
|
def ispromember(user):
|
|
if user and not user.is_anonymous:
|
|
try:
|
|
r = Rower.objects.get(user=user)
|
|
except Rower.DoesNotExist:
|
|
r = Rower(user=user)
|
|
r.save()
|
|
|
|
result = user.is_authenticated and isprorower(r)
|
|
else:
|
|
result = False
|
|
return result
|
|
|
|
# More User/Rower utils
|
|
def add_defaultfavorites(r):
|
|
for c in defaultfavoritecharts:
|
|
f = FavoriteChart(user=r,
|
|
yparam1=c['yparam1'],
|
|
yparam2=c['yparam2'],
|
|
xparam=c['xparam'],
|
|
plottype=c['plottype'],
|
|
workouttype=c['workouttype'],
|
|
reststrokes=c['reststrokes'],
|
|
notes=c['notes'])
|
|
|
|
f.save()
|
|
return 1
|
|
|
|
|
|
# Shows email form and sends it if submitted
|
|
def sendmail(request):
|
|
if request.method == 'POST':
|
|
# test recaptcha
|
|
response_string = request.POST.get('g-recaptcha-response')
|
|
# replace below with settings
|
|
recaptcha_secret = RECAPTCHA_SITE_SECRET
|
|
url = 'https://www.google.com/recaptcha/api/siteverify'
|
|
data = {
|
|
'secret':recaptcha_secret,
|
|
'response': response_string,
|
|
}
|
|
response = requests.post(url,data=data,verify=True)
|
|
success = False
|
|
if response.status_code == 200:
|
|
success = response.json().get('success')
|
|
|
|
form = EmailForm(request.POST)
|
|
if form.is_valid() and success:
|
|
firstname = form.cleaned_data['firstname']
|
|
lastname = form.cleaned_data['lastname']
|
|
email = form.cleaned_data['email']
|
|
subject = 'Rowsandall Contact Form:'+form.cleaned_data['subject']
|
|
message = form.cleaned_data['message']
|
|
fullemail = firstname + " " + lastname + " " + "<" + email + ">"
|
|
send_mail(subject, message, fullemail, ['info@rowsandall.com'])
|
|
return HttpResponseRedirect('/rowers/email/thankyou/')
|
|
else:
|
|
if not success:
|
|
messages.error(request,'Bots are not welcome')
|
|
else:
|
|
messages.error(request,'Something went wrong. Please try again')
|
|
return HttpResponseRedirect('/rowers/email/')
|
|
else:
|
|
return HttpResponseRedirect('/rowers/email/')
|
|
|
|
|
|
# Create workout data from Strava or Concept2
|
|
# data and create the associated Workout object and save it
|
|
def add_workout_from_strokedata(user,importid,data,strokedata,
|
|
source='c2',splitdata=None,
|
|
workoutsource='concept2'):
|
|
try:
|
|
workouttype = data['type']
|
|
except KeyError:
|
|
workouttype = 'rower'
|
|
|
|
if workouttype not in [x[0] for x in Workout.workouttypes]:
|
|
workouttype = 'other'
|
|
try:
|
|
comments = data['comments']
|
|
except:
|
|
comments = ' '
|
|
|
|
# comments = "Imported data \n %s" % comments
|
|
# comments = "Imported data \n"+comments # str(comments)
|
|
try:
|
|
thetimezone = tz(data['timezone'])
|
|
except:
|
|
thetimezone = 'UTC'
|
|
|
|
r = getrower(user)
|
|
try:
|
|
rowdatetime = iso8601.parse_date(data['date_utc'])
|
|
except KeyError:
|
|
rowdatetime = iso8601.parse_date(data['start_date'])
|
|
except ParseError:
|
|
rowdatetime = iso8601.parse_date(data['date'])
|
|
|
|
|
|
try:
|
|
c2intervaltype = data['workout_type']
|
|
|
|
except KeyError:
|
|
c2intervaltype = ''
|
|
|
|
try:
|
|
title = data['name']
|
|
except KeyError:
|
|
title = ""
|
|
try:
|
|
t = data['comments'].split('\n', 1)[0]
|
|
title += t[:20]
|
|
except:
|
|
title = 'Imported'
|
|
|
|
starttimeunix = arrow.get(rowdatetime).timestamp
|
|
|
|
res = make_cumvalues(0.1*strokedata['t'])
|
|
cum_time = res[0]
|
|
lapidx = res[1]
|
|
|
|
unixtime = cum_time+starttimeunix
|
|
# unixtime[0] = starttimeunix
|
|
seconds = 0.1*strokedata.loc[:,'t']
|
|
|
|
nr_rows = len(unixtime)
|
|
|
|
try:
|
|
latcoord = strokedata.loc[:,'lat']
|
|
loncoord = strokedata.loc[:,'lon']
|
|
except:
|
|
latcoord = np.zeros(nr_rows)
|
|
loncoord = np.zeros(nr_rows)
|
|
|
|
|
|
try:
|
|
strokelength = strokedata.loc[:,'strokelength']
|
|
except:
|
|
strokelength = np.zeros(nr_rows)
|
|
|
|
dist2 = 0.1*strokedata.loc[:,'d']
|
|
|
|
try:
|
|
spm = strokedata.loc[:,'spm']
|
|
except KeyError:
|
|
spm = 0*dist2
|
|
|
|
try:
|
|
hr = strokedata.loc[:,'hr']
|
|
except KeyError:
|
|
hr = 0*spm
|
|
pace = strokedata.loc[:,'p']/10.
|
|
pace = np.clip(pace,0,1e4)
|
|
pace = pace.replace(0,300)
|
|
|
|
velo = 500./pace
|
|
|
|
power = 2.8*velo**3
|
|
|
|
# save csv
|
|
# Create data frame with all necessary data to write to csv
|
|
df = pd.DataFrame({'TimeStamp (sec)':unixtime,
|
|
' Horizontal (meters)': dist2,
|
|
' Cadence (stokes/min)':spm,
|
|
' HRCur (bpm)':hr,
|
|
' longitude':loncoord,
|
|
' latitude':latcoord,
|
|
' Stroke500mPace (sec/500m)':pace,
|
|
' Power (watts)':power,
|
|
' DragFactor':np.zeros(nr_rows),
|
|
' DriveLength (meters)':np.zeros(nr_rows),
|
|
' StrokeDistance (meters)':strokelength,
|
|
' DriveTime (ms)':np.zeros(nr_rows),
|
|
' StrokeRecoveryTime (ms)':np.zeros(nr_rows),
|
|
' AverageDriveForce (lbs)':np.zeros(nr_rows),
|
|
' PeakDriveForce (lbs)':np.zeros(nr_rows),
|
|
' lapIdx':lapidx,
|
|
' ElapsedTime (sec)':seconds
|
|
})
|
|
|
|
|
|
df.sort_values(by='TimeStamp (sec)',ascending=True)
|
|
|
|
timestr = strftime("%Y%m%d-%H%M%S")
|
|
|
|
|
|
# Create CSV file name and save data to CSV file
|
|
csvfilename ='media/{code}_{importid}.csv'.format(
|
|
importid=importid,
|
|
code = uuid4().hex[:16]
|
|
)
|
|
|
|
res = df.to_csv(csvfilename+'.gz',index_label='index',
|
|
compression='gzip')
|
|
|
|
|
|
# with Concept2
|
|
if source=='c2':
|
|
try:
|
|
totaldist = data['distance']
|
|
totaltime = data['time']/10.
|
|
except KeyError:
|
|
totaldist = 0
|
|
totaltime = 0
|
|
else:
|
|
totaldist = 0
|
|
totaltime = 0
|
|
|
|
id,message = dataprep.save_workout_database(
|
|
csvfilename,r,
|
|
workouttype=workouttype,
|
|
title=title,notes=comments,
|
|
# totaldist=totaldist,
|
|
# totaltime=totaltime,
|
|
workoutsource=workoutsource,
|
|
dosummary=True
|
|
)
|
|
|
|
|
|
|
|
return id,message
|
|
|
|
|
|
|
|
|
|
def keyvalue_get_default(key,options,def_options):
|
|
|
|
try:
|
|
return options[key]
|
|
except KeyError:
|
|
return def_options[key]
|
|
|
|
|
|
|
|
|
|
# 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
|
|
|
|
import rowers.teams as teams
|
|
from rowers.models import C2WorldClassAgePerformance
|