diff --git a/rowers/datautils.py b/rowers/datautils.py index bff11450..7f8c1af4 100644 --- a/rowers/datautils.py +++ b/rowers/datautils.py @@ -452,7 +452,7 @@ def getfastest(df,thevalue,mode='distance'): starttime = griddata(restime,starttimes,[thevalue*60*1000],method='linear',rescale=True) duration = griddata(restime,restime,[thevalue*60*1000],method='linear',rescale=True) endtime = starttime+duration - #print(distance,starttime,endtime ) + print(distance,starttime,endtime ) return distance[0],starttime[0]/1000.,endtime[0]/1000. return 0 diff --git a/rowers/forms.py b/rowers/forms.py index dba009c4..eff14e8a 100644 --- a/rowers/forms.py +++ b/rowers/forms.py @@ -15,7 +15,7 @@ from rowers.rows import validate_file_extension,must_be_csv,validate_image_exten from django.contrib.auth.forms import UserCreationForm from django.contrib.auth.models import User from django.contrib.admin.widgets import AdminDateWidget -from django.forms.widgets import SelectDateWidget +from django.forms.widgets import SelectDateWidget,HiddenInput #from django.forms.extras.widgets import SelectDateWidget from django.utils import timezone,translation from django.forms import ModelForm, Select @@ -410,6 +410,8 @@ class UploadOptionsForm(forms.Form): initial='workout_edit_view', label='After Upload, go to') + raceid = forms.IntegerField(initial=0,widget=HiddenInput()) + class Meta: fields = ['make_plot','plottype','upload_toc2','makeprivate'] @@ -448,6 +450,7 @@ class UploadOptionsForm(forms.Form): if int(raceid) in [r.id for r in races]: therace = VirtualRace.objects.get(id=raceid) + self.fields['raceid'].initial = therace.id if therace.sessiontype == 'race': registrations = VirtualRaceResult.objects.filter(race=therace,userid=r.id) else: diff --git a/rowers/plannedsessions.py b/rowers/plannedsessions.py index b0ac380b..77cf6117 100644 --- a/rowers/plannedsessions.py +++ b/rowers/plannedsessions.py @@ -371,7 +371,7 @@ def add_workouts_plannedsession(ws,ps,r): ids = [w.id for w in wold] + [w.id for w in ws] ids = list(set(ids)) - if len(ids)>1 and ps.sessiontype in ['test','coursetest','race','fastest_distance','fastest_time']: + if len(ids)>1 and ps.sessiontype in ['test','coursetest','race']: errors.append('For tests, you can only attach one workout') return result,comments,errors @@ -1496,23 +1496,33 @@ def default_class(r,w,race): boatclass=boatclass, adaptiveclass=adaptiveclass, boattype=boattype, + sex=sex, ).order_by( "agemax","-agemin","boattype","sex","weightclass", "referencespeed" ) if standards.count()==0: + # omit adaptive class standards = CourseStandard.objects.filter( agemin__lt=age,agemax__gt=age, - boattype=boattype + boattype=boattype,sex=sex, ).order_by( "agemax","-agemin","boattype","sex", "weightclass","referencespeed") if standards.count()==0: + # omit boattype standards = CourseStandard.objects.filter( - agemin__lt=age,agemax__gt=age + agemin__lt=age,agemax__gt=age,sex=sex, ).order_by( "agemax","-agemin","boattype","sex", "weightclass","referencespeed") + if standards.count()==0: + # omit boattype + standards = CourseStandard.objects.filter( + agemin__lt=age,agemax__gt=age,sex='male', + ).order_by( + "agemax","-agemin","boattype","sex", + "weightclass","referencespeed") if standards.count()==0: # boolean, boattype, boatclass, adaptiveclass, weightclass, sex, coursestandard, @@ -1526,6 +1536,153 @@ def default_class(r,w,race): # No Course Standard return True,boattype,boatclass,adaptiveclass,weightclass,sex,5.0,None +def add_workout_fastestrace(ws, race, r, recordid=0, doregister=False): + result = 0 + comments = [] + errors = [] + + start_time = race.start_time + start_date = race.startdate + startdatetime = datetime.combine(start_date,start_time) + startdatetime = pytz.timezone(race.timezone).localize( + startdatetime + ) + + end_time = race.end_time + end_date = race.enddate + enddatetime = datetime.combine(end_date,end_time) + enddatetime = pytz.timezone(race.timezone).localize( + enddatetime + ) + + ids = [w.id for w in ws] + ids = list(set(ids)) + + if len(ids)>1 and race.sessiontype in ['test','coursetest','race','indoorrace','fastest_time','fastest_distance']: + errors.append('For tests, you can only attach one workout') + return result,comments,errors,0 + + username = r.user.first_name+' '+r.user.last_name + if r.birthdate: + age = calculate_age(r.birthdate) + else: + age = None + + try: + record = IndoorVirtualRaceResult.objects.get( + userid=r.id, + race=race, + id=recordid + ) + except IndoorVirtualRaceResult.DoesNotExist: + if doregister: + hasinitial,boattype,boatclass,adaptiveclass,weightclass,sex,referencespeed,initialcategory = default_class(r,ws[0],race) + if hasinitial: + record = IndoorVirtualRaceResult( + userid = r.id, + username = r.user.first_name+' '+r.user.last_name, + weightcategory=weightclass, + adaptiveclass=adaptiveclass, + race=race, + boatclass=boatclass, + sex=sex, + age = age, + referencespeed=referencespeed, + entrycategory=initialcategory, + ) + record.save() + else: + errors.append("Unable to find a suitable start category") + return result,comments,errors,0 + else: + errors.append("Couldn't find this entry") + return result,comments,errors,0 + + records = IndoorVirtualRaceResult.objects.filter( + userid=r.id, + race=race, + workoutid = ws[0].id + ) + + if ws[0].workouttype != record.boatclass: + errors.append('Your workout boat class is different than on your race registration') + return 0,comments,errors,0 + + if ws[0].workouttype not in mytypes.otwtypes: + errors.append('You must submit a on-the-water rowing workout') + return 0,comments, errors, 0 + + if record.weightcategory == 'lwt' and ws[0].weightcategory != record.weightcategory: + errors.append('Your workout weight category did not match the weight category you registered') + return 0,comments, errors,0 + + if ws[0].adaptiveclass != record.adaptiveclass: + errors.append('Your adaptive classification did not match the registration') + return 0,comments, errors, 0 + + # start adding sessions + if ws[0].startdatetime>=startdatetime and ws[0].startdatetime<=enddatetime: + ws[0].plannedsession = race + ws[0].save() + result += 1 + + else: + errors.append('Workout %i did not match the race window' % ws[0].id) + return result,comments,errors,0 + + if result>0: + for otherrecord in records: + otherrecord.workoutid = None + otherrecord.coursecompleted = False + otherrecord.save() + + result, comment, errors = add_workouts_plannedsession(ws,race,r) + if result: + record.coursecompleted = True + record.workoutid = ws[0].id + if race.sessiontype == 'fastest_distance': + df = dataprep.getsmallrowdata_db(['time','cumdist'],ids=[ws[0].id]) + fastest_milliseconds,startsecond,endsecond = datautils.getfastest(df,race.sessionvalue,mode='distance') + velo = race.sessionvalue/fastest_milliseconds + points = 100.*velo/record.referencespeed + + if fastest_milliseconds > 0: + duration = to_time(1000.*fastest_milliseconds) + record.coursecompleted = True + record.duration = duration + record.distance = race.sessionvalue + record.points = points + record.startsecond = startsecond + record.endsecond = endsecond + record.save() + if race.sessiontype == 'fastest_time': + df = dataprep.getsmallrowdata_db(['time','cumdist'],ids=[ws[0].id]) + fastest_meters, startsecond, endsecond = datautils.getfastest(df,race.sessionvalue,mode='time') + velo = fastest_meters/(60.*race.sessionvalue) + points = 100.*velo/record.referencespeed + + if fastest_meters > 0: + duration = dt.time(0,race.sessionvalue) + record.duration = duration + record.distance = fastest_meters + record.coursecompleted = True + record.points = points + record.startsecond = startsecond + record.endsecond = endsecond + record.save() + + + + if ws[0].privacy == 'private': + ws[0].privacy = 'visible' + ws[0].save() + comments.append('Workouts submitted to virtual events have to be public. We have changed the workout to a public workout.') + + record.save() + else: + errors.append('Could not find a valid interval in this workout') + + return result, comments, errors, 0 # Low Level functions - to be called by higher level methods diff --git a/rowers/templates/fastestvirtualeventcreate.html b/rowers/templates/fastestvirtualeventcreate.html index 7034c3f3..d577973e 100644 --- a/rowers/templates/fastestvirtualeventcreate.html +++ b/rowers/templates/fastestvirtualeventcreate.html @@ -55,7 +55,11 @@ The participants can row this challenge on any course, and their fastest time over the challenge distance (respectively the largest distance achieved over the challenge duration) is automatically extracted from the workout. No +<<<<<<< HEAD + need to program a set piece. +======= need to program a set piece. +>>>>>>> develop

Standard Times are a way to compare results in a race category with diff --git a/rowers/templates/menu_racing.html b/rowers/templates/menu_racing.html index 4ebd5e26..2c04ced0 100644 --- a/rowers/templates/menu_racing.html +++ b/rowers/templates/menu_racing.html @@ -6,15 +6,10 @@

  • - +  New Challenge
  • -
  • - -  New Indoor Challenge - -
  • {% if race %} {% if results %}
  • diff --git a/rowers/templates/newchallengechoice.html b/rowers/templates/newchallengechoice.html new file mode 100644 index 00000000..b08c0395 --- /dev/null +++ b/rowers/templates/newchallengechoice.html @@ -0,0 +1,60 @@ +{% extends "newbase.html" %} +{% load staticfiles %} +{% load rowerfilters %} + +{% block title %}New Virtual Challenge{% endblock %} + +{% block main %} + +

    What type of challenge do you want to create?

    + +
      +
    • +

      Course Challenge

      + +
      + Marked Course +
      +
      +

      On-the-water challenge with a course. You have to row through + all the gates on the course to create a valid entry. Ideal for + local challenges. +

      +
    • +
    • +

      Time or Distance Challenge

      + +
      + Marked Course +
      +
      +

      On-the-water challenge over a set time or distance. This can be rowed + on any body of water in the world. Ideal to create a global on-the-water + challenge. +

      +
    • +
    • +

      Indoor Rowing Challenge

      + +
      + Marked Course +
      +
      +

      Indoor rowing challenge over a set time or distance. This can be rowed + on any indoor rowing machine. Dial up the given distance or time and row! + Ideal to create a global indoor rowing + challenge. +

      +
    • +
    + + +{% endblock %} + + +{% block sidebar %} +{% include 'menu_racing.html' %} +{% endblock %} diff --git a/rowers/templates/racelist.html b/rowers/templates/racelist.html index b74f9e67..86d9dbaf 100644 --- a/rowers/templates/racelist.html +++ b/rowers/templates/racelist.html @@ -27,6 +27,10 @@ {{ race.name }} {% if race.sessiontype == 'race' %} {{ race.course.country }} + {% elif race.sessiontype == 'fastest_distance' %} + Worldwide On-the-water Challenge + {% elif race.sessiontype == 'fastest_time' %} + Worldwide On-the-water Challenge {% else %} Worldwide Indoor Challenge {% endif %} diff --git a/rowers/templates/virtualevent.html b/rowers/templates/virtualevent.html index a76e0f54..11257fb3 100644 --- a/rowers/templates/virtualevent.html +++ b/rowers/templates/virtualevent.html @@ -96,6 +96,14 @@ Course{{ race.course }} + {% elif race.sessiontype == 'fastest_time' %} + + Time ChallengeTo be rowed on the water + + {% elif race.sessiontype == 'fastest_distance' %} + + Distance ChallengeTo be rowed on the water + {% else %} Indoor RaceTo be rowed on a Concept2 ergometer @@ -314,8 +322,19 @@ {{ forloop.counter }} + {% if race.sessiontype == 'race' %} - {{ result.username }} + {{ result.username }} + {% elif race.sessiontype == 'fastest_time' %} + + {{ result.username }} + {% elif race.sessiontype == 'fastest_distance' %} + + {{ result.username }} + {% else %} + {{ result.username }} + {% endif %} + {{ result.teamname }} {% if race.coursestandards %} {{ result.entrycategory }} @@ -586,6 +605,21 @@ You cannot submit results rowed on other bodies of water.

    + {% elif race.sessiontype == 'fastest_time' %} +

    + This on-the-water challenge asks you to try to get + as far as you can over the + race duration. It automatically finds the fastest interval + of the given duration + in your entire workout. +

    + {% elif race.sessiontype == 'fastest_distance' %} +

    + This on-the-water challenge asks you to try row as hard + as you can over a set distance. + It automatically finds the fastest interval of the given length + in your entire workout. +

    {% else %}

    Indoor challenges are open for all, wherever you live. diff --git a/rowers/urls.py b/rowers/urls.py index f67379d8..b2d003a4 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -260,8 +260,11 @@ urlpatterns = [ # re_path(r'^list-workouts/(?P\d+-\d+-\d+)/(?P\d+-\d+-\d+)/$',views.workouts_view, # name='workouts_view'), re_path(r'^virtualevents/$',views.virtualevents_view,name='virtualevents_view'), + re_path(r'^virtualevent/createchoice/$', TemplateView.as_view(template_name='newchallengechoice.html'),name='newchallengechoice'), re_path(r'^virtualevent/create/$',views.virtualevent_create_view,name='virtualevent_create_view'), re_path(r'^virtualevent/createindoor/$',views.indoorvirtualevent_create_view,name='indoorvirtualevent_create_view'), + re_path(r'^virtualevent/createfastest/$',views.fastestvirtualevent_create_view, + name='fastestvirtualevent_create_view'), re_path(r'^raceregistration/togglenotification/(?P\d+)/$', views.virtualevent_toggle_email_view,name='virtualevent_toggle_email_view'), re_path(r'^indoorraceregistration/togglenotification/(?P\d+)/$', @@ -455,6 +458,7 @@ urlpatterns = [ re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/split/$',views.workout_split_view,name='workout_split_view'), # re_path(r'^workout/(?P\d+)/interactiveplot/$',views.workout_biginteractive_view), re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/view/entry/(?P\d+)/$',views.workout_view,name='workout_view'), + re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/view/entry/(?P\d+)/nocourse/$',views.workout_view,name='workout_view'), re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/view/session/(?P\d+)/$',views.workout_view,name='workout_view'), re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/view/$',views.workout_view,name='workout_view'), re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/video/$',views.workout_video_create_view, diff --git a/rowers/views/planviews.py b/rowers/views/planviews.py index e61ce15b..85cf88ac 100644 --- a/rowers/views/planviews.py +++ b/rowers/views/planviews.py @@ -1881,12 +1881,14 @@ def plannedsession_view(request,id=0,userid=0): ps = get_object_or_404(PlannedSession,pk=id) - - if ps.sessiontype in ['race','indoorrace']: + try: + r = VirtualRace.objects.get(id=ps.id) url = reverse('virtualevent_view', - kwargs={'id':ps.id} - ) + kwargs={'id':ps.id} + ) return HttpResponseRedirect(url) + except VirtualRace.DoesNotExist: + pass if ps.course: coursescript,coursediv = course_map(ps.course) diff --git a/rowers/views/racesviews.py b/rowers/views/racesviews.py index aed19ff6..a2867ee3 100644 --- a/rowers/views/racesviews.py +++ b/rowers/views/racesviews.py @@ -2706,6 +2706,153 @@ def indoorvirtualevent_create_view(request): }) +@login_required() +def fastestvirtualevent_create_view(request): + r = getrower(request.user) + + if request.method == 'POST': + racecreateform = IndoorVirtualRaceForm(request.POST) + if racecreateform.is_valid(): + cd = racecreateform.cleaned_data + startdate = cd['startdate'] + start_time = cd['start_time'] + enddate = cd['enddate'] + end_time = cd['end_time'] + comment = cd['comment'] + sessionunit = cd['sessionunit'] + sessionvalue = cd['sessionvalue'] + name = cd['name'] + registration_form = cd['registration_form'] + registration_closure = cd['registration_closure'] + evaluation_closure = cd['evaluation_closure'] + contact_phone = cd['contact_phone'] + contact_email = cd['contact_email'] + coursestandards = cd['coursestandards'] + + # correct times + + timezone_str = cd['timezone'] + + startdatetime = datetime.datetime.combine(startdate,start_time) + enddatetime = datetime.datetime.combine(enddate,end_time) + + + startdatetime = pytz.timezone(timezone_str).localize( + startdatetime + ) + enddatetime = pytz.timezone(timezone_str).localize( + enddatetime + ) + evaluation_closure = pytz.timezone(timezone_str).localize( + evaluation_closure.replace(tzinfo=None) + ) + + if registration_form == 'manual': + try: + registration_closure = pytz.timezone( + timezone_str + ).localize( + registration_closure.replace(tzinfo=None) + ) + except AttributeError: + registration_closure = startdatetime + elif registration_form == 'windowstart': + registration_closure = startdatetime + elif registration_form == 'windowend': + registration_closure = enddatetime + else: + registration_closure = evaluation_closure + + if sessionunit == 'min': + sessionmode = 'time' + sessiontype = 'fastest_time' + else: + sessionmode = 'distance' + sessiontype = 'fastest_distance' + + + vs = VirtualRace( + name=name, + startdate=startdate, + preferreddate = startdate, + start_time = start_time, + enddate=enddate, + end_time=end_time, + comment=comment, + sessiontype = sessiontype, + sessionunit = sessionunit, + sessionmode = sessionmode, + sessionvalue = sessionvalue, + course=None, + timezone=timezone_str, + coursestandards=coursestandards, + evaluation_closure=evaluation_closure, + registration_closure=registration_closure, + contact_phone=contact_phone, + contact_email=contact_email, + country = 'Indoor', + manager=request.user, + ) + + vs.save() + + # create Site Announcement & Tweet + if settings.DEBUG or settings.TESTING: + dotweet = False + elif 'dev' in settings.SITE_URL: + dotweet = False + else: + dotweet = True + + announcementshort = "New Virtual Challenge on rowsandall.com: {name}".format( + name = name, + ) + + announcement = announcementshort + " {siteurl}/rowers/virtualevent/{raceid}/".format( + siteurl = SITE_URL, + raceid = vs.id + ) + + + if len(announcement)>250: + announcement = announcementshort + + sa = SiteAnnouncement( + announcement = announcement, + dotweet = dotweet + ) + + sa.save() + + url = reverse('virtualevents_view') + return HttpResponseRedirect(url) + else: + + racecreateform = IndoorVirtualRaceForm(timezone=r.defaulttimezone) + + + breadcrumbs = [ + { + 'url':reverse('virtualevents_view'), + 'name': 'Challenges' + }, + { + 'url':reverse('indoorvirtualevent_create_view', + ), + 'name': 'New Indoor Virtual Regatta' + }, + ] + + return render(request,'fastestvirtualeventcreate.html', + { + 'form':racecreateform, + 'breadcrumbs':breadcrumbs, + 'rower':r, + 'active':'nav-racing', + + }) + + @login_required() def virtualevent_create_view(request): r = getrower(request.user) @@ -3172,13 +3319,16 @@ def virtualevent_submit_result_view(request,id=0,workoutid=0): if selectedworkout is not None: - - workouts = Workout.objects.filter(id=selectedworkout) + if race.sessiontype == 'race': result,comments,errors,jobid = add_workout_race( workouts,race,r, splitsecond=splitsecond,recordid=recordid) + elif race.sessiontype in ['fastest_time','fastest_distance']: + result, comments, errors, jobid = add_workout_fastestrace( + workouts,race,r,recordid=recordid + ) else: result,comments,errors,jobid = add_workout_indoorrace( workouts,race,r,recordid=recordid) diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index 8ece34b7..630a8dca 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -2323,7 +2323,7 @@ def workout_fusion_list(request,id=0, # Basic view of workout @permission_required('workout.view_workout',fn=get_workout_by_opaqueid,raise_exception=True) -def workout_view(request,id=0,raceresult=0,sessionresult=0): +def workout_view(request,id=0,raceresult=0,sessionresult=0,nocourseraceresult=0): request.session['referer'] = absolute(request)['PATH'] if not request.user.is_anonymous: @@ -2373,6 +2373,26 @@ def workout_view(request,id=0,raceresult=0,sessionresult=0): except CourseTestResult.DoesNotExist: pass + if nocourseraceresult != 0: + try: + result = IndoorVirtualRaceResult.objects.get(id=nocourseraceresult) + startsecond = result.startsecond + endsecond = result.endsecond + duration = row.duration + durationsecs = duration.hour*3600+duration.minute*60+duration.second + itime = [startsecond,endsecond-startsecond] + itype = [3,4] + intervaldata['itime'] = itime + intervaldata['itype'] = itype + + rowdata.updateinterval_metric(' AverageBoatSpeed (m/s)',0.1,mode='larger', + debug=False,smoothwindow=15., + activewindow = [startsecond,endsecond]) + summary = rowdata.allstats() + except CourseTestResult.DoesNotExist: + pass + + if raceresult != 0: try: result = VirtualRaceResult.objects.get(id=raceresult) @@ -5042,7 +5062,7 @@ def workout_upload_view(request, response = {} if request.method == 'POST': form = DocumentsForm(request.POST,request.FILES) - optionsform = UploadOptionsForm(request.POST,request=request,raceid=raceid) + optionsform = UploadOptionsForm(request.POST,request=request) if form.is_valid(): # f = request.FILES['file'] @@ -5083,6 +5103,7 @@ def workout_upload_view(request, upload_to_tp = optionsform.cleaned_data['upload_to_TrainingPeaks'] makeprivate = optionsform.cleaned_data['makeprivate'] landingpage = optionsform.cleaned_data['landingpage'] + raceid = optionsform.cleaned_data['raceid'] try: registrationid = optionsform.cleaned_data['submitrace'] @@ -5310,11 +5331,27 @@ def workout_upload_view(request, messages.info(request,c) for er in errors: messages.error(request,er) + elif race.sessiontype in ['fastest_time','fastest_distance']: + result,comments,errors,jobid = add_workout_fastestrace( + [w], race,r,doregister=True, + ) + if result: + messages.info(request,"We have submitted your workout to the race") + for c in comments: + messages.info(request,c) + for er in errors: + messages.error(request,er) if int(registrationid)>0: races = VirtualRace.objects.filter( registration_closure__gt=timezone.now() ) + if raceid != 0: + races = VirtualRace.objects.filter( + registration_closure__gt=timezone.now(), + id=raceid, + ) + registrations = IndoorVirtualRaceResult.objects.filter( race__in = races, id=registrationid, @@ -5331,8 +5368,13 @@ def workout_upload_view(request, registrations = registrations.filter(id=registrationid) if registrations: race = registrations[0].race - result,comments,errors,jobid = add_workout_indoorrace( - [w],race,r,recordid=registrations[0].id + if race.sessiontype == 'indoorrace': + result,comments,errors,jobid = add_workout_indoorrace( + [w],race,r,recordid=registrations[0].id + ) + elif race.sessiontype in ['fastest_time','fastest_distance']: + result,comments, errors,jobid = add_workout_fastestrace( + [w],race,r,recordid=registrations[0].id ) if result: @@ -5351,9 +5393,14 @@ def workout_upload_view(request, registrations = registrations2.filter(id=registrationid) if registrations: race = registrations[0].race - result,comments,errors,jobid = add_workout_race( - [w], race,r,recordid=registrations[0].id - ) + if race.sessiontype == 'race': + result,comments,errors,jobid = add_workout_race( + [w], race,r,recordid=registrations[0].id + ) + elif race.sessiontype in ['fastest_time','fastest_distance']: + result, comments, errors, jobid = add_workout_fastestrace( + [w],race,r,recordid=registrations[0].id + ) if result: messages.info( request, diff --git a/static/img/HOCR.png b/static/img/HOCR.png new file mode 100644 index 00000000..4bc040c4 Binary files /dev/null and b/static/img/HOCR.png differ diff --git a/static/img/domca.jpg b/static/img/domca.jpg new file mode 100644 index 00000000..1d7a672c Binary files /dev/null and b/static/img/domca.jpg differ diff --git a/static/img/domca.png b/static/img/domca.png new file mode 100644 index 00000000..3d8d983a Binary files /dev/null and b/static/img/domca.png differ diff --git a/static/img/erg.png b/static/img/erg.png new file mode 100644 index 00000000..24300647 Binary files /dev/null and b/static/img/erg.png differ diff --git a/static/img/sculler.png b/static/img/sculler.png new file mode 100644 index 00000000..1f375141 Binary files /dev/null and b/static/img/sculler.png differ