Private
Public Access
1
0
Files
rowsandall/rowers/views/statements.py

1492 lines
46 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import rowers.teams as teams
from rowers.serializers import RowerSerializer, WorkoutSerializer
from rowers.integrations import integrations
from rq import Queue, cancel_job
from redis import StrictRedis, Redis
from rowers.models import C2WorldClassAgePerformance
from rowers.utils import ProcessorCustomerError
import rowers.datautils as datautils
from rowers.utils import (
geo_distance, serialize_list, deserialize_list, uniqify,
str2bool, range_to_color_hex, absolute, myqueue, get_call,
calculate_age, rankingdistances, rankingdurations,
my_dict_from_instance, wavg, NoTokenError,
request_is_ajax, dologging
)
from rowers.celery import result as celery_result
from rowers.interactiveplots import *
from scipy.interpolate import griddata
from rowers.dataprep import getsmallrowdata_pd, read_data
from rowers.dataprep import timedeltaconv
from scipy.special import lambertw
from io import BytesIO
import rowers.plots as plots
from rowers.permissions import IsOwnerOrNot, IsCompetitorOrNot
from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework.decorators import api_view, renderer_classes, permission_classes
from rest_framework.response import Response
from rq.job import Job
from rules.contrib.views import permission_required, objectgetter
from django.core.cache import cache
from django.db import models
from django.utils.crypto import get_random_string
from rq.registry import StartedJobRegistry
from rq.exceptions import NoSuchJobError
import threading
import redis
import colorsys
import re
import gzip
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, View
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 django.shortcuts import get_object_or_404
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
from rules.contrib.views import PermissionRequiredMixin
import rowers.braintreestuff as braintreestuff
import rowers.payments as payments
from rowers.opaque import encoder
from rowers.rower_rules import (
ispromember, is_coach_user, is_team_member, is_rower_team_member,
is_workout_user, isplanmember, can_delete_session,
can_view_target, can_change_target, can_delete_target,
can_view_plan, can_change_plan, can_delete_plan,
can_view_cycle, can_change_cycle, can_delete_cycle,
can_add_workout_member, can_plan_user, is_paid_coach,
can_start_trial, can_start_plantrial, can_start_coachtrial,
can_plan, is_workout_team,
is_promember,user_is_basic, is_coachtrial, is_coach,
workout_is_strava
)
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,
HttpResponseBadRequest,
)
from django.contrib.auth import authenticate, login, logout
from rowers.forms import (
WorkoutBulkActions, ExportChoices,
ForceCurveOptionsForm, HistoForm, TeamMessageForm,
LoginForm, DocumentsForm, UploadOptionsForm, ImageForm, CourseForm,
CourseConfirmForm, ResampleForm,
TeamUploadOptionsForm, WorkFlowLeftPanelForm, WorkFlowMiddlePanelForm,
WorkFlowLeftPanelElement, WorkFlowMiddlePanelElement,
WorkoutNameTemplateElement,
LandingPageForm, PlannedSessionSelectForm, WorkoutSessionSelectForm,
PlannedSessionTeamForm, PlannedSessionTeamMemberForm,
VirtualRaceSelectForm, WorkoutRaceSelectForm, CourseSelectForm,
RaceResultFilterForm, PowerIntervalUpdateForm, FlexAxesForm,
FlexOptionsForm, DataFrameColumnsForm, OteWorkoutTypeForm,
MetricsForm, DisqualificationForm, disqualificationreasons,
disqualifiers, SearchForm, BillingForm, PlanSelectForm,
VideoAnalysisCreateForm, WorkoutSingleSelectForm,
VideoAnalysisMetricsForm, SurveyForm, HistorySelectForm,
StravaChartForm, FitnessFitForm, PerformanceManagerForm,
TrainingPlanBillingForm, InstantPlanSelectForm,
TrainingZonesForm, InstrokeForm, InStrokeMultipleCompareForm,
ForceCurveMultipleCompareForm, PlanByRscoreForm,
AssignChoices,
)
from django.urls import reverse, reverse_lazy
from django.core.exceptions import PermissionDenied, MultipleObjectsReturned
from django.template import RequestContext
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.conf import settings
from django.urls import resolve
from django.utils.decorators import method_decorator
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
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, StrokeDataForm,
StatsOptionsForm, PredictedPieceForm, DateRangeForm, DeltaDaysForm,
FitnessMetricForm, PredictedPieceFormNoDistance,
EmailForm, RegistrationForm, RegistrationFormTermsOfService,
RegistrationFormUniqueEmail, RegistrationFormSex,
CNsummaryForm,
StandardsForm,
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,
TrainingMacroCycleForm, createmacrofillers,
createmicrofillers, createmesofillers,
microcyclecheckdates, mesocyclecheckdates, macrocyclecheckdates,
TrainingMesoCycleForm, TrainingMicroCycleForm,
RaceLogo, RowerBillingAddressForm, PaidPlan,
AlertEditForm, ConditionEditForm,
PlannedSessionComment, CoachRequest, CoachOffer,
VideoAnalysis, ShareKey,
StandardCollection, CourseStandard,
VirtualRaceFollower, TombStone, InstantPlan,
PlannedSessionStep,InStrokeAnalysis, ForceCurveAnalysis, SyncRecord,
UserMessage,APIKey,
)
from rowers.models import ( RowerPowerForm, RowerHRZonesForm, SimpleRowerPowerForm,
RowerForm, RowerCPForm, GraphImage, AdvancedWorkoutForm,
RowerPowerZonesForm, AccountRowerForm, UserForm, Team, TeamForm,
TeamInviteForm, TeamInvite, TeamRequest, WorkoutComment,
WorkoutCommentForm, RowerExportForm, CalcAgePerformance,
PowerTimeFitnessMetric, BlogPost, PlannedSessionForm,
PlannedSessionTemplateForm, PlannedSessionFormSmall,
GeoCourseEditForm, VirtualRace, VirtualRaceForm,
VirtualRaceResultForm, RowerImportExportForm, WorkoutRPEForm,
IndoorVirtualRaceResultForm, IndoorVirtualRaceResult,
IndoorVirtualRaceForm, PlannedSessionCommentForm, Alert,
Condition, StaticChartRowerForm, FollowerForm,
VirtualRaceAthleteForm, InstantPlanForm, DataRowerForm,
StepEditorForm, iDokladToken,
RowerExportFormStrava, RowerExportFormPolar,
RowerExportFormSportTracks, RowerExportFormTrainingPeaks,
RowerExportFormConcept2, RowerExportFormGarmin,
RowerExportFormIntervals, RowerExportFormRP3,
RowerExportFormNK,
)
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
import sys
import datetime
import iso8601
import rowers.rojabo_stuff as rojabo_stuff
from iso8601 import ParseError
import rowers.rojabo_stuff as rojabo_stuff
import rowers.garmin_stuff as garmin_stuff
from rowers.rojabo_stuff import rojabo_open
from rowers.integrations import *
from polars.exceptions import ColumnNotFoundError
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,
TP_CLIENT_ID, TP_REDIRECT_URI, TP_CLIENT_KEY, TP_CLIENT_SECRET,
RP3_CLIENT_ID, RP3_REDIRECT_URI, RP3_CLIENT_KEY, RP3_CLIENT_SECRET,
BRAINTREE_MERCHANT_ID, BRAINTREE_PUBLIC_KEY, BRAINTREE_PRIVATE_KEY,
PAYMENT_PROCESSING_ON,
RECAPTCHA_SITE_KEY, RECAPTCHA_SITE_SECRET,
NK_REDIRECT_URI, NK_CLIENT_ID, NK_CLIENT_SECRET,
ROJABO_REDIRECT_URI, ROJABO_CLIENT_ID, ROJABO_CLIENT_SECRET,
IDOKLAD_REDIRECT_URI, IDOKLAD_CLIENT_ID, IDOKLAD_CLIENT_SECRET,
)
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_post_workout_api,
handle_sendemail_newftp,
instroke_static,
fetch_rojabo_session,
handle_sendemail_unrecognized, handle_sendemailnewcomment,
handle_request_post,
handle_sendemailsummary,
handle_rp3_async_workout,
handle_send_template_email,
handle_send_disqualification_email,
handle_send_withdraw_email,
handle_sendemailfile,
handle_sendemailkml,
handle_sendemailnewresponse, handle_updatedps,
long_test_task, long_test_task2,
handle_zip_file, handle_getagegrouprecords,
handle_update_empower,
handle_sendemailics,
handle_sendemail_userdeleted,
handle_sendemail_raceregistration,
handle_sendemail_racesubmission,
handle_sendemail_optout,
handle_sendemail_ical,
handle_c2_async_workout,
handle_send_email_instantplan_notification,
handle_nk_async_workout,
check_tp_workout_id,
handle_assignworkouts,
)
from scipy.signal import savgol_filter
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, send_confirm
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 base64
from django.http import HttpResponse
from django.contrib.auth import authenticate, login
def view_or_apikey(view, request, test_func, realm = "", *args, **kwargs):
if test_func(request.user):
return view(request, *args, **kwargs)
if 'Authorization' in request.META:
api_key = request.META.get('Authorization')
if api_key:
try:
api_key = APIKey.objects.get(key=api_key, is_active=True)
except APIKey.DoesNotExist:
raise AuthenticationFailed('Invalid API key')
login(request, api_key.user, backend='django.contrib.auth.backends.ModelBackend')
request.user = api_key.user
return view(request, *args, **kwargs)
response = HttpResponse()
response.status_code = 401
response['WWW-Authenticate'] = 'Basic realm="%s"' % realm
return response
#############################################################################
#
def view_or_basicauth(view, request, test_func, realm = "", *args, **kwargs):
"""
This is a helper function used by both 'logged_in_or_basicauth' and
'has_perm_or_basicauth' that does the nitty of determining if they
are already logged in or if they have provided proper http-authorization
and returning the view if all goes well, otherwise responding with a 401.
"""
if test_func(request.user):
# Already logged in, just return the view.
#
return view(request, *args, **kwargs)
# They are not logged in. See if they provided login credentials
#
if 'HTTP_AUTHORIZATION' in request.META:
auth = request.META['HTTP_AUTHORIZATION'].split()
if len(auth) == 2:
# NOTE: We are only support basic authentication for now.
#
if auth[0].lower() == "basic":
uname, passwd = base64.b64decode(auth[1]).decode("utf-8").split(':')
user = authenticate(username=uname, password=passwd)
if user is not None:
if user.is_active:
login(request, user)
request.user = user
return view(request, *args, **kwargs)
# Either they did not provide an authorization header or
# something in the authorization attempt failed. Send a 401
# back to them to ask them to authenticate.
#
response = HttpResponse()
response.status_code = 401
response['WWW-Authenticate'] = 'Basic realm="%s"' % realm
return response
#############################################################################
#
def logged_in_or_apikey(realm = ""):
def view_decorator(func):
def wrapper(request, *args, **kwargs):
return view_or_apikey(func, request,
lambda u: u.is_authenticated,
realm, *args, **kwargs)
return wrapper
return view_decorator
def logged_in_or_basicauth(realm = ""):
"""
A simple decorator that requires a user to be logged in. If they are not
logged in the request is examined for a 'authorization' header.
If the header is present it is tested for basic authentication and
the user is logged in with the provided credentials.
If the header is not present a http 401 is sent back to the
requestor to provide credentials.
The purpose of this is that in several django projects I have needed
several specific views that need to support basic authentication, yet the
web site as a whole used django's provided authentication.
The uses for this are for urls that are access programmatically such as
by rss feed readers, yet the view requires a user to be logged in. Many rss
readers support supplying the authentication credentials via http basic
auth (and they do NOT support a redirect to a form where they post a
username/password.)
Use is simple:
@logged_in_or_basicauth
def your_view:
...
You can provide the name of the realm to ask for authentication within.
"""
def view_decorator(func):
def wrapper(request, *args, **kwargs):
return view_or_basicauth(func, request,
lambda u: u.is_authenticated,
realm, *args, **kwargs)
return wrapper
return view_decorator
#############################################################################
#
def has_perm_or_basicauth(perm, realm = ""):
"""
This is similar to the above decorator 'logged_in_or_basicauth'
except that it requires the logged in user to have a specific
permission.
Use:
@logged_in_or_basicauth('asforums.view_forumcollection')
def your_view:
...
"""
def view_decorator(func):
def wrapper(request, *args, **kwargs):
return view_or_basicauth(func, request,
lambda u: u.has_perm(perm),
realm, *args, **kwargs)
return wrapper
return view_decorator
import django_rq
queue = django_rq.get_queue('default')
queuelow = django_rq.get_queue('low')
queuehigh = django_rq.get_queue('high')
g_acc = 9.80665
def get_totals(workouts):
totalseconds = 0
totalmeters = 0
for w in workouts:
totalmeters += w.distance
totalseconds += 60*(w.duration.hour*60 +
w.duration.minute)+w.duration.second
hours, remainder = divmod(totalseconds, 3600)
minutes, seconds = divmod(remainder, 60)
return totalmeters, hours, minutes, seconds
# creating shareable views
def allow_shares(view_func):
def sharify(request, *args, **kwargs):
shared = kwargs.get('__shared', None)
if shared is not None: # pragma: no cover
del kwargs["__shared"]
request.session['shared'] = True
return view_func(request, *args, **kwargs)
else:
return login_required(view_func)(request, *args, **kwargs)
return sharify
class SharifyError(Exception):
pass
def sharedPage(request, key):
try:
try:
shareKey = ShareKey.objects.get(pk=key)
except: # pragma: no cover
raise SharifyError
if shareKey.expired: # pragma: no cover
raise SharifyError
func, args, kwargs = resolve(shareKey.location)
kwargs["__shared"] = True
return func(request, *args, **kwargs)
except SharifyError: # pragma: no cover
# or add a more detailed error page. This either means that the key doesnt exist or is expired.
raise Http404
def createShareURL(request):
if request.method == 'POST':
url = request.POST['url']
ndays = int(request.POST['ndays'])
key = ShareKey.objects.create(pk=get_random_string(40),
expiration_seconds=60*60*24*ndays,
location=url)
key.save()
return render(request, 'share.html', {"key": key})
else: # pragma: no cover
raise Http404
def createShareModel(request, model_id): # pragma: no cover
task = MyModel.objects.get(pk=model_id)
key = ShareKey.objects.create(pk=get_random_string(40),
expiration_seconds=60*60*24, # 1 day
location=task.get_absolute_url(),
)
key.save()
return render(request, 'share.html', {"key": key})
# Utility to get stroke data in a JSON response
class JSONResponse(HttpResponse):
def __init__(self, data, **kwargs):
try:
content = JSONRenderer().render(data)
except ValueError:
content = ''
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: # pragma: no cover
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_logo_by_pk(request, *args, **kwargs): # pragma: no cover
id = kwargs['id']
return get_object_or_404(RaceLogo, pk=id)
def get_virtualevent_by_pk(request, *args, **kwargs):
id = kwargs['id']
return get_object_or_404(VirtualRace, pk=id)
def get_promember(request, *args, **kwargs): # pragma: no cover
return request.user
def get_course_by_pk(request, *args, **kwargs):
id = kwargs['id']
return get_object_or_404(GeoCourse, pk=id)
def get_workout_by_opaqueid(request, id, **kwargs):
pk = encoder.decode_hex(id)
return get_object_or_404(Workout, pk=pk)
def get_session_by_pk(request, *args, **kwargs):
id = kwargs['id']
return get_object_or_404(PlannedSession, pk=id)
def get_target_by_pk(request, *args, **kwargs):
id = kwargs['id']
return get_object_or_404(TrainingTarget, pk=id)
def get_plan_by_pk(request, *args, **kwargs):
id = kwargs['id']
return get_object_or_404(TrainingPlan, pk=id)
def get_macro_by_pk(request, *args, **kwargs): # pragma: no cover
id = kwargs['id']
return get_object_or_404(TrainingMacroCycle, pk=id)
def get_meso_by_pk(request, *args, **kwargs): # pragma: no cover
id = kwargs['id']
return get_object_or_404(TrainingMesoCycle, pk=id)
def get_micro_by_pk(request, *args, **kwargs): # pragma: no cover
id = kwargs['id']
return get_object_or_404(TrainingMicroCycle, pk=id)
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: # pragma: no cover
return reverse('workout_workflow_view', kwargs={'id': id})
def get_user_by_userid(*args, **kwargs):
request = args[0]
try:
id = kwargs['userid']
except KeyError:
id = request.user.id
if id is not None and int(id) == 0:
id = request.user.id
u = get_object_or_404(User, pk=id)
return u
def get_user_by_id(*args, **kwargs): # pragma: no cover
request = args[0]
try:
id = args[1]
except IndexError:
try:
id = kwargs['id']
except KeyError:
id = request.user.id
return get_object_or_404(User, pk=id)
def get_rower_by_id(request, id): # pragma: no cover
u = User.objects.get(id=id)
return u.rower
def getrequestrower(request, rowerid=0, userid=0, notpermanent=False):
userid = int(userid)
rowerid = int(rowerid)
# if userid == 0:
# userid = request.user.id
if notpermanent is 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)
u = r.user
elif userid != 0:
u = User.objects.get(id=userid)
r = getrower(u)
elif request.user.is_anonymous: # pragma: no cover
return None
else:
r = getrower(request.user)
u = r.user
except Rower.DoesNotExist: # pragma: no cover: # pragma: no cover
raise Http404("Rower doesn't exist")
if r.user == request.user:
request.session['rowerid'] = r.id
return r
if userid != 0 and not is_rower_team_member(request.user, u.rower):
request.session['rowerid'] = request.user.rower.id
raise PermissionDenied("You have no access to this user")
if notpermanent is False:
request.session['rowerid'] = r.id
request.session['rowerid'] = r.id
return r
def getrequestrowercoachee(request, rowerid=0, userid=0, notpermanent=False):
userid = int(userid)
rowerid = int(rowerid)
# if userid == 0:
# userid = request.user.id
if notpermanent is 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)
u = r.user
elif userid != 0:
u = User.objects.get(id=userid)
r = getrower(u)
elif request.user.is_anonymous: # pragma: no cover
return None
else:
r = getrower(request.user)
u = r.user
except Rower.DoesNotExist: # pragma: no cover: # pragma: no cover
raise Http404("Rower doesn't exist")
if r.user == request.user:
request.session['rowerid'] = r.id
return r
if userid != 0 and not is_coach_user(request.user, u): # pragma: no cover
request.session['rowerid'] = request.user.rower.id
raise PermissionDenied("You have no access to this user")
if notpermanent is False:
request.session['rowerid'] = r.id
request.session['rowerid'] = r.id
return r
def getrequestplanrower(request, rowerid=0, userid=0, notpermanent=False):
userid = int(userid)
rowerid = int(rowerid)
if notpermanent is 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:
try:
u = User.objects.get(id=userid)
except User.DoesNotExist: # pragma: no cover: # pragma: no cover
raise Http404("User does not exist")
r = getrower(u)
else:
r = getrower(request.user)
except Rower.DoesNotExist: # pragma: no cover: # pragma: no cover
raise Http404("Rower doesn't exist")
if 'shared' in request.session and request.session['shared']: # pragma: no cover
return r
if r.user != request.user and not can_plan_user(request.user, r):
request.session['rowerid'] = r.id
raise PermissionDenied("You have no access to this user")
if notpermanent is False:
request.session['rowerid'] = r.id
return r
def getrower(user):
try:
if user is None or user.is_anonymous:
return None
except AttributeError: # pragma: no cover
if User.objects.get(id=user).is_anonymous:
return None
try:
r = Rower.objects.get(user=user)
except Rower.DoesNotExist: # pragma: no cover:
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: # pragma: no cover:
raise Http404("Workout doesn't exist")
return w
def get_workoutuser(id, request):
try:
id = encoder.decode_hex(id)
w = Workout.objects.get(id=id)
except Workout.DoesNotExist: # pragma: no cover:
raise Http404("Workout doesn't exist")
if not is_workout_user(request.user, w): # pragma: no cover
raise PermissionDenied
return w
def getvalue(data): # pragma: no cover
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): # pragma: no cover
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()
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)
try:
from rest_framework_swagger.views import get_swagger_view
except ImportError: # pragma: no cover
pass
try:
from rest_framework import permissions, generics
except ImportError: # pragma: no cover
pass
USER_LANGUAGE = 'en-US'
# Define the API documentation
try:
schema_view = get_swagger_view(title='Rowsandall API')
except NameError: # pragma: no cover
pass
def remove_asynctask(request, id): # pragma: no cover
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): # pragma: no cover
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',
'check_race_course': 'Checking Course Result',
}
def get_job_status(jobid): # pragma: no cover
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'): # pragma: no cover
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): # pragma: no cover
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): # pragma: no cover
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): # pragma: no cover
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): # pragma: no cover
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
@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True)
def get_thumbnails(request, id):
row = get_workout_by_opaqueid(request, id)
r = getrower(request.user)
favorites, maxfav = getfavorites(r, row)
charts = []
charts = thumbnails_set(r, encoder.decode_hex(id), favorites)
try:
if charts[0]['script'] == '':
charts = []
except IndexError: # pragma: no cover
charts = []
return JSONResponse(charts)
def get_blog_posts(request): # pragma: no cover
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): # pragma: no cover
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 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
rowdata = rdata(csvfile=f1)
hascoordinates = 1
if rowdata != 0:
try:
latitude = rowdata.df[' latitude']
if not latitude.std(): # pragma: no cover
hascoordinates = 0
except (KeyError, AttributeError):
hascoordinates = 0
else: # pragma: no cover
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(csvfile=None, rower=rrower()):
if csvfile is None: # pragma: no cover
return 0
try:
res = rrdata(csvfile=csvfile, rower=rower)
except pd.errors.EmptyDataError: # pragma: no cover
res = 0
except (IOError, IndexError, EOFError, FileNotFoundError): # pragma: no cover
try:
res = rrdata(csvfile=csvfile+'.gz', rower=rower)
except (IOError, IndexError, EOFError, FileNotFoundError):
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: # pragma: no cover
teams1 = []
teams2 = Team.objects.filter(manager=user)
myteams = list(set(teams1).union(set(teams2)))
except TypeError:
myteams = []
return myteams
# Used for the interval editor - translates seconds to a time object
def get_time(second): # pragma: no cover
if (second <= 0) or (second > 1e9):
hours = 0
minutes = 0
sec = 0
microsecond = 0
elif math.isnan(second): # pragma: no cover
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): # pragma: no cover
return uri[len(uri)-length:]
def getidfromuri(uri): # pragma: no cover
m = re.search('/(\w.*)\/(\d+)', uri)
return m.group(2)
# 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: # pragma: no cover:
r = Rower(user=user)
r.save()
if user.is_authenticated:
if 'coach' in r.rowerplan:
return True
elif r.coachtrialexpires >= timezone.now().date():
return True
return False
# 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):
form = EmailForm()
if request.method == 'POST':
form = EmailForm(request.POST)
if form.is_valid(): # pragma: no cover
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 + ">"
d = {
'firstname': firstname,
'lastname': lastname,
'email': email,
'subject': subject,
'message': message,
}
res = send_template_email('Rowsandall <info@rowsandall.com>',
['Rowsandall <info@rowsandall.com>'],
subject,
'contactformemail.html',
d
)
if res:
messages.info(request,"Your contact form was submitted")
else:
messages.error(
request, "Something went wrong trying to send the form")
return HttpResponseRedirect('/rowers/email/thankyou/')
else:
if "captcha" in form.errors:
messages.error(request,"Bots are not welcome")
return HttpResponseRedirect(reverse("sendmail"))
return render(request,'email.html',
{
'form': form
})
def keyvalue_get_default(key, options, def_options): # pragma: no cover
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'))): # pragma: no cover
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): # pragma: no cover
try:
s = df[column]
if len(s) != aantal:
return np.zeros(aantal)
if not s.dtype in pl.NUMERIC_DTYPES:
return np.zeros(aantal)
except (KeyError, ColumnNotFoundError):
s = np.zeros(aantal)
return s