From 6db8b17ad6dcccaa1d719a786405de6196278291 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Thu, 21 Sep 2017 16:47:39 +0200 Subject: [PATCH] made metrics and strokedata model dynamic --- rowers/metrics.py | 254 +++++++++++++++++++++++++++++++++---- rowers/models.py | 100 ++++++++------- rowers/views.py | 16 +-- rowsandall_app/settings.py | 2 +- 4 files changed, 292 insertions(+), 80 deletions(-) diff --git a/rowers/metrics.py b/rowers/metrics.py index e41e7b57..90dc2f84 100644 --- a/rowers/metrics.py +++ b/rowers/metrics.py @@ -1,32 +1,236 @@ from utils import lbstoN -axes = ( - ('time','Time',0,1e5,'basic'), - ('distance', 'Distance (m)',0,1e5,'basic'), - ('cumdist', 'Cumulative Distance (m)',0,1e5,'basic'), - ('hr','Heart Rate (bpm)',100,200,'basic'), - ('spm', 'Stroke Rate (spm)',15,45,'basic'), - ('pace', 'Pace (/500m)',1.0e3*210,1.0e3*75,'basic'), - ('power', 'Power (Watt)',0,600,'basic'), - ('averageforce', 'Average Drive Force (N)',0,900,'pro'), - ('drivelength', 'Drive Length (m)',0.5,2.0,'pro'), - ('peakforce', 'Peak Drive Force (N)',0,900,'pro'), - ('forceratio', 'Average/Peak Force Ratio',0,1,'pro'), - ('driveenergy', 'Work per Stroke (J)',0,1000,'pro'), - ('drivespeed', 'Drive Speed (m/s)',0,4,'pro'), - ('slip', 'Slip (degrees)',0,20,'pro'), - ('catch', 'Catch (degrees)',-40,-75,'pro'), - ('finish', 'Finish (degrees)',20,55,'pro'), - ('wash', 'Wash (degrees)',0,30,'pro'), - ('peakforceangle', 'Peak Force Angle (degrees)',-20,20,'pro'), - ('totalangle', 'Drive Length (deg)',40,140,'pro'), - ('effectiveangle', 'Effective Drive Length (deg)',40,140,'pro'), - ('rhythm', 'Stroke Rhythm (%)',20,55,'pro'), - ('efficiency', 'OTW efficiency (%)',0,110,'pro'), - ('distanceperstroke','Distance per Stroke (m)',0,15,'pro'), - ('None', 'None',0,1,'basic'), +rowingmetrics = ( + ('time',{ + 'numtype':'float', + 'null':True, + 'verbose_name': 'Time', + 'ax_min': 0, + 'ax_max': 1e5, + 'mode':'both', + 'type': 'basic'}), + + ('hr',{ + 'numtype':'integer', + 'null':True, + 'verbose_name': 'Heart Rate (bpm)', + 'ax_min': 100, + 'ax_max': 200, + 'mode':'both', + 'type': 'basic'}), + + ('pace',{ + 'numtype':'float', + 'null':True, + 'verbose_name': 'Pace (/500m)', + 'ax_min': 1.0e3*210, + 'ax_max': 1.0e3*75, + 'mode':'both', + 'type': 'basic'}), + + ('spm',{ + 'numtype':'float', + 'null':True, + 'verbose_name': 'Stroke Rate (spm)', + 'ax_min': 15, + 'ax_max': 45, + 'mode':'both', + 'type': 'basic'}), + + ('driveenergy',{ + 'numtype':'float', + 'null':True, + 'verbose_name': 'Work Per Stroke (J)', + 'ax_min': 0, + 'ax_max': 1000, + 'mode':'both', + 'type': 'pro'}), + + ('power',{ + 'numtype':'float', + 'null':True, + 'verbose_name': 'Power (W)', + 'ax_min': 0, + 'ax_max': 600, + 'mode':'both', + 'type': 'basic'}), + + ('averageforce',{ + 'numtype':'float', + 'null':True, + 'verbose_name': 'Average Drive Force (N)', + 'ax_min': 0, + 'ax_max': 900, + 'mode':'both', + 'type': 'pro'}), + + ('peakforce',{ + 'numtype':'float', + 'null':True, + 'verbose_name': 'Peak Drive Force (N)', + 'ax_min': 0, + 'ax_max': 900, + 'mode':'both', + 'type': 'pro'}), + + ('drivelength',{ + 'numtype':'float', + 'null':True, + 'verbose_name': 'Drive Length (m)', + 'ax_min': 15, + 'ax_max': 45, + 'mode':'both', + 'type': 'pro'}), + + ('forceratio',{ + 'numtype':'float', + 'null':True, + 'verbose_name': 'Average/Peak Force Ratio', + 'ax_min': 0, + 'ax_max': 1, + 'mode':'both', + 'type': 'pro'}), + + ('distance',{ + 'numtype':'float', + 'null':True, + 'verbose_name': 'Distance (m)', + 'ax_min': 0, + 'ax_max': 1e5, + 'mode':'both', + 'type': 'basic'}), + + ('cumdist',{ + 'numtype':'float', + 'null':True, + 'verbose_name': 'Cumulative Distance (m)', + 'ax_min': 0, + 'ax_max': 1e5, + 'mode':'both', + 'type': 'basic'}), + + ('drivespeed',{ + 'numtype':'float', + 'null':True, + 'verbose_name': 'Drive Speed (m/s)', + 'ax_min': 0, + 'ax_max': 4, + 'default': 0, + 'mode':'both', + 'type': 'pro'}), + + ('catch',{ + 'numtype':'float', + 'null':True, + 'verbose_name': 'Catch Angle (degrees)', + 'ax_min': -40, + 'ax_max': -75, + 'default': 0, + 'mode':'water', + 'type': 'pro'}), + + ('slip',{ + 'numtype':'float', + 'null':True, + 'verbose_name': 'Slip (degrees)', + 'ax_min': 0, + 'ax_max': 20, + 'default': 0, + 'mode':'water', + 'type': 'pro'}), + + ('finish',{ + 'numtype':'float', + 'null':True, + 'verbose_name': 'Finish Angle (degrees)', + 'ax_min': 20, + 'ax_max': 55, + 'default': 0, + 'mode':'water', + 'type': 'pro'}), + + ('wash',{ + 'numtype':'float', + 'null':True, + 'verbose_name': 'Wash (degrees)', + 'ax_min': 0, + 'ax_max': 30, + 'default': 0, + 'mode':'water', + 'type': 'pro'}), + + ('peakforceangle',{ + 'numtype':'float', + 'null':True, + 'verbose_name': 'Peak Force Angle', + 'ax_min': -20, + 'ax_max': 20, + 'default': 0, + 'mode':'water', + 'type': 'pro'}), + + + ('totalangle',{ + 'numtype':'float', + 'null':True, + 'verbose_name': 'Drive Length (deg)', + 'ax_min': 40, + 'ax_max': 140, + 'default': 0, + 'mode':'water', + 'type': 'pro'}), + + + ('effectiveangle',{ + 'numtype':'float', + 'null':True, + 'verbose_name': 'Effective Drive Length (deg)', + 'ax_min': 40, + 'ax_max': 140, + 'default': 0, + 'mode':'water', + 'type': 'pro'}), + + ('rhythm',{ + 'numtype':'float', + 'null':True, + 'verbose_name': 'Stroke Rhythm', + 'ax_min': 20, + 'ax_max': 55, + 'default': 1.0, + 'mode':'erg', + 'type': 'pro'}), + + ('efficiency',{ + 'numtype':'float', + 'null':True, + 'verbose_name': 'OTW Efficiency (%)', + 'ax_min': 0, + 'ax_max': 110, + 'default': 0, + 'mode':'water', + 'type': 'pro'}), + + ('distanceperstroke',{ + 'numtype':'float', + 'null':True, + 'verbose_name': 'Distance per Stroke (m)', + 'ax_min': 0, + 'ax_max': 15, + 'default': 0, + 'mode':'both', + 'type': 'pro'}), + ) + + +axesnew = [ + (name,d['verbose_name'],d['ax_min'],d['ax_max'],d['type']) for name,d in rowingmetrics + ] + +axes = tuple(axesnew+[('None','None',0,1,'basic')]) + axlabels = {ax[0]:ax[1] for ax in axes} yaxminima = {ax[0]:ax[2] for ax in axes} diff --git a/rowers/models.py b/rowers/models.py index 35f1ffee..672b0445 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -519,57 +519,65 @@ def auto_delete_strokedata_on_delete(sender, instance, **kwargs): conn.close() engine.dispose() +from rowers.metrics import rowingmetrics + +strokedatafields = { + 'workoutid':models.IntegerField(null=True), + 'workoutstate':models.IntegerField(null=True,default=1), + 'ftime':models.CharField(max_length=30), + 'fpace':models.CharField(max_length=30), + 'hr_ut2':models.IntegerField(null=True), + 'hr_ut1':models.IntegerField(null=True), + 'hr_at':models.IntegerField(null=True), + 'hr_tr':models.IntegerField(null=True), + 'hr_an':models.IntegerField(null=True), + 'hr_max':models.IntegerField(null=True), + 'hr_bottom':models.IntegerField(null=True), + 'x_right':models.FloatField(null=True), + 'ergpace':models.FloatField(null=True), + 'nowindpace':models.FloatField(null=True), + 'equivergpower':models.FloatField(null=True), + 'fergpace':models.CharField(max_length=30), + 'fnowindpace':models.CharField(max_length=30), +} + +for name,d in rowingmetrics: + if d['numtype'] == 'float': + try: + strokedatafields[name] = models.FloatField( + null=d['null'], + default=d['default'], + verbose_name=d['verbose_name']) + except KeyError: + strokedatafields[name] = models.FloatField( + null=d['null'], + verbose_name=d['verbose_name']) + elif d['numtype'] == 'integer': + try: + strokedatafields[name] = models.IntegerField( + null=d['null'], + default=d['default'], + verbose_name=d['verbose_name']) + except KeyError: + strokedatafields[name] = models.IntegerField( + null=d['null'], + verbose_name=d['verbose_name']) +class Meta: + db_table = 'strokedata' + index_together = ['workoutid'] + app_label = 'rowers' + +attrs = {'__module__': '', 'Meta': Meta} +attrs.update(strokedatafields) + # Model of StrokeData table # the definition here is used only to enable easy Django migration # when the StrokeData are expanded. # No Django Instances of this model are managed. Strokedata table is # accesssed directly with SQL commands -class StrokeData(models.Model): - class Meta: - db_table = 'strokedata' - index_together = ['workoutid'] - - workoutid = models.IntegerField(null=True) - time = models.FloatField(null=True,verbose_name='Time') - hr = models.IntegerField(null=True,verbose_name='Heart Rate') - pace = models.FloatField(null=True,verbose_name='Pace') - workoutstate = models.IntegerField(null=True,default=1) - spm = models.FloatField(null=True,verbose_name='Stroke Rate') - cumdist = models.FloatField(null=True,verbose_name='Cumulative Distance') - ftime = models.CharField(max_length=30) - fpace = models.CharField(max_length=30) - driveenergy = models.FloatField(null=True,verbose_name='Work per Stroke') - power = models.FloatField(null=True,verbose_name='Power') - averageforce = models.FloatField(null=True,verbose_name='Average Force') - drivelength = models.FloatField(null=True,verbose_name='Drive Length') - peakforce = models.FloatField(null=True,verbose_name='Peak Force') - forceratio = models.FloatField(null=True,verbose_name='Average/Peak Force Ratio') - distance = models.FloatField(null=True,verbose_name='Distance') - drivespeed = models.FloatField(null=True,verbose_name='Drive Speed', - default=0) - hr_ut2 = models.IntegerField(null=True) - hr_ut1 = models.IntegerField(null=True) - hr_at = models.IntegerField(null=True) - hr_tr = models.IntegerField(null=True) - hr_an = models.IntegerField(null=True) - hr_max = models.IntegerField(null=True) - hr_bottom = models.IntegerField(null=True) - x_right = models.FloatField(null=True) - ergpace = models.FloatField(null=True) - nowindpace = models.FloatField(null=True) - equivergpower = models.FloatField(null=True) - fergpace = models.CharField(max_length=30) - fnowindpace = models.CharField(max_length=30) - catch = models.FloatField(default=0,null=True,verbose_name='Catch Angle') - slip = models.FloatField(default=0,null=True,verbose_name='Slip') - finish = models.FloatField(default=0,null=True,verbose_name='Finish Angle') - wash = models.FloatField(default=0,null=True,verbose_name='Wash') - peakforceangle = models.FloatField(default=0,null=True,verbose_name='Peak Force Angle') - rhythm = models.FloatField(default=1.0,null=True,verbose_name='Rhythm') - totalangle = models.FloatField(default=0.0,null=True,verbose_name='Total Stroke Length (deg)') - effectiveangle = models.FloatField(default=0.0,null=True,verbose_name='Effective Stroke Length (deg)') - efficiency = models.FloatField(default=-1,null=True,verbose_name='OTW Efficiency') - distanceperstroke = models.FloatField(default=-1,null=True,verbose_name='Distance per Stroke') +StrokeData = type(str('StrokeData'), (models.Model,), + attrs + ) # A wrapper around the png files class GraphImage(models.Model): diff --git a/rowers/views.py b/rowers/views.py index 7f7f745a..48afb7d4 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -48,6 +48,7 @@ from rowers.models import ( WorkoutComment,WorkoutCommentForm,RowerExportForm, ) from rowers.models import FavoriteForm,BaseFavoriteFormSet,SiteAnnouncement +from rowers.metrics import rowingmetrics from django.forms.formsets import formset_factory import StringIO from django.contrib.auth.decorators import login_required,user_passes_test @@ -5905,6 +5906,10 @@ def workout_flexchart3_view(request,*args,**kwargs): axchoicesbasic.pop("cumdist") if row.workouttype in ('water','coastal'): + for name,d in rowingmetrics: + if d['mode'] == 'erg': + axchoicespro.pop(name) + return render(request, 'flexchart3otw.html', {'the_script':script, @@ -5927,14 +5932,9 @@ def workout_flexchart3_view(request,*args,**kwargs): 'maxfav':maxfav, }) else: - axchoicespro.pop('slip') - axchoicespro.pop('wash') - axchoicespro.pop('catch') - axchoicespro.pop('finish') - axchoicespro.pop('totalangle') - axchoicespro.pop('effectiveangle') - axchoicespro.pop('peakforceangle') - axchoicespro.pop('efficiency') + for name,d in rowingmetrics: + if d['mode'] == 'water': + axchoicespro.pop(name) return render(request, 'flexchart3otw.html', diff --git a/rowsandall_app/settings.py b/rowsandall_app/settings.py index 6a7a614a..4efa6348 100644 --- a/rowsandall_app/settings.py +++ b/rowsandall_app/settings.py @@ -63,7 +63,7 @@ INSTALLED_APPS = [ 'analytical', 'cookielaw', 'django_extensions', - 'tz_detect' + 'tz_detect', ] AUTHENTICATION_BACKENDS = (