Private
Public Access
1
0

added access to video analysis from analytics

This commit is contained in:
Sander Roosendaal
2019-11-09 16:38:28 +01:00
parent b25a8aeb23
commit 1bc3638a96
8 changed files with 550 additions and 81 deletions

View File

@@ -822,6 +822,17 @@ class PlanSelectForm(forms.Form):
class CourseSelectForm(forms.Form):
course = forms.ModelChoiceField(queryset=GeoCourse.objects.all())
class WorkoutSingleSelectForm(forms.Form):
workout = forms.ModelChoiceField(
queryset=Workout.objects.filter(),
widget=forms.RadioSelect
)
def __init__(self, *args, **kwargs):
workouts = kwargs.pop('workouts',Workout.objects.filter().order_by('-date'))
super(WorkoutSingleSelectForm,self).__init__(*args,**kwargs)
self.fields['workout'].queryset = workouts
class WorkoutMultipleCompareForm(forms.Form):
workouts = forms.ModelMultipleChoiceField(
queryset=Workout.objects.filter(),

View File

@@ -12,14 +12,15 @@
<h1>Recent Graphs</h1>
<ul class="main-content">
{% if graphs %}
<li class="grid_2">
<li class="grid_2">
<form id="searchform" action="."
method="get" accept-charset="utf-8">
{{ searchform }}
<input type="submit" value="GO"></input>
</form>
</li>
{% if graphs %}
<li class="grid_2">
<p>
<span>
@@ -28,7 +29,7 @@
<a href="?page=1&q={{ request.GET.q }}">
<i class="fas fa-arrow-alt-to-left"></i>
</a>
<a href="?page={{ workouts.previous_page_number }}&q={{ request.GET.q }}">
<a href="?page={{ graphs.previous_page_number }}&q={{ request.GET.q }}">
<i class="fas fa-arrow-alt-left"></i>
</a>
{% else %}
@@ -40,11 +41,11 @@
</a>
{% endif %}
{% endif %}
<span>
Page {{ graphs.number }} of {{ graphs.paginator.num_pages }}.
</span>
{% if graphs.has_next %}
{% if request.GET.q %}
<a href="?page={{ graphs.next_page_number }}&q={{ request.GET.q }}">
@@ -69,7 +70,7 @@
<li>
<p class="caption">
<a href="/rowers/graph/{{ graph.id }}/">
<img src="/{{ graph.filename }}"
<img src="/{{ graph.filename }}"
onerror="this.src='/static/img/rowingtimer.gif'"
alt="{{ graph.filename }}" width="120" height="100">
</a>
@@ -90,4 +91,4 @@
{% block sidebar %}
{% include 'menu_workouts.html' %}
{% endblock %}
{% endblock %}

View File

@@ -0,0 +1,90 @@
{% extends "newbase.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% block title %}Rowsandall Recent Video Analyses{% endblock %}
{% block scripts %}
{% include "monitorjobs.html" %}
{% endblock %}
{% block main %}
<h1>Recent videos</h1>
<ul class="main-content">
<li class="grid_2">
<form id="searchform" action="."
method="get" accept-charset="utf-8">
{{ searchform }}
<input type="submit" value="GO"></input>
</form>
</li>
{% if analyses %}
<li class="grid_2">
<p>
<span>
{% if analyses.has_previous %}
{% if request.GET.q %}
<a href="?page=1&q={{ request.GET.q }}">
<i class="fas fa-arrow-alt-to-left"></i>
</a>
<a href="?page={{ analyses.previous_page_number }}&q={{ request.GET.q }}">
<i class="fas fa-arrow-alt-left"></i>
</a>
{% else %}
<a href="?page=1">
<i class="fas fa-arrow-alt-to-left"></i>
</a>
<a href="?page={{ analyses.previous_page_number }}">
<i class="fas fa-arrow-alt-left"></i>
</a>
{% endif %}
{% endif %}
<span>
Page {{ analyses.number }} of {{ analyses.paginator.num_pages }}.
</span>
{% if analyses.has_next %}
{% if request.GET.q %}
<a href="?page={{ analyses.next_page_number }}&q={{ request.GET.q }}">
<i class="fas fa-arrow-alt-right"></i>
</a>
<a href="?page={{ analyses.paginator.num_pages }}&q={{ request.GET.q }}">
<i class="fas fa-arrow-alt-to-right">
</a>
{% else %}
<a href="?page={{ analyses.next_page_number }}">
<i class="fas fa-arrow-alt-right"></i>
</a>
<a href="?page={{ analyses.paginator.num_pages }}">
<i class="fas fa-arrow-alt-to-right"></i>
</a>
{% endif %}
{% endif %}
</span>
</p>
</li>
{% for video in analyses %}
<li>
<div>{{ video.name }}</div>
<a href="/rowers/video/{{ video.id|encode }}/">
<img src="https://img.youtube.com/vi/{{ video.video_id }}/hqdefault.jpg"/>
</a>
</li>
{% endfor %}
{% else %}
<li class="grid_4">
<p>
No videos found
</p>
</li>
{% endif %}
</ul>
{% endblock %}
{% block sidebar %}
{% include 'menu_analytics.html' %}
{% endblock %}

View File

@@ -2,6 +2,24 @@
{% load rowerfilters %}
<h1><a href="/rowers/analysis/">Analysis</a></h1>
<ul class="cd-accordion-menu animated">
<li class="has-children" id="videos">
<input type="checkbox" name="group-videos" id="group-videos" checked>
<label for="group-videos">
<i class="fas fa-film fa-fw"></i>&nbsp;Video Analysis
</label>
<ul>
<li id="videos-analyses">
<a href="/rowers/videos/">
<i class="fas fa-film fa-fw"></i>&nbsp;Video Analyses
</a>
</li>
<li id="video-add">
<a href="/rowers/add-video/">
<i class="fas fa-video-plus fa-fw"></i>&nbsp;New Video Analysis
</a>
</li>
</ul>
</li>
<li class="has-children" id="fitness">
<input type="checkbox" name="group-fitness" id="group-fitness" checked>
<label for="group-fitness">
@@ -18,7 +36,7 @@
<i class="fas fa-balance-scale fa-fw"></i>&nbsp;Compare
</a>
{% endif %}
</li>
</li>
<li id="fitness-powerprogress">
<a href="/rowers/fitness-progress/">
<i class="far fa-watch-fitness fa-fw"></i>&nbsp;Power Progress
@@ -61,79 +79,79 @@
<a href="/rowers/user-analysis-select/stats/">
<i class="fal fa-table fa-fw"></i>&nbsp;Statistics
</a>
</li>
<li id="stats-histo">
<a href="/rowers/user-analysis-select/histo/">
<i class="fas fa-chart-bar fa-fw"></i>&nbsp;Histogram
</a>
</li>
<li>
<a href="/rowers/alerts/">
<i class="fas fa-bell fa-fw"></i>&nbsp;Alerts
</a>
</li>
</ul>
</li>
<li>
<a href="/rowers/user-analysis-select/flexall/">
<i class="fas fa-chart-line fa-fw"></i>&nbsp;Cumulative Flex Chart
</a>
</li>
<li>
<a href="/rowers/laboratory/">
<i class="fas fa-flask fa-fw"></i>&nbsp;Laboratory</a>
</li>
</ul><!-- cd-accordion-menu -->
</li>
<li id="stats-histo">
<a href="/rowers/user-analysis-select/histo/">
<i class="fas fa-chart-bar fa-fw"></i>&nbsp;Histogram
</a>
</li>
<li>
<a href="/rowers/alerts/">
<i class="fas fa-bell fa-fw"></i>&nbsp;Alerts
</a>
</li>
</ul>
</li>
<li>
<a href="/rowers/user-analysis-select/flexall/">
<i class="fas fa-chart-line fa-fw"></i>&nbsp;Cumulative Flex Chart
</a>
</li>
<li>
<a href="/rowers/laboratory/">
<i class="fas fa-flask fa-fw"></i>&nbsp;Laboratory</a>
</li>
</ul><!-- cd-accordion-menu -->
{% if user.is_authenticated and user|is_manager and rower %}
<p>&nbsp;</p>
{% if user.is_authenticated and user|is_manager and rower %}
<p>&nbsp;</p>
{% if user|coach_rowers %}
<ul class="cd-accordion-menu animated">
<li class="has-children" id="athletes">
<input type="checkbox" name="athlete-selector" id="athlete-selector">
<label for="athlete-selector"><i class="fas fa-users fa-fw"></i>&nbsp;Athletes</label>
<ul>
{% for member in user|coach_rowers %}
<a href={{ request.path|userurl:member.user }}>
<i class="fas fa-user fa-fw"></i>
{% if member.user == rower.user %}
&bull;
{% else %}
&nbsp;
{% endif %}
{{ member.user.first_name }} {{ member.user.last_name }}
</a>
{% endfor %}
</ul>
</li>
</ul>
{% endif %}
{% endif %}
{% if user|coach_rowers %}
<ul class="cd-accordion-menu animated">
<li class="has-children" id="athletes">
<input type="checkbox" name="athlete-selector" id="athlete-selector">
<label for="athlete-selector"><i class="fas fa-users fa-fw"></i>&nbsp;Athletes</label>
<ul>
{% for member in user|coach_rowers %}
<a href={{ request.path|userurl:member.user }}>
<i class="fas fa-user fa-fw"></i>
{% if member.user == rower.user %}
&bull;
{% else %}
&nbsp;
{% endif %}
{{ member.user.first_name }} {{ member.user.last_name }}
</a>
{% endfor %}
</ul>
</li>
</ul>
{% endif %}
{% endif %}
{% if user|user_teams %}
<p>&nbsp;</p>
<ul class="cd-accordion-menu animated">
<li class="has-children" id="teams">
<input type="checkbox" name="team-selector" id="team-selector">
<label for="team-selector"><i class="fas fa-bullhorn fa-fw"></i>&nbsp;Groups</label>
<ul>
{% for tteam in teams %}
<li>
<a href={{ request.path|teamurl:tteam }}>
<i class="fas fa-users fa-fw"></i>
{% if tteam == team %}
&bull;
{% else %}
&nbsp;
{% endif %}
{{ tteam.name }}
</a>
</li>
{% endfor %}
</ul>
</li>
</ul>
{% endif %}
{% if user|user_teams %}
<p>&nbsp;</p>
<ul class="cd-accordion-menu animated">
<li class="has-children" id="teams">
<input type="checkbox" name="team-selector" id="team-selector">
<label for="team-selector"><i class="fas fa-bullhorn fa-fw"></i>&nbsp;Groups</label>
<ul>
{% for tteam in teams %}
<li>
<a href={{ request.path|teamurl:tteam }}>
<i class="fas fa-users fa-fw"></i>
{% if tteam == team %}
&bull;
{% else %}
&nbsp;
{% endif %}
{{ tteam.name }}
</a>
</li>
{% endfor %}
</ul>
</li>
</ul>
{% endif %}
{% include 'menuscript.html' %}
{% include 'menuscript.html' %}

View File

@@ -0,0 +1,117 @@
{% extends "newbase.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% block title %}Workouts{% endblock %}
{% block main %}
<script>
function toggle(source) {
checkboxes = document.querySelectorAll("input[name='workouts']");
for(var i=0, n=checkboxes.length;i<n;i++) {
checkboxes[i].checked = source.checked;
}
}
</script>
<script src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
<script>
$(function() {
// Get the form fields and hidden div
var modality = $("#id_modality");
var hidden = $("#id_waterboattype");
// Hide the fields.
// Use JS to do this in case the user doesn't have JS
// enabled.
hidden.hide();
if (modality.val() == 'water') {
hidden.show();
}
// Setup an event listener for when the state of the
// checkbox changes.
modality.change(function() {
// Check to see if the checkbox is checked.
// If it is, show the fields and populate the input.
// If not, hide the fields.
var Value = modality.val();
var otwtypes = ['water','coastal','c-boat','churchboat']
if (otwtypes.includes(Value)) {
// Show the hidden fields.
hidden.show();
} else {
// Make sure that the hidden fields are indeed
// hidden.
hidden.hide();
// You may also want to clear the value of the
// hidden fields here. Just in case somebody
// shows the fields, enters data to them and then
// unticks the checkbox.
//
// This would do the job:
//
// $("#hidden_field").val("");
}
});
});
</script>
<ul class="main-content">
<li class="grid_4">
<p>Select the workout you want to add a video to</p>
</li>
<li class="grid_2 maxheight">
<form id="searchform" action=""
method="get" accept-charset="utf-8">
{{ searchform }}
<input type="submit" value="GO"></input>
</form>
<form enctype="multipart/form-data" action="" method="post">
{% if workouts %}
<table width="100%" class="listtable">
{{ form.as_table }}
</table>
{% else %}
<p> No workouts found </p>
{% endif %}
</li>
<li class="grid_2">
<form enctype="multipart/form-data" method="post">
<table>
{{ dateform.as_table }}
</table>
{% csrf_token %}
<input name='optionsform' type="submit" value="Submit">
<input name='workoutform' type='submit' value="Create Video">
</form>
</li>
</ul>
{% endblock %}
{% block scripts %}
{% if request.method == 'POST' %}
<script type='text/javascript'
src='https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js'>
</script>
{% endif %}
{% endblock %}
{% block sidebar %}
{% include 'menu_analytics.html' %}
{% endblock %}

View File

@@ -348,6 +348,8 @@ urlpatterns = [
re_path(r'^video/(?P<pk>\d+)/delete/$',views.VideoDelete.as_view(),name='video_delete'),
re_path(r'^video/(?P<id>\w.+)/$',views.workout_video_view,
name='workout_video_view'),
re_path(r'^videos/',views.list_videos,name='list_videos'),
re_path(r'^add-video/',views.video_selectworkout,name='video_selectworkout'),
# re_path(r'^workout/(?P<id>\d+)/$',views.workout_view,name='workout_view'),
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/$',views.workout_view,name='workout_view'),
re_path(r'^workout/fusion/(?P<id1>\b[0-9A-Fa-f]+\b)/(?P<id2>\b[0-9A-Fa-f]+\b)/$',views.workout_fusion_view,name='workout_fusion_view'),

View File

@@ -59,7 +59,7 @@ from rowers.forms import (
FlexOptionsForm,DataFrameColumnsForm,OteWorkoutTypeForm,
MetricsForm,DisqualificationForm,disqualificationreasons,
disqualifiers,SearchForm,BillingForm,PlanSelectForm,
VideoAnalysisCreateForm
VideoAnalysisCreateForm,WorkoutSingleSelectForm,
)
from django.urls import reverse, reverse_lazy

View File

@@ -693,6 +693,197 @@ def workouts_join_view(request):
url = reverse('workouts_join_select')
return HttpResponseRedirect(url)
@user_passes_test(ispromember, login_url="/rowers/paidplans",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def video_selectworkout(request,userid=0,teamid=0):
r = getrequestrower(request, userid=userid)
user = r.user
userid = user.id
workouts = Workout.objects.filter(user=r).order_by('-date')
try:
theteam = Team.objects.get(id=teamid)
except Team.DoesNotExist:
theteam = None
if 'options' in request.session:
options = request.session['options']
else:
options=defaultoptions
options['userid'] = userid
try:
workouttypes = options['workouttypes']
except KeyError:
workouttypes = ['rower','dynamic','slides']
try:
modalities = options['modalities']
modality = modalities[0]
except KeyError:
modalities = [m[0] for m in mytypes.workouttypes_ordered.items()]
modality = 'all'
try:
rankingonly = options['rankingonly']
except KeyError:
rankingonly = False
query = request.GET.get('q')
if query:
query_list = query.split()
workouts = workouts.filter(
reduce(operator.and_,
(Q(name__icontains=q) for q in query_list)) |
reduce(operator.and_,
(Q(notes__icontains=q) for q in query_list))
)
searchform = SearchForm(initial={'q':query})
else:
searchform = SearchForm()
if 'startdate' in request.session:
startdate = iso8601.parse_date(request.session['startdate'])
else:
startdate=timezone.now()-datetime.timedelta(days=42)
if 'enddate' in request.session:
enddate = iso8601.parse_date(request.session['enddate'])
else:
enddate = timezone.now()
workouts = workouts.filter(date__gte=startdate,date__lte=enddate)
waterboattype = mytypes.waterboattype
if request.method == 'POST':
thediv = get_call()
dateform = DateRangeForm(request.POST)
if dateform.is_valid():
startdate = dateform.cleaned_data['startdate']
enddate = dateform.cleaned_data['enddate']
startdatestring = startdate.strftime('%Y-%m-%d')
enddatestring = enddate.strftime('%Y-%m-%d')
request.session['startdate'] = startdatestring
request.session['enddate'] = enddatestring
form = WorkoutSingleSelectForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
selectedworkout = cd['workout']
url = reverse('workout_video_create_view',
kwargs={'id':encoder.encode_hex(selectedworkout.id)})
return HttpResponseRedirect(url)
else:
id = 0
else:
form = WorkoutSingleSelectForm(workouts=workouts)
thediv = ''
dateform = DateRangeForm(initial={
'startdate':startdate,
'enddate':enddate,
})
negtypes = []
for b in mytypes.boattypes:
if b[0] not in waterboattype:
negtypes.append(b[0])
startdate = datetime.datetime.combine(startdate,datetime.time())
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
if enddate < startdate:
s = enddate
enddate = startdate
startdate = s
negtypes = []
for b in mytypes.boattypes:
if b[0] not in waterboattype:
negtypes.append(b[0])
if theteam is not None and (theteam.viewing == 'allmembers' or theteam.manager == request.user):
workouts = Workout.objects.filter(team=theteam,
startdatetime__gte=startdate,
startdatetime__lte=enddate,
workouttype__in=modalities,
)
elif theteam is not None and theteam.viewing == 'coachonly':
workouts = Workout.objects.filter(team=theteam,user=r,
startdatetime__gte=startdate,
startdatetime__lte=enddate,
workouttype__in=modalities,
)
else:
workouts = Workout.objects.filter(user=r,
startdatetime__gte=startdate,
startdatetime__lte=enddate,
workouttype__in=modalities,
)
workouts = workouts.order_by(
"-date", "-starttime"
).exclude(boattype__in=negtypes)
if rankingonly:
workouts = workouts.exclude(rankingpiece=False)
optionsform = AnalysisOptionsForm(initial={
'modality':modality,
'waterboattype':waterboattype,
'rankingonly':rankingonly,
})
startdatestring = startdate.strftime('%Y-%m-%d')
enddatestring = enddate.strftime('%Y-%m-%d')
request.session['startdate'] = startdatestring
request.session['enddate'] = enddatestring
request.session['options'] = options
breadcrumbs = [
{
'url':'/rowers/analysis',
'name':'Analysis'
},
{
'url':reverse('analysis_new',kwargs={'userid':userid}),
'name': 'Analysis Select'
},
]
return render(request, 'video_selectworkout.html',
{'workouts': workouts,
'dateform':dateform,
'startdate':startdate,
'enddate':enddate,
'rower':r,
'breadcrumbs':breadcrumbs,
'theuser':user,
'the_div':thediv,
'form':form,
'active':'nav-analysis',
'searchform':searchform,
'teams':get_my_teams(request.user),
})
@user_passes_test(ispromember,login_url="/rowers/paidplans",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
@@ -4736,6 +4927,45 @@ def team_workout_upload_view(request,message="",
# A page with all the recent graphs (searchable on workout name)
@login_required()
def list_videos(request):
r = getrequestrower(request)
workouts = Workout.objects.filter(user=r).order_by("-date", "-starttime")
query = request.GET.get('q')
if query:
query_list = query.split()
if query:
query_list = query.split()
workouts = workouts.filter(
reduce(operator.and_,
(Q(name__icontains=q) for q in query_list)) |
reduce(operator.and_,
(Q(notes__icontains=q) for q in query_list))
)
searchform = SearchForm(initial={'q':query})
else:
searchform = SearchForm()
g = VideoAnalysis.objects.filter(workout__in=workouts).order_by("-workout__date")
paginator = Paginator(g,8)
page = request.GET.get('page')
try:
g = paginator.page(page)
except PageNotAnInteger:
g = paginator.page(1)
except EmptyPage:
g = paginator.page(paginator.num_pages)
return render(request, 'list_videos.html',
{'analyses': g,
'searchform':searchform,
'active':'nav-analysis',
'teams':get_my_teams(request.user),
})
@login_required()
def graphs_view(request):
request.session['referer'] = reverse('graphs_view')