From 2fcd9902bba8792f8187731790e5d5c247498ff5 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 2 Jun 2020 17:29:13 +0200 Subject: [PATCH 1/5] first cut at followers --- rowers/models.py | 10 +++++ rowers/templates/followerform.html | 47 +++++++++++++++++++++ rowers/templates/virtualevent.html | 5 ++- rowers/urls.py | 2 + rowers/views/racesviews.py | 68 ++++++++++++++++++++++++++++++ rowers/views/statements.py | 4 +- 6 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 rowers/templates/followerform.html diff --git a/rowers/models.py b/rowers/models.py index 3fea4dbb..6e57bfaa 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -2917,6 +2917,16 @@ def update_duplicates_on_delete(sender, instance, **kwargs): # conn.close() # engine.dispose() +class VirtualRaceFollower(models.Model): + user = models.ForeignKey(User,on_delete=models.CASCADE,null=True) + race = models.ForeignKey(VirtualRace,on_delete=models.CASCADE) + emailaddress = models.EmailField(max_length=254,blank=True,null=True) + +class FollowerForm(ModelForm): + class Meta: + model = VirtualRaceFollower + fields = ['emailaddress'] + # Virtual Race results (for keeping results when workouts are deleted) @python_2_unicode_compatible class VirtualRaceResult(models.Model): diff --git a/rowers/templates/followerform.html b/rowers/templates/followerform.html new file mode 100644 index 00000000..433dabbd --- /dev/null +++ b/rowers/templates/followerform.html @@ -0,0 +1,47 @@ +{% extends "newbase.html" %} +{% load staticfiles %} +{% load rowerfilters %} +{% load tz %} + +{% block scripts %} +{% include "monitorjobs.html" %} +{% endblock %} + +{% block title %}Comment Session {% endblock %} + +{% block main %} + +

Comments {{ plannedession.name }}

+ + + +{% endblock %} + +{% block sidebar %} +{% if 'racing' in active %} +{% include 'menu_racing.html' %} +{% else %} +{% include 'menu_plan.html' %} +{% endif %} +{% endblock %} diff --git a/rowers/templates/virtualevent.html b/rowers/templates/virtualevent.html index 3e2297ce..47debfd0 100644 --- a/rowers/templates/virtualevent.html +++ b/rowers/templates/virtualevent.html @@ -160,12 +160,14 @@
{% if request.user.is_anonymous %}

- Registered users of rowsandall.com can participate in this challenge. Participation is free, unless specified differently in the race comment above. + Registered users of rowsandall.com can participate in this challenge. + Participation is free, unless specified differently in the race comment above. {% if race.sessiontype == 'race' %}

Register

{% else %}

Register

{% endif %} +

Follow this challenge

{% else %}

@@ -184,6 +186,7 @@ {% else %}

Register

{% endif %} +

Follow this challenge

{% endif %} {% if button == 'submitbutton' %} diff --git a/rowers/urls.py b/rowers/urls.py index 1997e73f..b7fdeeb3 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -346,6 +346,8 @@ urlpatterns = [ views.virtualevent_uploadimage_view,name='virtualevent_uploadimage_view'), re_path(r'^virtualevent/(?P\d+)/setimage/(?P\d+)/$', views.virtualevent_setlogo_view,name='virtualevent_setlog_view'), + re_path(r'^virtualevent/(?P\d+)/follow/$', + views.addfollower_view,name='addfollower_view'), re_path(r'^logo/(?P\d+)/delete/$', views.logo_delete_view,name='logo_delete_view'), re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/darkskywind/$',views.workout_downloadwind_view,name='workout_downloadwind_view'), diff --git a/rowers/views/racesviews.py b/rowers/views/racesviews.py index ed1c53db..d58d1379 100644 --- a/rowers/views/racesviews.py +++ b/rowers/views/racesviews.py @@ -2820,3 +2820,71 @@ def virtualevent_submit_result_view(request,id=0,workoutid=0): 'rower':r, 'w_form':w_form, }) + +def addfollower_view(request,id=0): + try: + race = VirtualRace.objects.get(id=id) + except VirtualRace.DoesNotExist: + raise Http404("Virtual Challenge does not exist") + + if not request.user.is_anonymous: + follower = VirtualRaceFollower( + user=request.user, + race=race, + emailaddress = request.user.email + ) + follower.save() + messages.info(request,"You will receive challenge notifications per email") + url = reverse(virtualevent_view, + kwargs={'id':id}) + + return HttpResponseRedirect(url) + + # Anonymous + if request.method == 'POST': + form = FollowerForm(request.POST) + if form.is_valid(): + email = form.cleaned_data['emailaddress'] + + follower = VirtualRaceFollower( + race=race, + emailaddress = email + ) + follower.save() + + messages.info(request,"You will receive challenge notifications per email") + + url = reverse(virtualevent_view, + kwargs={'id':id}) + + return HttpResponseRedirect(url) + + else: + form = FollowerForm() + + breadcrumbs = [ + { + 'url':reverse('virtualevents_view'), + 'name': 'Challenges' + }, + { + 'url':reverse('virtualevent_view', + kwargs={'id':race.id} + ), + 'name': race.name + }, + { + 'url': reverse(addfollower_view, + kwargs = {'id':race.id} + ), + 'name': 'Follow' + } + ] + + return render(request,'followerform.html', + { + 'form':form, + 'active':'nav-racing', + 'breadcrumbs':breadcrumbs, + } + ) diff --git a/rowers/views/statements.py b/rowers/views/statements.py index 2a931d32..2bd13221 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -116,6 +116,7 @@ from rowers.models import ( PlannedSessionComment,CoachRequest,CoachOffer, VideoAnalysis,ShareKey, StandardCollection,CourseStandard, + VirtualRaceFollower, ) from rowers.models import ( RowerPowerForm,RowerForm,GraphImage,AdvancedWorkoutForm, @@ -129,7 +130,8 @@ from rowers.models import ( VirtualRaceForm,VirtualRaceResultForm,RowerImportExportForm, IndoorVirtualRaceResultForm,IndoorVirtualRaceResult, IndoorVirtualRaceForm,PlannedSessionCommentForm, - Alert, Condition, StaticChartRowerForm + Alert, Condition, StaticChartRowerForm, + FollowerForm, ) from rowers.models import ( FavoriteForm,BaseFavoriteFormSet,SiteAnnouncement,BasePlannedSessionFormSet, From b6c0b03c39f879333ed76a251758a97b244a8764 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 2 Jun 2020 17:52:46 +0200 Subject: [PATCH 2/5] follower --- rowers/templates/followerform.html | 17 ++--------- rowers/templates/mapcompare.html | 49 ++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 15 deletions(-) create mode 100644 rowers/templates/mapcompare.html diff --git a/rowers/templates/followerform.html b/rowers/templates/followerform.html index 433dabbd..248e2d04 100644 --- a/rowers/templates/followerform.html +++ b/rowers/templates/followerform.html @@ -3,11 +3,9 @@ {% load rowerfilters %} {% load tz %} -{% block scripts %} -{% include "monitorjobs.html" %} -{% endblock %} -{% block title %}Comment Session {% endblock %} + +{% block title %}Follow Virtual Challenge{% endblock %} {% block main %} @@ -15,17 +13,6 @@
  • - {% for c in comments %} -

    - {{ c.created }} - {{ c.user.first_name }} {{ c.user.last_name }} -

    -
    - {{ c.comment }} -
    -
    -

    - {% endfor %}
    {{ form.as_table }} diff --git a/rowers/templates/mapcompare.html b/rowers/templates/mapcompare.html new file mode 100644 index 00000000..e9ca1e92 --- /dev/null +++ b/rowers/templates/mapcompare.html @@ -0,0 +1,49 @@ +{% extends "newbase.html" %} +{% load staticfiles %} +{% load rowerfilters %} + +{% block title %}View Comparison {% endblock %} + +{% block main %} + + + + +{{ interactiveplot |safe }} + +

    Interactive Comparison

    + + +
      +
    • +
      + {{ the_div|safe }} +
      + +
    • +
    • + + {% csrf_token %} +
    + {{ chartform.as_table }} +
    + +

    + +

    +
    +
  • +
+ + +{% endblock %} + +{% block sidebar %} +{% if active == 'nav-racing' %} +{% include 'menu_racing.html' %} +{% else %} +{% include 'menu_workouts.html' %} +{% endif %} +{% endblock %} From 76769266f3bce16095ca182dfc35eaa4241871d2 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 2 Jun 2020 20:13:46 +0200 Subject: [PATCH 3/5] cange --- rowers/interactiveplots.py | 145 +++++++++++++++++++++++++++- rowers/models.py | 3 +- rowers/templates/followerform.html | 7 +- rowers/templates/virtualevent.html | 4 + rowers/templatetags/rowerfilters.py | 18 +++- rowers/views/racesviews.py | 77 ++++++++++++++- 6 files changed, 247 insertions(+), 7 deletions(-) diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index dbca08ba..51f4d105 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -2047,6 +2047,147 @@ def leaflet_chart(lat,lon,name=""): + return script,div + +def leaflet_chart_compare(workoutids,labeldict={},startenddict={}): + data = [] + for id in workoutids: + w = Workout.objects.get(id=id) + rowdata = rdata(w.csvfilename) + df = pd.DataFrame({ + 'id':id, + 'lat':rowdata.df[' latitude'], + 'lon':rowdata.df[' longitude'] + }) + data.append(f) + + df = pd.concat(data,axis=0) + + + + + # Throw out 0,0 + df = df.replace(0,np.nan) + df = df.loc[(df!=0).any(axis=1)] + df.fillna(method='bfill',axis=0,inplace=True) + df.fillna(method='ffill',axis=0,inplace=True) + lat = df['lat'] + lon = df['lon'] + if lat.empty or lon.empty: + return [0,"invalid coordinate data"] + + + + coordinates = zip(lat,lon) + + scoordinates = "[" + + for x,y in coordinates: + scoordinates += """[{x},{y}], + """.format( + x=x, + y=y + ) + + scoordinates += "]" + + script = """ + + """.format( + latmean=latmean, + lonmean=lonmean, + latbegin = latbegin, + latend=latend, + longbegin=longbegin, + longend=longend, + scoordinates=scoordinates, + ) + + div = """ +

 

+ """ + + + return script,div def leaflet_chart2(lat,lon,name=""): @@ -5057,13 +5198,11 @@ def interactive_multiple_compare_chart(ids,xparam,yparam,plottype='line', 'time','pace','workoutstate', 'workoutid'] - datadf = dataprep.getsmallrowdata_db(columns,ids=ids,doclean=False,compute=False) - + datadf = dataprep.getsmallrowdata_db(columns,ids=ids) datadf.dropna(axis=1,how='all',inplace=True) datadf.dropna(axis=0,how='any',inplace=True) - nrworkouts = len(ids) try: diff --git a/rowers/models.py b/rowers/models.py index 6e57bfaa..c7cbf391 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -2920,7 +2920,8 @@ def update_duplicates_on_delete(sender, instance, **kwargs): class VirtualRaceFollower(models.Model): user = models.ForeignKey(User,on_delete=models.CASCADE,null=True) race = models.ForeignKey(VirtualRace,on_delete=models.CASCADE) - emailaddress = models.EmailField(max_length=254,blank=True,null=True) + emailaddress = models.EmailField(max_length=254,blank=True,null=True, + verbose_name="Email Address") class FollowerForm(ModelForm): class Meta: diff --git a/rowers/templates/followerform.html b/rowers/templates/followerform.html index 248e2d04..15b2231c 100644 --- a/rowers/templates/followerform.html +++ b/rowers/templates/followerform.html @@ -9,9 +9,14 @@ {% block main %} -

Comments {{ plannedession.name }}

+

Follow {{ plannedession.name }}

+
    +
  • +

    To follow this challenge and receive all news per email, fill out your + email address and submit the form. +

  • diff --git a/rowers/templates/virtualevent.html b/rowers/templates/virtualevent.html index 47debfd0..86e61402 100644 --- a/rowers/templates/virtualevent.html +++ b/rowers/templates/virtualevent.html @@ -167,7 +167,9 @@ {% else %}

    Register

    {% endif %} + {% if request.user|isfollower:race.id %}

    Follow this challenge

    + {% endif %}

    {% else %}

    @@ -186,7 +188,9 @@ {% else %}

    Register

    {% endif %} + {% if request.user|isfollower:race.id %}

    Follow this challenge

    + {% endif %}

    {% endif %} {% if button == 'submitbutton' %} diff --git a/rowers/templatetags/rowerfilters.py b/rowers/templatetags/rowerfilters.py index 84b5c947..6a60d032 100644 --- a/rowers/templatetags/rowerfilters.py +++ b/rowers/templatetags/rowerfilters.py @@ -12,7 +12,8 @@ from rowers.utils import calculate_age from rowers.models import ( course_length,WorkoutComment, TrainingMacroCycle,TrainingMesoCycle, TrainingMicroCycle, - Rower,Workout,SiteAnnouncement, TeamInvite, TeamRequest, CoachOffer,CoachRequest + Rower,Workout,SiteAnnouncement, TeamInvite, TeamRequest, CoachOffer,CoachRequest, + VirtualRaceFollower,VirtualRace, ) from rowers.plannedsessions import ( race_can_register, race_can_submit,race_rower_status @@ -40,6 +41,21 @@ from django.template.defaultfilters import stringfilter from six import string_types +@register.filter +def isfollower(user,id): + + if user.is_anonymous: + return True + try: + race = VirtualRace.objects.get(id=id) + except VirtualRace.DoesNotExist: + return False + + followers = VirtualRaceFollower.objects.filter(race=race,user=user) + + + return followers.count()==0 + @register.filter def adaptive(s): u = s diff --git a/rowers/views/racesviews.py b/rowers/views/racesviews.py index d58d1379..09a11a5c 100644 --- a/rowers/views/racesviews.py +++ b/rowers/views/racesviews.py @@ -1625,6 +1625,39 @@ def virtualevent_addboat_view(request,id=0): "You have successfully registered for this race. Good luck!" ) + otherrecords = VirtualRaceResult.objects.filter( + race = race).exclude(userid = r.id) + + for otherrecord in otherrecords: + otheruser = Rower.objects.get(id=otherrecord.userid) + othername = otheruser.user.first_name+' '+otheruser.user.last_name + registeredname = r.user.first_name+' '+r.user.last_name + if otherrecord.emailnotifications: + job = myqueue( + queue, + handle_sendemail_raceregistration, + otheruser.user.email, othername, + registeredname, + race.name, + race.id + ) + + followers = VirtualRaceFollower.objects.filter(race = race) + + for follower in followers: + othername = '' + if follower.user: + othername = follower.user.first_name+' '+follower.user.last_name + + registeredname = r.user.first_name+' '+r.user.last_name + email = follower.emailaddress + job = myqueue( + queue, + handle_sendemail_raceregistration, + email, othername, + registeredname,race.name,race.id, + ) + url = reverse('virtualevent_view', kwargs = { 'id':race.id @@ -1804,7 +1837,12 @@ def virtualevent_register_view(request,id=0): add_rower_race(r,race) - otherrecords = IndoorVirtualRaceResult.objects.filter( + # remove followers + myfollows = VirtualRaceFollower.objects.filter(user=r.user,race=race) + for f in myfollows: + f.delete() + + otherrecords = VirtualRaceResult.objects.filter( race = race).exclude(userid = r.id) for otherrecord in otherrecords: @@ -1821,6 +1859,22 @@ def virtualevent_register_view(request,id=0): race.id ) + followers = VirtualRaceFollower.objects.filter(race = race) + + for follower in followers: + othername = '' + if follower.user: + othername = follower.user.first_name+' '+follower.user.last_name + + registeredname = r.user.first_name+' '+r.user.last_name + email = follower.emailaddress + job = myqueue( + queue, + handle_sendemail_raceregistration, + email, othername, + registeredname,race.name,race.id, + ) + messages.info( request, @@ -2039,6 +2093,11 @@ def indoorvirtualevent_register_view(request,id=0): add_rower_race(r,race) + # remove followers + myfollows = VirtualRaceFollower.objects.filter(user=r.user,race=race) + for f in myfollows: + f.delete() + otherrecords = IndoorVirtualRaceResult.objects.filter( race = race).exclude(userid = r.id) @@ -2056,6 +2115,22 @@ def indoorvirtualevent_register_view(request,id=0): race.id ) + followers = VirtualRaceFollower.objects.filter(race = race) + + for follower in followers: + othername = '' + if follower.user: + othername = follower.user.first_name+' '+follower.user.last_name + + registeredname = r.user.first_name+' '+r.user.last_name + email = follower.emailaddress + job = myqueue( + queue, + handle_sendemail_raceregistration, + email, othername, + registeredname,race.name,race.id, + ) + messages.info( request, From 5386f37b780d31961b9a7e1976fab0e7c8eb6c14 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 2 Jun 2020 20:16:44 +0200 Subject: [PATCH 4/5] fix --- rowers/interactiveplots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index 51f4d105..faa5c3c0 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -5198,7 +5198,7 @@ def interactive_multiple_compare_chart(ids,xparam,yparam,plottype='line', 'time','pace','workoutstate', 'workoutid'] - datadf = dataprep.getsmallrowdata_db(columns,ids=ids) + datadf = dataprep.getsmallrowdata_db(columns,ids=ids,doclean=False,compute=False) datadf.dropna(axis=1,how='all',inplace=True) datadf.dropna(axis=0,how='any',inplace=True) From 34d6e456f045707fe3a36faa093fae53da948d09 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 2 Jun 2020 20:27:41 +0200 Subject: [PATCH 5/5] followers --- rowers/views/racesviews.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/rowers/views/racesviews.py b/rowers/views/racesviews.py index 09a11a5c..e2b06a4d 100644 --- a/rowers/views/racesviews.py +++ b/rowers/views/racesviews.py @@ -2809,6 +2809,7 @@ def virtualevent_submit_result_view(request,id=0,workoutid=0): if not jobid: messages.info(request,"Result submitted successfully.") + for otherrecord in otherrecords: try: otheruser = Rower.objects.get(id=otherrecord.userid) @@ -2826,7 +2827,21 @@ def virtualevent_submit_result_view(request,id=0,workoutid=0): except Rower.DoesNotExist: pass + followers = VirtualRaceFollower.objects.filter(race = race) + for follower in followers: + othername = '' + if follower.user: + othername = follower.user.first_name+' '+follower.user.last_name + + registeredname = r.user.first_name+' '+r.user.last_name + email = follower.emailaddress + job = myqueue( + queue, + handle_sendemail_racesubmission, + email, othername, + registeredname,race.name,race.id, + ) # redirect to race page