From db7c224beb2c47b4d7593b35784ed1430d69b1d7 Mon Sep 17 00:00:00 2001
From: Sander Roosendaal
Date: Tue, 10 Dec 2019 17:33:00 +0100
Subject: [PATCH 1/9] google gauge working
---
rowers/templates/embedded_video.html | 17 +++--
static/js/videogauges.js | 92 +++++++++++-----------------
2 files changed, 48 insertions(+), 61 deletions(-)
diff --git a/rowers/templates/embedded_video.html b/rowers/templates/embedded_video.html
index 69641929..e957f6fd 100644
--- a/rowers/templates/embedded_video.html
+++ b/rowers/templates/embedded_video.html
@@ -29,6 +29,8 @@
{% block meta %}
{% leaflet_js %}
{% leaflet_css %}
+
+
-
-
{% endlanguage %}
{% endblock %}
diff --git a/static/js/videogauges.js b/static/js/videogauges.js
index f6c068e4..0b6395d7 100644
--- a/static/js/videogauges.js
+++ b/static/js/videogauges.js
@@ -1,60 +1,42 @@
-// 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);
+google.charts.load('current', {'packages':['gauge']});
+google.charts.setOnLoadCallback(drawSPMChart);
-// https://github.com/bernii/gauge.js/issues/193
+var spmdata = [
+ ['Label', 'Value'],
+ ['SPM', 21.0],
+
+];
+
+var spmoptions = {
+ min:0, max: 50,
+ width: 400, height: 120,
+ greenFrom: 20, greenTo: 30,
+ yellowFrom: 30,yellowTo: 40,
+ redFrom: 40, redTo: 50,
+ majorTicks: ['0','10','20','30','40','50'],
+ minorTicks: 10
+};
+
+var data = null;
+var spmchart = null;
+
+// SPM chart
+function drawSPMChart() {
+
+ data = new google.visualization.arrayToDataTable(spmdata);
-var opts = {
- angle: 0, // The span of the gauge arc
- lineWidth: 0.2, // The line thickness
- radiusScale: 0.89, // Relative radius
- pointer: {
- length: 0.54, // // Relative to gauge radius
- strokeWidth: 0.053, // The thickness
- color: '#000000' // Fill color
- },
- limitMax: false, // If false, max value increases automatically if value > maxValue
- limitMin: false, // If true, the min value of the gauge will be fixed
- colorStart: '#6FADCF', // Colors
- colorStop: '#8FC0DA', // just experiment with them
- strokeColor: '#E0E0E0', // to see which ones work best for you
- generateGradient: true,
- highDpiSupport: true, // High resolution support
- staticZones: [
- {strokeStyle: "#00FF00", min: 0, max: 2}, // Greem
- {strokeStyle: "#0000FF", min: 2, max: 3}, // Blue`
- {strokeStyle: "#00FFFF", min: 3, max: 4}, // Cyan
- {strokeStyle: "#FFDD00", min: 4, max: 5}, // Orange
- {strokeStyle: "#FF0000", min: 5, max: 6} // Red
- ],
- };
- var target = document.getElementById('basic'); // your canvas element
- var gaugeboatspeed = new Gauge(target).setOptions(opts); // create sexy gauge!
- gaugeboatspeed.maxValue = 6; // set max gauge value
- gaugeboatspeed.setMinValue(0); // Prefer setter over gauge.minValue = 0
- gaugeboatspeed.animationSpeed = 10; // set animation speed (32 is default value)
- gaugeboatspeed.set(0); // set actual value
+ spmchart = new google.visualization.Gauge(document.getElementById('basic'));
-// Define set_basic(values) so that gauges can be set by metricsgroups
- function set_basic() {
- gaugeboatspeed.set(boatspeed_now);
- }
+ // spmchart.draw(data, spmoptions);
+
+ // Define set_basic(values) so that gauges can be set by metricsgroups
+
+};
+
+var spmchart;
+function set_basic() {
+ data.setCell(0,1,spm_now);
+ spmchart.draw(data, spmoptions)
+}
From 557a7fa6e29d86f6139b2b33e5cc3d251999e0d1 Mon Sep 17 00:00:00 2001
From: Sander Roosendaal
Date: Tue, 10 Dec 2019 22:07:19 +0100
Subject: [PATCH 2/9] playing with gauges
---
rowers/templates/embedded_video.html | 36 +++++++++++--
rowers/templatetags/rowerfilters.py | 65 +++++++++++-----------
static/js/videogauges.js | 81 +++++++++++++++++++++++++---
3 files changed, 136 insertions(+), 46 deletions(-)
diff --git a/rowers/templates/embedded_video.html b/rowers/templates/embedded_video.html
index e957f6fd..97196d18 100644
--- a/rowers/templates/embedded_video.html
+++ b/rowers/templates/embedded_video.html
@@ -254,7 +254,7 @@ function copyText() {
// 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 }}")
+ // console.log(datatime,{{ id }},{{ id }}_now, "{{ metric.name }}")
{% endfor %}
@@ -374,11 +374,21 @@ function copyText() {
{{ metric.unit }}
{% endfor %}
- {% for group in metricsgroups %}
-
-
+
+
+ {% if 'basic' in metricsgroups %}
+ -
+
- {% endfor %}
+ -
+
+
+ {% endif %}
+ {% if 'forcepower' in metricsgroups %}
+ -
+
+
+ {% endif %}
@@ -394,6 +404,22 @@ function copyText() {
{% endlanguage %}
diff --git a/rowers/templatetags/rowerfilters.py b/rowers/templatetags/rowerfilters.py
index 5c1c70ca..648e0d80 100644
--- a/rowers/templatetags/rowerfilters.py
+++ b/rowers/templatetags/rowerfilters.py
@@ -121,7 +121,26 @@ def is_coach(rower,rowers):
return True
+@register.filter
+def waterpower(x,rower):
+ return int(x*(100-rower.otwslack)/100.)
+@register.filter
+def round20(x):
+ return int(20.*(1+int(int(x)/20)))
+
+@register.filter
+def round100(x):
+ return int(100.*(1+int(int(x)/100)))
+
+@register.filter
+def majorticks(maxval):
+ ticks = range(1+int(maxval/100.))
+ newticks =[]
+ for t in ticks:
+ newticks.append(t*100)
+
+ return newticks
def strfdeltah(tdelta):
hours, rest = divmod(tdelta.seconds,3600)
diff --git a/rowsandall_app/settings.py b/rowsandall_app/settings.py
index a916a5f2..2df72fc8 100644
--- a/rowsandall_app/settings.py
+++ b/rowsandall_app/settings.py
@@ -84,7 +84,7 @@ MIDDLEWARE = [
'django.middleware.common.CommonMiddleware',
'django.middleware.common.BrokenLinkEmailsMiddleware',
'django.middleware.gzip.GZipMiddleware',
- 'htmlmin.middleware.HtmlMinifyMiddleware',
+# 'htmlmin.middleware.HtmlMinifyMiddleware',
# 'htmlmin.middleware.MarkRequestMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.middleware.security.SecurityMiddleware',
diff --git a/static/js/videogauges.js b/static/js/videogauges.js
index 8b18f129..31da7567 100644
--- a/static/js/videogauges.js
+++ b/static/js/videogauges.js
@@ -2,15 +2,18 @@ google.charts.load('current', {'packages':['gauge']});
google.charts.setOnLoadCallback(drawSPMChart);
google.charts.setOnLoadCallback(drawSpeedChart);
google.charts.setOnLoadCallback(drawPowerChart);
+google.charts.setOnLoadCallback(drawHRChart);
+
+console.log('initializing data');
var spmdata = [
['Label', 'Value'],
- ['SPM', 21.0],
+ ['SPM', 21],
];
var speeddata = [
['Label', 'Value'],
- ['V m/s', 0.0],
+ ['V m/s', 0],
];
var powerdata = [
@@ -18,6 +21,21 @@ var powerdata = [
['PWR',150],
]
+var hrdata = [
+ ['Label','Value'],
+ ['HR',110],
+]
+
+var hroptions = {
+ min: 100, max: 200,
+ width: 400, height: 120,
+ greenFrom: 100, greenTo: 135,
+ yellowFrom: 135,yellowTo: 157,
+ redFrom: 157, redTo: 200,
+ minorTicks: 5
+};
+
+
var spmoptions = {
min:0, max: 50,
width: 400, height: 120,
@@ -58,13 +76,16 @@ var datapower = null;
// SPM chart
function drawSPMChart() {
-
+ console.log('first draw SPM chart');
dataspm = new google.visualization.arrayToDataTable(spmdata);
-
+ try {
spmchart = new google.visualization.Gauge(document.getElementById('basic_spm'));
+ spmchart.draw(dataspm,spmoptions);
+} catch(err) {
+}
// spmchart.draw(data, spmoptions);
// Define set_basic(values) so that gauges can be set by metricsgroups
@@ -75,13 +96,35 @@ function drawSPMChart() {
function drawSpeedChart() {
dataspeed = new google.visualization.arrayToDataTable(speeddata);
+ try {
speedchart = new google.visualization.Gauge(document.getElementById('basic_boatspeed'));
+ speedchart.draw(dataspeed,speedoptions);
+} catch(err) {
+
+}
}
// Power chart
function drawPowerChart() {
datapower = new google.visualization.arrayToDataTable(powerdata);
+ try {
powerchart = new google.visualization.Gauge(document.getElementById('forcepower_power'));
+ powerchart.draw(datapower,poweroptions)
+} catch(err) {
+
+}
+
+}
+
+// Power chart
+function drawHRChart() {
+ datahr = new google.visualization.arrayToDataTable(hrdata);
+ try {
+ hrchart = new google.visualization.Gauge(document.getElementById('athlete_hr'));
+ hrchart.draw(datahr,hroptions);
+} catch(err) {
+ console.log('no hr div');
+}
}
@@ -94,7 +137,10 @@ function set_basic() {
}
function set_athlete() {
-
+ datahr.setCell(0,1,hr_now);
+ try {
+ hrchart.draw(datahr,hroptions);
+ } catch(err) {}
}
function set_stroke() {
@@ -103,5 +149,7 @@ function set_stroke() {
function set_forcepower() {
datapower.setCell(0,1,power_now);
+ try {
powerchart.draw(datapower,poweroptions);
+} catch(err) {}
}
From 04ffc15f240e0dd0c5d65acbf3f5a4d54afc86b6 Mon Sep 17 00:00:00 2001
From: Sander Roosendaal
Date: Wed, 11 Dec 2019 00:33:53 +0100
Subject: [PATCH 4/9] small improvements HR gauge
---
rowers/templates/embedded_video.html | 2 +-
rowers/templatetags/rowerfilters.py | 10 ++++++++++
2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/rowers/templates/embedded_video.html b/rowers/templates/embedded_video.html
index fa7eacaa..f4dae1e7 100644
--- a/rowers/templates/embedded_video.html
+++ b/rowers/templates/embedded_video.html
@@ -462,7 +462,7 @@ function copyText() {
hroptions.yellowTo = {{ rower.an }}
hroptions.redFrom = {{ rower.an }}
hroptions.redTo = {{ rower.max }}
- hroptions.majorTicks = {{ rower.max|round20|majorticks }}
+ hroptions.majorTicks = {{ rower.max|round20|hrmajorticks:rower.rest }}
hr_now = hr_values[0];
set_athlete();
{% endif %}
diff --git a/rowers/templatetags/rowerfilters.py b/rowers/templatetags/rowerfilters.py
index 648e0d80..b7f3be2f 100644
--- a/rowers/templatetags/rowerfilters.py
+++ b/rowers/templatetags/rowerfilters.py
@@ -142,6 +142,16 @@ def majorticks(maxval):
return newticks
+@register.filter
+def hrmajorticks(maxval,minval):
+ ticks = range(int((maxval-minval)/20.)-1)
+ newticks =[]
+ for t in ticks:
+ newticks.append(100+t*20)
+
+ print(newticks)
+ return newticks
+
def strfdeltah(tdelta):
hours, rest = divmod(tdelta.seconds,3600)
minutes,seconds = divmod(rest,60)
From 00f7a5a3ef07ee139ac08601ce0b9edd62402d8a Mon Sep 17 00:00:00 2001
From: Sander Roosendaal
Date: Wed, 11 Dec 2019 15:17:20 +0100
Subject: [PATCH 5/9] stroke angle chart prototype
---
rowers/metrics.py | 2 +-
rowers/templates/embedded_video.html | 11 +++++
static/js/videogauges.js | 72 ++++++++++++++++++++++++----
3 files changed, 75 insertions(+), 10 deletions(-)
diff --git a/rowers/metrics.py b/rowers/metrics.py
index 242cfe56..fbcee514 100644
--- a/rowers/metrics.py
+++ b/rowers/metrics.py
@@ -258,7 +258,7 @@ rowingmetrics = (
'ax_min': 0,
'ax_max': 30,
'default': 0,
- 'sigfigs': 0,
+ 'sigfigs': 1,
'mode':'water',
'type': 'pro',
'group': 'stroke'}),
diff --git a/rowers/templates/embedded_video.html b/rowers/templates/embedded_video.html
index f4dae1e7..4327f266 100644
--- a/rowers/templates/embedded_video.html
+++ b/rowers/templates/embedded_video.html
@@ -394,8 +394,14 @@ function copyText() {
{% endif %}
+ {% if 'stroke' in metricsgroups %}
+
+
+
+ {% endif %}
+
{% if analysis and user.is_authenticated and user == analysis.workout.user.user %}
@@ -411,6 +417,7 @@ function copyText() {
$(document).ready( function() {
$(window).load(function() {
// initialize data fields
+ console.log('initializing first value of data');
{% for id, metric in metrics.items %}
{{ id }}_now = {{ id }}_values[0];
document.getElementById("{{ id }}").innerHTML = {{ id }}_now;
@@ -466,6 +473,10 @@ function copyText() {
hr_now = hr_values[0];
set_athlete();
{% endif %}
+ {% if 'stroke' in metricsgroups %}
+ anglesoptions.PieStartAngle = 90+catch_now;
+ set_stroke();
+ {% endif %}
// cookie reader
function createCookie(name,value,days) {
diff --git a/static/js/videogauges.js b/static/js/videogauges.js
index 31da7567..42ac8ff9 100644
--- a/static/js/videogauges.js
+++ b/static/js/videogauges.js
@@ -1,10 +1,9 @@
-google.charts.load('current', {'packages':['gauge']});
+google.charts.load('current', {'packages':['gauge','corechart']});
google.charts.setOnLoadCallback(drawSPMChart);
google.charts.setOnLoadCallback(drawSpeedChart);
google.charts.setOnLoadCallback(drawPowerChart);
google.charts.setOnLoadCallback(drawHRChart);
-
-console.log('initializing data');
+google.charts.setOnLoadCallback(drawStrokeAngleChart);
var spmdata = [
['Label', 'Value'],
@@ -26,6 +25,33 @@ var hrdata = [
['HR',110],
]
+var angledata = [
+ ['Angle', 'deg'],
+ ['slip', 10 ],
+ ['load', 50],
+ ['unload', 50],
+ ['wash', 10],
+ ['recovery',240 ]
+]
+
+var anglesoptions = {
+ title: 'Stroke Angles',
+ legend: 'none',
+ pieHole: 0.5,
+ chartArea: { width: "100%" },
+ pieStartAngle: 35,
+ pieSliceText: 'value',
+ pieSliceTextStyle: {color:'black',fontSize:12},
+ slices: {
+ 0: { color: 'lightblue' },
+ 1: { color: 'lightgreen' },
+ 2: { color: 'yellow'},
+ 3: { color: 'orange'},
+ 4: { color: 'transparent', textStyle: {color:'transparent'}}
+ }
+};
+
+
var hroptions = {
min: 100, max: 200,
width: 400, height: 120,
@@ -73,6 +99,7 @@ var spmchart = null;
var speedchart = null;
var powerchart = null;
var datapower = null;
+var dataangles = null;
// SPM chart
function drawSPMChart() {
@@ -116,16 +143,28 @@ function drawPowerChart() {
}
-// Power chart
+// HR chart
function drawHRChart() {
datahr = new google.visualization.arrayToDataTable(hrdata);
try {
- hrchart = new google.visualization.Gauge(document.getElementById('athlete_hr'));
- hrchart.draw(datahr,hroptions);
-} catch(err) {
- console.log('no hr div');
+ hrchart = new google.visualization.Gauge(document.getElementById('athlete_hr'));
+ hrchart.draw(datahr,hroptions);
+ } catch(err) {
+ console.log('no hr div');
+ }
+
}
+// Stroke angle chart
+function drawStrokeAngleChart() {
+ dataangles = new google.visualization.arrayToDataTable(angledata);
+ try {
+ angleschart = new google.visualization.PieChart(document.getElementById('stroke_angles'));
+ angleschart.draw(dataangles,anglesoptions);
+ } catch(err) {
+ console.log('no angles div');
+ }
+
}
function set_basic() {
@@ -144,7 +183,22 @@ function set_athlete() {
}
function set_stroke() {
-
+ var piestartangle = 90+catch_now;
+ var load = Math.max(-catch_now-slip_now+peakforceangle_now);
+ var unload = Math.max(finish_now-wash_now-peakforceangle_now) ;
+ var recovery = Math.max(360+catch_now-finish_now);
+ // console.log('load ',load,'; unload ',unload,'; recovery ',recovery,'; pie start angle ',piestartangle);
+ dataangles.setCell(0,1,slip_now);
+ dataangles.setCell(1,1,load);
+ dataangles.setCell(2,1,unload);
+ dataangles.setCell(3,1,wash_now);
+ dataangles.setCell(4,1,recovery);
+ anglesoptions.pieStartAngle = piestartangle;
+ try {
+ angleschart.draw(dataangles,anglesoptions);
+ } catch(err) {
+ console.log('failed: ',err);
+ }
}
function set_forcepower() {
From 0681d17717f8e67be6c760ea2b1db2168c767771 Mon Sep 17 00:00:00 2001
From: Sander Roosendaal
Date: Wed, 11 Dec 2019 20:50:41 +0100
Subject: [PATCH 6/9] stroke angles only for water workouts
---
rowers/templates/embedded_video.html | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/rowers/templates/embedded_video.html b/rowers/templates/embedded_video.html
index 4327f266..8e19b6c9 100644
--- a/rowers/templates/embedded_video.html
+++ b/rowers/templates/embedded_video.html
@@ -395,10 +395,12 @@ function copyText() {
{% endif %}
{% if 'stroke' in metricsgroups %}
+ {% if workout.workouttype == 'water' %}
{% endif %}
+ {% endif %}
@@ -474,9 +476,11 @@ function copyText() {
set_athlete();
{% endif %}
{% if 'stroke' in metricsgroups %}
+ {% if workout.workouttype == 'water'}
anglesoptions.PieStartAngle = 90+catch_now;
set_stroke();
{% endif %}
+ {% endif %}
// cookie reader
function createCookie(name,value,days) {
From e5db9b0f75d4c8caebeff146b0a7a2d49dbc51e9 Mon Sep 17 00:00:00 2001
From: Sander Roosendaal
Date: Wed, 11 Dec 2019 23:06:12 +0100
Subject: [PATCH 7/9] correct error
---
rowers/templates/embedded_video.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rowers/templates/embedded_video.html b/rowers/templates/embedded_video.html
index 8e19b6c9..8e7666f9 100644
--- a/rowers/templates/embedded_video.html
+++ b/rowers/templates/embedded_video.html
@@ -476,7 +476,7 @@ function copyText() {
set_athlete();
{% endif %}
{% if 'stroke' in metricsgroups %}
- {% if workout.workouttype == 'water'}
+ {% if workout.workouttype == 'water' %}
anglesoptions.PieStartAngle = 90+catch_now;
set_stroke();
{% endif %}
From c97ac0e6fd73a8657da2617e530f2920ced6dc4e Mon Sep 17 00:00:00 2001
From: Sander Roosendaal
Date: Thu, 2 Jan 2020 16:55:57 +0100
Subject: [PATCH 8/9] added non-tested Dockerfile & updated video gauges
---
Dockerfile | 7 +++++++
rowers/templates/embedded_video.html | 11 +++++++++++
2 files changed, 18 insertions(+)
create mode 100644 Dockerfile
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 00000000..3cf83085
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,7 @@
+FROM python:3-alpine
+COPY requirements.txt ./
+WORKDIR /usr/src/app/
+RUN pip install --no-cache-dir -r requirements.txt
+COPY . .
+EXPOSE 8000
+CMD ["python", "./manage.py", "runserver"]
diff --git a/rowers/templates/embedded_video.html b/rowers/templates/embedded_video.html
index f31fc280..7d66b3de 100644
--- a/rowers/templates/embedded_video.html
+++ b/rowers/templates/embedded_video.html
@@ -411,6 +411,17 @@ function copyText() {
{% endif %}
+
+ We're building Gauges to show the video data based on the
+ Google charts API.
+ You can contribute by developing your own gauge in
+ JSFiddle like
+ this example.
+ Please send us the link to the JSFiddle example by email using the
+ Contact Form and we'll discuss the possibilities
+ with you.
+
+
From 45d1823b7067af98d2dea7e07cdf4d5d542e0d91 Mon Sep 17 00:00:00 2001
From: Sander Roosendaal
Date: Thu, 2 Jan 2020 17:00:29 +0100
Subject: [PATCH 9/9] updated Dockerfile
---
Dockerfile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Dockerfile b/Dockerfile
index 3cf83085..fd6a80c9 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,5 @@
FROM python:3-alpine
-COPY requirements.txt ./
+COPY ./requirements.txt ./
WORKDIR /usr/src/app/
RUN pip install --no-cache-dir -r requirements.txt
COPY . .