From b85636a37380cc3ea9fd659298afccf29ea505fe Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 26 Nov 2024 21:13:46 +0100 Subject: [PATCH 1/2] adding external yaml for rowing metrics --- rowers/metrics.py | 335 ++------------------------------------------- rowingmetrics.yaml | 288 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 296 insertions(+), 327 deletions(-) create mode 100644 rowingmetrics.yaml diff --git a/rowers/metrics.py b/rowers/metrics.py index 995a4da6..832453aa 100644 --- a/rowers/metrics.py +++ b/rowers/metrics.py @@ -1,7 +1,7 @@ from rowers.utils import lbstoN import numpy as np - +import yaml import pandas as pd from scipy import optimize from django.utils import timezone @@ -44,335 +44,16 @@ nometrics = [ 'cum_dist.1' ] -rowingmetrics = ( - ('time', { - 'numtype': 'float', - 'null': True, - 'verbose_name': 'Time', - 'ax_min': 0, - 'ax_max': 1e5, - 'mode': 'both', - 'type': 'basic', - 'group': 'basic', - 'maysmooth': False, - 'sigfigs': -1}), - - ('hr', { - 'numtype': 'integer', - 'null': True, - 'verbose_name': 'Heart Rate (bpm)', - 'ax_min': 100, - 'ax_max': 200, - 'mode': 'both', - 'type': 'basic', - 'maysmooth': False, - 'group': 'athlete', - 'sigfigs': 0, }), - - ('pace', { - 'numtype': 'float', - 'null': True, - 'verbose_name': 'Pace (/500m)', - 'ax_min': 1.0e3*210, - 'ax_max': 1.0e3*75, - 'mode': 'both', - 'type': 'basic', - 'sigfigs': 1, - 'maysmooth': True, - 'group': 'basic'}), - - ('velo', { - 'numtype': 'float', - 'null': True, - 'verbose_name': 'Boat Speed (m/s)', - 'ax_min': 0, - 'ax_max': 8, - 'default': 0, - 'mode': 'both', - 'sigfigs': 1, - 'maysmooth': True, - 'type': 'pro', - 'group': 'basic'}), - - # ('powerhr',{ - # 'numtype':'float', - # 'null':True, - # 'verbose_name': 'Power Heart Rate Efficiency (J/beat)', - # 'ax_min':0, - # 'ax_max':300, - # 'mode':'both', - # 'type':'pro', - # 'group':'athlete'}), - - ('spm', { - 'numtype': 'float', - 'null': True, - 'verbose_name': 'Stroke Rate (spm)', - 'ax_min': 15, - 'ax_max': 45, - 'mode': 'both', - 'sigfigs': 1, - 'type': 'basic', - 'maysmooth': True, - 'group': 'basic'}), - - ('driveenergy', { - 'numtype': 'float', - 'null': True, - 'verbose_name': 'Work Per Stroke (J)', - 'ax_min': 0, - 'ax_max': 1000, - 'mode': 'both', - 'sigfigs': 0, - 'type': 'pro', - 'maysmooth': True, - 'group': 'forcepower'}), - - ('power', { - 'numtype': 'float', - 'null': True, - 'verbose_name': 'Power (W)', - 'ax_min': 0, - 'ax_max': 600, - 'mode': 'both', - 'sigfigs': 0, - 'type': 'basic', - 'maysmooth': True, - 'group': 'forcepower'}), - - ('averageforce', { - 'numtype': 'float', - 'null': True, - 'verbose_name': 'Average Drive Force (N)', - 'ax_min': 0, - 'ax_max': 1200, - 'mode': 'both', - 'sigfigs': 0, - 'maysmooth': True, - 'type': 'pro', - 'group': 'forcepower'}), - - ('peakforce', { - 'numtype': 'float', - 'null': True, - 'verbose_name': 'Peak Drive Force (N)', - 'ax_min': 0, - 'ax_max': 1500, - 'sigfigs': 0, - 'mode': 'both', - 'maysmooth': True, - 'type': 'pro', - 'group': 'forcepower'}), - - ('drivelength', { - 'numtype': 'float', - 'null': True, - 'verbose_name': 'Drive Length (m)', - 'ax_min': 0, - 'ax_max': 2.5, - 'mode': 'rower', - 'sigfigs': 2, - 'maysmooth': False, - 'type': 'pro', - 'group': 'stroke'}), - - ('forceratio', { - 'numtype': 'float', - 'null': True, - 'verbose_name': 'Average/Peak Force Ratio', - 'ax_min': 0, - 'ax_max': 1, - 'sigfigs': 2, - 'maysmooth': True, - 'mode': 'both', - 'type': 'pro', - 'group': 'forcepower'}), - - ('distance', { - 'numtype': 'float', - 'null': True, - 'verbose_name': 'Interval Distance (m)', - 'ax_min': 0, - 'ax_max': 1e5, - 'sigfigs': 0, - 'mode': 'both', - 'maysmooth': False, - 'type': 'basic', - 'group': 'basic'}), - - ('cumdist', { - 'numtype': 'float', - 'null': True, - 'verbose_name': 'Cumulative Distance (m)', - 'ax_min': 0, - 'ax_max': 1e5, - 'mode': 'both', - 'sigfigs': 0, - 'maysmooth': False, - 'type': 'basic', - 'group': 'basic'}), - - ('drivespeed', { - 'numtype': 'float', - 'null': True, - 'verbose_name': 'Drive Speed (m/s)', - 'ax_min': 0, - 'ax_max': 4, - 'default': 0, - 'sigfigs': 2, - 'maysmooth': True, - 'mode': 'both', - 'type': 'pro', - 'group': 'stroke'}), - - - ('catch', { - 'numtype': 'float', - 'null': True, - 'verbose_name': 'Catch Angle (degrees)', - 'ax_min': -40, - 'ax_max': -75, - 'default': 0, - 'sigfigs': 0, - 'mode': 'water', - 'maysmooth': True, - 'type': 'pro', - 'group': 'stroke'}), - - ('slip', { - 'numtype': 'float', - 'null': True, - 'verbose_name': 'Slip (degrees)', - 'ax_min': 0, - 'ax_max': 20, - 'default': 0, - 'sigfigs': 1, - 'maysmooth': True, - 'mode': 'water', - 'type': 'pro', - 'group': 'stroke'}), - - ('finish', { - 'numtype': 'float', - 'null': True, - 'verbose_name': 'Finish Angle (degrees)', - 'ax_min': 20, - 'ax_max': 55, - 'default': 0, - 'sigfigs': 0, - 'maysmooth': True, - 'mode': 'water', - 'type': 'pro', - 'group': 'stroke'}), - - ('wash', { - 'numtype': 'float', - 'null': True, - 'verbose_name': 'Wash (degrees)', - 'ax_min': 0, - 'ax_max': 30, - 'default': 0, - 'sigfigs': 1, - 'maysmooth': True, - 'mode': 'water', - 'type': 'pro', - 'group': 'stroke'}), - - ('peakforceangle', { - 'numtype': 'float', - 'null': True, - 'verbose_name': 'Peak Force Angle', - 'ax_min': -50, - 'ax_max': 50, - 'default': 0, - 'sigfigs': 0, - 'maysmooth': True, - 'mode': 'water', - 'type': 'pro', - 'group': 'stroke'}), - - - ('totalangle', { - 'numtype': 'float', - 'null': True, - 'verbose_name': 'Drive Length (deg)', - 'ax_min': 40, - 'ax_max': 140, - 'default': 0, - 'sigfigs': 0, - 'maysmooth': True, - 'mode': 'water', - 'type': 'pro', - 'group': 'stroke'}), - - ('check_factor', { - 'numtype': 'float', - 'null': True, - 'verbose_name': 'Check Factor', - 'ax_min': 0, - 'ax_max': 100, - 'default': 0, - 'sigfigs': 1, - 'maysmooth': True, - 'mode': 'water', - 'type': 'pro', - 'group': 'stroke'}), - - - ('effectiveangle', { - 'numtype': 'float', - 'null': True, - 'verbose_name': 'Effective Drive Length (deg)', - 'ax_min': 40, - 'ax_max': 140, - 'default': 0, - 'sigfigs': 0, - 'mode': 'water', - 'maysmooth': True, - 'type': 'pro', - 'group': 'stroke'}), - - ('rhythm', { - 'numtype': 'float', - 'null': True, - 'verbose_name': 'Stroke Rhythm', - 'ax_min': 20, - 'ax_max': 55, - 'default': 1.0, - 'sigfigs': 0, - 'mode': 'erg', - 'maysmooth': True, - 'type': 'pro', - 'group': 'stroke'}), - - ('efficiency', { - 'numtype': 'float', - 'null': True, - 'verbose_name': 'OTW Efficiency (%)', - 'ax_min': 0, - 'ax_max': 110, - 'default': 0, - 'sigfigs': 0, - 'mode': 'water', - 'maysmooth': True, - 'type': 'pro', - 'group': 'forcepower'}), - - ('distanceperstroke', { - 'numtype': 'float', - 'null': True, - 'verbose_name': 'Distance per Stroke (m)', - 'ax_min': 0, - 'ax_max': 15, - 'default': 0, - 'sigfigs': 1, - 'maysmooth': True, - 'mode': 'both', - 'type': 'basic', - 'group': 'basic'}), +with open("rowingmetrics.yaml", "r") as file: + td = yaml.safe_load(file) +rowingmetrics = tuple( + (key, value) + for key, value in td.items() ) + + metricsdicts = {} for key, dict in rowingmetrics: metricsdicts[key] = dict diff --git a/rowingmetrics.yaml b/rowingmetrics.yaml new file mode 100644 index 00000000..73957883 --- /dev/null +++ b/rowingmetrics.yaml @@ -0,0 +1,288 @@ +time: + numtype: float + 'null': true + verbose_name: Time + ax_min: 0 + ax_max: 100000.0 + mode: both + type: basic + group: basic + maysmooth: false + sigfigs: -1 +hr: + numtype: integer + 'null': true + verbose_name: Heart Rate (bpm) + ax_min: 100 + ax_max: 200 + mode: both + type: basic + maysmooth: false + group: athlete + sigfigs: 0 +pace: + numtype: float + 'null': true + verbose_name: Pace (/500m) + ax_min: 210000.0 + ax_max: 75000.0 + mode: both + type: basic + sigfigs: 1 + maysmooth: true + group: basic +velo: + numtype: float + 'null': true + verbose_name: Boat Speed (m/s) + ax_min: 0 + ax_max: 8 + default: 0 + mode: both + sigfigs: 1 + maysmooth: true + type: pro + group: basic +spm: + numtype: float + 'null': true + verbose_name: Stroke Rate (spm) + ax_min: 15 + ax_max: 45 + mode: both + sigfigs: 1 + type: basic + maysmooth: true + group: basic +driveenergy: + numtype: float + 'null': true + verbose_name: Work Per Stroke (J) + ax_min: 0 + ax_max: 1000 + mode: both + sigfigs: 0 + type: pro + maysmooth: true + group: forcepower +power: + numtype: float + 'null': true + verbose_name: Power (W) + ax_min: 0 + ax_max: 600 + mode: both + sigfigs: 0 + type: basic + maysmooth: true + group: forcepower +averageforce: + numtype: float + 'null': true + verbose_name: Average Drive Force (N) + ax_min: 0 + ax_max: 1200 + mode: both + sigfigs: 0 + maysmooth: true + type: pro + group: forcepower +peakforce: + numtype: float + 'null': true + verbose_name: Peak Drive Force (N) + ax_min: 0 + ax_max: 1500 + sigfigs: 0 + mode: both + maysmooth: true + type: pro + group: forcepower +drivelength: + numtype: float + 'null': true + verbose_name: Drive Length (m) + ax_min: 0 + ax_max: 2.5 + mode: rower + sigfigs: 2 + maysmooth: false + type: pro + group: stroke +forceratio: + numtype: float + 'null': true + verbose_name: Average/Peak Force Ratio + ax_min: 0 + ax_max: 1 + sigfigs: 2 + maysmooth: true + mode: both + type: pro + group: forcepower +distance: + numtype: float + 'null': true + verbose_name: Interval Distance (m) + ax_min: 0 + ax_max: 100000.0 + sigfigs: 0 + mode: both + maysmooth: false + type: basic + group: basic +cumdist: + numtype: float + 'null': true + verbose_name: Cumulative Distance (m) + ax_min: 0 + ax_max: 100000.0 + mode: both + sigfigs: 0 + maysmooth: false + type: basic + group: basic +drivespeed: + numtype: float + 'null': true + verbose_name: Drive Speed (m/s) + ax_min: 0 + ax_max: 4 + default: 0 + sigfigs: 2 + maysmooth: true + mode: both + type: pro + group: stroke +catch: + numtype: float + 'null': true + verbose_name: Catch Angle (degrees) + ax_min: -40 + ax_max: -75 + default: 0 + sigfigs: 0 + mode: water + maysmooth: true + type: pro + group: stroke +slip: + numtype: float + 'null': true + verbose_name: Slip (degrees) + ax_min: 0 + ax_max: 20 + default: 0 + sigfigs: 1 + maysmooth: true + mode: water + type: pro + group: stroke +finish: + numtype: float + 'null': true + verbose_name: Finish Angle (degrees) + ax_min: 20 + ax_max: 55 + default: 0 + sigfigs: 0 + maysmooth: true + mode: water + type: pro + group: stroke +wash: + numtype: float + 'null': true + verbose_name: Wash (degrees) + ax_min: 0 + ax_max: 30 + default: 0 + sigfigs: 1 + maysmooth: true + mode: water + type: pro + group: stroke +peakforceangle: + numtype: float + 'null': true + verbose_name: Peak Force Angle + ax_min: -50 + ax_max: 50 + default: 0 + sigfigs: 0 + maysmooth: true + mode: water + type: pro + group: stroke +totalangle: + numtype: float + 'null': true + verbose_name: Drive Length (deg) + ax_min: 40 + ax_max: 140 + default: 0 + sigfigs: 0 + maysmooth: true + mode: water + type: pro + group: stroke +check_factor: + numtype: float + 'null': true + verbose_name: Check Factor + ax_min: 0 + ax_max: 100 + default: 0 + sigfigs: 1 + maysmooth: true + mode: water + type: pro + group: stroke +effectiveangle: + numtype: float + 'null': true + verbose_name: Effective Drive Length (deg) + ax_min: 40 + ax_max: 140 + default: 0 + sigfigs: 0 + mode: water + maysmooth: true + type: pro + group: stroke +rhythm: + numtype: float + 'null': true + verbose_name: Stroke Rhythm + ax_min: 20 + ax_max: 55 + default: 1.0 + sigfigs: 0 + mode: erg + maysmooth: true + type: pro + group: stroke +efficiency: + numtype: float + 'null': true + verbose_name: OTW Efficiency (%) + ax_min: 0 + ax_max: 110 + default: 0 + sigfigs: 0 + mode: water + maysmooth: true + type: pro + group: forcepower +distanceperstroke: + numtype: float + 'null': true + verbose_name: Distance per Stroke (m) + ax_min: 0 + ax_max: 15 + default: 0 + sigfigs: 1 + maysmooth: true + mode: both + type: basic + group: basic From 32c5aa3a5f800c387877b9720b37a0b2d21235c8 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 27 Nov 2024 20:41:10 +0100 Subject: [PATCH 2/2] tested --- rowers/tests/testdata/testdata.tcx.gz | Bin 4000 -> 3998 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/rowers/tests/testdata/testdata.tcx.gz b/rowers/tests/testdata/testdata.tcx.gz index e623b2e3e6bca45b93f4f20e085899e23fdf181d..81d458fd4adb5110095b9dff6dca80052114e59c 100644 GIT binary patch delta 3911 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 delta 3913 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 X#l!S8UZhJO-uWLx*sApe#DD<+3aII)