diff --git a/rowers/forms.py b/rowers/forms.py index 3a529876..0be90550 100644 --- a/rowers/forms.py +++ b/rowers/forms.py @@ -1206,7 +1206,14 @@ class WorkoutRaceSelectForm(forms.Form): ) # self.fields['evaluate_after'] = - + +# form to send messages to team members +class TeamMessageForm(forms.Form): + message = forms.CharField(required=True, + initial='', + widget=forms.Textarea() + ) + # Form to select team by rower class RowerTeamForm(forms.Form): team = forms.ModelChoiceField( diff --git a/rowers/models.py b/rowers/models.py index 6aec9f3a..7b42bf24 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -861,7 +861,7 @@ class Rower(models.Model): privacy = models.CharField(default='visible',max_length=30, choices=privacychoices) - team = models.ManyToManyField(Team,blank=True) + team = models.ManyToManyField(Team,blank=True,related_name='rower') # Export and Time Zone Settings defaulttimezone = models.CharField(default='UTC',max_length=100, diff --git a/rowers/tasks.py b/rowers/tasks.py index a3ffea10..065ecca4 100644 --- a/rowers/tasks.py +++ b/rowers/tasks.py @@ -1321,6 +1321,44 @@ def handle_sendemailcsv(first_name, last_name, email, csvfile,**kwargs): return 1 +@app.task +def handle_sendemail_ical(first_name, last_name, email, url, icsfile, **kwargs): + # send email with attachment + fullemail = first_name + " " + last_name + " " + "<" + email + ">" + subject = "Calendar File for your sessions from Rowsandall.com" + + if 'debug' in kwargs: + debug = kwargs['debug'] + else: + debug = False + + siteurl = SITE_URL + if debug: + progressurl = SITE_URL_DEV + siteurl = SITE_URL_DEV + + + d = {'first_name':first_name, + 'siteurl':siteurl, + 'url':url, + } + + from_email = 'Rowsandall ' + + + res = send_template_email(from_email,[fullemail], + subject,'icsemail.html',d, + attach_file=icsfile,**kwargs) + + + try: + os.remove(csvfile) + except: + pass + + return 1 + + @app.task def handle_sendemailfile(first_name, last_name, email, csvfile,**kwargs): @@ -1982,6 +2020,25 @@ def handle_sendemailnewcomment(first_name, return 1 +@app.task +def handle_sendemail_message(email,fromemail,rowername,message,teamname,managername, + debug=False,**kwargs): + + fullemail = email + subject = 'New message from team ' + teamname + from_email = fromemail + + d = { + 'rowername':rowername, + 'teamname':teamname, + 'managername':managername, + 'message':message, + } + + res = send_template_email(from_email,[fullemail],subject, + 'teammessage.html',d,**kwargs) + + return 1 @app.task def handle_sendemail_request(email, name, code, teamname, requestor, id, diff --git a/rowers/teams.py b/rowers/teams.py index 7a99f6d3..a11cbdae 100644 --- a/rowers/teams.py +++ b/rowers/teams.py @@ -37,6 +37,7 @@ from rowers.tasks import ( handle_sendemail_coachoffer_accepted, handle_sendemail_coachrequest_rejected, handle_sendemail_coachrequest_accepted, + handle_sendemail_message, ) from rowers.models import ValidationError @@ -170,11 +171,21 @@ def remove_coach(coach,rower): return (1,'Coach removed') +def rower_get_managers(rower): + managers = [] + for team in rower.team.all(): + managers.append(team.manager.rower) + + return managers + def rower_get_coaches(rower): coaches = [] for group in rower.coachinggroups.all(): - coach = Rower.objects.get(mycoachgroup=group) - coaches.append(coach) + try: + coach = Rower.objects.get(mycoachgroup=group) + coaches.append(coach) + except Rower.DoesNotExist: + pass return coaches @@ -567,7 +578,17 @@ def send_invite_accept_email(invitation): return (1,'Invitation email sent') +def send_team_message(team,message): + rowers = team.rower.all() + managername = team.manager.first_name + " " + team.manager.last_name + for rower in rowers: + rowername = rower.user.first_name + " " + rower.user.last_name + res = myqueue(queuehigh, + handle_sendemail_message, + rower.user.email,team.manager.email,rowername,message,team.name,managername) + + return (1,'message sent') def send_request_email(rekwest): email = rekwest.team.manager.email @@ -606,7 +627,7 @@ def process_request_code(manager,code): rekwest.delete() - return result + return (result,'The member was added') def process_invite_code(user,code): code = code.upper() diff --git a/rowers/templates/icsemail.html b/rowers/templates/icsemail.html index ffab16ea..4914a4a0 100644 --- a/rowers/templates/icsemail.html +++ b/rowers/templates/icsemail.html @@ -4,10 +4,14 @@

Dear {{ first_name }},

- Please find attached the requested ICS Calendar file. You can import + Please find attached the ICS Calendar file for your training sessions. You can import this file to your calendar app.

+

+ You can see the training plan here: {{ siteurl }}{{ url }} +

+

Best Regards, the Rowsandall Team

diff --git a/rowers/templates/plannedsessionscoach.html b/rowers/templates/plannedsessionscoach.html index dd0a76fa..23540b2d 100644 --- a/rowers/templates/plannedsessionscoach.html +++ b/rowers/templates/plannedsessionscoach.html @@ -6,9 +6,9 @@ {% block main %} {% if theteam %} -

Coach Overview. Team {{ theteam.name }}

+

Group Overview. Team {{ theteam.name }}

{% else %} -

Coach Overview

+

Group Overview

{% endif %}
    @@ -83,6 +83,17 @@ {% endfor %} +

    + + Get Calendar File +

    + {% if rower|is_coach:rowers %} +

    + + Send Calendar File to all rowers + +

    + {% endif %} {% if unmatchedworkouts %} diff --git a/rowers/templates/team.html b/rowers/templates/team.html index 242abdd5..59c0fc34 100644 --- a/rowers/templates/team.html +++ b/rowers/templates/team.html @@ -72,6 +72,19 @@

    {% endif %} +
  • + {% if team.manager == user %} +

    Message

    +

    Use this form to send a message to all team members by email.

    +
    + + {{ groupmessageform.as_table }} +
    + {% csrf_token %} + +
    + {% endif %} +
diff --git a/rowers/templates/teammessage.html b/rowers/templates/teammessage.html new file mode 100644 index 00000000..a660c5af --- /dev/null +++ b/rowers/templates/teammessage.html @@ -0,0 +1,14 @@ +{% extends "emailbase.html" %} + +{% block body %} +

Dear {{ first_name }},

+ +

+ Here is a message from {{ managername }}, the team manager of rowsandall.com team {{ teamname }}. +

+

+ + {{ message }} + +

+{% endblock %} diff --git a/rowers/templatetags/rowerfilters.py b/rowers/templatetags/rowerfilters.py index 3ee2a7e2..820c8a54 100644 --- a/rowers/templatetags/rowerfilters.py +++ b/rowers/templatetags/rowerfilters.py @@ -66,6 +66,17 @@ def strfdelta(tdelta): return res +from rowers.teams import rower_get_managers + +@register.filter +def is_coach(rower,rowers): + for r in rowers: + if rower not in rower_get_managers(r): + print(r,rower) + return False + + return True + def strfdeltah(tdelta): hours, rest = divmod(tdelta.seconds,3600) minutes,seconds = divmod(rest,60) diff --git a/rowers/urls.py b/rowers/urls.py index a2271402..02d7b112 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -673,6 +673,9 @@ urlpatterns = [ name='plannedsessions_coach_view'), re_path(r'^sessions/coach/user/(?P\d+)/$',views.plannedsessions_coach_view, name='plannedsessions_coach_view'), + re_path(r'^sessions/coach/sendcalendar/user/(?P\d+)/$', + views.plannedsessions_coach_icsemail_view, + name='plannedsessions_coach_icsemail_view'), re_path(r'^sessions/print/?/$',views.plannedsessions_print_view, name='plannedsessions_print_view'), re_path(r'^sessions/(?P\d+)/comments/user/(?P\d+)/$',views.plannedsession_comment_view, diff --git a/rowers/views/exportviews.py b/rowers/views/exportviews.py index 66496a55..eb9588c8 100644 --- a/rowers/views/exportviews.py +++ b/rowers/views/exportviews.py @@ -71,6 +71,69 @@ def plannedsessions_icsemail_view(request,userid=0): return response +@login_required() +def plannedsessions_coach_icsemail_view(request,userid=0): + therower = getrequestplanrower(request,userid=userid) + startdate,enddate = get_dates_timeperiod(request) + + if 'coach' in request.user.rower.rowerplan: + sps = get_sessions_manager(request.user,teamid=0, + enddate=enddate, + startdate=startdate) + else: + rteams = therower.team.filter(viewing='allmembers') + sps = get_sessions(therower,startdate=startdate,enddate=enddate) + + if therower.rowerplan != 'freecoach': + rowers = [therower] + else: + rowers = [] + + rowers = list(set(rowers)) + + cal = Calendar() + cal.add('prodid','rowsandall') + cal.add('version','1.0') + + for ps in sps: + event = Event() + comment = '{d} {u} {c}'.format( + d=ps.sessionvalue, + u = ps.sessionunit, + c = ps.criterium) + event.add('summary',ps.name) + event.add('dtstart',ps.preferreddate) + event.add('dtend',ps.preferreddate) + event['uid'] = 'plannedsession_'+str(ps.id) + event.add('description',ps.comment) + event.add('comment',comment) + cal.add_component(event) + + + icalstring = cal.to_ical() + fname = "media/training_plan_{d1}_{d2}".format( + d1 = startdate.strftime("%Y%m%d"), + d2 = enddate.strftime("%Y%m%d"), + ) + + url = reverse('plannedsessions_coach_view')+'?when={d1}/{d2}'.format( + d1 = startdate.strftime("%Y-%m-%d"), + d2 = enddate.strftime("%Y-%m-%d"), + ) + + for rower in rowers: + fname2 = fname+"_{u}.ics".format(u=rower.id) + with open(fname2,'wb') as fop: + fop.write(icalstring) + + job = myqueue(queue,handle_sendemail_ical, + rower.user.first_name, + rower.user.last_name, + rower.user.email, + url, + fname2,debug=False) + + return HttpResponseRedirect(url) @login_required() def course_kmldownload_view(request,id=0): diff --git a/rowers/views/statements.py b/rowers/views/statements.py index e2b470c3..5dd22c60 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -48,7 +48,7 @@ from django.http import ( ) from django.contrib.auth import authenticate, login, logout from rowers.forms import ( - ForceCurveOptionsForm,HistoForm, + ForceCurveOptionsForm,HistoForm,TeamMessageForm, LoginForm,DocumentsForm,UploadOptionsForm,ImageForm,CourseForm, TeamUploadOptionsForm,WorkFlowLeftPanelForm,WorkFlowMiddlePanelForm, WorkFlowLeftPanelElement,WorkFlowMiddlePanelElement, @@ -183,6 +183,7 @@ from rowers.tasks import ( handle_sendemail_userdeleted, handle_sendemail_raceregistration, handle_sendemail_racesubmission, + handle_sendemail_ical, ) from scipy.signal import savgol_filter diff --git a/rowers/views/teamviews.py b/rowers/views/teamviews.py index b1c730a4..8610beaa 100644 --- a/rowers/views/teamviews.py +++ b/rowers/views/teamviews.py @@ -27,7 +27,7 @@ def team_view(request,id=0,userid=0): q = q | q2 - if request.method == 'POST' and request.user == t.manager: + if request.method == 'POST' and request.user == t.manager and 'email' in request.POST: inviteform = TeamInviteForm(request.POST) inviteform.fields['user'].queryset = q @@ -45,12 +45,24 @@ def team_view(request,id=0,userid=0): else: message = text messages.error(request,message) + groupmessageform = TeamMessageForm() + elif request.method == 'POST' and request.user == t.manager and 'message' in request.POST: + groupmessageform = TeamMessageForm(request.POST) + inviteform = TeamInviteForm() + if groupmessageform.is_valid(): + message = groupmessageform.cleaned_data['message'] + teams.send_team_message(t,message) + messages.info(request,'Message was sent to all team members') + groupmessageform = TeamMessageForm() elif request.user == t.manager: inviteform = TeamInviteForm() inviteform.fields['user'].queryset = q + groupmessageform = TeamMessageForm() else: inviteform = '' + groupmessageform = '' + members = Rower.objects.filter(team=t).order_by('user__last_name','user__first_name') thisteammyrequests = TeamRequest.objects.filter(team=t,user=request.user) @@ -63,7 +75,7 @@ def team_view(request,id=0,userid=0): breadcrumbs = [ { 'url':reverse(rower_teams_view), - 'name': 'Teams' + 'name': 'Groups' }, { 'url':reverse(team_view,kwargs={'id':id}), @@ -78,6 +90,7 @@ def team_view(request,id=0,userid=0): 'teams':get_my_teams(request.user), 'myteams':myteams, 'memberteams':memberteams, + 'groupmessageform': groupmessageform, 'members':members, 'breadcrumbs':breadcrumbs, 'active':'nav-teams', @@ -98,7 +111,7 @@ def team_leaveconfirm_view(request,id=0): breadcrumbs = [ { 'url':reverse(rower_teams_view), - 'name': 'Teams' + 'name': 'Groups' }, { 'url':reverse(team_view,kwargs={'id':id}), @@ -242,7 +255,7 @@ def rower_teams_view(request,message='',successmessage=''): breadcrumbs = [ { 'url':reverse(rower_teams_view), - 'name': 'Teams' + 'name': 'Groups' } ] @@ -328,7 +341,7 @@ def athlete_drop_coach_confirm_view(request,id): breadcrumbs = [ { 'url':reverse('rower_teams_view'), - 'name': 'Teams' + 'name': 'Groups' }, { 'url':reverse('athlete_drop_coach_confirm_view',kwargs={'id':id}), @@ -355,7 +368,7 @@ def coach_drop_athlete_confirm_view(request,id): breadcrumbs = [ { 'url':reverse('rower_teams_view'), - 'name': 'Teams' + 'name': 'Groups' }, { 'url':reverse('coach_drop_athlete_confirm_view',kwargs={'id':id}), @@ -609,7 +622,7 @@ def team_edit_view(request,id=0): breadcrumbs = [ { 'url':reverse(rower_teams_view), - 'name': 'Teams' + 'name': 'Groups' }, { 'url':reverse(team_view,kwargs={'id':id}), @@ -675,7 +688,7 @@ def team_create_view(request): breadcrumbs = [ { 'url':reverse(rower_teams_view), - 'name': 'Teams' + 'name': 'Groups' }, { 'url':reverse(team_create_view), @@ -708,7 +721,7 @@ def team_deleteconfirm_view(request,id): breadcrumbs = [ { 'url':reverse(rower_teams_view), - 'name': 'Teams' + 'name': 'Groups' }, { 'url':reverse(team_view,kwargs={'id':id}), @@ -764,7 +777,7 @@ def team_members_stats_view(request,id): breadcrumbs = [ { 'url':reverse(rower_teams_view), - 'name': 'Teams' + 'name': 'Groups' }, { 'url':reverse(team_view,kwargs={'id':id}),