Private
Public Access
1
0

Merge branch 'release/teamcontinued'

This commit is contained in:
Sander Roosendaal
2017-02-15 16:29:24 +01:00
14 changed files with 251 additions and 34 deletions
+1
View File
@@ -12,6 +12,7 @@ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'rowsandall_app.settings_dev')
from django.conf import settings # noqa
app = Celery('tasks',
broker='redis://localhost',
backend='redis://localhost',)
+16 -6
View File
@@ -228,7 +228,8 @@ def timedeltaconv(x):
# Processes painsled CSV file to database
def save_workout_database(f2,r,dosmooth=True,workouttype='rower',
dosummary=True,title='Workout',
notes='',totaldist=0,totaltime=0):
notes='',totaldist=0,totaltime=0,
makeprivate=False):
message = None
powerperc = 100*np.array([r.pw_ut2,
r.pw_ut1,
@@ -313,12 +314,18 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower',
workoutstarttime = row.rowdatetime.strftime('%H:%M:%S')
workoutstartdatetime = thetimezone.localize(row.rowdatetime).astimezone(utc)
if makeprivate:
privacy = 'private'
else:
privacy = 'visible'
# check for duplicate start times
ws = Workout.objects.filter(starttime=workoutstarttime,
user=r)
if (len(ws) != 0):
message = "Warning: This workout probably already exists in the database"
w = Workout(user=r,name=title,date=workoutdate,
workouttype=workouttype,
duration=duration,distance=totaldist,
@@ -326,15 +333,16 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower',
starttime=workoutstarttime,
csvfilename=f2,notes=notes,summary=summary,
maxhr=maxhr,averagehr=averagehr,
startdatetime=workoutstartdatetime)
startdatetime=workoutstartdatetime,
privacy=privacy)
w.save()
ts = Team.objects.filter(rower=r)
for t in ts:
w.team.add(t)
if privacy == 'visible':
ts = Team.objects.filter(rower=r)
for t in ts:
w.team.add(t)
# put stroke data in database
res = dataprep(row.df,id=w.id,bands=True,
@@ -422,6 +430,7 @@ def handle_nonpainsled(f2,fileformat,summary=''):
def new_workout_from_file(r,f2,
workouttype='rower',
title='Workout',
makeprivate=False,
notes=''):
message = None
fileformat = get_file_type(f2)
@@ -470,6 +479,7 @@ def new_workout_from_file(r,f2,
dosummary = (fileformat != 'fit')
id,message = save_workout_database(f2,r,
workouttype=workouttype,
makeprivate=makeprivate,
dosummary=dosummary,
title=title)
+7 -4
View File
@@ -76,14 +76,17 @@ class UploadOptionsForm(forms.Form):
('distanceplot','Distance Plot'),
('pieplot','Pie Chart'),
)
make_plot = forms.BooleanField(initial=False)
make_plot = forms.BooleanField(initial=False,required=False)
plottype = forms.ChoiceField(required=False,
choices=plotchoices,
initial='timeplot')
upload_to_C2 = forms.BooleanField(initial=False)
initial='timeplot',
label='Plot Type')
upload_to_C2 = forms.BooleanField(initial=False,required=False)
makeprivate = forms.BooleanField(initial=False,required=False,
label='Make Workout Private')
class Meta:
fields = ['make_plot','plottype','upload_toc2']
fields = ['make_plot','plottype','upload_toc2','makeprivate']
# This form is used on the Analysis page to add a custom distance/time
# trial and predict the pace
+28 -2
View File
@@ -104,19 +104,27 @@ class Team(models.Model):
('private','private'),
('open','open'),
)
viewchoices = (
('coachonly','Coach Only'),
('allmembers','All Members')
)
name = models.CharField(max_length=150,unique=True,verbose_name='Team Name')
notes = models.CharField(blank=True,max_length=200,verbose_name='Team Purpose')
manager = models.ForeignKey(User)
private = models.CharField(max_length=30,choices=choices,default='open',
verbose_name='Team Type')
viewing = models.CharField(max_length=30,choices=viewchoices,default='allmembers',verbose_name='Sharing Behavior')
def __unicode__(self):
return self.name
class TeamForm(ModelForm):
class Meta:
model = Team
fields = ['name','notes','private']
fields = ['name','notes','private','viewing']
widgets = {
'notes': forms.Textarea,
}
@@ -196,6 +204,11 @@ class Rower(models.Model):
('coach','coach')
)
privacychoices = (
('visible','Visible'),
('hidden','Hidden'),
)
rowerplan = models.CharField(default='basic',max_length=30,
choices=plans)
@@ -203,8 +216,11 @@ class Rower(models.Model):
teamplanexpires = models.DateField(default=timezone.now)
clubsize = models.IntegerField(default=0)
# Friends/Team
friends = models.ManyToManyField("self",blank=True)
privacy = models.CharField(default='visible',max_length=30,
choices=privacychoices)
team = models.ManyToManyField(Team,blank=True)
@@ -352,6 +368,11 @@ class Workout(models.Model):
('8+', '8+ (eight)'),
)
privacychoices = (
('private','Private'),
('visible','Visible'),
)
user = models.ForeignKey(Rower)
team = models.ManyToManyField(Team,blank=True)
name = models.CharField(max_length=150)
@@ -374,6 +395,8 @@ class Workout(models.Model):
uploadedtosporttracks = models.IntegerField(default=0)
notes = models.CharField(blank=True,null=True,max_length=200)
summary = models.TextField(blank=True)
privacy = models.CharField(default='visible',max_length=30,
choices=privacychoices)
def __str__(self):
@@ -491,7 +514,7 @@ class WorkoutForm(ModelForm):
duration = forms.TimeInput(format='%H:%M:%S.%f')
class Meta:
model = Workout
fields = ['name','date','starttime','duration','distance','workouttype','boattype','notes']
fields = ['name','date','starttime','duration','distance','workouttype','notes','privacy','boattype']
widgets = {
'date': DateInput(),
'notes': forms.Textarea,
@@ -500,6 +523,9 @@ class WorkoutForm(ModelForm):
def __init__(self, *args, **kwargs):
super(WorkoutForm, self).__init__(*args, **kwargs)
# this line to be removed
del self.fields['privacy']
if self.instance.workouttype != 'water':
del self.fields['boattype']
+5 -1
View File
@@ -10,7 +10,7 @@ import rowingdata
from rowingdata import main as rmain
from rowingdata import rowingdata as rdata
import rowingdata
#from rowers.models import Workout
from matplotlib.backends.backend_agg import FigureCanvas
#from matplotlib.backends.backend_cairo import FigureCanvasCairo as FigureCanvas
import matplotlib.pyplot as plt
@@ -244,6 +244,10 @@ def handle_sendemail_invite(email,name,code,teamname,manager):
message = 'Dear '+name+',\n\n'
message += manager+' is inviting you to join his team '+teamname
message += ' on rowsandall.com\n\n'
message += 'By accepting the invite, you will have access to your'
message += " team's workouts on rowsandall.com and your workouts will "
message += " be visible to "
message += "the members of the team.\n\n"
message += 'If you already have an account on rowsandall.com, you can login to the site and you will find the invitation here on the Teams page:\n'
message += ' https://rowsandall.com/rowers/me/teams \n\n'
message += 'You can also click the direct link: \n'
+4 -3
View File
@@ -43,7 +43,7 @@ def handle_add_workouts_team(ws,t):
return 1
def update_team(t,name,manager,private,notes):
def update_team(t,name,manager,private,notes,viewing):
if t.manager != manager:
return (0,'You are not the manager of this team')
try:
@@ -51,16 +51,17 @@ def update_team(t,name,manager,private,notes):
t.manager = manager
t.private = private
t.notes = notes
t.viewing = viewing
t.save()
except IntegrityError:
return (0,'Team name duplication')
return (1,'Team Updated')
def create_team(name,manager,private='open',notes=''):
def create_team(name,manager,private='open',notes='',viewing='allmembers'):
# needs some error testing
try:
t = Team(name=name,manager=manager,notes=notes,
private=private)
private=private,viewing=viewing)
t.save()
r = Rower.objects.get(user=manager)
res = add_member(t.id,r)
+4
View File
@@ -29,6 +29,10 @@
</table>
</p>
<p>
You can select one static plot to be generated immediately for this workout. You can select to upload to Concept2 automatically. If you check "make private", this workout will not be visible to your followers and will not show up in your teams' workouts list.
</p>
<p>
Valid file types are:
<ul>
+1 -1
View File
@@ -62,7 +62,7 @@
<td> {{ workout.date |truncatechars:15}} </td>
<td> {{ workout.starttime }} </td>
<td>
{% if workout.user.user == user %}
{% if workout.user.user == user or user == team.manager %}
{% if workout.name != '' %}
<a href="/rowers/workout/{{ workout.id }}/edit">{{ workout.name }}</a> </td>
{% else %}
+11 -2
View File
@@ -4,9 +4,11 @@
{% block title %}New Team{% endblock %}
{% block content %}
<form enctype="multipart/form-data" action="{{ formloc }}" method="post">
<div class="grid_12 alpha">
<h1>Create a new Team</h1>
</div>
<div id="left" class="grid_6 alpha">
<h1>Create a new Team</h1>
<form enctype="multipart/form-data" action="{{ formloc }}" method="post">
{% if form.errors %}
<p style="color: red;">
Please correct the error{{ form.errors|pluralize }} below.
@@ -21,6 +23,13 @@
<input class="button green" type="submit" value="Submit">
</div>
</div>
<div id="right" class="grid_6 omega">
<ul>
<li>Team Type: A private team is invisible on the Teams Management page, except for its members. The only way to add members is for the manager to send an invitation. An open team is visible for all rowsandall.com users. In addition to the invitation mechanism, any user can request to be added to this team. The team manager will always have to approve membership.</li>
<li>Sharing Behavior: When set to "All Members", all members of a team will see each other's workouts. This is the recommended setting. If te sharing bhavior is set to "Coach Only", team members only see their own workouts. The coach sees all team members' workouts.</li>
<li>These settings can be changed at any point in time through the Team Edit page</li>
</ul>
</div>
+11 -2
View File
@@ -4,9 +4,11 @@
{% block title %}New Team{% endblock %}
{% block content %}
<form enctype="multipart/form-data" action="{{ formloc }}" method="post">
<div class="grid_12 alpha">
<h1>Edit Team {{ team.name }}</h1>
</div>
<div id="left" class="grid_6 alpha">
<h1>Edit Team {{ team.name }}</h1>
<form enctype="multipart/form-data" action="{{ formloc }}" method="post">
{% if form.errors %}
<p style="color: red;">
Please correct the error{{ form.errors|pluralize }} below.
@@ -21,6 +23,13 @@
<input class="button green" type="submit" value="Submit">
</div>
</div>
<div id="right" class="grid_6 omega">
<ul>
<li>Team Type: A private team is invisible on the Teams Management page, except for its members. The only way to add members is for the manager to send an invitation. An open team is visible for all rowsandall.com users. In addition to the invitation mechanism, any user can request to be added to this team. The team manager will always have to approve membership.</li>
<li>Sharing Behavior: When set to "All Members", all members of a team will see each other's workouts. This is the recommended setting. If te sharing bhavior is set to "Coach Only", team members only see their own workouts. The coach sees all team members' workouts.</li>
<li>These settings can be changed at any point in time through the Team Edit page</li>
</ul>
</div>
+10 -1
View File
@@ -69,7 +69,16 @@
{{ form.as_table }}
</table>
{% csrf_token %}
<div id="formbutton" class="grid_1 prefix_4 suffix_1">
<div class="grid_2 prefix_2 alpha tooltip">
{% if workout.privacy == 'visible' %}
<a class="button blue small" href="/rowers/workout/{{ workout.id }}/setprivate">Set Private</a>
<span class="tooltiptext">Only you can see this workout</span>
{% else %}
<a class="button blue small" href="/rowers/workout/{{ workout.id }}/makepublic">Make Public</a>
<span class="tooltiptext">Make this workout visible to your teams and followers</span>
{% endif %}
</div>
<div id="formbutton" class="grid_1 suffix_1 omega">
<input class="button green" type="submit" value="Save">
</div>
</form>
+26 -1
View File
@@ -2,7 +2,7 @@ from django.test import TestCase, Client,override_settings
from django.test.client import RequestFactory
from .views import checkworkoutuser,c2_open
from rowers.models import Workout, User, Rower, WorkoutForm,RowerForm,GraphImage
from rowers.forms import DocumentsForm,CNsummaryForm
from rowers.forms import DocumentsForm,CNsummaryForm,RegistrationFormUniqueEmail
import rowers.plots as plots
import rowers.interactiveplots as iplots
import datetime
@@ -276,6 +276,31 @@ class TestErrorPages(TestCase):
self.assertEqual(response.status_code, 500)
self.assertIn('500 Internal Server Error', unicode(response))
class NewUserRegistrationTest(TestCase):
def setUp(self):
self.c = Client()
def test_newuser(self):
form_data = {
'first_name':'Jan',
'last_name':'Roeiert',
'email':'jan@loop.nl',
'username':'janderoeiert',
'password1':'aapindewei2',
'password2':'aapindewei2',
'tos':True,
}
form = RegistrationFormUniqueEmail(form_data)
self.assertTrue(form.is_valid())
response = self.c.post('/rowers/register', form_data, follow=True)
self.assertRedirects(response,
expected_url='/rowers/register/thankyou/',
status_code=302,target_status_code=200)
class WorkoutTests(TestCase):
def setUp(self):
u = User.objects.create_user('john',
+2
View File
@@ -157,6 +157,8 @@ urlpatterns = [
url(r'^workout/(?P<id>\d+)/edit/c/(?P<message>.+.*)$',views.workout_edit_view),
url(r'^workout/(?P<id>\d+)/edit/s/(?P<successmessage>.+.*)$',views.workout_edit_view),
url(r'^workout/(?P<id>\d+)/edit$',views.workout_edit_view),
url(r'^workout/(?P<id>\d+)/setprivate$',views.workout_setprivate_view),
url(r'^workout/(?P<id>\d+)/makepublic$',views.workout_makepublic_view),
url(r'^workout/(?P<id>\d+)/advanced/c/(?P<message>.+.*)$',views.workout_advanced_view),
url(r'^workout/(?P<id>\d+)/advanced/s/(?P<successmessage>.+.*)$',views.workout_advanced_view),
url(r'^workout/(?P<id>\d+)/geeky$',views.workout_geeky_view),
+123 -9
View File
@@ -302,7 +302,8 @@ def sendmail(request):
def checkworkoutuser(user,workout):
try:
r = Rower.objects.get(user=user)
return (workout.user == r)
managers = [team.manager for team in workout.team.all()]
return (workout.user == r or user in managers)
except Rower.DoesNotExist:
return(False)
@@ -1885,6 +1886,70 @@ def workout_recalcsummary_view(request,id=0):
return HttpResponseRedirect(url)
@login_required()
def workout_makepublic_view(request,id,
message='',
successmessage=''):
try:
row = Workout.objects.get(id=id)
except Workout.DoesNotExist:
raise Http404("Workout doesn't exist")
if (checkworkoutuser(request.user,row)==False):
message = "You are not allowed to edit this workout"
url = reverse(workouts_view,args=[str(message)])
return HttpResponseRedirect(url)
row.privacy = 'visible'
row.save()
rr = Rower.objects.get(user=request.user)
teams = rr.team.all()
for team in teams:
row.team.add(team)
message = "Workout set to public. Your followers and team members will see it"
url = reverse(workout_edit_view,
kwargs = {
'id':str(id),
'successmessage':str(message),
})
return HttpResponseRedirect(url)
@login_required()
def workout_setprivate_view(request,id,
message='',
successmessage=''):
try:
row = Workout.objects.get(id=id)
except Workout.DoesNotExist:
raise Http404("Workout doesn't exist")
if (checkworkoutuser(request.user,row)==False):
message = "You are not allowed to edit this workout"
url = reverse(workouts_view,args=[str(message)])
return HttpResponseRedirect(url)
row.privacy = 'private'
row.save()
for team in row.team.all():
row.team.remove(team)
message = "Workout set to private. Only you will see it"
url = reverse(workout_edit_view,
kwargs = {
'id':str(id),
'successmessage':str(message),
})
return HttpResponseRedirect(url)
# List Workouts
@login_required()
def workouts_view(request,message='',successmessage='',
@@ -1928,9 +1993,14 @@ def workouts_view(request,message='',successmessage='',
except Team.DoesNotExist:
raise Http404("Team doesn't exist")
workouts = Workout.objects.filter(team=theteam,
if theteam.viewing == 'allmembers' or theteam.manager == request.user:
workouts = Workout.objects.filter(team=theteam,
startdatetime__gte=startdate,
startdatetime__lte=enddate).order_by("-date", "-starttime")
elif theteam.viewing == 'coachonly':
workouts = Workout.objects.filter(team=theteam,user=r,
startdatetime__gte=startdate,
startdatetime__lte=enddate).order_by("-date","-starttime")
else:
@@ -2063,6 +2133,9 @@ def workout_view(request,id=0):
try:
# check if valid ID exists (workout exists)
row = Workout.objects.get(id=id)
if row.privacy == 'private':
raise Http404("Not allowed to view this workout")
g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime")
r = Rower.objects.get(id=row.user.id)
u = User.objects.get(id=r.user.id)
@@ -3259,6 +3332,10 @@ def workout_edit_view(request,id=0,message="",successmessage=""):
boattype = request.POST['boattype']
except KeyError:
boattype = Workout.objects.get(id=id).boattype
try:
privacy = request.POST['privacy']
except KeyError:
privacy = Workout.objects.get(id=id).privacy
startdatetime = (str(date) + ' ' + str(starttime))
startdatetime = datetime.datetime.strptime(startdatetime,
"%Y-%m-%d %H:%M:%S")
@@ -3277,6 +3354,7 @@ def workout_edit_view(request,id=0,message="",successmessage=""):
row.duration = duration
row.distance = distance
row.boattype = boattype
row.privacy = privacy
row.save()
# change data in csv file
@@ -4103,7 +4181,26 @@ def workout_getc2workout_view(request,c2id):
# This is the main view for processing uploaded files
@login_required()
def workout_upload_view(request,message=""):
def workout_upload_view(request,message="",
uploadoptions={
'makeprivate':False,
'make_plot':False,
'upload_to_C2':False,
'plottype':'timeplot',
}):
if 'uploadoptions' in request.session:
uploadoptions = request.session['uploadoptions']
else:
request.session['uploadoptions'] = uploadoptions
makeprivate = uploadoptions['makeprivate']
make_plot = uploadoptions['make_plot']
plottype = uploadoptions['plottype']
upload_toc2 = uploadoptions['upload_to_C2']
r = Rower.objects.get(user=request.user)
if request.method == 'POST':
form = DocumentsForm(request.POST,request.FILES)
@@ -4115,10 +4212,22 @@ def workout_upload_view(request,message=""):
workouttype = form.cleaned_data['workouttype']
notes = form.cleaned_data['notes']
make_plot = request.POST.getlist('make_plot')
plottype = request.POST['plottype']
upload_to_c2 = request.POST.getlist('upload_to_C2')
if optionsform.is_valid():
make_plot = optionsform.cleaned_data['make_plot']
plottype = optionsform.cleaned_data['plottype']
upload_to_c2 = optionsform.cleaned_data['upload_to_C2']
makeprivate = optionsform.cleaned_data['makeprivate']
uploadoptions = {
'makeprivate':makeprivate,
'make_plot':make_plot,
'plottype':plottype,
'upload_to_C2':upload_to_c2,
}
request.session['uploadoptions'] = uploadoptions
f1 = res[0] # file name
f2 = res[1] # file name incl media directory
@@ -4126,6 +4235,7 @@ def workout_upload_view(request,message=""):
id,message = dataprep.new_workout_from_file(r,f2,
workouttype=workouttype,
makeprivate=makeprivate,
title = t,
notes='')
if not id:
@@ -4266,7 +4376,7 @@ def workout_upload_view(request,message=""):
return response
else:
form = DocumentsForm()
optionsform = UploadOptionsForm()
optionsform = UploadOptionsForm(initial=uploadoptions)
return render(request, 'document_form.html',
{'form':form,
'optionsform': optionsform,
@@ -5432,7 +5542,9 @@ def team_edit_view(request,id=0):
notes = cd['notes']
manager = request.user
private = cd['private']
res,message=teams.update_team(t,name,manager,private,notes)
viewing = cd['viewing']
res,message=teams.update_team(t,name,manager,private,notes,
viewing)
if res:
url = reverse(team_view,
kwargs={
@@ -5470,7 +5582,9 @@ def team_create_view(request):
notes = cd['notes']
manager = request.user
private = cd['private']
res,message=teams.create_team(name,manager,private,notes)
viewing = cd['viewing']
res,message=teams.create_team(name,manager,private,notes,
viewing)
url = reverse(rower_teams_view)
response = HttpResponseRedirect(url)
return response