Merge branch 'release/v18.6.29'
This commit is contained in:
@@ -90,7 +90,6 @@ zien welk ploeglid bij Willem III roeit en welk ploeglid bij De Hoop.</p>
|
||||
<li>Spaarne Lenterace</li>
|
||||
<li>Dutch Masters Open (alleen uitslagen van races die zonder startverschil worden geroeid)</li>
|
||||
<li>Cottwich</li>
|
||||
<li>Eemhead</li>
|
||||
<li>Tromp Boat Races</li>
|
||||
<li>Novembervieren</li>
|
||||
</ul>
|
||||
|
||||
+5
-2
@@ -1188,9 +1188,12 @@ def save_workout_database(f2, r, dosmooth=True, workouttype='rower',
|
||||
if workouttype in otetypes:
|
||||
dragfactor = row.dragfactor
|
||||
|
||||
t = datetime.datetime.strptime(duration, "%H:%M:%S.%f")
|
||||
|
||||
#t = datetime.datetime.strptime(duration, "%H:%M:%S.%f")
|
||||
|
||||
|
||||
delta = datetime.timedelta(
|
||||
hours=t.hour, minutes=t.minute, seconds=t.second)
|
||||
seconds=totaltime)
|
||||
|
||||
workoutenddatetime = workoutstartdatetime+delta
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<h1>In Stroke Metrics</h1>
|
||||
<ul class="main-content">
|
||||
<li class="grid_4">
|
||||
{% if user.rower.rowerplan == 'basic' %}
|
||||
{% if user.rower|is_basic %}
|
||||
|
||||
<p>
|
||||
This is a preview of the page with advanced functionality for Pro users.
|
||||
@@ -40,13 +40,8 @@
|
||||
|
||||
<li>
|
||||
{% if instrokemetrics %}
|
||||
{% for metric in instrokemetrics %}
|
||||
<p>
|
||||
<a href="/rowers/workout/{{ workout.id|encode }}/instroke/{{ metric }}/">{{ metric }}</a>
|
||||
</p>
|
||||
{% endfor %}
|
||||
<p>
|
||||
<a href="/rowers/workout/{{ workout.id|encode }}/instroke/interactive/">NEW: Dynamic</a>
|
||||
<a href="/rowers/workout/{{ workout.id|encode }}/instroke/interactive/">Dynamic</a>
|
||||
</p>
|
||||
{% else %}
|
||||
<p>Unfortunately, this workout doesn't have any in stroke metrics</p>
|
||||
|
||||
@@ -119,10 +119,15 @@ $( function() {
|
||||
|
||||
<h1>In Stroke Metrics</h1>
|
||||
<ul class="main-content">
|
||||
<li class="grid_4">
|
||||
<p>This is an experimental prototype. It's behavior is not guaranteed. Response may be slow.
|
||||
</li>
|
||||
<li class="grid_4" >
|
||||
{% if user.rower|is_basic %}
|
||||
|
||||
<p>
|
||||
This is a preview of the page with advanced functionality for Pro users.
|
||||
See <a href="/rowers/about/">the About page</a> for more information
|
||||
and to sign up for Pro Membership
|
||||
</p>
|
||||
{% endif %}
|
||||
<form id="instrokeform" enctype="multipart/form-data" action="" method="post">
|
||||
{% csrf_token %}
|
||||
<table>
|
||||
|
||||
@@ -334,7 +334,7 @@
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<li id="advanced-instroke">
|
||||
<a href="/rowers/workout/{{ workout.id|encode }}/instroke/">
|
||||
<a href="/rowers/workout/{{ workout.id|encode }}/instroke/interactive/">
|
||||
<i class="fas fa-search-plus fa-fw"></i>
|
||||
In-Stroke Metrics
|
||||
</a>
|
||||
|
||||
@@ -26,7 +26,7 @@ from rowers.teams import coach_getcoachees
|
||||
from rowers import credits
|
||||
from rowers import c2stuff
|
||||
from rowers.c2stuff import c2_open
|
||||
from rowers.rower_rules import is_coach_user, is_workout_user, isplanmember, ispromember
|
||||
from rowers.rower_rules import *
|
||||
from rowers.mytypes import (
|
||||
otwtypes, adaptivetypes, sexcategories, weightcategories, workouttypes,
|
||||
workouttypes_icons)
|
||||
@@ -252,8 +252,11 @@ def nogoals(user):
|
||||
|
||||
@register.filter
|
||||
def notfree(rower):
|
||||
return rower.rowerplan not in ['basic', 'freecoach']
|
||||
return user_is_not_basic(rower.user)
|
||||
|
||||
@register.filter
|
||||
def is_basic(rower):
|
||||
return not user_is_not_basic(rower.user)
|
||||
|
||||
def strfdelta(tdelta):
|
||||
minutes, seconds = divmod(tdelta.seconds, 60)
|
||||
@@ -606,12 +609,10 @@ def mayeditplan(obj, request):
|
||||
if obj.manager is not None:
|
||||
return request.user == obj.manager.user
|
||||
|
||||
rr = Rower.objects.get(user=request.user) # pragma: no cover
|
||||
if is_coach_user(request.user, obj.rower) and rr.rowerplan not in ['basic', 'pro']: # pragma: no cover
|
||||
mayedit = True
|
||||
|
||||
return mayedit # pragma: no cover
|
||||
if can_plan(request.user) and is_coach_user(request.user, obj.rower):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@register.filter
|
||||
def iterrows(df): # pragma: no cover
|
||||
@@ -744,7 +745,7 @@ def openactions(user):
|
||||
def team_rowers(user): # pragma: no cover
|
||||
try:
|
||||
therower = Rower.objects.get(user=user)
|
||||
if therower.rowerplan == 'basic':
|
||||
if not user_is_not_basic(user):
|
||||
return []
|
||||
teams = Team.objects.filter(manager=user)
|
||||
members = Rower.objects.filter(
|
||||
|
||||
@@ -5,7 +5,7 @@ from __future__ import unicode_literals
|
||||
|
||||
from .statements import *
|
||||
from rowers.mytypes import rowtypes
|
||||
from rowers.utils import allmonths,allsundays
|
||||
from rowers.utils import allmonths,allsundays, totaltime_sec_to_string
|
||||
from rowers.models import update_records
|
||||
|
||||
from django.db import transaction
|
||||
@@ -94,6 +94,25 @@ class OtherUnitTests(TestCase):
|
||||
# time zone should be US/Pacific
|
||||
self.assertTrue('US/Pacific' in str(startdate.tzinfo))
|
||||
|
||||
def test_totaltime_sec_to_string(self):
|
||||
results = (
|
||||
(0,"0:00"),
|
||||
(120,"2:00"),
|
||||
(3634.2,"1:00:34"),
|
||||
)
|
||||
|
||||
for duration, expected in results:
|
||||
self.assertEqual(totaltime_sec_to_string(duration, shorten=True), expected)
|
||||
|
||||
results = (
|
||||
(0,"00:00:00.0"),
|
||||
(120,"00:02:00.0"),
|
||||
(3634.2,"01:00:34.2"),
|
||||
)
|
||||
|
||||
for duration, expected in results:
|
||||
self.assertEqual(totaltime_sec_to_string(duration, shorten=False), expected)
|
||||
|
||||
|
||||
|
||||
def test_summaryfromsplitdata(self):
|
||||
|
||||
+46
-1
@@ -428,32 +428,77 @@ def wavg(group, avg_name, weight_name):
|
||||
except ZeroDivisionError: # pragma: no cover
|
||||
return d.mean()
|
||||
|
||||
from string import Formatter
|
||||
|
||||
def totaltime_sec_to_string(totaltime, shorten=False):
|
||||
if np.isnan(totaltime):
|
||||
return ''
|
||||
|
||||
fmt = '{H:02}:{M:02}:{S:02}.{t}'
|
||||
if shorten:
|
||||
fmt = '{H}:{M:02}:{S:02}'
|
||||
if totaltime < 3600:
|
||||
fmt = '{M}:{S:02}'
|
||||
|
||||
remainder = int(totaltime)
|
||||
tenths = totaltime-remainder
|
||||
|
||||
f = Formatter()
|
||||
desired_fields = [field_tuple[1] for field_tuple in f.parse(fmt)]
|
||||
|
||||
possible_fields = ('D','H', 'M', 'S')
|
||||
constants = {'D':86400, 'H': 3600, 'M': 60, 'S': 1}
|
||||
values = {}
|
||||
for field in possible_fields:
|
||||
if field in desired_fields and field in constants:
|
||||
values[field], remainder = divmod(remainder, constants[field])
|
||||
|
||||
values['t'] = round(10*tenths)
|
||||
|
||||
return f.format(fmt, **values)
|
||||
|
||||
|
||||
def totaltime_sec_to_string_old(totaltime, shorten=False):
|
||||
if np.isnan(totaltime):
|
||||
return ''
|
||||
hours = int(totaltime / 3600.)
|
||||
if hours > 23: # pragma: no cover
|
||||
message = 'Warning: The workout duration was longer than 23 hours. '
|
||||
hours = 23
|
||||
elif hours < 0:
|
||||
message = 'Warning: invalid workout duration'
|
||||
hours = 0
|
||||
|
||||
minutes = int((totaltime - 3600. * hours) / 60.)
|
||||
if minutes > 59: # pragma: no cover
|
||||
minutes = 59
|
||||
if not message: # pragma: no cover
|
||||
message = 'Warning: there is something wrong with the workout duration'
|
||||
elif minutes < 0:
|
||||
minutes = 0
|
||||
if not message:
|
||||
message = 'Warning: invalid workout duration'
|
||||
|
||||
seconds = int(totaltime - 3600. * hours - 60. * minutes)
|
||||
if seconds > 59: # pragma: no cover
|
||||
seconds = 59
|
||||
if not message: # pragma: no cover
|
||||
message = 'Warning: there is something wrong with the workout duration'
|
||||
elif seconds < 0:
|
||||
seconds = 0
|
||||
if not message:
|
||||
message = 'Warning: invalid workout duration'
|
||||
|
||||
tenths = int(10 * (totaltime - 3600. * hours - 60. * minutes - seconds))
|
||||
|
||||
tenths = round(10 * (totaltime - 3600. * hours - 60. * minutes - seconds))
|
||||
if tenths > 9: # pragma: no cover
|
||||
tenths = 9
|
||||
if not message: # pragma: no cover
|
||||
message = 'Warning: there is something wrong with the workout duration'
|
||||
elif tenths < 0:
|
||||
tenths = 0
|
||||
if not message:
|
||||
message = 'Warning: invalid workout duration'
|
||||
|
||||
duration = ""
|
||||
if not shorten:
|
||||
|
||||
@@ -2927,6 +2927,54 @@ def instroke_chart_interactive(request, id=0):
|
||||
|
||||
form = InstrokeForm(choices=instrokemetrics)
|
||||
|
||||
breadcrumbs = [
|
||||
{
|
||||
'url': '/rowers/list-workouts/',
|
||||
'name': 'Workouts'
|
||||
},
|
||||
{
|
||||
'url': get_workout_default_page(request, id),
|
||||
'name': w.name
|
||||
},
|
||||
{
|
||||
'url': reverse('instroke_chart_interactive', kwargs={'id': id}),
|
||||
'name': 'In-Stroke Metrics'
|
||||
}
|
||||
|
||||
]
|
||||
|
||||
if not instrokemetrics:
|
||||
script = ''
|
||||
div = 'No Instroke Metrics available for this workout'
|
||||
spm_min = 15
|
||||
spm_max = 45
|
||||
ds = ''
|
||||
dd = ''
|
||||
activeminutesmin = 0
|
||||
activeminutesmax = int(rowdata.duration/60.)
|
||||
maxminutes = activeminutesmax
|
||||
|
||||
return render(request,
|
||||
'instroke_interactive.html',
|
||||
{
|
||||
'workout': w,
|
||||
'rower': w.user,
|
||||
'active': 'nav-workouts',
|
||||
'breadcrumbs': breadcrumbs,
|
||||
'teams': get_my_teams(request.user),
|
||||
'instrokemetrics': instrokemetrics,
|
||||
'form':form,
|
||||
'the_script': script,
|
||||
'the_div': div,
|
||||
'spm_min': spm_min,
|
||||
'spm_max': spm_max,
|
||||
'ds': ds,
|
||||
'dd': dd,
|
||||
'activeminutesmin': activeminutesmin,
|
||||
'activeminutesmax': activeminutesmax,
|
||||
'maxminutes': maxminutes,
|
||||
})
|
||||
|
||||
script = ''
|
||||
div = get_call()
|
||||
|
||||
@@ -2991,23 +3039,6 @@ def instroke_chart_interactive(request, id=0):
|
||||
id), promember=1, intervaldata=intervaldata)
|
||||
|
||||
|
||||
|
||||
breadcrumbs = [
|
||||
{
|
||||
'url': '/rowers/list-workouts/',
|
||||
'name': 'Workouts'
|
||||
},
|
||||
{
|
||||
'url': get_workout_default_page(request, id),
|
||||
'name': w.name
|
||||
},
|
||||
{
|
||||
'url': reverse('instroke_view', kwargs={'id': id}),
|
||||
'name': 'In-Stroke Metrics'
|
||||
}
|
||||
|
||||
]
|
||||
|
||||
if is_ajax:
|
||||
response = json.dumps({
|
||||
'script': script,
|
||||
|
||||
Reference in New Issue
Block a user