diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index e9273ba7..76aa0a2d 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -2049,30 +2049,25 @@ def leaflet_chart(lat,lon,name=""): return script,div -def leaflet_chart_compare(workoutids,labeldict={},startenddict={}): +def leaflet_chart_compare(course,workoutids,labeldict={},startenddict={}): data = [] for id in workoutids: w = Workout.objects.get(id=id) rowdata = rdata(w.csvfilename) df = pd.DataFrame({ 'id':id, - 'latitude':rowdata.df[' latitude'], - 'longitude':rowdata.df[' longitude'] + 'lat':rowdata.df[' latitude'], + 'lon':rowdata.df[' longitude'] }) - data.append(f) + data.append(df) df = pd.concat(data,axis=0) - if lat.empty or lon.empty: - return [0,"invalid coordinate data"] + latmean,lonmean,coordinates = course_coord_center(course) + lat_min, lat_max, long_min, long_max = course_coord_maxmin(course) # Throw out 0,0 - df = pd.DataFrame({ - 'lat':lat, - 'lon':lon - }) - df = df.replace(0,np.nan) df = df.loc[(df!=0).any(axis=1)] df.fillna(method='bfill',axis=0,inplace=True) @@ -2082,8 +2077,6 @@ def leaflet_chart_compare(workoutids,labeldict={},startenddict={}): if lat.empty or lon.empty: return [0,"invalid coordinate data"] - latmean = lat.mean() - lonmean = lon.mean() latbegin = lat[lat.index[0]] longbegin = lon[lon.index[0]] latend = lat[lat.index[-1]] @@ -5210,12 +5203,10 @@ def interactive_multiple_compare_chart(ids,xparam,yparam,plottype='line', 'workoutid'] 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) - nrworkouts = len(ids) try: diff --git a/rowers/models.py b/rowers/models.py index 3fea4dbb..c7cbf391 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -2917,6 +2917,17 @@ 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, + verbose_name="Email Address") + +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..15b2231c --- /dev/null +++ b/rowers/templates/followerform.html @@ -0,0 +1,39 @@ +{% extends "newbase.html" %} +{% load staticfiles %} +{% load rowerfilters %} +{% load tz %} + + + +{% block title %}Follow Virtual Challenge{% endblock %} + +{% block main %} + +

Follow {{ plannedession.name }}

+ + +
    +
  • +

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

  • +
  • +
    + + {{ form.as_table }} +
    + {% csrf_token %} + +
    +
  • +
+ +{% 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..86e61402 100644 --- a/rowers/templates/virtualevent.html +++ b/rowers/templates/virtualevent.html @@ -160,12 +160,16 @@
{% 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 %} + {% if request.user|isfollower:race.id %} +

Follow this challenge

+ {% endif %}

{% else %}

@@ -184,6 +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/urls.py b/rowers/urls.py index 3b0c6a4c..150a947c 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -348,6 +348,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..e2b06a4d 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, @@ -2734,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) @@ -2751,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 @@ -2820,3 +2910,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, diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index 2765a2d3..c672d11d 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -1542,7 +1542,7 @@ def virtualevent_mapcompare_view(request,id=0): int(w.id): w.__str__() for w in workouts } - script,div = leaflet_chart_compare(workoutids, + script,div = leaflet_chart_compare(race.course,workoutids, labeldict=labeldict, startenddict=startenddict)