From 39c489b9356811bcd1c0cd8f39c742c6f4b632e8 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 13 Nov 2019 18:08:54 +0100 Subject: [PATCH 01/11] flex chart for swim data --- rowers/dataprep.py | 2 ++ rowers/interactiveplots.py | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/rowers/dataprep.py b/rowers/dataprep.py index 0b50fc05..f041b8ab 100644 --- a/rowers/dataprep.py +++ b/rowers/dataprep.py @@ -1707,6 +1707,7 @@ def getrowdata_db(id=0, doclean=False, convertnewtons=True, def getsmallrowdata_db(columns, ids=[], doclean=True,workstrokesonly=True,compute=True): # prepmultipledata(ids) + if ids: csvfilenames = ['media/strokedata_{id}.parquet.gz'.format(id=id) for id in ids] else: @@ -1716,6 +1717,7 @@ def getsmallrowdata_db(columns, ids=[], doclean=True,workstrokesonly=True,comput columns = [c for c in columns if c != 'None'] columns = list(set(columns)) + if len(ids)>1: for id,f in zip(ids,csvfilenames): try: diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index c4ab7eb1..6224c108 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -3475,16 +3475,16 @@ def interactive_flex_chart2(id=0,promember=0, columns = [xparam,yparam1,yparam2, 'ftime','distance','fpace', 'power','hr','spm','driveenergy', - 'time','pace','workoutstate','time'] + 'time','pace','workoutstate'] rowdata = dataprep.getsmallrowdata_db(columns,ids=[id],doclean=True, workstrokesonly=workstrokesonly) - if rowdata.empty: + if len(rowdata)<2: rowdata = dataprep.getsmallrowdata_db(columns,ids=[id],doclean=True, workstrokesonly=False) workstrokesonly=False - if rowdata.empty: + if len(rowdata)<2: rowdata = dataprep.getsmallrowdata_db(columns,ids=[id], doclean=False, workstrokesonly=False) From 28f3941f613cfb23c8d4ea36474146adb541c9da Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Thu, 14 Nov 2019 21:56:35 +0100 Subject: [PATCH 02/11] quick and dirty OTE --- rowers/dataprep.py | 10 +- rowers/templates/embedded_video.html | 1 + rowers/templates/embedded_video_ote.html | 335 +++++++++++++++++++++++ rowers/views/workoutviews.py | 14 +- 4 files changed, 357 insertions(+), 3 deletions(-) create mode 100644 rowers/templates/embedded_video_ote.html diff --git a/rowers/dataprep.py b/rowers/dataprep.py index c9a8fd80..91d5ab86 100644 --- a/rowers/dataprep.py +++ b/rowers/dataprep.py @@ -153,7 +153,15 @@ def get_video_data(w,groups=['basic']): boatspeed = (100*df2['velo']).astype(int)/100. - coordinates = get_latlon_time(w.id) + try: + coordinates = get_latlon_time(w.id) + except KeyError: + nulseries = df['time']*0 + coordinates = pd.DataFrame({ + 'time': df['time'], + 'latitude': nulseries, + 'longitude': nulseries, + }) coordinates.set_index(pd.to_timedelta(coordinates['time'],unit='s'),inplace=True) coordinates = coordinates.resample('1s').mean().interpolate() diff --git a/rowers/templates/embedded_video.html b/rowers/templates/embedded_video.html index 19164b1b..7e172740 100644 --- a/rowers/templates/embedded_video.html +++ b/rowers/templates/embedded_video.html @@ -85,6 +85,7 @@ // 1. Code for the map {{ mapscript | safe }} + // 2. This code loads the IFrame Player API code asynchronously. var tag = document.createElement('script'); diff --git a/rowers/templates/embedded_video_ote.html b/rowers/templates/embedded_video_ote.html new file mode 100644 index 00000000..30925742 --- /dev/null +++ b/rowers/templates/embedded_video_ote.html @@ -0,0 +1,335 @@ +{% extends "newbase.html" %} +{% load staticfiles %} +{% load rowerfilters %} +{% load i18n %} +{% load leaflet_tags %} + + + +{% block title %}Workout Video{% endblock %} + +{% block meta %} +{% leaflet_js %} +{% leaflet_css %} +{% endblock %} + +{% block main %} + + + +{% language 'en' %} +

Video Analysis for {{ workout.name }}

+ +

 

+
+
    + {% if form %} +
  • + + {{ form.as_table }} +
    + {% csrf_token %} + {% if not analysis.id %} + + {% endif %} + +
  • +
  • + + {{ metricsform.as_table }} +
    +
  • + {% endif %} +
  • + {% if analysis and user.is_authenticated and user == rower.user %} +

    + Delete Analysis +

    + {% endif %} +
  • +
+
+ +{% endlanguage %} + +{% endblock %} + +{% block sidebar %} +{% include 'menu_workout.html' %} +{% endblock %} diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index 032f4564..ca7db320 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -196,9 +196,14 @@ def workout_video_create_view(request,id=0): # get data data, metrics, maxtime = dataprep.get_video_data(w,groups=metricsgroups) + hascoordinates = pd.Series(data['latitude']).std() > 0 + # create map - mapscript, mapdiv = leaflet_chart_video(data['latitude'],data['longitude'], + if hascoordinates: + mapscript, mapdiv = leaflet_chart_video(data['latitude'],data['longitude'], w.name) + else: + mapscript, mapdiv = interactive_chart(w.id,promember=1) breadcrumbs = [ { @@ -218,8 +223,13 @@ def workout_video_create_view(request,id=0): analysis = {'delay':delay} + if hascoordinates: + template = 'embedded_video.html' + else: + template = 'embedded_video_ote.html' + return render(request, - 'embedded_video.html', + template, { 'workout':w, 'rower':request.user.rower, From 4004b9a66d0246491f0244880fa037c2d14b0bd7 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Fri, 15 Nov 2019 12:19:23 +0100 Subject: [PATCH 03/11] Clearer message on user-passes-test --- rowers/braintreestuff.py | 51 ++++++++++++++++++----------------- rowers/views/analysisviews.py | 32 +++++++++++----------- rowers/views/workoutviews.py | 31 ++++++++++----------- 3 files changed, 59 insertions(+), 55 deletions(-) diff --git a/rowers/braintreestuff.py b/rowers/braintreestuff.py index ad529751..f67cc135 100644 --- a/rowers/braintreestuff.py +++ b/rowers/braintreestuff.py @@ -20,7 +20,7 @@ from rowers.tasks import ( handle_send_email_subscription_create, handle_send_email_failed_cancel, ) - + import pandas as pd from rowsandall_app.settings import ( @@ -70,7 +70,7 @@ def create_customer(rower,force=False): else: return rower.customer_id - + def get_client_token(rower): try: @@ -79,10 +79,10 @@ def get_client_token(rower): }) except ValueError: customer_id = create_customer(rower,force=True) - + client_token = gateway.client_token.generate({ "customer_id": customer_id, - }) + }) return client_token @@ -90,7 +90,7 @@ def get_plans_costs(): plans = gateway.plan.all() localplans = PaidPlan.object.filter(paymentprocessor='braintree') - + for plan in localplans: for btplan in btplans: if int(btplan.id) == plan.external_id: @@ -108,7 +108,7 @@ def make_payment(rower,data): amount = data['amount'] amount = '{amount:.f2}'.format(amount=amount) - + result = gateway.transaction.sale({ "amount": amount, "payment_method_nonce": nonce_from_the_client, @@ -123,7 +123,7 @@ def make_payment(rower,data): f = rower.user.first_name, l = rower.user.last_name, ) - + job = myqueue(queuehigh,handle_send_email_transaction, name, rower.user.email, amount) @@ -143,7 +143,7 @@ def update_subscription(rower,data,method='up'): amount = data['amount'] amount = '{amount:.2f}'.format(amount=amount) - + gatewaydata = { "price": amount, "plan_id": plan.external_id, @@ -192,7 +192,7 @@ def update_subscription(rower,data,method='up'): coachgroup.save() rower.mycoachgroup = coachgroup rower.save() - + athletes = Rower.objects.filter(coachinggroups__in=[rower.mycoachgroup]).distinct() for athlete in athletes: athlete.coachinggroups.remove(rower.mycoachgroup) @@ -217,7 +217,7 @@ def update_subscription(rower,data,method='up'): amount, result.subscription.billing_period_end_date.strftime('%Y-%m-%d'), method) - + return True,amount else: errors = result.errors.for_object("subscription") @@ -230,12 +230,12 @@ def update_subscription(rower,data,method='up'): if create_new: return create_subscription(rower,data) - + return False,0 return False,0 - - + + def create_subscription(rower,data): nonce_from_the_client = data['payment_method_nonce'] nonce = gateway.payment_method_nonce.find(nonce_from_the_client) @@ -249,8 +249,8 @@ def create_subscription(rower,data): planid = data['plan'] plan = PaidPlan.objects.get(id=planid) - - + + # create or find payment method result = gateway.payment_method.create({ "customer_id": rower.customer_id, @@ -270,6 +270,7 @@ def create_subscription(rower,data): if result.is_success: + yesterday = (timezone.now()-datetime.timedelta(days=1)).date() rower.paidplan = plan rower.planexpires = result.subscription.billing_period_end_date rower.teamplanexpires = result.subscription.billing_period_end_date @@ -277,6 +278,8 @@ def create_subscription(rower,data): rower.paymenttype = plan.paymenttype rower.rowerplan = plan.shortname rower.subscription_id = result.subscription.id + rower.protrialexpires = yesterday + rower.plantrialexpires = yesterday rower.save() name = '{f} {l}'.format( @@ -286,7 +289,7 @@ def create_subscription(rower,data): recurring = plan.paymenttype - + job = myqueue( queuehigh, handle_send_email_subscription_create, @@ -312,15 +315,15 @@ def cancel_subscription(rower,id): themessages.append("Subscription canceled") except: errormessages.append("We could not find the subscription record in our customer database. We have notified the site owner, who will contact you.") - + name = '{f} {l}'.format(f = rower.user.first_name, l = rower.user.last_name) - - + + job = myqueue(queuehigh, handle_send_email_failed_cancel, name, rower.user.email,rower.user.username,id) - + return False, themessages, errormessages basicplans = PaidPlan.objects.filter(price=0,paymentprocessor='braintree') @@ -343,7 +346,7 @@ def find_subscriptions(rower): raise ProcessorCustomerError("We could not find the customer in the database") active_subscriptions = [] - + cards = result.credit_cards for card in cards: for subscription in card.subscriptions: @@ -360,12 +363,12 @@ def find_subscriptions(rower): pass result = [] - + for subscription in active_subscriptions: plan = PaidPlan.objects.filter(paymentprocessor="braintree", external_id=subscription.plan_id)[0] - + thedict = { 'end_date': subscription.billing_period_end_date, 'plan_id': subscription.plan_id, @@ -444,7 +447,7 @@ def get_transactions(start_date,end_date): transaction.credit_card_details.country_of_issuance) statuses.append(transaction.status) - + df = pd.DataFrame({ 'name':names, 'email':emails, diff --git a/rowers/views/analysisviews.py b/rowers/views/analysisviews.py index d76f3a57..a7ea6a70 100644 --- a/rowers/views/analysisviews.py +++ b/rowers/views/analysisviews.py @@ -31,7 +31,7 @@ defaultoptions = { @user_passes_test(ispromember, login_url="/rowers/paidplans", - message="This functionality requires a Pro plan or higher", + message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) def analysis_new(request,userid=0,function='boxplot',teamid=0): r = getrequestrower(request, userid=userid) @@ -667,7 +667,7 @@ def boxplotdata(workouts,options): return(script,div) @user_passes_test(ispromember,login_url="/rowers/paidplans", - message="This functionality requires a Pro plan or higher", + message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) def analysis_view_data(request,userid=0): if not request.is_ajax(): @@ -725,7 +725,7 @@ def analysis_view_data(request,userid=0): # Histogram for a date/time range @user_passes_test(ispromember,login_url="/rowers/paidplans", - message="This functionality requires a Pro plan or higher", + message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) def histo(request,theuser=0, startdate=timezone.now()-datetime.timedelta(days=365), @@ -2148,7 +2148,7 @@ def rankings_view2(request,theuser=0, # Show ranking distances including predicted paces @user_passes_test(ispromember,login_url="/rowers/paidplans", - message="This functionality requires a Pro plan or higher", + message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) def otwrankings_view(request,theuser=0, startdate=timezone.now()-datetime.timedelta(days=365), @@ -2553,7 +2553,7 @@ def otwcp_toadmin_view(request,theuser=0, # Show ranking distances including predicted paces @user_passes_test(ispromember,login_url="/rowers/paidplans", - message="This functionality requires a Pro plan or higher", + message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) def oterankings_view(request,theuser=0, startdate=timezone.now()-datetime.timedelta(days=365), @@ -2904,7 +2904,7 @@ def oterankings_view(request,theuser=0, # Multi Flex Chart with Grouping @user_passes_test(ispromember,login_url="/rowers/paidplans", - message="This functionality requires a Pro plan or higher", + message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) def user_multiflex_select(request, startdatestring="", @@ -3110,7 +3110,7 @@ def user_multiflex_select(request, }) @user_passes_test(ispromember,login_url="/rowers/paidplans", - message="This functionality requires a Pro plan or higher", + message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) def multiflex_data(request,userid=0, options={ @@ -3363,7 +3363,7 @@ def multiflex_data(request,userid=0, @user_passes_test(ispromember,login_url="/rowers/paidplans", - message="This functionality requires a Pro plan or higher", + message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) def multiflex_view(request,userid=0, options={ @@ -3530,7 +3530,7 @@ def multiflex_view(request,userid=0, # Box plots @user_passes_test(ispromember,login_url="/rowers/paidplans", - message="This functionality requires a Pro plan or higher", + message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) def user_boxplot_select(request, startdatestring="", @@ -3738,7 +3738,7 @@ def user_boxplot_select(request, }) @user_passes_test(ispromember,login_url="/rowers/paidplans", - message="This functionality requires a Pro plan or higher", + message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) def boxplot_view_data(request,userid=0, options={ @@ -3855,7 +3855,7 @@ def boxplot_view_data(request,userid=0, }) @user_passes_test(ispromember,login_url="/rowers/paidplans", - message="This functionality requires a Pro plan or higher", + message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) def boxplot_view(request,userid=0, options={ @@ -3974,7 +3974,7 @@ def boxplot_view(request,userid=0, # Cumulative stats page -@user_passes_test(ispromember,login_url="/rowers/paidplans",message="This functionality requires a Pro plan or higher",redirect_field_name=None) +@user_passes_test(ispromember,login_url="/rowers/paidplans",message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",redirect_field_name=None) def cumstats(request,theuser=0, startdate=timezone.now()-datetime.timedelta(days=30), enddate=timezone.now(), @@ -4323,7 +4323,7 @@ def agegrouprecordview(request,sex='male',weightcategory='hwt', # alert overview view @user_passes_test(ispromember, login_url="/rowers/paidplans", - message="This functionality requires a Pro plan or higher", + message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) def alerts_view(request,userid=0): r = getrequestrower(request,userid=userid) @@ -4357,7 +4357,7 @@ def alerts_view(request,userid=0): # alert create view @user_passes_test(ispromember, login_url="/rowers/paidplans", - message="This functionality requires a Pro plan or higher", + message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) def alert_create_view(request,userid=0): r = getrequestrower(request,userid=userid) @@ -4437,7 +4437,7 @@ def alert_create_view(request,userid=0): # alert report view @user_passes_test(ispromember, login_url="/rowers/paidplans", - message="This functionality requires a Pro plan or higher", + message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) def alert_report_view(request,id=0,userid=0,nperiod=0): r = getrequestrower(request,userid=userid) @@ -4494,7 +4494,7 @@ def alert_report_view(request,id=0,userid=0,nperiod=0): # alert edit view @user_passes_test(ispromember, login_url="/rowers/paidplans", - message="This functionality requires a Pro plan or higher", + message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) def alert_edit_view(request,id=0,userid=0): r = getrequestrower(request,userid=userid) diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index 19d8d8c6..7f06aa68 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -8,7 +8,7 @@ import rowers.teams as teams # Show the EMpower Oarlock generated Stroke Profile @user_passes_test(ispromember,login_url="/rowers/paidplans/", - message="This functionality requires a Pro plan or higher", + message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) def workout_forcecurve_view(request,id=0,workstrokesonly=False): row = get_workout(id) @@ -336,8 +336,9 @@ def fitness_metric_view(request,mode='rower',days=42): return HttpResponse("job queued") +@login_required() @user_passes_test(ispromember,login_url="/rowers/paidplans", - message="This functionality requires a Pro plan or higher", + message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) def workout_update_cp_view(request,id=0): row = get_workout(id) @@ -400,7 +401,7 @@ def workout_recalcsummary_view(request,id=0): # Joining workout @user_passes_test(ispromember,login_url="/rowers/paidplans", - message="This functionality requires a Pro plan or higher", + message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) def workouts_join_view(request): promember=0 @@ -446,7 +447,7 @@ def workouts_join_view(request): return HttpResponseRedirect(url) @user_passes_test(ispromember,login_url="/rowers/paidplans", - message="This functionality requires a Pro plan or higher", + message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) def workouts_join_select(request, startdatestring="", @@ -604,7 +605,7 @@ def workouts_join_select(request, # Team comparison @user_passes_test(ispromember,login_url='/rowers/paidplans/', - message="This functionality requires a Pro plan or higher", + message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) def team_comparison_select(request, startdatestring="", @@ -1387,7 +1388,7 @@ def workouts_view(request,message='',successmessage='', # List of workouts to compare a selected workout to @user_passes_test(ispromember,login_url="/rowers/paidplans", - message="This functionality requires a Pro plan or higher", + message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) def workout_fusion_list(request,id=0,message='',successmessage='', startdatestring="",enddatestring="", @@ -1593,7 +1594,7 @@ def workout_view(request,id=0): # Resets stroke data to raw data (pace) @user_passes_test(ispromember,login_url="/rowers/paidplans", - message="This functionality requires a Pro plan or higher", + message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) def workout_undo_smoothenpace_view( request,id=0,message="",successmessage="" @@ -1632,7 +1633,7 @@ def workout_undo_smoothenpace_view( # Data smoothing of pace data @user_passes_test(ispromember,login_url="/rowers/paidplans", - message="This functionality requires a Pro plan or higher", + message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) def workout_smoothenpace_view(request,id=0,message="",successmessage=""): row = get_workout(id) @@ -1685,7 +1686,7 @@ def workout_smoothenpace_view(request,id=0,message="",successmessage=""): # Process CrewNerd Summary CSV and update summary @user_passes_test(ispromember,login_url="/rowers/paidplans", - message="This functionality requires a Pro plan or higher", + message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) def workout_crewnerd_summary_view(request,id=0,message="",successmessage=""): row = get_workout(id) @@ -1762,7 +1763,7 @@ def workout_crewnerd_summary_view(request,id=0,message="",successmessage=""): # Get weather for given location and date/time @user_passes_test(ispromember,login_url="/rowers/paidplans", - message="This functionality requires a Pro plan or higher", + message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) def workout_downloadwind_view(request,id=0, airportcode=None, @@ -1836,7 +1837,7 @@ def workout_downloadwind_view(request,id=0, return response # Get weather for given location and date/time -@user_passes_test(ispromember,login_url="/rowers/paidplans",message="This functionality requires a Pro plan or higher",redirect_field_name=None) +@user_passes_test(ispromember,login_url="/rowers/paidplans",message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",redirect_field_name=None) def workout_downloadmetar_view(request,id=0, airportcode=None, message="",successmessage=""): @@ -1911,7 +1912,7 @@ def workout_downloadmetar_view(request,id=0, # Show form to update wind data -@user_passes_test(ispromember,login_url="/rowers/paidplans",message="This functionality requires a Pro plan or higher",redirect_field_name=None) +@user_passes_test(ispromember,login_url="/rowers/paidplans",message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",redirect_field_name=None) def workout_wind_view(request,id=0,message="",successmessage=""): row = get_workout(id) r = getrower(request.user) @@ -2047,7 +2048,7 @@ def workout_wind_view(request,id=0,message="",successmessage=""): # Show form to update River stream data (for river dwellers) -@user_passes_test(ispromember,login_url="/rowers/paidplans",message="This functionality requires a Pro plan or higher",redirect_field_name=None) +@user_passes_test(ispromember,login_url="/rowers/paidplans",message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",redirect_field_name=None) def workout_stream_view(request,id=0,message="",successmessage=""): row = get_workout(id) r = getrower(request.user) @@ -4628,7 +4629,7 @@ def workout_summary_restore_view(request,id,message="",successmessage=""): # Split a workout @user_passes_test(ispromember,login_url="/rowers/paidplans", - message="This functionality requires a Pro plan or higher", + message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality", redirect_field_name=None) def workout_split_view(request,id=0): row = get_workout_permitted(request.user,id) @@ -4724,7 +4725,7 @@ def workout_split_view(request,id=0): # Fuse two workouts -@user_passes_test(ispromember,login_url="/rowers/paidplans",message="This functionality requires a Pro plan or higher",redirect_field_name=None) +@user_passes_test(ispromember,login_url="/rowers/paidplans",message="This functionality requires a Pro plan or higher. If you are already a Pro user, please log in to access this functionality",redirect_field_name=None) def workout_fusion_view(request,id1=0,id2=1): try: From 40e1d2aafc2a80cc63a57227b47634384ecf7a0e Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Fri, 15 Nov 2019 12:55:37 +0100 Subject: [PATCH 04/11] bug fix --- rowers/dataprepnodjango.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/rowers/dataprepnodjango.py b/rowers/dataprepnodjango.py index bdd1f162..ab1f8506 100644 --- a/rowers/dataprepnodjango.py +++ b/rowers/dataprepnodjango.py @@ -726,7 +726,10 @@ def getsmallrowdata_db(columns,ids=[],debug=False): except ValueError: df = pd.DataFrame() else: - df = pd.read_parquet(csvfilenames[0],columns=columns,engine='pyarrow') + try: + df = pd.read_parquet(csvfilenames[0],columns=columns,engine='pyarrow') + except (OSError,IndexError): + pass return df @@ -1257,7 +1260,7 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True, data.fillna(0,inplace=True) for k, v in dtypes.items(): try: - data[k] = data[k].astype(v) + data[k] = data[k].astype(v) except KeyError: pass From 93859fe1f3ba02bc53eb4bb6b83c0ff33e6eb572 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sat, 16 Nov 2019 15:19:22 +0100 Subject: [PATCH 05/11] bug fix interactive chart --- rowers/interactiveplots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index 6224c108..2399bc51 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -2693,7 +2693,7 @@ def interactive_chart(id=0,promember=0,intervaldata = {}): TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair' - columns = ['time','pace','hr','fpace','ftime'] + columns = ['time','pace','hr','fpace','ftime','spm'] datadf = dataprep.getsmallrowdata_db(columns,ids=[id]) datadf.dropna(axis=0,how='any',inplace=True) From 465527d2118b1ad3a21828089314aa834373c48c Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sat, 16 Nov 2019 16:07:18 +0100 Subject: [PATCH 06/11] trying to get slider to work --- rowers/interactiveplots.py | 37 +++++++++++++++++-- rowers/templates/embedded_video_ote.html | 47 ++---------------------- rowers/views/workoutviews.py | 6 ++- 3 files changed, 43 insertions(+), 47 deletions(-) diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index 1c1ef0b4..d00c8a53 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -2806,13 +2806,16 @@ def interactive_streamchart(id=0,promember=0): return [script,div] -def interactive_chart(id=0,promember=0,intervaldata = {}): +def interactive_chart(id=0,promember=0,intervaldata = {}, timepos = None): # Add hover to this comma-separated string and see what changes if (promember==1): TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair' else: TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair' + if timepos: + TOOLS = '' + columns = ['time','pace','hr','fpace','ftime','spm'] datadf = dataprep.getsmallrowdata_db(columns,ids=[id]) @@ -2931,6 +2934,32 @@ def interactive_chart(id=0,promember=0,intervaldata = {}): right='time_r',source=intervalsource,color='mediumvioletred', y_range_name='spmax',fill_alpha=0.2,line_alpha=0.2) + if timepos: + timepos = datetime.timedelta(seconds=timepos) + timeline = ColumnDataSource( + data=dict( + x = [timepos,timepos], + y = [10,100] + )) + plot.line('x','y',source=timeline,y_range_name='spmax') + + callback = CustomJS(args = dict(timeline=timeline), + code=""" + var data = timeline.data + var x = data['x'] + var y = data['y'] + var sliderpos = cb_obj.value + data['x'] = [sliderpos,sliderpos] + timeline.change.emit(); + """ + ) + timemax = int(datadf['time'].max()/1000.) + slider = Slider(start=0,end=100,step=1.0,callback=callback) + slider.js_on_change('value',callback) + layout = layoutcolumn([plot,slider]) + else: + layout = plot + hover = plot.select(dict(type=HoverTool)) @@ -2945,9 +2974,11 @@ def interactive_chart(id=0,promember=0,intervaldata = {}): hover.mode = 'mouse' hover.names = ["spm","pace"] - script, div = components(plot) + script, div = components(layout) + js_resources = INLINE.render_js() + css_resources = INLINE.render_css() - return [script,div] + return [script,div,js_resources,css_resources] def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='', ploterrorbars=False, diff --git a/rowers/templates/embedded_video_ote.html b/rowers/templates/embedded_video_ote.html index 30925742..64ca6a79 100644 --- a/rowers/templates/embedded_video_ote.html +++ b/rowers/templates/embedded_video_ote.html @@ -15,46 +15,8 @@ {% block main %} - +{{ js_res|safe }} +{{ css_res|safe }} {% language 'en' %}

Video Analysis for {{ workout.name }}

@@ -75,15 +37,14 @@ {{ mapdiv | safe}} -
- +
  • + - {{ mapscript | safe }} + {% endblock %} {% block main %} @@ -75,8 +77,9 @@ {{ mapdiv | safe}}
  • -
    - +
    +
  • diff --git a/rowers/templates/embedded_video_ote.html b/rowers/templates/embedded_video_ote.html deleted file mode 100644 index 64ca6a79..00000000 --- a/rowers/templates/embedded_video_ote.html +++ /dev/null @@ -1,296 +0,0 @@ -{% extends "newbase.html" %} -{% load staticfiles %} -{% load rowerfilters %} -{% load i18n %} -{% load leaflet_tags %} - - - -{% block title %}Workout Video{% endblock %} - -{% block meta %} -{% leaflet_js %} -{% leaflet_css %} -{% endblock %} - -{% block main %} - -{{ js_res|safe }} -{{ css_res|safe }} - -{% language 'en' %} -

    Video Analysis for {{ workout.name }}

    -
      - {% if analysis and user.is_authenticated and user == rower.user and not locked %} -
    • -

      Paste link to you tube video below

      -

      Use the slider to locate start point for video on workout map

      -

      Playing the video will advance the data in synchonization. Use the regular youtube controls - to move around in the video and play it.

      -

      You can make manual adjustments to the delay to fine tune the alignment. - Once you are finished, check "Lock Video and Data" to lock the video and the data.

      -

      Once you are happy with the alignment, you can save the analysis, and share with other people.

      -
    • - {% endif %} -
    • -
      - {{ mapdiv | safe}} - -
      -
      -
      -
    • -
    • -
      - - - - {{ mapscript | safe }} - - -
    • - {% if analysis and user.is_authenticated and user == rower.user %} -
    • - Lock Data and Video -
    • - {% endif %} -
    • - Data Time - - seconds -
    • -
    • - Video Time - - seconds -
    • - {% for id, metric in metrics.items %} -
    • - {{ metric.name }} - - {{ metric.unit }} -
    • - {% endfor %} - -
    -

     

    -
    -
      - {% if form %} -
    • - - {{ form.as_table }} -
      - {% csrf_token %} - {% if not analysis.id %} - - {% endif %} - -
    • -
    • - - {{ metricsform.as_table }} -
      -
    • - {% endif %} -
    • - {% if analysis and user.is_authenticated and user == rower.user %} -

      - Delete Analysis -

      - {% endif %} -
    • -
    -
    - -{% endlanguage %} - -{% endblock %} - -{% block sidebar %} -{% include 'menu_workout.html' %} -{% endblock %} diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index ba4c2054..ae3d25a3 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -202,10 +202,8 @@ def workout_video_create_view(request,id=0): if hascoordinates: mapscript, mapdiv = leaflet_chart_video(data['latitude'],data['longitude'], w.name) - js_res = '' - css_res = '' else: - mapscript, mapdiv,js_res,css_res = interactive_chart(w.id,promember=1,timepos=78) + mapscript, mapdiv = interactive_chart_video(w.id) breadcrumbs = [ { @@ -225,10 +223,7 @@ def workout_video_create_view(request,id=0): analysis = {'delay':delay} - if hascoordinates: - template = 'embedded_video.html' - else: - template = 'embedded_video_ote.html' + template = 'embedded_video.html' return render(request, template, @@ -246,8 +241,6 @@ def workout_video_create_view(request,id=0): 'maxtime':maxtime, 'metrics':metrics, 'locked': False, - 'js_res': js_res, - 'css_res': css_res }) # Show the EMpower Oarlock generated Stroke Profile From cf9ea7d05846accb52b9730878228d85d41e058f Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sun, 17 Nov 2019 14:49:34 +0100 Subject: [PATCH 09/11] some small additions --- rowers/interactiveplots.py | 24 ++++++++++++++++++------ rowers/templates/embedded_video.html | 4 ++-- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index b8cd6aea..5115779e 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -2971,6 +2971,20 @@ def interactive_chart_video(id=0): except KeyError: datadf['pace'] = 0 + time = datadf['time'] + + data = zip(time.tolist(),spm.tolist()) + + data2 = [] + + for time,spm in data: + data2.append( + { + 'x':time/1000., + 'y':spm, + } + ) + div = """ @@ -2978,19 +2992,17 @@ def interactive_chart_video(id=0): script = """ var ctx = document.getElementById("myChart").getContext('2d'); - var data = { - x: [1,2,3,4], - y: [1,4,9,16] - } + var data = %s var myChart = new Chart(ctx, { - type: 'line', + type: 'scatter', + label: 'pace', data: { datasets: [{ data: data, }] }, }); - """ + """ % data2 return [script,div] diff --git a/rowers/templates/embedded_video.html b/rowers/templates/embedded_video.html index a4ec93b3..7a276676 100644 --- a/rowers/templates/embedded_video.html +++ b/rowers/templates/embedded_video.html @@ -73,11 +73,11 @@
  • {% endif %}
  • -
    +
    {{ mapdiv | safe}}
    -
    +
    From 797cc9ee403d799155eb0ddd63418e55a5d9b54b Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sun, 17 Nov 2019 15:45:30 +0100 Subject: [PATCH 10/11] bla --- rowers/interactiveplots.py | 42 ++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index 5115779e..174a7aaf 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -2993,14 +2993,48 @@ def interactive_chart_video(id=0): script = """ var ctx = document.getElementById("myChart").getContext('2d'); var data = %s - var myChart = new Chart(ctx, { + var marker = { + datapoint: { + 'x': 0, + 'y': 0, + 'r': 10, + }, + setLatLng: function (LatLng) { + var lat = LatLng.lat; + var lng = LatLng.lng; + this.datapoint = { + 'x': lat, + 'y': lng, + 'r': 10, + } + console.log(this.datapoint); + } + } + marker.setLatLng({ + 'lat': data[0]['x'], + 'lng': data[0]['y'] + }) + console.log(marker.datapoint); + var myChart = new Chart(ctx, { type: 'scatter', label: 'pace', - data: { - datasets: [{ + data: + { + datasets: [ + { + type: 'bubble', + label: 'now', + data: [marker.datapoint], + backgroundColor: '#36a2eb', + }, + { + label: 'spm', data: data, - }] + }, + + ] }, + }); """ % data2 From 1393e5449ce55b9231188a89096383fb8a855169 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sun, 17 Nov 2019 16:41:38 +0100 Subject: [PATCH 11/11] working prototype Chart.js --- rowers/interactiveplots.py | 83 ++++++++++++---------------- rowers/templates/embedded_video.html | 1 + rowers/views/workoutviews.py | 6 +- 3 files changed, 41 insertions(+), 49 deletions(-) diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index 174a7aaf..5224ea7a 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -2949,42 +2949,31 @@ def interactive_chart(id=0,promember=0,intervaldata = {}): return [script,div] -def interactive_chart_video(id=0): +def interactive_chart_video(videodata): - columns = ['time','pace','hr','fpace','ftime','spm'] - datadf = dataprep.getsmallrowdata_db(columns,ids=[id]) - - datadf.dropna(axis=0,how='any',inplace=True) - - row = Workout.objects.get(id=id) - if datadf.empty: - return "","No Valid Data Available" + spm = videodata['spm'] + time = range(len(spm)) - try: - spm = datadf['spm'] - except KeyError: - datadf['spm'] = 0 - try: - pace = datadf['pace'] - except KeyError: - datadf['pace'] = 0 - - time = datadf['time'] - - data = zip(time.tolist(),spm.tolist()) + data = zip(time,spm) data2 = [] - for time,spm in data: + for t,s in data: data2.append( { - 'x':time/1000., - 'y':spm, + 'x':t, + 'y':s, } ) + markerpoint = { + 'x': time[0], + 'y': spm[0], + 'r': 10, + } + div = """ @@ -2993,28 +2982,7 @@ def interactive_chart_video(id=0): script = """ var ctx = document.getElementById("myChart").getContext('2d'); var data = %s - var marker = { - datapoint: { - 'x': 0, - 'y': 0, - 'r': 10, - }, - setLatLng: function (LatLng) { - var lat = LatLng.lat; - var lng = LatLng.lng; - this.datapoint = { - 'x': lat, - 'y': lng, - 'r': 10, - } - console.log(this.datapoint); - } - } - marker.setLatLng({ - 'lat': data[0]['x'], - 'lng': data[0]['y'] - }) - console.log(marker.datapoint); + var myChart = new Chart(ctx, { type: 'scatter', label: 'pace', @@ -3024,7 +2992,7 @@ def interactive_chart_video(id=0): { type: 'bubble', label: 'now', - data: [marker.datapoint], + data: [ %s ], backgroundColor: '#36a2eb', }, { @@ -3036,7 +3004,26 @@ def interactive_chart_video(id=0): }, }); - """ % data2 + var marker = { + datapoint: %s , + setLatLng: function (LatLng) { + var lat = LatLng.lat; + var lng = LatLng.lng; + this.datapoint = { + 'x': lat, + 'y': lng, + 'r': 10, + } + myChart.data.datasets[0].data[0] = this.datapoint; + myChart.update(); + } + } + marker.setLatLng({ + 'lat': data[0]['x'], + 'lng': data[0]['y'] + }) + + """ % (data2, markerpoint, markerpoint) return [script,div] diff --git a/rowers/templates/embedded_video.html b/rowers/templates/embedded_video.html index 7a276676..4a28416d 100644 --- a/rowers/templates/embedded_video.html +++ b/rowers/templates/embedded_video.html @@ -158,6 +158,7 @@ {% endfor %} // gauge.set(catch_now); var newLatLng = new L.LatLng(lat, lon); + console.log(newLatLng); marker.setLatLng(newLatLng); } if(videotime !== oldTime) { diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index ae3d25a3..efee957e 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -203,7 +203,9 @@ def workout_video_create_view(request,id=0): mapscript, mapdiv = leaflet_chart_video(data['latitude'],data['longitude'], w.name) else: - mapscript, mapdiv = interactive_chart_video(w.id) + mapscript, mapdiv = interactive_chart_video(data) + data['longitude'] = data['spm'] + data['latitude'] = list(range(len(data['spm']))) breadcrumbs = [ { @@ -225,6 +227,8 @@ def workout_video_create_view(request,id=0): template = 'embedded_video.html' + print(data['latitude'],'aap') + return render(request, template, {