diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index 856154b3..8777367b 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -477,12 +477,11 @@ def interactive_forcecurve(theworkouts): rowdata = dataprep.getsmallrowdata_pl(columns, ids=ids, workstrokesonly=False) - rowdata = rowdata.fill_nan(None).drop_nulls() - - if rowdata.is_empty(): return "", "No Valid Data Available" + rowdata = rowdata.fill_nan(None).drop_nulls() + data_dict = rowdata.to_dicts() thresholdforce = 100. if 'x' in boattype else 200. @@ -3131,105 +3130,6 @@ def interactive_multiple_compare_chart(ids, xparam, yparam, plottype='line', return [script, div, message, errormessage] -def interactive_otw_advanced_pace_chart(id=0, promember=0): - # check if valid ID exists (workout exists) - rowdata, row = dataprep.getrowdata_db(id=id) - rowdata.dropna(axis=1, how='all', inplace=True) - rowdata.dropna(axis=0, how='any', inplace=True) - - if rowdata.empty: - return "", "No Valid Data Available" - - # 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: # pragma: no cover - TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair' - - source = ColumnDataSource( - rowdata - ) - - plot = figure(x_axis_type="datetime", y_axis_type="datetime", - tools=TOOLS, - width=920, - toolbar_sticky=False) - - # add watermark - watermarkurl = "/static/img/logo7.png" - watermarkrange = Range1d(start=0, end=1) - watermarkalpha = 0.6 - watermarkx = 0.99 - watermarky = 0.01 - watermarkw = 184 - watermarkh = 35 - watermarkanchor = 'bottom_right' - plot.extra_y_ranges = {"watermark": watermarkrange} - plot.extra_x_ranges = {"watermark": watermarkrange} - #plot.sizing_mode = 'scale_both' - - plot.image_url([watermarkurl], watermarkx, watermarky, - watermarkw, watermarkh, - global_alpha=watermarkalpha, - w_units='screen', - h_units='screen', - anchor=watermarkanchor, - dilate=True, - x_range_name="watermark", - y_range_name="watermark", - ) - - try: - plot.title.text = row.name - except ValueError: # pragma: no cover - plot.title.text = "" - #plot.title.text_font_size = value("1.2em") - plot.xaxis.axis_label = "Time" - plot.yaxis.axis_label = "Pace (/500m)" - plot.xaxis[0].formatter = DatetimeTickFormatter( - hours=["%H"], - minutes=["%M"], - seconds=["%S"], - days=["0"], - months=[""], - years=[""] - ) - plot.yaxis[0].formatter = DatetimeTickFormatter( - seconds=["%S"], - minutes=["%M"] - ) - - ymax = 1.0e3*90 - ymin = 1.0e3*210 - - plot.y_range = Range1d(ymin, ymax) - - hover = plot.select(dict(type=HoverTool)) - - plot.line('time', 'pace', source=source, - legend_label="Pace", color="black") - plot.line('time', 'nowindpace', source=source, - legend_label="Corrected Pace", color="red") - - hover.tooltips = OrderedDict([ - ('Time', '@ftime'), - ('Pace', '@fpace'), - ('Corrected Pace', '@fnowindpace'), - ('HR', '@hr{int}'), - ('SPM', '@spm{1.1}'), - ]) - - hover.mode = 'mouse' - - try: - script, div = components(plot) - except: # pragma: no cover - script = '' - div = '' - - return [script, div] - - def get_zones_report(rower, startdate, enddate, trainingzones='hr', date_agg='week', yaxis='time'): diff --git a/rowers/tests/test_urls.py b/rowers/tests/test_urls.py index af6e07e1..4e1f0f4f 100644 --- a/rowers/tests/test_urls.py +++ b/rowers/tests/test_urls.py @@ -146,7 +146,6 @@ class URLTests(TestCase): '/rowers/workout/'+encoded1+'/addtimeplot/', '/rowers/workout/'+encoded1+'/addtimeplot2/', '/rowers/workout/'+encoded1+'/comment/', - '/rowers/workout/'+encoded1+'/darkskywind/', '/rowers/workout/'+encoded1+'/data/', '/rowers/workout/'+encoded1+'/edit/', '/rowers/workout/'+encoded1+'/editintervals/', diff --git a/rowers/tests/testdata/testdata.tcx.gz b/rowers/tests/testdata/testdata.tcx.gz index 2f3d726f..e3b8adc0 100644 Binary files a/rowers/tests/testdata/testdata.tcx.gz and b/rowers/tests/testdata/testdata.tcx.gz differ diff --git a/rowers/urls.py b/rowers/urls.py index ca762683..0135688a 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -510,14 +510,6 @@ urlpatterns = [ name='workout_erase_column_view'), re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/zeropower-confirm/$', views.remove_power_confirm_view, name='remove_power_confirm_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/zeropower/$', views.remove_power_view, - name='remove_power_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/otwsetpower/$', - views.workout_otwsetpower_view, name='workout_otwsetpower_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/interactiveotwplot/$', - views.workout_otwpowerplot_view, name='workout_otwpowerplot_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/wind/$', - views.workout_wind_view, name='workout_wind_view'), re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/image/$', views.workout_uploadimage_view, name='workout_uploadimage_view'), re_path(r'^virtualevent/(?P\d+)/compare/$', @@ -540,8 +532,6 @@ urlpatterns = [ views.workout_downloadwind_view, name='workout_downloadwind_view'), re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/metar/(?P\w+)/$', views.workout_downloadmetar_view, name='workout_downloadmetar_view'), - re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/stream/$', - views.workout_stream_view, name='workout_stream_view'), re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/editintervals/$', views.workout_summary_edit_view, name='workout_summary_edit_view'), re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/restore/$', diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index e7a951ee..987c9bdb 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -2763,348 +2763,6 @@ def workout_downloadmetar_view(request, id=0, return response -# Show form to update wind data -@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) -@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_workoutuser(id, request) - r = getrower(request.user) - breadcrumbs = [ - { - 'url': '/rowers/list-workouts/', - 'name': 'Workouts' - }, - { - 'url': get_workout_default_page(request, id), - 'name': row.name - }, - { - 'url': reverse('workout_wind_view', kwargs={'id': id}), - 'name': 'Wind' - } - - ] - - # get data - f1 = row.csvfilename - u = row.user.user - r = getrower(u) - - # create bearing - rowdata = rdata(csvfile=f1) - if row == 0: # pragma: no cover - return HttpResponse("Error: CSV Data File Not Found") - - hascoordinates = 1 - try: - latitude = rowdata.df.loc[:, ' latitude'] - except KeyError: - hascoordinates = 0 - - if hascoordinates and not latitude.std(): # pragma: no cover - hascoordinates = 0 - - try: - _ = rowdata.df.loc[:, 'bearing'].values - except KeyError: - rowdata.add_bearing() - rowdata.write_csv(f1, gzip=True) - - if hascoordinates: - avglat = rowdata.df[' latitude'].mean() - avglon = rowdata.df[' longitude'].mean() - airportcode, newlat, newlon, airportdistance = get_airport_code( - avglat, avglon) - airportcode = airportcode.upper() - airportdistance = airportdistance[0] - else: - airportcode = 'UNKNOWN' - airportdistance = 0 - - if request.method == 'POST': - # process form - form = UpdateWindForm(request.POST) - - if form.is_valid(): - - vwind1 = form.cleaned_data['vwind1'] - vwind2 = form.cleaned_data['vwind2'] - dist1 = form.cleaned_data['dist1'] - dist2 = form.cleaned_data['dist2'] - winddirection1 = form.cleaned_data['winddirection1'] - winddirection2 = form.cleaned_data['winddirection2'] - windunit = form.cleaned_data['windunit'] - - rowdata.update_wind(vwind1, vwind2, - winddirection1, - winddirection2, - dist1, dist2, - units=windunit) - - rowdata.write_csv(f1, gzip=True) - - else: # pragma: no cover - message = "Invalid Form" - messages.error(request, message) - kwargs = { - 'id': id - } - url = reverse('workout_wind_view', kwargs=kwargs) - _ = HttpResponseRedirect(url) - - else: - form = UpdateWindForm() - - # create interactive plot - res = interactive_windchart(encoder.decode_hex(id), promember=1) - script = res[0] - div = res[1] - - if hascoordinates: - gmscript, gmdiv = leaflet_chart( - rowdata.df[' latitude'], - rowdata.df[' longitude'], - row.name) - else: - gmscript = "" - gmdiv = "No GPS data available" - - messages.info(request, successmessage) - messages.error(request, message) - - return render(request, - 'windedit.html', - {'workout': row, - 'rower': r, - 'breadcrumbs': breadcrumbs, - 'active': 'nav-workouts', - 'teams': get_my_teams(request.user), - 'interactiveplot': script, - 'form': form, - 'airport': airportcode, - 'airportdistance': airportdistance, - 'the_div': div, - 'gmap': gmscript, - 'gmapdiv': gmdiv}) - - -# Show form to update River stream data (for river dwellers) -@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) -@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_workoutuser(id, request) - r = getrower(request.user) - - # create interactive plot - f1 = row.csvfilename - u = row.user.user - r = getrower(u) - - rowdata = rdata(csvfile=f1) - if rowdata == 0: # pragma: no cover - messages.info(request, "Error: CSV data file not found") - url = reverse('workout_edit_view', kwargs={ - 'id': encoder.encode_hex(row.id)}) - return HttpResponseRedirect(url) - - if request.method == 'POST': - # process form - form = UpdateStreamForm(request.POST) - - if form.is_valid(): - - dist1 = form.cleaned_data['dist1'] - dist2 = form.cleaned_data['dist2'] - stream1 = form.cleaned_data['stream1'] - stream2 = form.cleaned_data['stream2'] - streamunit = form.cleaned_data['streamunit'] - - rowdata.update_stream(stream1, stream2, dist1, dist2, - units=streamunit) - - rowdata.write_csv(f1, gzip=True) - - else: # pragma: no cover - message = "Invalid Form" - messages.error(request, message) - kwargs = { - 'id': id} - url = reverse('workout_wind_view', kwargs=kwargs) - _ = HttpResponseRedirect(url) - - else: - form = UpdateStreamForm() - - # create interactive plot - res = interactive_streamchart(encoder.decode_hex(id), promember=1) - script = res[0] - div = res[1] - - breadcrumbs = [ - { - 'url': '/rowers/list-workouts/', - 'name': 'Workouts' - }, - { - 'url': get_workout_default_page(request, id), - 'name': row.name - }, - { - 'url': reverse('workout_stream_view', kwargs={'id': id}), - 'name': 'Stream' - } - - ] - - messages.info(request, successmessage) - messages.error(request, message) - return render(request, - 'streamedit.html', - {'workout': row, - 'rower': r, - 'breadcrumbs': breadcrumbs, - 'active': 'nav-workouts', - 'teams': get_my_teams(request.user), - 'interactiveplot': script, - 'form': form, - 'the_div': div}) - -# Form to set average crew weight and boat type, then run power calcs - - -@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) -@user_passes_test(ispromember, login_url="/rowers/paidplans", redirect_field_name=None) -def workout_otwsetpower_view(request, id=0, message="", successmessage=""): - w = get_workoutuser(id, request) - r = getrower(request.user) - - mayedit = 1 - - if request.method == 'POST': - # process form - form = AdvancedWorkoutForm(request.POST) - - if form.is_valid(): - boattype = form.cleaned_data['boattype'] - weightvalue = form.cleaned_data['weightvalue'] - coastalbrand = form.cleaned_data['boatbrand'] - boatclass = w.workouttype - w.boattype = boattype - w.weightvalue = weightvalue - w.boatbrand = coastalbrand - w.save() - - # load row data & create power/wind/bearing columns if not set - f1 = w.csvfilename - rowdata = rdata(csvfile=f1) - if rowdata == 0: # pragma: no cover - return HttpResponse("Error: CSV Data File Not Found") - try: - _ = rowdata.df['vstream'] - except KeyError: - rowdata.add_stream(0) - rowdata.write_csv(f1, gzip=True) - - try: - _ = rowdata.df['bearing'] - except KeyError: - rowdata.add_bearing() - rowdata.write_csv(f1, gzip=True) - - try: - _ = rowdata.df['vwind'] - except KeyError: - rowdata.add_wind(0, 0) - rowdata.write_csv(f1, gzip=True) - - # do power calculation (asynchronous) - r = w.user - u = r.user - - first_name = u.first_name - last_name = u.last_name - emailaddress = u.email - - job = myqueue(queue, - handle_otwsetpower, f1, boattype, boatclass, coastalbrand, - weightvalue, - first_name, last_name, emailaddress, encoder.decode_hex( - id), - ps=[r.p0, r.p1, r.p2, r.p3], - ratio=r.cpratio, - # quick_calc = quick_calc, - # go_service=go_service, - emailbounced=r.emailbounced - ) - - try: - request.session['async_tasks'] += [(job.id, 'otwsetpower')] - except KeyError: - request.session['async_tasks'] = [(job.id, 'otwsetpower')] - - successmessage = 'Your calculations have been submitted." \ - " You will receive an email when they are done." \ - " You can check the status of your calculations" \ - " here' - messages.info(request, successmessage) - kwargs = { - 'id': id} - - try: - url = request.session['referer'] - except KeyError: - url = reverse('workout_edit_view', kwargs=kwargs) - - response = HttpResponseRedirect(url) - return response - - else: # pragma: no cover - message = "Invalid Form" - messages.error(request, message) - kwargs = { - 'id': id} - url = reverse('workout_otwsetpower_view', kwargs=kwargs) - response = HttpResponseRedirect(url) - - else: - form = AdvancedWorkoutForm(instance=w) - - breadcrumbs = [ - { - 'url': '/rowers/list-workouts/', - 'name': 'Workouts' - }, - { - 'url': get_workout_default_page(request, id), - 'name': w.name - }, - { - 'url': reverse('workout_otwsetpower_view', kwargs={'id': id}), - 'name': 'OTW Power' - } - - ] - - messages.error(request, message) - messages.info(request, successmessage) - return render(request, - 'otwsetpower.html', - {'workout': w, - 'rower': w, - 'mayedit': mayedit, - 'active': 'nav-workouts', - 'breadcrumbs': breadcrumbs, - 'teams': get_my_teams(request.user), - 'form': form, - }) @permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True) @@ -4501,57 +4159,6 @@ def workout_flexchart_stacked_view(request, *args, **kwargs): # The interactive plot with wind corrected pace for OTW outings -def workout_otwpowerplot_view(request, id=0, message="", successmessage=""): - w = get_workout(id) - r = getrower(request.user) - - breadcrumbs = [ - { - 'url': '/rowers/list-workouts/', - 'name': 'Workouts' - }, - { - 'url': get_workout_default_page(request, id), - 'name': w.name - }, - { - 'url': reverse('workout_otwpowerplot_view', kwargs={'id': id}), - 'name': 'Interactive OTW Power Plot' - } - - ] - - # check if user is owner of this workout - - # create interactive plot - - promember = 0 - mayedit = 0 - result = request.user.is_authenticated and ispromember(request.user) - if result: - promember = 1 - if request.user == w.user.user: - mayedit = 1 - - # create interactive plot - res = interactive_otw_advanced_pace_chart( - encoder.decode_hex(id), promember=promember) - script = res[0] - div = res[1] - - messages.error(request, message) - messages.info(request, successmessage) - - return render(request, - 'otwinteractive.html', - {'workout': w, - 'rower': r, - 'active': 'nav-workouts', - 'breadcrumbs': breadcrumbs, - 'teams': get_my_teams(request.user), - 'interactiveplot': script, - 'the_div': div, - 'mayedit': mayedit}) #