Private
Public Access
1
0

Merge branch 'feature/teamcontinued' into develop

This commit is contained in:
Sander Roosendaal
2017-02-15 16:17:19 +01:00
10 changed files with 212 additions and 31 deletions

View File

@@ -228,7 +228,8 @@ def timedeltaconv(x):
# Processes painsled CSV file to database # Processes painsled CSV file to database
def save_workout_database(f2,r,dosmooth=True,workouttype='rower', def save_workout_database(f2,r,dosmooth=True,workouttype='rower',
dosummary=True,title='Workout', dosummary=True,title='Workout',
notes='',totaldist=0,totaltime=0): notes='',totaldist=0,totaltime=0,
makeprivate=False):
message = None message = None
powerperc = 100*np.array([r.pw_ut2, powerperc = 100*np.array([r.pw_ut2,
r.pw_ut1, r.pw_ut1,
@@ -313,12 +314,18 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower',
workoutstarttime = row.rowdatetime.strftime('%H:%M:%S') workoutstarttime = row.rowdatetime.strftime('%H:%M:%S')
workoutstartdatetime = thetimezone.localize(row.rowdatetime).astimezone(utc) workoutstartdatetime = thetimezone.localize(row.rowdatetime).astimezone(utc)
if makeprivate:
privacy = 'private'
else:
privacy = 'visible'
# check for duplicate start times # check for duplicate start times
ws = Workout.objects.filter(starttime=workoutstarttime, ws = Workout.objects.filter(starttime=workoutstarttime,
user=r) user=r)
if (len(ws) != 0): if (len(ws) != 0):
message = "Warning: This workout probably already exists in the database" message = "Warning: This workout probably already exists in the database"
w = Workout(user=r,name=title,date=workoutdate, w = Workout(user=r,name=title,date=workoutdate,
workouttype=workouttype, workouttype=workouttype,
duration=duration,distance=totaldist, duration=duration,distance=totaldist,
@@ -326,13 +333,14 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower',
starttime=workoutstarttime, starttime=workoutstarttime,
csvfilename=f2,notes=notes,summary=summary, csvfilename=f2,notes=notes,summary=summary,
maxhr=maxhr,averagehr=averagehr, maxhr=maxhr,averagehr=averagehr,
startdatetime=workoutstartdatetime) startdatetime=workoutstartdatetime,
privacy=privacy)
w.save() w.save()
if privacy == 'visible':
ts = Team.objects.filter(rower=r) ts = Team.objects.filter(rower=r)
for t in ts: for t in ts:
w.team.add(t) w.team.add(t)
@@ -422,6 +430,7 @@ def handle_nonpainsled(f2,fileformat,summary=''):
def new_workout_from_file(r,f2, def new_workout_from_file(r,f2,
workouttype='rower', workouttype='rower',
title='Workout', title='Workout',
makeprivate=False,
notes=''): notes=''):
message = None message = None
fileformat = get_file_type(f2) fileformat = get_file_type(f2)
@@ -470,6 +479,7 @@ def new_workout_from_file(r,f2,
dosummary = (fileformat != 'fit') dosummary = (fileformat != 'fit')
id,message = save_workout_database(f2,r, id,message = save_workout_database(f2,r,
workouttype=workouttype, workouttype=workouttype,
makeprivate=makeprivate,
dosummary=dosummary, dosummary=dosummary,
title=title) title=title)

View File

@@ -76,14 +76,15 @@ class UploadOptionsForm(forms.Form):
('distanceplot','Distance Plot'), ('distanceplot','Distance Plot'),
('pieplot','Pie Chart'), ('pieplot','Pie Chart'),
) )
make_plot = forms.BooleanField(initial=False) make_plot = forms.BooleanField(initial=False,required=False)
plottype = forms.ChoiceField(required=False, plottype = forms.ChoiceField(required=False,
choices=plotchoices, choices=plotchoices,
initial='timeplot') initial='timeplot')
upload_to_C2 = forms.BooleanField(initial=False) upload_to_C2 = forms.BooleanField(initial=False,required=False)
makeprivate = forms.BooleanField(initial=False,required=False)
class Meta: 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 # This form is used on the Analysis page to add a custom distance/time
# trial and predict the pace # trial and predict the pace

View File

@@ -104,19 +104,27 @@ class Team(models.Model):
('private','private'), ('private','private'),
('open','open'), ('open','open'),
) )
viewchoices = (
('coachonly','Coach Only'),
('allmembers','All Members')
)
name = models.CharField(max_length=150,unique=True,verbose_name='Team Name') name = models.CharField(max_length=150,unique=True,verbose_name='Team Name')
notes = models.CharField(blank=True,max_length=200,verbose_name='Team Purpose') notes = models.CharField(blank=True,max_length=200,verbose_name='Team Purpose')
manager = models.ForeignKey(User) manager = models.ForeignKey(User)
private = models.CharField(max_length=30,choices=choices,default='open', private = models.CharField(max_length=30,choices=choices,default='open',
verbose_name='Team Type') verbose_name='Team Type')
viewing = models.CharField(max_length=30,choices=viewchoices,default='allmembers',verbose_name='Sharing Behavior')
def __unicode__(self): def __unicode__(self):
return self.name return self.name
class TeamForm(ModelForm): class TeamForm(ModelForm):
class Meta: class Meta:
model = Team model = Team
fields = ['name','notes','private'] fields = ['name','notes','private','viewing']
widgets = { widgets = {
'notes': forms.Textarea, 'notes': forms.Textarea,
} }
@@ -196,6 +204,11 @@ class Rower(models.Model):
('coach','coach') ('coach','coach')
) )
privacychoices = (
('visible','Visible'),
('hidden','Hidden'),
)
rowerplan = models.CharField(default='basic',max_length=30, rowerplan = models.CharField(default='basic',max_length=30,
choices=plans) choices=plans)
@@ -203,8 +216,11 @@ class Rower(models.Model):
teamplanexpires = models.DateField(default=timezone.now) teamplanexpires = models.DateField(default=timezone.now)
clubsize = models.IntegerField(default=0) clubsize = models.IntegerField(default=0)
# Friends/Team # Friends/Team
friends = models.ManyToManyField("self",blank=True) friends = models.ManyToManyField("self",blank=True)
privacy = models.CharField(default='visible',max_length=30,
choices=privacychoices)
team = models.ManyToManyField(Team,blank=True) team = models.ManyToManyField(Team,blank=True)
@@ -352,6 +368,11 @@ class Workout(models.Model):
('8+', '8+ (eight)'), ('8+', '8+ (eight)'),
) )
privacychoices = (
('private','Private'),
('visible','Visible'),
)
user = models.ForeignKey(Rower) user = models.ForeignKey(Rower)
team = models.ManyToManyField(Team,blank=True) team = models.ManyToManyField(Team,blank=True)
name = models.CharField(max_length=150) name = models.CharField(max_length=150)
@@ -374,6 +395,8 @@ class Workout(models.Model):
uploadedtosporttracks = models.IntegerField(default=0) uploadedtosporttracks = models.IntegerField(default=0)
notes = models.CharField(blank=True,null=True,max_length=200) notes = models.CharField(blank=True,null=True,max_length=200)
summary = models.TextField(blank=True) summary = models.TextField(blank=True)
privacy = models.CharField(default='visible',max_length=30,
choices=privacychoices)
def __str__(self): def __str__(self):
@@ -491,7 +514,7 @@ class WorkoutForm(ModelForm):
duration = forms.TimeInput(format='%H:%M:%S.%f') duration = forms.TimeInput(format='%H:%M:%S.%f')
class Meta: class Meta:
model = Workout model = Workout
fields = ['name','date','starttime','duration','distance','workouttype','boattype','notes'] fields = ['name','date','starttime','duration','distance','workouttype','notes','privacy','boattype']
widgets = { widgets = {
'date': DateInput(), 'date': DateInput(),
'notes': forms.Textarea, 'notes': forms.Textarea,
@@ -500,6 +523,9 @@ class WorkoutForm(ModelForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(WorkoutForm, self).__init__(*args, **kwargs) super(WorkoutForm, self).__init__(*args, **kwargs)
# this line to be removed
del self.fields['privacy']
if self.instance.workouttype != 'water': if self.instance.workouttype != 'water':
del self.fields['boattype'] del self.fields['boattype']

View File

@@ -43,7 +43,7 @@ def handle_add_workouts_team(ws,t):
return 1 return 1
def update_team(t,name,manager,private,notes): def update_team(t,name,manager,private,notes,viewing):
if t.manager != manager: if t.manager != manager:
return (0,'You are not the manager of this team') return (0,'You are not the manager of this team')
try: try:
@@ -51,16 +51,17 @@ def update_team(t,name,manager,private,notes):
t.manager = manager t.manager = manager
t.private = private t.private = private
t.notes = notes t.notes = notes
t.viewing = viewing
t.save() t.save()
except IntegrityError: except IntegrityError:
return (0,'Team name duplication') return (0,'Team name duplication')
return (1,'Team Updated') 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 # needs some error testing
try: try:
t = Team(name=name,manager=manager,notes=notes, t = Team(name=name,manager=manager,notes=notes,
private=private) private=private,viewing=viewing)
t.save() t.save()
r = Rower.objects.get(user=manager) r = Rower.objects.get(user=manager)
res = add_member(t.id,r) res = add_member(t.id,r)

View File

@@ -62,7 +62,7 @@
<td> {{ workout.date |truncatechars:15}} </td> <td> {{ workout.date |truncatechars:15}} </td>
<td> {{ workout.starttime }} </td> <td> {{ workout.starttime }} </td>
<td> <td>
{% if workout.user.user == user %} {% if workout.user.user == user or user == team.manager %}
{% if workout.name != '' %} {% if workout.name != '' %}
<a href="/rowers/workout/{{ workout.id }}/edit">{{ workout.name }}</a> </td> <a href="/rowers/workout/{{ workout.id }}/edit">{{ workout.name }}</a> </td>
{% else %} {% else %}

View File

@@ -4,9 +4,11 @@
{% block title %}New Team{% endblock %} {% block title %}New Team{% endblock %}
{% block content %} {% block content %}
<form enctype="multipart/form-data" action="{{ formloc }}" method="post"> <div class="grid_12 alpha">
<div id="left" class="grid_6 alpha">
<h1>Create a new Team</h1> <h1>Create a new Team</h1>
</div>
<div id="left" class="grid_6 alpha">
<form enctype="multipart/form-data" action="{{ formloc }}" method="post">
{% if form.errors %} {% if form.errors %}
<p style="color: red;"> <p style="color: red;">
Please correct the error{{ form.errors|pluralize }} below. Please correct the error{{ form.errors|pluralize }} below.
@@ -21,6 +23,13 @@
<input class="button green" type="submit" value="Submit"> <input class="button green" type="submit" value="Submit">
</div> </div>
</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>

View File

@@ -4,9 +4,11 @@
{% block title %}New Team{% endblock %} {% block title %}New Team{% endblock %}
{% block content %} {% block content %}
<form enctype="multipart/form-data" action="{{ formloc }}" method="post"> <div class="grid_12 alpha">
<div id="left" class="grid_6 alpha">
<h1>Edit Team {{ team.name }}</h1> <h1>Edit Team {{ team.name }}</h1>
</div>
<div id="left" class="grid_6 alpha">
<form enctype="multipart/form-data" action="{{ formloc }}" method="post">
{% if form.errors %} {% if form.errors %}
<p style="color: red;"> <p style="color: red;">
Please correct the error{{ form.errors|pluralize }} below. Please correct the error{{ form.errors|pluralize }} below.
@@ -21,6 +23,13 @@
<input class="button green" type="submit" value="Submit"> <input class="button green" type="submit" value="Submit">
</div> </div>
</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>

View File

@@ -69,7 +69,16 @@
{{ form.as_table }} {{ form.as_table }}
</table> </table>
{% csrf_token %} {% 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"> <input class="button green" type="submit" value="Save">
</div> </div>
</form> </form>

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/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/s/(?P<successmessage>.+.*)$',views.workout_edit_view),
url(r'^workout/(?P<id>\d+)/edit$',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/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+)/advanced/s/(?P<successmessage>.+.*)$',views.workout_advanced_view),
url(r'^workout/(?P<id>\d+)/geeky$',views.workout_geeky_view), url(r'^workout/(?P<id>\d+)/geeky$',views.workout_geeky_view),

View File

@@ -302,7 +302,8 @@ def sendmail(request):
def checkworkoutuser(user,workout): def checkworkoutuser(user,workout):
try: try:
r = Rower.objects.get(user=user) 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: except Rower.DoesNotExist:
return(False) return(False)
@@ -1885,6 +1886,70 @@ def workout_recalcsummary_view(request,id=0):
return HttpResponseRedirect(url) 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 # List Workouts
@login_required() @login_required()
def workouts_view(request,message='',successmessage='', def workouts_view(request,message='',successmessage='',
@@ -1928,9 +1993,14 @@ def workouts_view(request,message='',successmessage='',
except Team.DoesNotExist: except Team.DoesNotExist:
raise Http404("Team doesn't exist") raise Http404("Team doesn't exist")
if theteam.viewing == 'allmembers' or theteam.manager == request.user:
workouts = Workout.objects.filter(team=theteam, workouts = Workout.objects.filter(team=theteam,
startdatetime__gte=startdate, startdatetime__gte=startdate,
startdatetime__lte=enddate).order_by("-date", "-starttime") 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: else:
@@ -2063,6 +2133,9 @@ def workout_view(request,id=0):
try: try:
# check if valid ID exists (workout exists) # check if valid ID exists (workout exists)
row = Workout.objects.get(id=id) 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") g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime")
r = Rower.objects.get(id=row.user.id) r = Rower.objects.get(id=row.user.id)
u = User.objects.get(id=r.user.id) u = User.objects.get(id=r.user.id)
@@ -3259,6 +3332,10 @@ def workout_edit_view(request,id=0,message="",successmessage=""):
try: try:
boattype = request.POST['boattype'] boattype = request.POST['boattype']
except KeyError: except KeyError:
boattype = Workout.objects.get(id=id).boattype
try:
privacy = request.POST['privacy']
except KeyError:
privacy = Workout.objects.get(id=id).privacy privacy = Workout.objects.get(id=id).privacy
startdatetime = (str(date) + ' ' + str(starttime)) startdatetime = (str(date) + ' ' + str(starttime))
startdatetime = datetime.datetime.strptime(startdatetime, startdatetime = datetime.datetime.strptime(startdatetime,
@@ -3277,6 +3354,7 @@ def workout_edit_view(request,id=0,message="",successmessage=""):
row.notes = notes row.notes = notes
row.duration = duration row.duration = duration
row.distance = distance row.distance = distance
row.boattype = boattype
row.privacy = privacy row.privacy = privacy
row.save() row.save()
# change data in csv file # change data in csv file
@@ -4103,7 +4181,26 @@ def workout_getc2workout_view(request,c2id):
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
# This is the main view for processing uploaded files # This is the main view for processing uploaded files
@login_required() @login_required()
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) r = Rower.objects.get(user=request.user)
if request.method == 'POST': if request.method == 'POST':
@@ -4115,10 +4212,22 @@ def workout_upload_view(request,message=""):
t = form.cleaned_data['title'] t = form.cleaned_data['title']
workouttype = form.cleaned_data['workouttype'] workouttype = form.cleaned_data['workouttype']
notes = form.cleaned_data['notes']
make_plot = request.POST.getlist('make_plot')
plottype = request.POST['plottype']
notes = form.cleaned_data['notes'] notes = form.cleaned_data['notes']
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 request.session['uploadoptions'] = uploadoptions
f1 = res[0] # file name f1 = res[0] # file name
@@ -4126,6 +4235,7 @@ def workout_upload_view(request,message=""):
id,message = dataprep.new_workout_from_file(r,f2, id,message = dataprep.new_workout_from_file(r,f2,
workouttype=workouttype,
makeprivate=makeprivate, makeprivate=makeprivate,
title = t, title = t,
notes='') notes='')
@@ -4266,7 +4376,7 @@ def workout_upload_view(request,message=""):
return response return response
else: else:
form = DocumentsForm() form = DocumentsForm()
optionsform = UploadOptionsForm(initial=uploadoptions) optionsform = UploadOptionsForm(initial=uploadoptions)
return render(request, 'document_form.html', return render(request, 'document_form.html',
{'form':form, {'form':form,
@@ -5432,7 +5542,9 @@ def team_edit_view(request,id=0):
name = cd['name'] name = cd['name']
notes = cd['notes'] notes = cd['notes']
manager = request.user manager = request.user
private = cd['private'] private = cd['private']
viewing = cd['viewing']
res,message=teams.update_team(t,name,manager,private,notes,
viewing) viewing)
if res: if res:
url = reverse(team_view, url = reverse(team_view,
@@ -5470,7 +5582,9 @@ def team_create_view(request):
name = cd['name'] name = cd['name']
notes = cd['notes'] notes = cd['notes']
manager = request.user manager = request.user
private = cd['private'] private = cd['private']
viewing = cd['viewing']
res,message=teams.create_team(name,manager,private,notes,
viewing) viewing)
url = reverse(rower_teams_view) url = reverse(rower_teams_view)
response = HttpResponseRedirect(url) response = HttpResponseRedirect(url)