1344 lines
40 KiB
Python
1344 lines
40 KiB
Python
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.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
|
||
)
|
||
|
||
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 (
|
||
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
|
||
)
|
||
|
||
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,
|
||
)
|
||
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, )
|
||
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,
|
||
)
|
||
|
||
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,
|
||
)
|
||
|
||
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 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 doesn’t 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
|