first non working attemt with Chart.js
This commit is contained in:
@@ -2806,16 +2806,13 @@ def interactive_streamchart(id=0,promember=0):
|
|||||||
|
|
||||||
return [script,div]
|
return [script,div]
|
||||||
|
|
||||||
def interactive_chart(id=0,promember=0,intervaldata = {}, timepos = None):
|
def interactive_chart(id=0,promember=0,intervaldata = {}):
|
||||||
# Add hover to this comma-separated string and see what changes
|
# Add hover to this comma-separated string and see what changes
|
||||||
if (promember==1):
|
if (promember==1):
|
||||||
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
|
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
|
||||||
else:
|
else:
|
||||||
TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
|
TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
|
||||||
|
|
||||||
if timepos:
|
|
||||||
TOOLS = ''
|
|
||||||
|
|
||||||
|
|
||||||
columns = ['time','pace','hr','fpace','ftime','spm']
|
columns = ['time','pace','hr','fpace','ftime','spm']
|
||||||
datadf = dataprep.getsmallrowdata_db(columns,ids=[id])
|
datadf = dataprep.getsmallrowdata_db(columns,ids=[id])
|
||||||
@@ -2934,32 +2931,6 @@ def interactive_chart(id=0,promember=0,intervaldata = {}, timepos = None):
|
|||||||
right='time_r',source=intervalsource,color='mediumvioletred',
|
right='time_r',source=intervalsource,color='mediumvioletred',
|
||||||
y_range_name='spmax',fill_alpha=0.2,line_alpha=0.2)
|
y_range_name='spmax',fill_alpha=0.2,line_alpha=0.2)
|
||||||
|
|
||||||
if timepos:
|
|
||||||
timepos = datetime.timedelta(seconds=timepos)
|
|
||||||
timeline = ColumnDataSource(
|
|
||||||
data=dict(
|
|
||||||
x = [timepos,timepos],
|
|
||||||
y = [10,100]
|
|
||||||
))
|
|
||||||
plot.line('x','y',source=timeline,y_range_name='spmax')
|
|
||||||
|
|
||||||
callback = CustomJS(args = dict(timeline=timeline),
|
|
||||||
code="""
|
|
||||||
var data = timeline.data
|
|
||||||
var x = data['x']
|
|
||||||
var y = data['y']
|
|
||||||
var sliderpos = cb_obj.value
|
|
||||||
data['x'] = [sliderpos,sliderpos]
|
|
||||||
timeline.change.emit();
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
timemax = int(datadf['time'].max()/1000.)
|
|
||||||
slider = Slider(start=0,end=100,step=1.0,callback=callback)
|
|
||||||
slider.js_on_change('value',callback)
|
|
||||||
layout = layoutcolumn([plot,slider])
|
|
||||||
else:
|
|
||||||
layout = plot
|
|
||||||
|
|
||||||
|
|
||||||
hover = plot.select(dict(type=HoverTool))
|
hover = plot.select(dict(type=HoverTool))
|
||||||
|
|
||||||
@@ -2975,10 +2946,55 @@ def interactive_chart(id=0,promember=0,intervaldata = {}, timepos = None):
|
|||||||
hover.names = ["spm","pace"]
|
hover.names = ["spm","pace"]
|
||||||
|
|
||||||
script, div = components(layout)
|
script, div = components(layout)
|
||||||
js_resources = INLINE.render_js()
|
|
||||||
css_resources = INLINE.render_css()
|
|
||||||
|
|
||||||
return [script,div,js_resources,css_resources]
|
return [script,div]
|
||||||
|
|
||||||
|
def interactive_chart_video(id=0):
|
||||||
|
|
||||||
|
columns = ['time','pace','hr','fpace','ftime','spm']
|
||||||
|
datadf = dataprep.getsmallrowdata_db(columns,ids=[id])
|
||||||
|
|
||||||
|
datadf.dropna(axis=0,how='any',inplace=True)
|
||||||
|
|
||||||
|
row = Workout.objects.get(id=id)
|
||||||
|
if datadf.empty:
|
||||||
|
return "","No Valid Data Available"
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
spm = datadf['spm']
|
||||||
|
except KeyError:
|
||||||
|
datadf['spm'] = 0
|
||||||
|
|
||||||
|
try:
|
||||||
|
pace = datadf['pace']
|
||||||
|
except KeyError:
|
||||||
|
datadf['pace'] = 0
|
||||||
|
|
||||||
|
div = """
|
||||||
|
<canvas id="myChart">
|
||||||
|
</canvas>
|
||||||
|
"""
|
||||||
|
|
||||||
|
script = """
|
||||||
|
var ctx = document.getElementById("myChart").getContext('2d');
|
||||||
|
var data = {
|
||||||
|
x: [1,2,3,4],
|
||||||
|
y: [1,4,9,16]
|
||||||
|
}
|
||||||
|
var myChart = new Chart(ctx, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
datasets: [{
|
||||||
|
data: data,
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
});
|
||||||
|
"""
|
||||||
|
|
||||||
|
return [script,div]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='',
|
def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='',
|
||||||
ploterrorbars=False,
|
ploterrorbars=False,
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
{% block meta %}
|
{% block meta %}
|
||||||
{% leaflet_js %}
|
{% leaflet_js %}
|
||||||
{% leaflet_css %}
|
{% leaflet_css %}
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.js"></script>
|
||||||
|
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
@@ -75,8 +77,9 @@
|
|||||||
{{ mapdiv | safe}}
|
{{ mapdiv | safe}}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="slidecontainer">
|
<div>
|
||||||
<input type="range" min="0" max="{{ maxtime }}" value="{{ analysis.delay }}" class="slider" id="myRange">
|
<input type="range" min="0" max="{{ maxtime }}" value="{{ analysis.delay }}"
|
||||||
|
id="myRange">
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="grid_2">
|
<li class="grid_2">
|
||||||
|
|||||||
@@ -1,296 +0,0 @@
|
|||||||
{% extends "newbase.html" %}
|
|
||||||
{% load staticfiles %}
|
|
||||||
{% load rowerfilters %}
|
|
||||||
{% load i18n %}
|
|
||||||
{% load leaflet_tags %}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{% block title %}Workout Video{% endblock %}
|
|
||||||
|
|
||||||
{% block meta %}
|
|
||||||
{% leaflet_js %}
|
|
||||||
{% leaflet_css %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block main %}
|
|
||||||
|
|
||||||
{{ js_res|safe }}
|
|
||||||
{{ css_res|safe }}
|
|
||||||
|
|
||||||
{% language 'en' %}
|
|
||||||
<h1>Video Analysis for {{ workout.name }}</h1>
|
|
||||||
<ul class="main-content">
|
|
||||||
{% if analysis and user.is_authenticated and user == rower.user and not locked %}
|
|
||||||
<li class="grid_4">
|
|
||||||
<p>Paste link to you tube video below</p>
|
|
||||||
<p>Use the slider to locate start point for video on workout map</p>
|
|
||||||
<p>Playing the video will advance the data in synchonization. Use the regular youtube controls
|
|
||||||
to move around in the video and play it.</p>
|
|
||||||
<p>You can make manual adjustments to the delay to fine tune the alignment.
|
|
||||||
Once you are finished, check "Lock Video and Data" to lock the video and the data.</p>
|
|
||||||
<p>Once you are happy with the alignment, you can save the analysis, and share with other people.</p>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
<li class="grid_2">
|
|
||||||
<div style="height:100%" id="theplot" class="flexplot mapdiv">
|
|
||||||
{{ mapdiv | safe}}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div id="myRange">
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li class="grid_2">
|
|
||||||
<div id="player"></div>
|
|
||||||
<script src="https://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
|
|
||||||
<script src="https://cdn.pydata.org/bokeh/release/bokeh-widgets-1.0.4.min.js"></script>
|
|
||||||
|
|
||||||
{{ mapscript | safe }}
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
|
|
||||||
// 2. This code loads the IFrame Player API code asynchronously.
|
|
||||||
var tag = document.createElement('script');
|
|
||||||
|
|
||||||
tag.src = "https://www.youtube.com/iframe_api";
|
|
||||||
var firstScriptTag = document.getElementsByTagName('script')[0];
|
|
||||||
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
|
|
||||||
|
|
||||||
// 3. This function creates an <iframe> (and YouTube player)
|
|
||||||
// after the API code downloads.
|
|
||||||
var player;
|
|
||||||
var playing;
|
|
||||||
var videotime = 0;
|
|
||||||
var data = JSON.parse('{{ data|safe }}');
|
|
||||||
{% for id, metric in metrics.items %}
|
|
||||||
var {{ id }}_values = data["{{ id }}"];
|
|
||||||
// console.log("{{ id }}_values",{{ id }}_values);
|
|
||||||
{% endfor %}
|
|
||||||
// var boatspeed = data["boatspeed"];
|
|
||||||
var latitude = data["latitude"];
|
|
||||||
var longitude = data["longitude"];
|
|
||||||
// var spm = data["spm"];
|
|
||||||
// var ctch = data["catch"];
|
|
||||||
|
|
||||||
function onYouTubeIframeAPIReady() {
|
|
||||||
player = new YT.Player('player', {
|
|
||||||
height: '390',
|
|
||||||
width: '640',
|
|
||||||
videoId: '{{ video_id }}',
|
|
||||||
events: {
|
|
||||||
'onReady': onPlayerReady,
|
|
||||||
'onStateChange': onPlayerStateChange
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. The API will call this function when the video player is ready.
|
|
||||||
function onPlayerReady(event) {
|
|
||||||
// event.target.playVideo();
|
|
||||||
function updateTime() {
|
|
||||||
var oldTime = videotime;
|
|
||||||
var slider = document.getElementById("myRange");
|
|
||||||
var lock = document.getElementById("lock");
|
|
||||||
if(player && player.getCurrentTime) {
|
|
||||||
videotime = player.getCurrentTime();
|
|
||||||
var delay = document.getElementById("id_delay").value;
|
|
||||||
sliderpos = Math.round(videotime) + Math.round(delay);
|
|
||||||
slider.value = sliderpos;
|
|
||||||
|
|
||||||
var datatime = parseFloat(videotime)+parseFloat(delay);
|
|
||||||
// velo = boatspeed[Math.round(datatime)];
|
|
||||||
lat = latitude[Math.round(datatime)];
|
|
||||||
lon = longitude[Math.round(datatime)];
|
|
||||||
// strokerate = spm[Math.round(datatime)];
|
|
||||||
// catchangle = ctch[Math.round(datatime)];
|
|
||||||
{% for id, metric in metrics.items %}
|
|
||||||
{{ id }}_now = {{ id }}_values[Math.round(datatime)];
|
|
||||||
// console.log(datatime,{{ id }}_now, "{{ metric.name }}")
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
document.getElementById("time").innerHTML = Math.round(videotime);
|
|
||||||
document.getElementById("datatime").innerHTML = Math.round(datatime);
|
|
||||||
// document.getElementById("speed").innerHTML = velo;
|
|
||||||
// document.getElementById("spm").innerHTML = strokerate;
|
|
||||||
// document.getElementById("catch").innerHTML = catchangle;
|
|
||||||
{% for id, metric in metrics.items %}
|
|
||||||
document.getElementById("{{ id }}").innerHTML = {{ id }}_now;
|
|
||||||
{% endfor %}
|
|
||||||
// gauge.set(catch_now);
|
|
||||||
// var newLatLng = new L.LatLng(lat, lon);
|
|
||||||
// marker.setLatLng(newLatLng);
|
|
||||||
}
|
|
||||||
if(videotime !== oldTime) {
|
|
||||||
onProgress(videotime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
timeupdater = setInterval(updateTime, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// when the time changes, this will be called.
|
|
||||||
function onProgress(currentTime) {
|
|
||||||
var slider = document.getElementById("myRange");
|
|
||||||
var lock = document.getElementById("lock");
|
|
||||||
videotime = player.getCurrentTime();
|
|
||||||
var delay = document.getElementById("id_delay").value;
|
|
||||||
sliderpos = Math.round(videotime) + Math.round(delay);
|
|
||||||
slider.value = sliderpos;
|
|
||||||
}
|
|
||||||
|
|
||||||
function stopVideo() {
|
|
||||||
player.stopVideo();
|
|
||||||
}
|
|
||||||
|
|
||||||
// call this function when player state changes
|
|
||||||
function onPlayerStateChange(event) {
|
|
||||||
if (event.data == YT.PlayerState.PLAYING) {
|
|
||||||
playing = false;
|
|
||||||
} else {
|
|
||||||
playing = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</li>
|
|
||||||
{% if analysis and user.is_authenticated and user == rower.user %}
|
|
||||||
<li class="grid_4">
|
|
||||||
<input type="checkbox" name="lock" id="lock" value="Lock">Lock Data and Video
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
<li>
|
|
||||||
Data Time
|
|
||||||
<span id="datatime">
|
|
||||||
</span> seconds
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Video Time
|
|
||||||
<span id="time">
|
|
||||||
</span> seconds
|
|
||||||
</li>
|
|
||||||
{% for id, metric in metrics.items %}
|
|
||||||
<li>
|
|
||||||
{{ metric.name }}
|
|
||||||
<span id="{{ id }}">
|
|
||||||
</span> {{ metric.unit }}
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
<!-- <li class="grid_2">
|
|
||||||
<canvas id="angles"></canvas>
|
|
||||||
<script type="text/javascript" src="https://bernii.github.io/gauge.js/dist/gauge.js"></script>
|
|
||||||
<script>
|
|
||||||
|
|
||||||
var opts = {
|
|
||||||
lines: 12,
|
|
||||||
angle: 0.15,
|
|
||||||
lineWidth: 0.44,
|
|
||||||
pointer: {
|
|
||||||
length: 0.9,
|
|
||||||
strokeWidth: 0.035,
|
|
||||||
color: '#000000'
|
|
||||||
},
|
|
||||||
limitMax: 'false',
|
|
||||||
// percentColors: [[0.0, "#a9d70b" ], [0.50, "#a9d70b"], [1.0, "#a9d70b"]], // !!!!
|
|
||||||
strokeColor: '#E0E0E0',
|
|
||||||
generateGradient: true
|
|
||||||
};
|
|
||||||
var target = document.getElementById('angles');
|
|
||||||
var gauge = new Gauge(target).setOptions(opts);
|
|
||||||
gauge.maxValue = 90;
|
|
||||||
gauge.minValue = -90;
|
|
||||||
gauge.animationSpeed = 5;
|
|
||||||
gauge.set(-75);
|
|
||||||
</script>
|
|
||||||
</li> -->
|
|
||||||
</ul>
|
|
||||||
<p> </p>
|
|
||||||
<form enctype="multipart/form-data" action="" method="post">
|
|
||||||
<ul class="main-content">
|
|
||||||
{% if form %}
|
|
||||||
<li class="grid_2">
|
|
||||||
<table>
|
|
||||||
{{ form.as_table }}
|
|
||||||
</table>
|
|
||||||
{% csrf_token %}
|
|
||||||
{% if not analysis.id %}
|
|
||||||
<input type="submit" name="reload_button" value="Reload">
|
|
||||||
{% endif %}
|
|
||||||
<input type="submit" name="save_button" value="Save">
|
|
||||||
</li>
|
|
||||||
<li class="grid_2">
|
|
||||||
<table>
|
|
||||||
{{ metricsform.as_table }}
|
|
||||||
</table>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
<li>
|
|
||||||
{% if analysis and user.is_authenticated and user == rower.user %}
|
|
||||||
<p>
|
|
||||||
<a href="/rowers/video/{{ analysis.id }}/delete/">Delete Analysis</a>
|
|
||||||
</p>
|
|
||||||
{% endif %}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</form>
|
|
||||||
<script>
|
|
||||||
// lock
|
|
||||||
var lock = document.getElementById("lock");
|
|
||||||
{% if locked %}
|
|
||||||
lock.checked = true;
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
|
|
||||||
// slider
|
|
||||||
var slider = document.getElementById("myRange");
|
|
||||||
{% if analysis and user.is_authenticated and user == rower.user %}
|
|
||||||
document.getElementById("myRange").style.display = "block";
|
|
||||||
{% else %}
|
|
||||||
document.getElementById("myRange").style.display = "none";
|
|
||||||
{% endif %}
|
|
||||||
var output = document.getElementById("id_delay");
|
|
||||||
try {
|
|
||||||
output.value = Math.round(slider.value)-Math.round(player.getCurrentTime()); // Display the default slider value
|
|
||||||
}
|
|
||||||
catch(err) {
|
|
||||||
output.value = Math.round(slider.value);
|
|
||||||
}
|
|
||||||
// Update the current slider value (each time you drag the slider handle)
|
|
||||||
slider.oninput = function() {
|
|
||||||
if (lock.checked) {
|
|
||||||
if (this.value-output.value > 0) {
|
|
||||||
player.seekTo(this.value-output.value);
|
|
||||||
} else {
|
|
||||||
if (playing) {
|
|
||||||
player.seekTo(0);
|
|
||||||
player.startVideo();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
player.seekTo(0);
|
|
||||||
player.pauseVideo();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
output.value = this.value-Math.round(player.getCurrentTime());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
output.oninput = function() {
|
|
||||||
slider.value = this.value+Math.round(player.getCurrentTime());
|
|
||||||
}
|
|
||||||
|
|
||||||
// lock delay form field if checkbox checked
|
|
||||||
lock.oninput = function() {
|
|
||||||
if (this.checked) {
|
|
||||||
output.disabled = true;
|
|
||||||
} else {
|
|
||||||
output.disabled = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
{% endlanguage %}
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block sidebar %}
|
|
||||||
{% include 'menu_workout.html' %}
|
|
||||||
{% endblock %}
|
|
||||||
@@ -202,10 +202,8 @@ def workout_video_create_view(request,id=0):
|
|||||||
if hascoordinates:
|
if hascoordinates:
|
||||||
mapscript, mapdiv = leaflet_chart_video(data['latitude'],data['longitude'],
|
mapscript, mapdiv = leaflet_chart_video(data['latitude'],data['longitude'],
|
||||||
w.name)
|
w.name)
|
||||||
js_res = ''
|
|
||||||
css_res = ''
|
|
||||||
else:
|
else:
|
||||||
mapscript, mapdiv,js_res,css_res = interactive_chart(w.id,promember=1,timepos=78)
|
mapscript, mapdiv = interactive_chart_video(w.id)
|
||||||
|
|
||||||
breadcrumbs = [
|
breadcrumbs = [
|
||||||
{
|
{
|
||||||
@@ -225,10 +223,7 @@ def workout_video_create_view(request,id=0):
|
|||||||
|
|
||||||
analysis = {'delay':delay}
|
analysis = {'delay':delay}
|
||||||
|
|
||||||
if hascoordinates:
|
template = 'embedded_video.html'
|
||||||
template = 'embedded_video.html'
|
|
||||||
else:
|
|
||||||
template = 'embedded_video_ote.html'
|
|
||||||
|
|
||||||
return render(request,
|
return render(request,
|
||||||
template,
|
template,
|
||||||
@@ -246,8 +241,6 @@ def workout_video_create_view(request,id=0):
|
|||||||
'maxtime':maxtime,
|
'maxtime':maxtime,
|
||||||
'metrics':metrics,
|
'metrics':metrics,
|
||||||
'locked': False,
|
'locked': False,
|
||||||
'js_res': js_res,
|
|
||||||
'css_res': css_res
|
|
||||||
})
|
})
|
||||||
|
|
||||||
# Show the EMpower Oarlock generated Stroke Profile
|
# Show the EMpower Oarlock generated Stroke Profile
|
||||||
|
|||||||
Reference in New Issue
Block a user