Private
Public Access
1
0

Merge branch 'release/v10.83'

This commit is contained in:
Sander Roosendaal
2020-01-06 21:14:09 +01:00
14 changed files with 175 additions and 60 deletions

View File

@@ -1,7 +0,0 @@
#!/bin/bash
source /srv/venv/bin/activate
cd /srv/app
python manage.py migrate
python manage.py collectstatic --noinput
python manage.py runserver

View File

@@ -18,6 +18,7 @@ from iso8601 import ParseError
import numpy import numpy
import json import json
from json.decoder import JSONDecodeError
from rowsandall_app.settings import ( from rowsandall_app.settings import (
C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET

View File

@@ -1782,6 +1782,11 @@ def delete_strokedata(id):
dirname = 'media/strokedata_{id}.parquet.gz'.format(id=id) dirname = 'media/strokedata_{id}.parquet.gz'.format(id=id)
try: try:
shutil.rmtree(dirname) shutil.rmtree(dirname)
except OSError:
try:
os.remove(dirname)
except FileNotFoundError:
pass
except FileNotFoundError: except FileNotFoundError:
pass pass

View File

@@ -45,7 +45,7 @@ class Command(BaseCommand):
# explanatorytexts # explanatorytexts
othertexts = [alert.description()] othertexts = [alert.description()]
# send email # send email
job = myqueue(queue,handle_send_email_alert, job = myqueue(queue,handle_send_email_alert,
alert.manager.email, alert.manager.email,
@@ -58,11 +58,11 @@ class Command(BaseCommand):
# advance next_run # advance next_run
if not testing: if not testing:
alert.next_run = datetime.date.today() + datetime.timedelta(days=alert.period) alert.next_run = datetime.date.today() + datetime.timedelta(days=alert.period-1)
alert.save() alert.save()
if testing: if testing:
print('{nr} alerts found'.format(nr = len(todaysalerts))) print('{nr} alerts found'.format(nr = len(todaysalerts)))
self.stdout.write(self.style.SUCCESS( self.stdout.write(self.style.SUCCESS(
'Successfully processed alerts')) 'Successfully processed alerts'))

View File

@@ -706,6 +706,7 @@ def get_team(request):
def get_dates_timeperiod(request,startdatestring='',enddatestring='', def get_dates_timeperiod(request,startdatestring='',enddatestring='',
defaulttimeperiod='thisweek'): defaulttimeperiod='thisweek'):
# set start end date according timeperiod # set start end date according timeperiod
# should always return datetime.date
timeperiod = request.GET.get('when') timeperiod = request.GET.get('when')
@@ -720,8 +721,8 @@ def get_dates_timeperiod(request,startdatestring='',enddatestring='',
startdate = dt.datetime.strptime(startdatestring,'%Y-%m-%d').date() startdate = dt.datetime.strptime(startdatestring,'%Y-%m-%d').date()
enddate = dt.datetime.strptime(enddatestring,'%Y-%m-%d').date() enddate = dt.datetime.strptime(enddatestring,'%Y-%m-%d').date()
except ValueError: except ValueError:
startdate = parser.parse(startdatestring,fuzzy=True) startdate = parser.parse(startdatestring,fuzzy=True).date()
enddate = parser.parse(enddatestring, fuzzy=True) enddate = parser.parse(enddatestring, fuzzy=True).date()
return startdate,enddate return startdate,enddate

View File

@@ -47,7 +47,7 @@
function copyText() { function copyText() {
var tempInput = document.createElement("input"); var tempInput = document.createElement("input");
tempInput.style = "position: absolute; left: -1000px; top: -1000px"; tempInput.style = "position: absolute; left: -1000px; top: -1000px";
tempInput.value = "{{ siteurl }}/rowers/video/{{ analysis.id|encode }}/"; tempInput.value = "{{ siteurl }}/rowers/video/{{ analysis.id|encode }}/m/";
document.body.appendChild(tempInput); document.body.appendChild(tempInput);
tempInput.select(); tempInput.select();
document.execCommand("copy"); document.execCommand("copy");
@@ -108,19 +108,22 @@ function copyText() {
<li class="grid_4"> <li class="grid_4">
<p>Playing the video will advance the data in synchronization. Use the regular <p>Playing the video will advance the data in synchronization. Use the regular
YouTube controls to move around in the video and play it.</p> YouTube controls to move around in the video and play it.</p>
<p>In this reduced view, you cannot edit the video analysis. There is a separate
view for that <a href="{{ siteurl }}/rowers/video/{{ analysis.id|encode }}">here</a>.
</p>
</li> </li>
<li class="grid_4"> <li class="grid_4">
<span class="fb-share-button" style="height:20px; vertical-align: top;" <span class="fb-share-button" style="height:20px; vertical-align: top;"
data-href="{{ siteurl }}/rowers/video/{{ analysis.id|encode }}" data-href="{{ siteurl }}/rowers/video/{{ analysis.id|encode }}/m/"
data-layout="button" data-size="small" data-mobile-iframe="false"> data-layout="button" data-size="small" data-mobile-iframe="false">
<a class="fb-xfbml-parse-ignore" target="_blank" <a class="fb-xfbml-parse-ignore" target="_blank"
href="https://www.facebook.com/sharer/sharer.php?u=https://rowsandall.com/rowers/video/{{ analysis.id|encode }}/"> href="https://www.facebook.com/sharer/sharer.php?u=https://rowsandall.com/rowers/video/{{ analysis.id|encode }}/m/">
Share</a> Share</a>
</span> </span>
<span> <span>
<a class="twitter-share-button" <a class="twitter-share-button"
href="https://twitter.com/intent/tweet" href="https://twitter.com/intent/tweet"
data-url="{{ siteurl }}/rowers/video/{{ analysis.id|encode }}/" data-url="{{ siteurl }}/rowers/video/{{ analysis.id|encode }}/m/"
data-text="@rowsandall #rowingdata">Tweet</a> data-text="@rowsandall #rowingdata">Tweet</a>
</span> </span>
<span> <span>

View File

@@ -7,6 +7,17 @@
Thank you. We have received the payment of &euro; {{ amount }} for Rowsandall related services. Thank you. We have received the payment of &euro; {{ amount }} for Rowsandall related services.
</p> </p>
<p>
With your contribution, you are helping to keep this service to the rowing community
running. As the founder and developer of the Rowsandall.com site I am very interested
to know what you think of Rowsandall.com, as well as suggestions for improvements.
</p>
<p>
So don't hesitate to respond to this email and let me know. I will read and respond to each
email.
</p>
<p> <p>
Please contact our customer service by replying to this email if you have any further Please contact our customer service by replying to this email if you have any further
questions regarding the payment. questions regarding the payment.
@@ -16,4 +27,3 @@
Best Regards, the Rowsandall Team Best Regards, the Rowsandall Team
</p> </p>
{% endblock %} {% endblock %}

View File

@@ -0,0 +1,31 @@
{% extends "emailbase.html" %}
{% block body %}
<p>Dear <strong>{{ first_name }}</strong>,</p>
<p>
Welcome on the trial for the Self-Coach plan.
</p>
<p>
As the developer of the Rowsandall.com site I am very interested to know what you think of Rowsandall.com. Especially, I'd like to understand how you started using the site, what you are looking for, and what you think could be improved.
</p>
<p>
The Self-Coach functionality allows you to create training plans and sessions for you and your training group. Feel free to contact me any time you need help.
</p>
<p>
I'd also love to hear a bit about your rowing background.
</p>
<p>
So don't hesitate to respond to this email and let me know. I will read and respond to each
email.
</p>
<p>
Thank you very much for your help and for supporting <a href="rowsandall.com">rowsandall.com</a>.
</p>
<p>
Best Regards, the Rowsandall Team
</p>
{% endblock %}

View File

@@ -0,0 +1,33 @@
{% extends "emailbase.html" %}
{% block body %}
<p>Dear <strong>{{ first_name }}</strong>,</p>
<p>
Welcome on the trial for the Pro plan.
</p>
<p>
As the developer of the Rowsandall.com site I am very interested to know
what you think of Rowsandall.com.
Especially, I'd like to understand how you started using the site,
what you are looking for, and what you think could be improved.
</p>
<p>
I'd also love to hear a bit about your rowing background.
</p>
<p>
So don't hesitate to respond to this email and let me know. I will read and respond to each
email.
</p>
<p>
Thank you very much for your help and for supporting <a href="rowsandall.com">rowsandall.com</a>.
</p>
<p>
Best Regards, the Rowsandall Team
</p>
{% endblock %}

View File

@@ -5,22 +5,34 @@
<p> <p>
Thank you. We have received the payment of &euro; {{ amount }} for Thank you. We have received the payment of &euro; {{ amount }} for
your updated Rowsandall subscription. your updated Rowsandall subscription.
You are now on the Rowsandall paid plan "{{ planname }}". You are now on the Rowsandall paid plan "{{ planname }}".
</p> </p>
<p>
With your contribution, you are helping to keep this service to the rowing community
running. As the founder and developer of the Rowsandall.com site I am very interested
to know what you think of Rowsandall.com, as well as suggestions for improvements.
</p>
<p>
So don't hesitate to respond to this email and let me know. I will read and respond to each
email.
</p>
{% if recurring=='recurring' %} {% if recurring=='recurring' %}
<p> <p>
Some more information about the subscription.
The subscription cost is &euro;{{ price }} per year. The subscription cost is &euro;{{ price }} per year.
Your next charge is due on {{ end_of_billing_period }}. We will charge you automatically Your next charge is due on {{ end_of_billing_period }}. We will charge you automatically
on that date. on that date.
</p> </p>
<p> <p>
The subscription will keep running until you change or stop it. At any point in time you The subscription will keep running until you change or stop it. At any point in time you
can change the automatically renewing subscription to a "one year only" subscription through can change the automatically renewing subscription to a "one year only" subscription through
<a href="{{ siteurl}}/rowers/upgrade/">the upgrade page</a>. On this page, you can also <a href="{{ siteurl}}/rowers/upgrade/">the upgrade page</a>. On this page, you can also
upgrade your subscription. upgrade your subscription.
</p> </p>
{% else %} {% else %}
@@ -28,7 +40,7 @@
The price of the subscription is &euro;{{ price }}. You have paid &euro;{{ amount }} as a The price of the subscription is &euro;{{ price }}. You have paid &euro;{{ amount }} as a
prorated cost of your upgrade. prorated cost of your upgrade.
This one year subscription will automatically end on {{ end_of_billing_period }}. You can This one year subscription will automatically end on {{ end_of_billing_period }}. You can
renew your subscription after that. renew your subscription after that.
</p> </p>
<p> <p>
@@ -60,4 +72,3 @@
Best Regards, the Rowsandall Team Best Regards, the Rowsandall Team
</p> </p>
{% endblock %} {% endblock %}

View File

@@ -11,11 +11,29 @@ from django.http import Http404
from rowers.views import get_workout from rowers.views import get_workout
class TestDateTime(TestCase):
def setUp(self):
self.factory = RequestFactory()
def tearDown(self):
pass
def test_get_dates(self):
request = self.factory.get('/rowers/sessions/create/user/230/?startdate=13.01.2020&enddate=19.01.2020')
startdate,enddate = get_dates_timeperiod(request)
teststart = datetime.date(2020,1,1)
self.assertTrue(teststart<startdate)
testend = datetime.date(2020,12,12)
self.assertTrue(testend>enddate)
# tests simple functions from views.py # tests simple functions from views.py
class SimpleViewTest(TestCase): class SimpleViewTest(TestCase):
def setUp(self): def setUp(self):
self.u = UserFactory() self.u = UserFactory()
self.r = Rower.objects.create(user=self.u, self.r = Rower.objects.create(user=self.u,
birthdate=faker.profile()['birthdate'], birthdate=faker.profile()['birthdate'],
gdproptin=True, gdproptin=True,
@@ -36,11 +54,11 @@ class SimpleViewTest(TestCase):
except (IOError, FileNotFoundError,OSError): except (IOError, FileNotFoundError,OSError):
pass pass
def test_getrequestrower(self): def test_getrequestrower(self):
user_no_rower = UserFactory(username='norower') user_no_rower = UserFactory(username='norower')
user_no_rower.set_password(faker.word()) user_no_rower.set_password(faker.word())
login = self.c.login(username=self.u.username, password=self.password) login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login) self.assertTrue(login)
@@ -75,7 +93,7 @@ class SimpleViewTest(TestCase):
self.assertRedirects(response, self.assertRedirects(response,
expected_url='/rowers/email/', expected_url='/rowers/email/',
status_code=302,target_status_code=200) status_code=302,target_status_code=200)
def test_getworkout(self): def test_getworkout(self):
with assert_raises(Http404): with assert_raises(Http404):
@@ -108,7 +126,3 @@ class SimpleViewTest(TestCase):
login = self.c.login(username=self.u.username, password=self.password) login = self.c.login(username=self.u.username, password=self.password)
self.assertEqual(login,False) self.assertEqual(login,False)

View File

@@ -445,8 +445,9 @@ def plannedsession_create_view(request,
sps = get_sessions(r,startdate=startdate,enddate=enddate).exclude( sps = get_sessions(r,startdate=startdate,enddate=enddate).exclude(
sessiontype='race') sessiontype='race')
sessiontemplates = PlannedSession.objects.filter(
sessiontemplates = PlannedSession.objects.filter(manager=request.user,is_template=True) manager=request.user,
is_template=True).order_by("name")
try: try:
trainingplan = TrainingPlan.objects.filter( trainingplan = TrainingPlan.objects.filter(
@@ -1511,11 +1512,11 @@ def plannedsession_teamclone_view(request,id=0):
ps.name += ' (copy)' ps.name += ' (copy)'
ps.is_template = False ps.is_template = False
deltadays = ps.enddate-ps.startdate deltadays = ps.preferreddate-ps.startdate
ps.startdate = timezone.now().date() ps.startdate = startdate
ps.enddate = (timezone.now()+deltadays).date() ps.enddate = enddate
ps.preferreddate = ps.preferreddate+deltadays ps.preferreddate = startdate+deltadays
ps.save() ps.save()

View File

@@ -13,23 +13,30 @@ def start_trial_view(request):
messages.error(request,'You do not qualify for a trial') messages.error(request,'You do not qualify for a trial')
url = '/rowers/paidplans' url = '/rowers/paidplans'
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
r.protrialexpires = datetime.date.today()+datetime.timedelta(13) r.protrialexpires = datetime.date.today()+datetime.timedelta(13)
r.save() r.save()
url = reverse('workouts_view') url = reverse('workouts_view')
messages.info(request,'We have started your 14 day trial period') messages.info(request,'We have started your 14 day trial period')
subject2 = "User started Pro Trial" subject2 = "User started Pro Trial"
message2 = "User Started Pro Trial.\n" message2 = "User Started Pro Trial.\n"
message2 += request.user.email + "\n" message2 += request.user.email + "\n"
message2 += "User name: "+request.user.username message2 += "User name: "+request.user.username
send_mail(subject2, message2, send_mail(subject2, message2,
'Rowsandall Server <info@rowsandall.com>', 'Rowsandall Server <info@rowsandall.com>',
['roosendaalsander@gmail.com']) ['roosendaalsander@gmail.com'])
send_template_email('Rowsandall <info@rowsandall.com>',
[r.user.email],
'Welcome to the Rowsandall Pro Trial',
'protrialewelcome.html',
{'first_name':r.user.first_name,
'last_name':r.user.last_name})
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
@login_required() @login_required()
@@ -40,7 +47,7 @@ def start_plantrial_view(request):
messages.error(request,'You do not qualify for a trial') messages.error(request,'You do not qualify for a trial')
url = '/rowers/paidplans' url = '/rowers/paidplans'
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
r.plantrialexpires = datetime.date.today()+datetime.timedelta(13) r.plantrialexpires = datetime.date.today()+datetime.timedelta(13)
r.protrialexpires = datetime.date.today()+datetime.timedelta(13) r.protrialexpires = datetime.date.today()+datetime.timedelta(13)
r.save() r.save()
@@ -48,16 +55,23 @@ def start_plantrial_view(request):
url = reverse('workouts_view') url = reverse('workouts_view')
messages.info(request,'We have started your 14 day trial period') messages.info(request,'We have started your 14 day trial period')
subject2 = "User started Plan Trial" subject2 = "User started Plan Trial"
message2 = "User Started Plan Trial.\n" message2 = "User Started Plan Trial.\n"
message2 += request.user.email + "\n" message2 += request.user.email + "\n"
message2 += "User name: "+request.user.username message2 += "User name: "+request.user.username
send_mail(subject2, message2, send_mail(subject2, message2,
'Rowsandall Server <info@rowsandall.com>', 'Rowsandall Server <info@rowsandall.com>',
['roosendaalsander@gmail.com']) ['roosendaalsander@gmail.com'])
send_template_email('Rowsandall <info@rowsandall.com>',
[r.user.email],
'Welcome to the Rowsandall Self-Coach Trial',
'plantrialwelcome.html',
{'first_name':r.user.first_name,
'last_name':r.user.last_name})
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
# Page where user can manage his favorite charts # Page where user can manage his favorite charts
@@ -79,7 +93,7 @@ def rower_favoritecharts_view(request,userid=0):
FavoriteChartFormSet = formset_factory(FavoriteForm,formset=BaseFavoriteFormSet,extra=0) FavoriteChartFormSet = formset_factory(FavoriteForm,formset=BaseFavoriteFormSet,extra=0)
if aantal==0: if aantal==0:
FavoriteChartFormSet = formset_factory(FavoriteForm,formset=BaseFavoriteFormSet,extra=1) FavoriteChartFormSet = formset_factory(FavoriteForm,formset=BaseFavoriteFormSet,extra=1)
if request.method == 'POST': if request.method == 'POST':
favorites_formset = FavoriteChartFormSet(request.POST) favorites_formset = FavoriteChartFormSet(request.POST)
@@ -115,7 +129,7 @@ def rower_favoritecharts_view(request,userid=0):
messages.error(request,message) messages.error(request,message)
else: else:
favorites_formset = FavoriteChartFormSet(initial=favorites_data) favorites_formset = FavoriteChartFormSet(initial=favorites_data)
context = { context = {
'favorites_formset':favorites_formset, 'favorites_formset':favorites_formset,
@@ -123,8 +137,8 @@ def rower_favoritecharts_view(request,userid=0):
'rower':r, 'rower':r,
} }
return render(request,'favoritecharts.html',context) return render(request,'favoritecharts.html',context)
# page where user sets his export settings # page where user sets his export settings
@@ -154,7 +168,7 @@ def rower_exportsettings_view(request,userid=0):
'name': 'Export Settings' 'name': 'Export Settings'
} }
] ]
return render(request, 'rower_exportsettings.html', return render(request, 'rower_exportsettings.html',
{'form':form, {'form':form,
'rower':r, 'rower':r,
@@ -367,7 +381,7 @@ def rower_prefs_view(request,userid=0,message=""):
'rower':r, 'rower':r,
}) })
# Revoke an app that you granted access through the API. # Revoke an app that you granted access through the API.
# this views is called when you press a button on the User edit page # this views is called when you press a button on the User edit page
# the button is only there when you have granted access to an app # the button is only there when you have granted access to an app
@@ -401,7 +415,7 @@ def rower_update_empower_view(
r = getrower(request.user) r = getrower(request.user)
except Rower.DoesNotExist: except Rower.DoesNotExist:
raise Http404("Rower doesn't exist") raise Http404("Rower doesn't exist")
if request.method == 'POST' and 'daterange' in request.POST: if request.method == 'POST' and 'daterange' in request.POST:
dateform = DateRangeForm(request.POST) dateform = DateRangeForm(request.POST)
if dateform.is_valid(): if dateform.is_valid():
@@ -416,7 +430,7 @@ def rower_update_empower_view(
'startdate':startdate, 'startdate':startdate,
'enddate':enddate, 'enddate':enddate,
}) })
if request.method == 'POST' and 'workouts' in request.POST: if request.method == 'POST' and 'workouts' in request.POST:
form = WorkoutMultipleCompareForm(request.POST) form = WorkoutMultipleCompareForm(request.POST)
@@ -424,7 +438,7 @@ def rower_update_empower_view(
cd = form.cleaned_data cd = form.cleaned_data
workouts = cd['workouts'] workouts = cd['workouts']
workoutdicts = [] workoutdicts = []
for w in workouts: for w in workouts:
if w.user != r: if w.user != r:
message = "You can only alter your own workouts" message = "You can only alter your own workouts"
@@ -440,7 +454,7 @@ def rower_update_empower_view(
messages.error(request,message) messages.error(request,message)
else: else:
workoutdict = { workoutdict = {
'id':w.id, 'id':w.id,
'boattype':w.boattype, 'boattype':w.boattype,
@@ -454,7 +468,7 @@ def rower_update_empower_view(
w.workoutsource = 'speedcoach2corrected' w.workoutsource = 'speedcoach2corrected'
w.save() w.save()
job = myqueue(queuelow,handle_update_empower, job = myqueue(queuelow,handle_update_empower,
request.user.email,workoutdicts, request.user.email,workoutdicts,
debug=False, debug=False,
@@ -479,7 +493,7 @@ def rower_update_empower_view(
workoutsource='speedcoach2', workoutsource='speedcoach2',
user=r, user=r,
).order_by("-date","-starttime") ).order_by("-date","-starttime")
form = WorkoutMultipleCompareForm() form = WorkoutMultipleCompareForm()
form.fields["workouts"].queryset = workouts form.fields["workouts"].queryset = workouts
# GET request = prepare form # GET request = prepare form
@@ -491,5 +505,3 @@ def rower_update_empower_view(
'form':form, 'form':form,
'rower':r 'rower':r
}) })

View File

@@ -131,7 +131,7 @@ def workout_video_view_mini(request,id=''):
'name': w.name 'name': w.name
}, },
{ {
'url':reverse('workout_video_view',kwargs={'id':encoder.encode_hex(analysis.id)}), 'url':reverse('workout_video_view_mini',kwargs={'id':encoder.encode_hex(analysis.id)}),
'name': 'Video Analysis' 'name': 'Video Analysis'
} }