verbeteringen
This commit is contained in:
@@ -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'):
|
||||
|
||||
|
||||
@@ -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/',
|
||||
|
||||
BIN
rowers/tests/testdata/testdata.tcx.gz
vendored
BIN
rowers/tests/testdata/testdata.tcx.gz
vendored
Binary file not shown.
@@ -510,14 +510,6 @@ urlpatterns = [
|
||||
name='workout_erase_column_view'),
|
||||
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/zeropower-confirm/$', views.remove_power_confirm_view,
|
||||
name='remove_power_confirm_view'),
|
||||
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/zeropower/$', views.remove_power_view,
|
||||
name='remove_power_view'),
|
||||
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/otwsetpower/$',
|
||||
views.workout_otwsetpower_view, name='workout_otwsetpower_view'),
|
||||
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/interactiveotwplot/$',
|
||||
views.workout_otwpowerplot_view, name='workout_otwpowerplot_view'),
|
||||
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/wind/$',
|
||||
views.workout_wind_view, name='workout_wind_view'),
|
||||
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/image/$',
|
||||
views.workout_uploadimage_view, name='workout_uploadimage_view'),
|
||||
re_path(r'^virtualevent/(?P<id>\d+)/compare/$',
|
||||
@@ -540,8 +532,6 @@ urlpatterns = [
|
||||
views.workout_downloadwind_view, name='workout_downloadwind_view'),
|
||||
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/metar/(?P<airportcode>\w+)/$',
|
||||
views.workout_downloadmetar_view, name='workout_downloadmetar_view'),
|
||||
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/stream/$',
|
||||
views.workout_stream_view, name='workout_stream_view'),
|
||||
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/editintervals/$', views.workout_summary_edit_view,
|
||||
name='workout_summary_edit_view'),
|
||||
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/restore/$',
|
||||
|
||||
@@ -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" \
|
||||
" <a href="/rowers/jobs-status/" target="_blank">here</a>'
|
||||
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})
|
||||
|
||||
|
||||
#
|
||||
|
||||
Reference in New Issue
Block a user