Merge branch 'develop' into feature/restapi
This commit is contained in:
@@ -162,8 +162,13 @@ def prepmultipledata(ids,verbose=False):
|
|||||||
res = list(itertools.chain.from_iterable(res.fetchall()))
|
res = list(itertools.chain.from_iterable(res.fetchall()))
|
||||||
conn.close()
|
conn.close()
|
||||||
engine.dispose()
|
engine.dispose()
|
||||||
|
|
||||||
res = list(set(ids)-set(res))
|
try:
|
||||||
|
ids2 = [int(id) for id in ids]
|
||||||
|
except ValueError:
|
||||||
|
ids2 = ids
|
||||||
|
|
||||||
|
res = list(set(ids2)-set(res))
|
||||||
for id in res:
|
for id in res:
|
||||||
rowdata,row = getrowdata(id=id)
|
rowdata,row = getrowdata(id=id)
|
||||||
if verbose:
|
if verbose:
|
||||||
@@ -196,7 +201,8 @@ def read_cols_df_sql(ids,columns):
|
|||||||
columns = cls,
|
columns = cls,
|
||||||
ids = tuple(ids),
|
ids = tuple(ids),
|
||||||
))
|
))
|
||||||
|
|
||||||
|
connection = engine.raw_connection()
|
||||||
df = pd.read_sql_query(query,engine)
|
df = pd.read_sql_query(query,engine)
|
||||||
engine.dispose()
|
engine.dispose()
|
||||||
return df
|
return df
|
||||||
@@ -276,7 +282,7 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True,
|
|||||||
if windowsize <= 3:
|
if windowsize <= 3:
|
||||||
windowsize = 5
|
windowsize = 5
|
||||||
|
|
||||||
if windowsize > 3:
|
if windowsize > 3 and windowsize<len(hr):
|
||||||
spm = savgol_filter(spm,windowsize,3)
|
spm = savgol_filter(spm,windowsize,3)
|
||||||
hr = savgol_filter(hr,windowsize,3)
|
hr = savgol_filter(hr,windowsize,3)
|
||||||
drivelength = savgol_filter(drivelength,windowsize,3)
|
drivelength = savgol_filter(drivelength,windowsize,3)
|
||||||
@@ -348,7 +354,7 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True,
|
|||||||
driveenergy = rowdatadf.ix[:,'driveenergy']
|
driveenergy = rowdatadf.ix[:,'driveenergy']
|
||||||
drivelength = driveenergy/(averageforce*4.44822)
|
drivelength = driveenergy/(averageforce*4.44822)
|
||||||
slip = rowdatadf.ix[:,'slip']
|
slip = rowdatadf.ix[:,'slip']
|
||||||
if windowsize > 3:
|
if windowsize > 3 and windowsize<len(slip):
|
||||||
wash = savgol_filter(wash,windowsize,3)
|
wash = savgol_filter(wash,windowsize,3)
|
||||||
slip = savgol_filter(slip,windowsize,3)
|
slip = savgol_filter(slip,windowsize,3)
|
||||||
catch = savgol_filter(catch,windowsize,3)
|
catch = savgol_filter(catch,windowsize,3)
|
||||||
|
|||||||
@@ -48,6 +48,64 @@ import stravastuff
|
|||||||
from rowers.dataprep import rdata
|
from rowers.dataprep import rdata
|
||||||
import rowers.dataprep as dataprep
|
import rowers.dataprep as dataprep
|
||||||
|
|
||||||
|
axlabels = {
|
||||||
|
'time': 'Time',
|
||||||
|
'distance': 'Distance (m)',
|
||||||
|
'cumdist': 'Distance (m)',
|
||||||
|
'hr': 'Heart Rate (bpm)',
|
||||||
|
'spm': 'Stroke Rate (spm)',
|
||||||
|
'pace': 'Pace (/500m)',
|
||||||
|
'power': 'Power (Watt)',
|
||||||
|
'averageforce': 'Average Drive Force (lbs)',
|
||||||
|
'drivelength': 'Drive Length (m)',
|
||||||
|
'peakforce': 'Peak Drive Force (lbs)',
|
||||||
|
'forceratio': 'Average/Peak Drive Force Ratio',
|
||||||
|
'driveenergy': 'Work per Stroke (J)',
|
||||||
|
'drivespeed': 'Drive Speed (m/s)',
|
||||||
|
'slip': 'Slip (degrees)',
|
||||||
|
'catch': 'Catch (degrees)',
|
||||||
|
'finish': 'Finish (degrees)',
|
||||||
|
'wash': 'Wash (degrees)',
|
||||||
|
'peakforceangle': 'Peak Force Angle (degrees)',
|
||||||
|
'None': '',
|
||||||
|
}
|
||||||
|
|
||||||
|
yaxminima = {
|
||||||
|
'hr':100,
|
||||||
|
'spm':15,
|
||||||
|
'pace': 1.0e3*210,
|
||||||
|
'power': 0,
|
||||||
|
'averageforce': 0,
|
||||||
|
'peakforce': 0,
|
||||||
|
'forceratio':0,
|
||||||
|
'drivelength':0.5,
|
||||||
|
'driveenergy': 0,
|
||||||
|
'drivespeed': 0,
|
||||||
|
'slip': 0,
|
||||||
|
'catch': -40,
|
||||||
|
'finish': 20,
|
||||||
|
'wash': 0,
|
||||||
|
'peakforceangle': -20,
|
||||||
|
}
|
||||||
|
|
||||||
|
yaxmaxima = {
|
||||||
|
'hr':200,
|
||||||
|
'spm':45,
|
||||||
|
'pace': 1.0e3*75,
|
||||||
|
'power': 600,
|
||||||
|
'averageforce':200,
|
||||||
|
'peakforce':400,
|
||||||
|
'forceratio':1,
|
||||||
|
'drivelength':2.0,
|
||||||
|
'driveenergy': 1000,
|
||||||
|
'drivespeed':4,
|
||||||
|
'slip': 15,
|
||||||
|
'catch': -75,
|
||||||
|
'finish': 55,
|
||||||
|
'wash': 30,
|
||||||
|
'peakforceangle': 20,
|
||||||
|
}
|
||||||
|
|
||||||
def tailwind(bearing,vwind,winddir):
|
def tailwind(bearing,vwind,winddir):
|
||||||
""" Calculates head-on head/tailwind in direction of rowing
|
""" Calculates head-on head/tailwind in direction of rowing
|
||||||
|
|
||||||
@@ -132,7 +190,7 @@ def interactive_histoall(theworkouts):
|
|||||||
|
|
||||||
def googlemap_chart(lat,lon,name=""):
|
def googlemap_chart(lat,lon,name=""):
|
||||||
# plot tools
|
# plot tools
|
||||||
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,resize,crosshair'
|
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,resize'
|
||||||
|
|
||||||
map_options = GMapOptions(lat = lat.mean(),lng=lon.mean(),
|
map_options = GMapOptions(lat = lat.mean(),lng=lon.mean(),
|
||||||
map_type="roadmap",zoom=11)
|
map_type="roadmap",zoom=11)
|
||||||
@@ -497,7 +555,7 @@ def interactive_chart(id=0,promember=0):
|
|||||||
TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
|
TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
|
||||||
|
|
||||||
|
|
||||||
columns = ['time','pace','hr']
|
columns = ['time','pace','hr','fpace','ftime']
|
||||||
datadf = dataprep.getsmallrowdata_db(columns,ids=[id])
|
datadf = dataprep.getsmallrowdata_db(columns,ids=[id])
|
||||||
row = Workout.objects.get(id=id)
|
row = Workout.objects.get(id=id)
|
||||||
if datadf.empty:
|
if datadf.empty:
|
||||||
@@ -554,7 +612,6 @@ def interactive_chart(id=0,promember=0):
|
|||||||
('Pace','@fpace'),
|
('Pace','@fpace'),
|
||||||
('HR','@hr{int}'),
|
('HR','@hr{int}'),
|
||||||
('SPM','@spm{1.1}'),
|
('SPM','@spm{1.1}'),
|
||||||
('Distance','@cumdist{1.1}'),
|
|
||||||
])
|
])
|
||||||
|
|
||||||
hover.mode = 'mouse'
|
hover.mode = 'mouse'
|
||||||
@@ -579,52 +636,10 @@ def interactive_cum_flex_chart2(theworkouts,promember=0,
|
|||||||
ids = [int(w.id) for w in theworkouts]
|
ids = [int(w.id) for w in theworkouts]
|
||||||
datadf = dataprep.getsmallrowdata_db([xparam,yparam1,yparam2],ids=ids)
|
datadf = dataprep.getsmallrowdata_db([xparam,yparam1,yparam2],ids=ids)
|
||||||
|
|
||||||
axlabels = {
|
|
||||||
'time': 'Time',
|
|
||||||
'distance': 'Distance (m)',
|
|
||||||
'hr': 'Heart Rate (bpm)',
|
|
||||||
'spm': 'Stroke Rate (spm)',
|
|
||||||
'pace': 'Pace (/500m)',
|
|
||||||
'power': 'Power (Watt)',
|
|
||||||
'averageforce': 'Average Drive Force (lbs)',
|
|
||||||
'drivelength': 'Drive Length (m)',
|
|
||||||
'peakforce': 'Peak Drive Force (lbs)',
|
|
||||||
'forceratio': 'Average/Peak Drive Force Ratio',
|
|
||||||
'driveenergy': 'Work per Stroke (J)',
|
|
||||||
'drivespeed': 'Drive Speed (m/s)',
|
|
||||||
'None': '',
|
|
||||||
}
|
|
||||||
|
|
||||||
yparamname1 = axlabels[yparam1]
|
yparamname1 = axlabels[yparam1]
|
||||||
yparamname2 = axlabels[yparam2]
|
yparamname2 = axlabels[yparam2]
|
||||||
|
|
||||||
yaxminima = {
|
|
||||||
'hr':100,
|
|
||||||
'spm':15,
|
|
||||||
'pace': 1.0e3*210,
|
|
||||||
'power': 0,
|
|
||||||
'averageforce': 0,
|
|
||||||
'peakforce': 0,
|
|
||||||
'forceratio':0,
|
|
||||||
'drivelength':0.5,
|
|
||||||
'driveenergy': 0,
|
|
||||||
'drivespeed': 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
yaxmaxima = {
|
|
||||||
'hr':200,
|
|
||||||
'spm':45,
|
|
||||||
'pace':1.0e3*90,
|
|
||||||
'power': 600,
|
|
||||||
'averageforce':200,
|
|
||||||
'peakforce':400,
|
|
||||||
'forceratio':1,
|
|
||||||
'drivelength':2.0,
|
|
||||||
'driveenergy': 1000,
|
|
||||||
'drivespeed':4,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
datadf = datadf[datadf[yparam1] > 0]
|
datadf = datadf[datadf[yparam1] > 0]
|
||||||
|
|
||||||
@@ -880,63 +895,6 @@ def interactive_flex_chart2(id=0,promember=0,
|
|||||||
workstrokesonly=False):
|
workstrokesonly=False):
|
||||||
|
|
||||||
|
|
||||||
axlabels = {
|
|
||||||
'time': 'Time',
|
|
||||||
'distance': 'Distance (m)',
|
|
||||||
'cumdist': 'Distance (m)',
|
|
||||||
'hr': 'Heart Rate (bpm)',
|
|
||||||
'spm': 'Stroke Rate (spm)',
|
|
||||||
'pace': 'Pace (/500m)',
|
|
||||||
'power': 'Power (Watt)',
|
|
||||||
'averageforce': 'Average Drive Force (lbs)',
|
|
||||||
'drivelength': 'Drive Length (m)',
|
|
||||||
'peakforce': 'Peak Drive Force (lbs)',
|
|
||||||
'forceratio': 'Average/Peak Drive Force Ratio',
|
|
||||||
'driveenergy': 'Work per Stroke (J)',
|
|
||||||
'drivespeed': 'Drive Speed (m/s)',
|
|
||||||
'slip': 'Slip (degrees)',
|
|
||||||
'catch': 'Catch (degrees)',
|
|
||||||
'finish': 'Finish (degrees)',
|
|
||||||
'wash': 'Wash (degrees)',
|
|
||||||
'peakforceangle': 'Peak Force Angle (degrees)',
|
|
||||||
'None': '',
|
|
||||||
}
|
|
||||||
|
|
||||||
yaxminima = {
|
|
||||||
'hr':100,
|
|
||||||
'spm':15,
|
|
||||||
'pace': 1.0e3*210,
|
|
||||||
'power': 0,
|
|
||||||
'averageforce': 0,
|
|
||||||
'peakforce': 0,
|
|
||||||
'forceratio':0,
|
|
||||||
'drivelength':0.5,
|
|
||||||
'driveenergy': 0,
|
|
||||||
'drivespeed': 0,
|
|
||||||
'slip': 0,
|
|
||||||
'catch': -70,
|
|
||||||
'finish': 30,
|
|
||||||
'wash': 0,
|
|
||||||
'peakforceangle': -20,
|
|
||||||
}
|
|
||||||
|
|
||||||
yaxmaxima = {
|
|
||||||
'hr':200,
|
|
||||||
'spm':45,
|
|
||||||
'pace': 1.0e3*75,
|
|
||||||
'power': 600,
|
|
||||||
'averageforce':200,
|
|
||||||
'peakforce':400,
|
|
||||||
'forceratio':1,
|
|
||||||
'drivelength':2.0,
|
|
||||||
'driveenergy': 1000,
|
|
||||||
'drivespeed':4,
|
|
||||||
'slip': 30,
|
|
||||||
'catch': -30,
|
|
||||||
'finish': 70,
|
|
||||||
'wash': 30,
|
|
||||||
'peakforceangle': 20,
|
|
||||||
}
|
|
||||||
|
|
||||||
#rowdata,row = dataprep.getrowdata_db(id=id)
|
#rowdata,row = dataprep.getrowdata_db(id=id)
|
||||||
columns = [xparam,yparam1,yparam2,
|
columns = [xparam,yparam1,yparam2,
|
||||||
@@ -1174,6 +1132,7 @@ def interactive_flex_chart2(id=0,promember=0,
|
|||||||
var time1 = data['time']
|
var time1 = data['time']
|
||||||
var pace1 = data['pace']
|
var pace1 = data['pace']
|
||||||
var hr1 = data['hr']
|
var hr1 = data['hr']
|
||||||
|
var fpace1 = data['fpace']
|
||||||
var distance1 = data['distance']
|
var distance1 = data['distance']
|
||||||
var power1 = data['power']
|
var power1 = data['power']
|
||||||
var xname = data['xname'][0]
|
var xname = data['xname'][0]
|
||||||
@@ -1195,6 +1154,7 @@ def interactive_flex_chart2(id=0,promember=0,
|
|||||||
data2['time'] = []
|
data2['time'] = []
|
||||||
data2['pace'] = []
|
data2['pace'] = []
|
||||||
data2['hr'] = []
|
data2['hr'] = []
|
||||||
|
data2['fpace'] = []
|
||||||
data2['distance'] = []
|
data2['distance'] = []
|
||||||
data2['power'] = []
|
data2['power'] = []
|
||||||
data2['x1mean'] = []
|
data2['x1mean'] = []
|
||||||
@@ -1212,6 +1172,7 @@ def interactive_flex_chart2(id=0,promember=0,
|
|||||||
data2['y2'].push(y2[i])
|
data2['y2'].push(y2[i])
|
||||||
data2['spm'].push(spm1[i])
|
data2['spm'].push(spm1[i])
|
||||||
data2['time'].push(time1[i])
|
data2['time'].push(time1[i])
|
||||||
|
data2['fpace'].push(fpace1[i])
|
||||||
data2['pace'].push(pace1[i])
|
data2['pace'].push(pace1[i])
|
||||||
data2['hr'].push(hr1[i])
|
data2['hr'].push(hr1[i])
|
||||||
data2['distance'].push(distance1[i])
|
data2['distance'].push(distance1[i])
|
||||||
|
|||||||
115
rowers/models.py
115
rowers/models.py
@@ -6,6 +6,7 @@ from django import forms
|
|||||||
from django.forms import ModelForm
|
from django.forms import ModelForm
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.forms.widgets import SplitDateTimeWidget
|
from django.forms.widgets import SplitDateTimeWidget
|
||||||
|
from django.forms.formsets import BaseFormSet
|
||||||
from datetimewidget.widgets import DateTimeWidget
|
from datetimewidget.widgets import DateTimeWidget
|
||||||
import os
|
import os
|
||||||
|
|
||||||
@@ -80,6 +81,120 @@ class Rower(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.user.username
|
return self.user.username
|
||||||
|
|
||||||
|
class FavoriteChart(models.Model):
|
||||||
|
y1params = (
|
||||||
|
('hr','Heart Rate'),
|
||||||
|
('pace','Pace'),
|
||||||
|
('spm','SPM'),
|
||||||
|
('driveenergy','Work per Stroke'),
|
||||||
|
('power','Power'),
|
||||||
|
('drivelength','Drivelength'),
|
||||||
|
('averageforce','Average Force'),
|
||||||
|
('peakforce','Peak Force'),
|
||||||
|
('forceratio','Average/Peak Force Ratio'),
|
||||||
|
('drivespeed','Drive Speed'),
|
||||||
|
('wash','Wash'),
|
||||||
|
('slip','Slip'),
|
||||||
|
('catch','Catch Angle'),
|
||||||
|
('finish','Finish Angle'),
|
||||||
|
('peakforceangle','Peak Force Angle')
|
||||||
|
)
|
||||||
|
|
||||||
|
y2params = (
|
||||||
|
('hr','Heart Rate'),
|
||||||
|
('spm','SPM'),
|
||||||
|
('driveenergy','Work per Stroke'),
|
||||||
|
('power','Power'),
|
||||||
|
('drivelength','Drivelength'),
|
||||||
|
('averageforce','Average Force'),
|
||||||
|
('peakforce','Peak Force'),
|
||||||
|
('forceratio','Average/Peak Force Ratio'),
|
||||||
|
('drivespeed','Drive Speed'),
|
||||||
|
('wash','Wash'),
|
||||||
|
('slip','Slip'),
|
||||||
|
('catch','Catch Angle'),
|
||||||
|
('finish','Finish Angle'),
|
||||||
|
('peakforceangle','Peak Force Angle'),
|
||||||
|
('None','None')
|
||||||
|
)
|
||||||
|
|
||||||
|
xparams = (
|
||||||
|
('time','Time'),
|
||||||
|
('distance','Distance'),
|
||||||
|
('hr','Heart Rate'),
|
||||||
|
('spm','SPM'),
|
||||||
|
('driveenergy','Work per Stroke'),
|
||||||
|
('power','Power'),
|
||||||
|
('drivelength','Drivelength'),
|
||||||
|
('averageforce','Average Force'),
|
||||||
|
('peakforce','Peak Force'),
|
||||||
|
('forceratio','Average/Peak Force Ratio'),
|
||||||
|
('drivespeed','Drive Speed'),
|
||||||
|
('wash','Wash'),
|
||||||
|
('slip','Slip'),
|
||||||
|
('catch','Catch Angle'),
|
||||||
|
('finish','Finish Angle'),
|
||||||
|
('peakforceangle','Peak Force Angle'),
|
||||||
|
)
|
||||||
|
|
||||||
|
workouttypechoices = (
|
||||||
|
('ote','Erg/SkiErg'),
|
||||||
|
('otw','On The Water'),
|
||||||
|
('both','both')
|
||||||
|
)
|
||||||
|
|
||||||
|
plottypes = (
|
||||||
|
('line','Line Chart'),
|
||||||
|
('scatter','Scatter Chart')
|
||||||
|
)
|
||||||
|
|
||||||
|
yparam1 = models.CharField(max_length=50,choices=y1params,verbose_name='Y1')
|
||||||
|
yparam2 = models.CharField(max_length=50,choices=y2params,verbose_name='Y2',default='None',blank=True)
|
||||||
|
xparam = models.CharField(max_length=50,choices=xparams,verbose_name='X')
|
||||||
|
plottype = models.CharField(max_length=50,choices=plottypes,
|
||||||
|
default='line',
|
||||||
|
verbose_name='Chart Type')
|
||||||
|
workouttype = models.CharField(max_length=50,choices=workouttypechoices,
|
||||||
|
default='both',
|
||||||
|
verbose_name='Workout Type')
|
||||||
|
reststrokes = models.BooleanField(default=True,verbose_name="Incl. Rest")
|
||||||
|
user = models.ForeignKey(Rower)
|
||||||
|
|
||||||
|
|
||||||
|
class FavoriteForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = FavoriteChart
|
||||||
|
fields = ['xparam','yparam1','yparam2',
|
||||||
|
'plottype','workouttype','reststrokes']
|
||||||
|
|
||||||
|
class BaseFavoriteFormSet(BaseFormSet):
|
||||||
|
def clean(self):
|
||||||
|
if any(self.errors):
|
||||||
|
return
|
||||||
|
|
||||||
|
for form in self.forms:
|
||||||
|
if form.cleaned_data:
|
||||||
|
xparam = form.cleaned_data['xparam']
|
||||||
|
yparam1 = form.cleaned_data['yparam1']
|
||||||
|
yparam2 = form.cleaned_data['yparam2']
|
||||||
|
plottype = form.cleaned_data['plottype']
|
||||||
|
reststrokes = form.cleaned_data['reststrokes']
|
||||||
|
|
||||||
|
if not xparam:
|
||||||
|
raise forms.ValidationError(
|
||||||
|
'Must have x parameter.',
|
||||||
|
code='missing_xparam'
|
||||||
|
)
|
||||||
|
|
||||||
|
if not yparam1:
|
||||||
|
raise forms.ValidationError(
|
||||||
|
'Must have Y1 parameter.',
|
||||||
|
code='missing_yparam1'
|
||||||
|
)
|
||||||
|
|
||||||
|
if not yparam2:
|
||||||
|
yparam2 = 'None'
|
||||||
|
|
||||||
class Workout(models.Model):
|
class Workout(models.Model):
|
||||||
workouttypes = (
|
workouttypes = (
|
||||||
('water','On-water'),
|
('water','On-water'),
|
||||||
|
|||||||
@@ -142,6 +142,7 @@ You will be taken to the secure PayPal payment site.
|
|||||||
|
|
||||||
<p>
|
<p>
|
||||||
<ul>
|
<ul>
|
||||||
|
<li>2016-12-07 Favorite Flex Charts for Premium users</li>
|
||||||
<li>2016-12-01 Support for NK Empower Oarlock parameters (catch and
|
<li>2016-12-01 Support for NK Empower Oarlock parameters (catch and
|
||||||
finish angles, slip and wash, and power as measured by the Oarlock</li>
|
finish angles, slip and wash, and power as measured by the Oarlock</li>
|
||||||
<li>2016-11-10 Power based Pie Charts</li>
|
<li>2016-11-10 Power based Pie Charts</li>
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
{% if user.rower.rowerplan == 'pro' %}
|
{% if user.rower.rowerplan == 'pro' %}
|
||||||
<a class="button blue small" href="/rowers/histo">Power Histogram</a>
|
<a class="button blue small" href="/rowers/histo">Power Histogram</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a class="button blue small" href="/rowers/about">Power Histogram</a>
|
<a class="button blue small" href="/rowers/promembership">Power Histogram</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
@@ -73,4 +73,4 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
40
rowers/templates/favoritecharts.html
Normal file
40
rowers/templates/favoritecharts.html
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}Change Favorite Charts{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ favorites_formset.management_form }}
|
||||||
|
|
||||||
|
{% for favorites_form in favorites_formset %}
|
||||||
|
<div class="fav-formset grid_4 alpha">
|
||||||
|
<h2>Chart {{ forloop.counter }}</h2>
|
||||||
|
<table>
|
||||||
|
{{ favorites_form.as_table }}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
<div class="grid_12 alpha">
|
||||||
|
<p> </p>
|
||||||
|
</div>
|
||||||
|
<div class="grid_12 alpha">
|
||||||
|
<div class="grid_2">
|
||||||
|
<p><input type="submit" value="Update Favorites" class="button green small"/></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Include formset plugin - including jQuery dependency -->
|
||||||
|
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
|
||||||
|
<script src="/static/js/jquery.formset.js"></script>
|
||||||
|
<script>
|
||||||
|
$('.fav-formset').formset({
|
||||||
|
addText: '<div class="grid_12"> </div><div class="button grid_2 green small">add chart</div>',
|
||||||
|
deleteText: '<div class="grid_12"><p> </p></div><div class="button grid_1 red small">remove</div>'
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
@@ -163,6 +163,43 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% if user.rower.rowerplan == 'pro' %}
|
||||||
|
<div id="favorites" class="grid_12 alpha">
|
||||||
|
<div class="grid_2 suffix_4 alpha">
|
||||||
|
{% if maxfav >= 0 %}
|
||||||
|
<a class="button gray small" href="/rowers/me/favoritecharts">Manage Favorites</a>
|
||||||
|
{% else %}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="grid_1">
|
||||||
|
{% if favoritenr > 0 %}
|
||||||
|
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart?favoritechart={{ favoritenr|add:-1 }}"><</a>
|
||||||
|
{% else %}
|
||||||
|
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart?favoritechart={{ maxfav }}"><</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="grid_2">
|
||||||
|
<form enctype="multipart/form-data" action="{{ formloc }}" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input class="grid_2 alpha button blue small" type="hidden" name="savefavorite" value="True">
|
||||||
|
{% if workstrokesonly %}
|
||||||
|
<input type="hidden" name="workstrokesonlysave" value="False">
|
||||||
|
{% else %}
|
||||||
|
<input type="hidden" name="workstrokesonlysave" value="True">
|
||||||
|
{% endif %}
|
||||||
|
<input class="grid_2 alpha button blue small" value="Make Favorite" type="Submit">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="grid_1">
|
||||||
|
{% if favoritenr < maxfav %}
|
||||||
|
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart?favoritechart={{ favoritenr|add:1 }}">></a>
|
||||||
|
{% else %}
|
||||||
|
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart?favoritechart=0">></a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% endlocaltime %}
|
{% endlocaltime %}
|
||||||
|
|||||||
@@ -198,6 +198,43 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% if user.rower.rowerplan == 'pro' %}
|
||||||
|
<div id="favorites" class="grid_12 alpha">
|
||||||
|
<div class="grid_2 suffix_4 alpha">
|
||||||
|
{% if maxfav >= 0 %}
|
||||||
|
<a class="button gray small" href="/rowers/me/favoritecharts">Manage Favorites</a>
|
||||||
|
{% else %}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="grid_1">
|
||||||
|
{% if favoritenr > 0 %}
|
||||||
|
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart?favoritechart={{ favoritenr|add:-1 }}"><</a>
|
||||||
|
{% else %}
|
||||||
|
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart?favoritechart={{ maxfav }}"><</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="grid_2">
|
||||||
|
<form enctype="multipart/form-data" action="{{ formloc }}" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input class="grid_2 alpha button blue small" type="hidden" name="savefavorite" value="True">
|
||||||
|
{% if workstrokesonly %}
|
||||||
|
<input type="hidden" name="workstrokesonlysave" value="False">
|
||||||
|
{% else %}
|
||||||
|
<input type="hidden" name="workstrokesonlysave" value="True">
|
||||||
|
{% endif %}
|
||||||
|
<input class="grid_2 alpha button blue small" value="Make Favorite" type="Submit">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="grid_1">
|
||||||
|
{% if favoritenr < maxfav %}
|
||||||
|
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart?favoritechart={{ favoritenr|add:1 }}">></a>
|
||||||
|
{% else %}
|
||||||
|
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart?favoritechart=0">></a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% endlocaltime %}
|
{% endlocaltime %}
|
||||||
|
|||||||
@@ -3,37 +3,45 @@
|
|||||||
{% block title %}Change Rower {% endblock %}
|
{% block title %}Change Rower {% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% if form.errors %}
|
{% if form.errors %}
|
||||||
<p style="color: red;">
|
<p style="color: red;">
|
||||||
Please correct the error{{ form.errors|pluralize }} below.
|
Please correct the error{{ form.errors|pluralize }} below.
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="grid_6 alpha">
|
<div class="grid_6 alpha">
|
||||||
<h1>Heart Rate Bands</h1>
|
<h1>Heart Rate Bands</h1>
|
||||||
|
|
||||||
|
<form enctype="multipart/form-data" action="" method="post">
|
||||||
|
<table>
|
||||||
|
{{ form.as_table }}
|
||||||
|
</table>
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="grid_2 prefix_2 suffix_2">
|
||||||
|
<input class="button green" type="submit" value="Save">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="grid_6 omega">
|
||||||
|
<h1>Functional Threshold Power</h1>
|
||||||
|
<p>
|
||||||
<form enctype="multipart/form-data" action="" method="post">
|
<form enctype="multipart/form-data" action="" method="post">
|
||||||
<table>
|
<table>
|
||||||
{{ form.as_table }}
|
|
||||||
</table>
|
|
||||||
{% csrf_token %}
|
|
||||||
<div class="grid_2 prefix_2 suffix_2">
|
|
||||||
<input class="button green" type="submit" value="Save">
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="grid_6 omega">
|
|
||||||
<h1>Functional Threshold Power</h1>
|
|
||||||
<p>
|
|
||||||
<form enctype="multipart/form-data" action="" method="post">
|
|
||||||
<table>
|
|
||||||
{{ powerform.as_table }}
|
{{ powerform.as_table }}
|
||||||
</table>
|
</table>
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<div class="grid_2 prefix_2 suffix_2">
|
<div class="grid_2 prefix_2 suffix_2">
|
||||||
<input class="button green" type="submit" value="Save">
|
<input class="button green" type="submit" value="Save">
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
|
<div class="grid_6 prefix_6 alpha">
|
||||||
|
<div class="grid_2 suffix_4 alpha">
|
||||||
|
<a class="button gray small" href="/rowers/me/favoritecharts">Manage Favorite Charts</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|||||||
@@ -146,6 +146,7 @@ urlpatterns = [
|
|||||||
url(r'^me/sporttracksauthorize/$',views.rower_sporttracks_authorize),
|
url(r'^me/sporttracksauthorize/$',views.rower_sporttracks_authorize),
|
||||||
url(r'^me/sporttracksrefresh/$',views.rower_sporttracks_token_refresh),
|
url(r'^me/sporttracksrefresh/$',views.rower_sporttracks_token_refresh),
|
||||||
url(r'^me/c2refresh/$',views.rower_c2_token_refresh),
|
url(r'^me/c2refresh/$',views.rower_c2_token_refresh),
|
||||||
|
url(r'^me/favoritecharts/$',views.rower_favoritecharts_view),
|
||||||
url(r'^email/send/$', views.sendmail),
|
url(r'^email/send/$', views.sendmail),
|
||||||
url(r'^email/thankyou/$', TemplateView.as_view(template_name='thankyou.html'), name='thankyou'),
|
url(r'^email/thankyou/$', TemplateView.as_view(template_name='thankyou.html'), name='thankyou'),
|
||||||
url(r'^email/$', TemplateView.as_view(template_name='email.html'), name='email'),
|
url(r'^email/$', TemplateView.as_view(template_name='email.html'), name='email'),
|
||||||
|
|||||||
213
rowers/views.py
213
rowers/views.py
@@ -2,6 +2,7 @@ import time
|
|||||||
import operator
|
import operator
|
||||||
from django.views.generic.base import TemplateView
|
from django.views.generic.base import TemplateView
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
from django.db import IntegrityError, transaction
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.http import (
|
from django.http import (
|
||||||
HttpResponse, HttpResponseRedirect,
|
HttpResponse, HttpResponseRedirect,
|
||||||
@@ -20,8 +21,10 @@ from django.core.mail import send_mail, BadHeaderError
|
|||||||
from rowers.forms import EmailForm, RegistrationForm, RegistrationFormTermsOfService,RegistrationFormUniqueEmail,CNsummaryForm,UpdateWindForm,UpdateStreamForm
|
from rowers.forms import EmailForm, RegistrationForm, RegistrationFormTermsOfService,RegistrationFormUniqueEmail,CNsummaryForm,UpdateWindForm,UpdateStreamForm
|
||||||
from rowers.forms import PredictedPieceForm,DateRangeForm,DeltaDaysForm
|
from rowers.forms import PredictedPieceForm,DateRangeForm,DeltaDaysForm
|
||||||
from rowers.forms import SummaryStringForm,IntervalUpdateForm,StrokeDataForm
|
from rowers.forms import SummaryStringForm,IntervalUpdateForm,StrokeDataForm
|
||||||
from rowers.models import Workout, User, Rower, WorkoutForm
|
from rowers.models import Workout, User, Rower, WorkoutForm,FavoriteChart
|
||||||
from rowers.models import RowerPowerForm,RowerForm,GraphImage,AdvancedWorkoutForm
|
from rowers.models import RowerPowerForm,RowerForm,GraphImage,AdvancedWorkoutForm
|
||||||
|
from rowers.models import FavoriteForm,BaseFavoriteFormSet
|
||||||
|
from django.forms.formsets import formset_factory
|
||||||
import StringIO
|
import StringIO
|
||||||
from django.contrib.auth.decorators import login_required,user_passes_test
|
from django.contrib.auth.decorators import login_required,user_passes_test
|
||||||
from time import strftime,strptime,mktime,time,daylight
|
from time import strftime,strptime,mktime,time,daylight
|
||||||
@@ -424,7 +427,7 @@ def add_workout_from_strokedata(user,importid,data,strokedata,source='c2'):
|
|||||||
|
|
||||||
df['originalvelo'] = velo
|
df['originalvelo'] = velo
|
||||||
|
|
||||||
if windowsize > 3:
|
if windowsize > 3 and windowsize < len(velo):
|
||||||
velo2 = savgol_filter(velo,windowsize,3)
|
velo2 = savgol_filter(velo,windowsize,3)
|
||||||
else:
|
else:
|
||||||
velo2=velo
|
velo2=velo
|
||||||
@@ -656,7 +659,7 @@ def add_workout_from_stdata(user,importid,data):
|
|||||||
|
|
||||||
df['originalvelo'] = velo
|
df['originalvelo'] = velo
|
||||||
|
|
||||||
if windowsize > 3:
|
if windowsize > 3 and windowsize<len(velo):
|
||||||
velo2 = savgol_filter(velo,windowsize,3)
|
velo2 = savgol_filter(velo,windowsize,3)
|
||||||
else:
|
else:
|
||||||
velo2 = velo
|
velo2 = velo
|
||||||
@@ -1450,7 +1453,7 @@ def histo(request,theuser=0,
|
|||||||
promember=1
|
promember=1
|
||||||
|
|
||||||
if not promember:
|
if not promember:
|
||||||
return HttpResponseRedirect("/rowers/about/")
|
return HttpResponseRedirect("/rowers/promembership/")
|
||||||
|
|
||||||
# get all indoor rows of in date range
|
# get all indoor rows of in date range
|
||||||
|
|
||||||
@@ -1913,7 +1916,7 @@ def workouts_view(request,message='',successmessage='',
|
|||||||
except Rower.DoesNotExist:
|
except Rower.DoesNotExist:
|
||||||
return HttpResponse("User has no rower instance")
|
return HttpResponse("User has no rower instance")
|
||||||
|
|
||||||
@user_passes_test(promember,login_url="/login")
|
@user_passes_test(promember,login_url="/",redirect_field_name=None)
|
||||||
def workout_comparison_list(request,id=0,message='',successmessage='',
|
def workout_comparison_list(request,id=0,message='',successmessage='',
|
||||||
startdatestring="",enddatestring="",
|
startdatestring="",enddatestring="",
|
||||||
startdate=timezone.now()-datetime.timedelta(days=365),
|
startdate=timezone.now()-datetime.timedelta(days=365),
|
||||||
@@ -2027,7 +2030,7 @@ def workout_view(request,id=0):
|
|||||||
return HttpResponseNotFound("Workout doesn't exist")
|
return HttpResponseNotFound("Workout doesn't exist")
|
||||||
|
|
||||||
|
|
||||||
@user_passes_test(promember,login_url="/login")
|
@user_passes_test(promember,login_url="/",redirect_field_name=None)
|
||||||
def workout_undo_smoothenpace_view(request,id=0,message="",successmessage=""):
|
def workout_undo_smoothenpace_view(request,id=0,message="",successmessage=""):
|
||||||
row = Workout.objects.get(id=id)
|
row = Workout.objects.get(id=id)
|
||||||
if (checkworkoutuser(request.user,row)==False):
|
if (checkworkoutuser(request.user,row)==False):
|
||||||
@@ -2054,7 +2057,7 @@ def workout_undo_smoothenpace_view(request,id=0,message="",successmessage=""):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
@user_passes_test(promember,login_url="/login")
|
@user_passes_test(promember,login_url="/",redirect_field_name=None)
|
||||||
def workout_smoothenpace_view(request,id=0,message="",successmessage=""):
|
def workout_smoothenpace_view(request,id=0,message="",successmessage=""):
|
||||||
row = Workout.objects.get(id=id)
|
row = Workout.objects.get(id=id)
|
||||||
if (checkworkoutuser(request.user,row)==False):
|
if (checkworkoutuser(request.user,row)==False):
|
||||||
@@ -2089,7 +2092,7 @@ def workout_smoothenpace_view(request,id=0,message="",successmessage=""):
|
|||||||
|
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@user_passes_test(promember,login_url="/login")
|
@user_passes_test(promember,login_url="/",redirect_field_name=None)
|
||||||
def workout_crewnerd_summary_view(request,id=0,message="",successmessage=""):
|
def workout_crewnerd_summary_view(request,id=0,message="",successmessage=""):
|
||||||
row = Workout.objects.get(id=id)
|
row = Workout.objects.get(id=id)
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
@@ -2133,7 +2136,7 @@ def workout_crewnerd_summary_view(request,id=0,message="",successmessage=""):
|
|||||||
{'form':form,
|
{'form':form,
|
||||||
'id':row.id})
|
'id':row.id})
|
||||||
|
|
||||||
@user_passes_test(promember,login_url="/login")
|
@user_passes_test(promember,login_url="/",redirect_field_name=None)
|
||||||
def workout_downloadwind_view(request,id=0,message="",successmessage=""):
|
def workout_downloadwind_view(request,id=0,message="",successmessage=""):
|
||||||
row = Workout.objects.get(id=id)
|
row = Workout.objects.get(id=id)
|
||||||
f1 = row.csvfilename
|
f1 = row.csvfilename
|
||||||
@@ -2190,7 +2193,7 @@ def workout_downloadwind_view(request,id=0,message="",successmessage=""):
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
@user_passes_test(promember,login_url="/login")
|
@user_passes_test(promember,login_url="/",redirect_field_name=None)
|
||||||
def workout_wind_view(request,id=0,message="",successmessage=""):
|
def workout_wind_view(request,id=0,message="",successmessage=""):
|
||||||
row = Workout.objects.get(id=id)
|
row = Workout.objects.get(id=id)
|
||||||
if (checkworkoutuser(request.user,row)==False):
|
if (checkworkoutuser(request.user,row)==False):
|
||||||
@@ -2286,7 +2289,7 @@ def workout_wind_view(request,id=0,message="",successmessage=""):
|
|||||||
'gmapdiv':gmdiv})
|
'gmapdiv':gmdiv})
|
||||||
|
|
||||||
|
|
||||||
@user_passes_test(promember,login_url="/login")
|
@user_passes_test(promember,login_url="/",redirect_field_name=None)
|
||||||
def workout_stream_view(request,id=0,message="",successmessage=""):
|
def workout_stream_view(request,id=0,message="",successmessage=""):
|
||||||
row = Workout.objects.get(id=id)
|
row = Workout.objects.get(id=id)
|
||||||
if (checkworkoutuser(request.user,row)==False):
|
if (checkworkoutuser(request.user,row)==False):
|
||||||
@@ -2348,7 +2351,7 @@ def workout_stream_view(request,id=0,message="",successmessage=""):
|
|||||||
'the_div':div})
|
'the_div':div})
|
||||||
|
|
||||||
|
|
||||||
@user_passes_test(promember, login_url="/login")
|
@user_passes_test(promember, login_url="/",redirect_field_name=None)
|
||||||
def workout_otwsetpower_view(request,id=0,message="",successmessage=""):
|
def workout_otwsetpower_view(request,id=0,message="",successmessage=""):
|
||||||
row = Workout.objects.get(id=id)
|
row = Workout.objects.get(id=id)
|
||||||
if (checkworkoutuser(request.user,row)==False):
|
if (checkworkoutuser(request.user,row)==False):
|
||||||
@@ -2478,7 +2481,7 @@ def workout_geeky_view(request,id=0,message="",successmessage=""):
|
|||||||
'interactiveplot':script,
|
'interactiveplot':script,
|
||||||
'the_div':div})
|
'the_div':div})
|
||||||
|
|
||||||
#@user_passes_test(promember,login_url="/login")
|
#@user_passes_test(promember,login_url="/",redirect_field_name=None)
|
||||||
@login_required()
|
@login_required()
|
||||||
def workout_advanced_view(request,id=0,message="",successmessage=""):
|
def workout_advanced_view(request,id=0,message="",successmessage=""):
|
||||||
row = Workout.objects.get(id=id)
|
row = Workout.objects.get(id=id)
|
||||||
@@ -2577,18 +2580,24 @@ def workout_comparison_view2(request,id1=0,id2=0,xparam='distance',
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
def workout_flexchart3_view(request,id=0,xparam='distance',yparam1='pace',
|
def workout_flexchart3_view(request,*args,**kwargs):
|
||||||
yparam2='hr',plottype='line',
|
|
||||||
promember=0):
|
|
||||||
|
|
||||||
if request.method == 'POST':
|
try:
|
||||||
workstrokesonly = request.POST['workstrokesonly']
|
id = kwargs['id']
|
||||||
if workstrokesonly == 'True':
|
except KeyError:
|
||||||
workstrokesonly = True
|
return HttpResponse("Invalid workout number")
|
||||||
else:
|
|
||||||
workstrokesonly = False
|
if 'promember' in kwargs:
|
||||||
|
promember = kwargs['promember']
|
||||||
else:
|
else:
|
||||||
workstrokesonly = False
|
promember = 0
|
||||||
|
|
||||||
|
try:
|
||||||
|
favoritenr = int(request.GET['favoritechart'])
|
||||||
|
except:
|
||||||
|
favoritenr = 0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
row = Workout.objects.get(id=id)
|
row = Workout.objects.get(id=id)
|
||||||
promember=0
|
promember=0
|
||||||
@@ -2601,6 +2610,82 @@ def workout_flexchart3_view(request,id=0,xparam='distance',yparam1='pace',
|
|||||||
if request.user == row.user.user:
|
if request.user == row.user.user:
|
||||||
mayedit=1
|
mayedit=1
|
||||||
|
|
||||||
|
|
||||||
|
workouttype = 'ote'
|
||||||
|
if row.workouttype == 'water':
|
||||||
|
workouttype = 'otw'
|
||||||
|
|
||||||
|
|
||||||
|
favorites = FavoriteChart.objects.filter(user=r,
|
||||||
|
workouttype__in=[workouttype,'both']).order_by("id")
|
||||||
|
maxfav = len(favorites)-1
|
||||||
|
|
||||||
|
# check if favoritenr is not out of range
|
||||||
|
if favorites:
|
||||||
|
try:
|
||||||
|
t = favorites[favoritenr].xparam
|
||||||
|
except IndexError:
|
||||||
|
favoritenr=0
|
||||||
|
|
||||||
|
if 'xparam' in kwargs:
|
||||||
|
xparam = kwargs['xparam']
|
||||||
|
else:
|
||||||
|
if favorites:
|
||||||
|
xparam = favorites[favoritenr].xparam
|
||||||
|
else:
|
||||||
|
xparam = 'distance'
|
||||||
|
|
||||||
|
if 'yparam1' in kwargs:
|
||||||
|
yparam1 = kwargs['yparam1']
|
||||||
|
else:
|
||||||
|
if favorites:
|
||||||
|
yparam1 = favorites[favoritenr].yparam1
|
||||||
|
else:
|
||||||
|
yparam1 = 'pace'
|
||||||
|
|
||||||
|
if 'yparam2' in kwargs:
|
||||||
|
yparam2 = kwargs['yparam2']
|
||||||
|
if yparam2 == '':
|
||||||
|
yparam2 = 'None'
|
||||||
|
else:
|
||||||
|
if favorites:
|
||||||
|
yparam2 = favorites[favoritenr].yparam2
|
||||||
|
if yparam2 == '':
|
||||||
|
yparam2 = 'None'
|
||||||
|
else:
|
||||||
|
yparam2 = 'hr'
|
||||||
|
|
||||||
|
if 'plottype' in kwargs:
|
||||||
|
plottype = kwargs['plottype']
|
||||||
|
else:
|
||||||
|
if favorites:
|
||||||
|
plottype = favorites[favoritenr].plottype
|
||||||
|
else:
|
||||||
|
plottype = 'line'
|
||||||
|
|
||||||
|
if 'workstrokesonly' in kwargs:
|
||||||
|
workstrokesonly = kwargs['workstrokesonly']
|
||||||
|
else:
|
||||||
|
if favorites:
|
||||||
|
workstrokesonly = not favorites[favoritenr].reststrokes
|
||||||
|
else:
|
||||||
|
workstrokesonly = False
|
||||||
|
|
||||||
|
if request.method == 'POST' and 'savefavorite' in request.POST:
|
||||||
|
workstrokesonly = request.POST['workstrokesonlysave']
|
||||||
|
reststrokes = not workstrokesonly
|
||||||
|
f = FavoriteChart(user=r,xparam=xparam,
|
||||||
|
yparam1=yparam1,yparam2=yparam2,
|
||||||
|
plottype=plottype,workouttype=workouttype,
|
||||||
|
reststrokes=reststrokes)
|
||||||
|
f.save()
|
||||||
|
if request.method == 'POST' and 'workstrokesonly' in request.POST:
|
||||||
|
workstrokesonly = request.POST['workstrokesonly']
|
||||||
|
if workstrokesonly == 'True':
|
||||||
|
workstrokesonly = True
|
||||||
|
else:
|
||||||
|
workstrokesonly = False
|
||||||
|
|
||||||
# create interactive plot
|
# create interactive plot
|
||||||
res = interactive_flex_chart2(id,xparam=xparam,yparam1=yparam1,
|
res = interactive_flex_chart2(id,xparam=xparam,yparam1=yparam1,
|
||||||
yparam2=yparam2,
|
yparam2=yparam2,
|
||||||
@@ -2626,6 +2711,8 @@ def workout_flexchart3_view(request,id=0,xparam='distance',yparam1='pace',
|
|||||||
'mayedit':mayedit,
|
'mayedit':mayedit,
|
||||||
'promember':promember,
|
'promember':promember,
|
||||||
'workstrokesonly': not workstrokesonly,
|
'workstrokesonly': not workstrokesonly,
|
||||||
|
'favoritenr':favoritenr,
|
||||||
|
'maxfav':maxfav,
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
return render(request,
|
return render(request,
|
||||||
@@ -2642,6 +2729,8 @@ def workout_flexchart3_view(request,id=0,xparam='distance',yparam1='pace',
|
|||||||
'mayedit':mayedit,
|
'mayedit':mayedit,
|
||||||
'promember':promember,
|
'promember':promember,
|
||||||
'workstrokesonly': not workstrokesonly,
|
'workstrokesonly': not workstrokesonly,
|
||||||
|
'favoritenr':favoritenr,
|
||||||
|
'maxfav':maxfav,
|
||||||
})
|
})
|
||||||
|
|
||||||
def testbokeh(request):
|
def testbokeh(request):
|
||||||
@@ -2735,7 +2824,7 @@ def testbokeh(request):
|
|||||||
'css_res':css_resources,
|
'css_res':css_resources,
|
||||||
})
|
})
|
||||||
|
|
||||||
#@user_passes_test(promember,login_url="/login")
|
#@user_passes_test(promember,login_url="/",redirect_field_name=None)
|
||||||
def workout_biginteractive_view(request,id=0,message="",successmessage=""):
|
def workout_biginteractive_view(request,id=0,message="",successmessage=""):
|
||||||
row = Workout.objects.get(id=id)
|
row = Workout.objects.get(id=id)
|
||||||
# check if user is owner of this workout
|
# check if user is owner of this workout
|
||||||
@@ -2977,7 +3066,7 @@ def workout_edit_view(request,id=0,message="",successmessage=""):
|
|||||||
'workout_form.html',
|
'workout_form.html',
|
||||||
{'form':form})
|
{'form':form})
|
||||||
|
|
||||||
@user_passes_test(promember,login_url="/login")
|
@user_passes_test(promember,login_url="/",redirect_field_name=None)
|
||||||
def workout_add_otw_powerplot_view(request,id):
|
def workout_add_otw_powerplot_view(request,id):
|
||||||
w = Workout.objects.get(id=id)
|
w = Workout.objects.get(id=id)
|
||||||
if (checkworkoutuser(request.user,w)==False):
|
if (checkworkoutuser(request.user,w)==False):
|
||||||
@@ -3199,7 +3288,7 @@ def workout_add_distanceplot_view(request,id):
|
|||||||
url = "/rowers/workout/"+str(w.id)+"/edit"
|
url = "/rowers/workout/"+str(w.id)+"/edit"
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@user_passes_test(promember,login_url="/login")
|
@user_passes_test(promember,login_url="/",redirect_field_name=None)
|
||||||
def workout_add_distanceplot2_view(request,id):
|
def workout_add_distanceplot2_view(request,id):
|
||||||
w = Workout.objects.get(id=id)
|
w = Workout.objects.get(id=id)
|
||||||
if (checkworkoutuser(request.user,w)==False):
|
if (checkworkoutuser(request.user,w)==False):
|
||||||
@@ -3243,7 +3332,7 @@ def workout_add_distanceplot2_view(request,id):
|
|||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
|
|
||||||
@user_passes_test(promember,login_url="/login")
|
@user_passes_test(promember,login_url="/",redirect_field_name=None)
|
||||||
def workout_add_timeplot2_view(request,id):
|
def workout_add_timeplot2_view(request,id):
|
||||||
w = Workout.objects.get(id=id)
|
w = Workout.objects.get(id=id)
|
||||||
if (checkworkoutuser(request.user,w)==False):
|
if (checkworkoutuser(request.user,w)==False):
|
||||||
@@ -3639,7 +3728,7 @@ def workout_upload_view(request,message=""):
|
|||||||
if not 'originalvelo' in row.df:
|
if not 'originalvelo' in row.df:
|
||||||
row.df['originalvelo'] = velo
|
row.df['originalvelo'] = velo
|
||||||
|
|
||||||
if windowsize > 3:
|
if windowsize > 3 and windowsize<len(velo):
|
||||||
velo2 = savgol_filter(velo,windowsize,3)
|
velo2 = savgol_filter(velo,windowsize,3)
|
||||||
else:
|
else:
|
||||||
velo2 = velo
|
velo2 = velo
|
||||||
@@ -3711,7 +3800,7 @@ def workout_upload_view(request,message=""):
|
|||||||
w.save()
|
w.save()
|
||||||
# put stroke data in database
|
# put stroke data in database
|
||||||
res = dataprep.dataprep(row.df,id=w.id,bands=True,barchart=True,otwpower=True,empower=True)
|
res = dataprep.dataprep(row.df,id=w.id,bands=True,barchart=True,otwpower=True,empower=True)
|
||||||
|
|
||||||
# Make Plot
|
# Make Plot
|
||||||
if (make_plot):
|
if (make_plot):
|
||||||
imagename = f1[:-4]+'.png'
|
imagename = f1[:-4]+'.png'
|
||||||
@@ -4428,6 +4517,72 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@user_passes_test(promember,login_url="/rowers/me/edit",redirect_field_name=None)
|
||||||
|
def rower_favoritecharts_view(request):
|
||||||
|
message = ''
|
||||||
|
successmessage = ''
|
||||||
|
r = Rower.objects.get(user=request.user)
|
||||||
|
favorites = FavoriteChart.objects.filter(user=r).order_by('id')
|
||||||
|
aantal = len(favorites)
|
||||||
|
favorites_data = [{'yparam1':f.yparam1,
|
||||||
|
'yparam2':f.yparam2,
|
||||||
|
'xparam':f.xparam,
|
||||||
|
'plottype':f.plottype,
|
||||||
|
'workouttype':f.workouttype,
|
||||||
|
'reststrokes':f.reststrokes}
|
||||||
|
for f in favorites]
|
||||||
|
FavoriteChartFormSet = formset_factory(FavoriteForm,formset=BaseFavoriteFormSet,extra=0)
|
||||||
|
if aantal==0:
|
||||||
|
FavoriteChartFormSet = formset_factory(FavoriteForm,formset=BaseFavoriteFormSet,extra=1)
|
||||||
|
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
favorites_formset = FavoriteChartFormSet(request.POST)
|
||||||
|
|
||||||
|
if favorites_formset.is_valid():
|
||||||
|
new_instances = []
|
||||||
|
for favorites_form in favorites_formset:
|
||||||
|
yparam1 = favorites_form.cleaned_data.get('yparam1')
|
||||||
|
yparam2 = favorites_form.cleaned_data.get('yparam2')
|
||||||
|
xparam = favorites_form.cleaned_data.get('xparam')
|
||||||
|
plottype = favorites_form.cleaned_data.get('plottype')
|
||||||
|
workouttype = favorites_form.cleaned_data.get('workouttype')
|
||||||
|
reststrokes = favorites_form.cleaned_data.get('reststrokes')
|
||||||
|
new_instances.append(FavoriteChart(user=r,
|
||||||
|
yparam1=yparam1,
|
||||||
|
yparam2=yparam2,
|
||||||
|
xparam=xparam,
|
||||||
|
plottype=plottype,
|
||||||
|
workouttype=workouttype,
|
||||||
|
reststrokes=reststrokes))
|
||||||
|
try:
|
||||||
|
with transaction.atomic():
|
||||||
|
FavoriteChart.objects.filter(user=r).delete()
|
||||||
|
FavoriteChart.objects.bulk_create(new_instances)
|
||||||
|
successmessage = "You have updated your favorites"
|
||||||
|
FavoriteChartFormSet=formset_factory(FavoriteForm,formset=BaseFavoriteFormSet)
|
||||||
|
print new_instances
|
||||||
|
print "aap",len(new_instances)
|
||||||
|
if len(new_instances)==0:
|
||||||
|
FavoriteChartFormSet=formset_factory(FavoriteForm,formset=BaseFavoriteFormSet,extra=1)
|
||||||
|
|
||||||
|
favorites_formset = FavoriteChartFormSet()
|
||||||
|
except IntegrityError:
|
||||||
|
message = "something went wrong"
|
||||||
|
|
||||||
|
else:
|
||||||
|
favorites_formset = FavoriteChartFormSet(initial=favorites_data)
|
||||||
|
|
||||||
|
|
||||||
|
context = {
|
||||||
|
'favorites_formset':favorites_formset,
|
||||||
|
'message':message,
|
||||||
|
'successmessage':successmessage,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return render(request,'favoritecharts.html',context)
|
||||||
|
|
||||||
@login_required()
|
@login_required()
|
||||||
def rower_edit_view(request,message=""):
|
def rower_edit_view(request,message=""):
|
||||||
|
|||||||
231
static/js/jquery.formset.js
Normal file
231
static/js/jquery.formset.js
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
/**
|
||||||
|
* jQuery Formset 1.3-pre
|
||||||
|
* @author Stanislaus Madueke (stan DOT madueke AT gmail DOT com)
|
||||||
|
* @requires jQuery 1.2.6 or later
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009, Stanislaus Madueke
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the New BSD License
|
||||||
|
* See: http://www.opensource.org/licenses/bsd-license.php
|
||||||
|
*/
|
||||||
|
;(function($) {
|
||||||
|
$.fn.formset = function(opts)
|
||||||
|
{
|
||||||
|
var options = $.extend({}, $.fn.formset.defaults, opts),
|
||||||
|
flatExtraClasses = options.extraClasses.join(' '),
|
||||||
|
totalForms = $('#id_' + options.prefix + '-TOTAL_FORMS'),
|
||||||
|
maxForms = $('#id_' + options.prefix + '-MAX_NUM_FORMS'),
|
||||||
|
minForms = $('#id_' + options.prefix + '-MIN_NUM_FORMS'),
|
||||||
|
childElementSelector = 'input,select,textarea,label,div',
|
||||||
|
$$ = $(this),
|
||||||
|
|
||||||
|
applyExtraClasses = function(row, ndx) {
|
||||||
|
if (options.extraClasses) {
|
||||||
|
row.removeClass(flatExtraClasses);
|
||||||
|
row.addClass(options.extraClasses[ndx % options.extraClasses.length]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
updateElementIndex = function(elem, prefix, ndx) {
|
||||||
|
var idRegex = new RegExp(prefix + '-(\\d+|__prefix__)-'),
|
||||||
|
replacement = prefix + '-' + ndx + '-';
|
||||||
|
if (elem.attr("for")) elem.attr("for", elem.attr("for").replace(idRegex, replacement));
|
||||||
|
if (elem.attr('id')) elem.attr('id', elem.attr('id').replace(idRegex, replacement));
|
||||||
|
if (elem.attr('name')) elem.attr('name', elem.attr('name').replace(idRegex, replacement));
|
||||||
|
},
|
||||||
|
|
||||||
|
hasChildElements = function(row) {
|
||||||
|
return row.find(childElementSelector).length > 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
showAddButton = function() {
|
||||||
|
return maxForms.length == 0 || // For Django versions pre 1.2
|
||||||
|
(maxForms.val() == '' || (maxForms.val() - totalForms.val() > 0));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether delete link(s) can be displayed - when total forms > min forms
|
||||||
|
*/
|
||||||
|
showDeleteLinks = function() {
|
||||||
|
return minForms.length == 0 || // For Django versions pre 1.7
|
||||||
|
(minForms.val() == '' || (totalForms.val() - minForms.val() > 0));
|
||||||
|
},
|
||||||
|
|
||||||
|
insertDeleteLink = function(row) {
|
||||||
|
var delCssSelector = $.trim(options.deleteCssClass).replace(/\s+/g, '.'),
|
||||||
|
addCssSelector = $.trim(options.addCssClass).replace(/\s+/g, '.');
|
||||||
|
if (row.is('TR')) {
|
||||||
|
// If the forms are laid out in table rows, insert
|
||||||
|
// the remove button into the last table cell:
|
||||||
|
row.children(':last').append('<a class="' + options.deleteCssClass +'" href="javascript:void(0)">' + options.deleteText + '</a>');
|
||||||
|
} else if (row.is('UL') || row.is('OL')) {
|
||||||
|
// If they're laid out as an ordered/unordered list,
|
||||||
|
// insert an <li> after the last list item:
|
||||||
|
row.append('<li><a class="' + options.deleteCssClass + '" href="javascript:void(0)">' + options.deleteText +'</a></li>');
|
||||||
|
} else {
|
||||||
|
// Otherwise, just insert the remove button as the
|
||||||
|
// last child element of the form's container:
|
||||||
|
row.append('<a class="' + options.deleteCssClass + '" href="javascript:void(0)">' + options.deleteText +'</a>');
|
||||||
|
}
|
||||||
|
// Check if we're under the minimum number of forms - not to display delete link at rendering
|
||||||
|
if (!showDeleteLinks()){
|
||||||
|
row.find('a.' + delCssSelector).hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
row.find('a.' + delCssSelector).click(function() {
|
||||||
|
var row = $(this).parents('.' + options.formCssClass),
|
||||||
|
del = row.find('input:hidden[id $= "-DELETE"]'),
|
||||||
|
buttonRow = row.siblings("a." + addCssSelector + ', .' + options.formCssClass + '-add'),
|
||||||
|
forms;
|
||||||
|
if (del.length) {
|
||||||
|
// We're dealing with an inline formset.
|
||||||
|
// Rather than remove this form from the DOM, we'll mark it as deleted
|
||||||
|
// and hide it, then let Django handle the deleting:
|
||||||
|
del.val('on');
|
||||||
|
row.hide();
|
||||||
|
forms = $('.' + options.formCssClass).not(':hidden');
|
||||||
|
} else {
|
||||||
|
row.remove();
|
||||||
|
// Update the TOTAL_FORMS count:
|
||||||
|
forms = $('.' + options.formCssClass).not('.formset-custom-template');
|
||||||
|
totalForms.val(forms.length);
|
||||||
|
}
|
||||||
|
for (var i=0, formCount=forms.length; i<formCount; i++) {
|
||||||
|
// Apply `extraClasses` to form rows so they're nicely alternating:
|
||||||
|
applyExtraClasses(forms.eq(i), i);
|
||||||
|
if (!del.length) {
|
||||||
|
// Also update names and IDs for all child controls (if this isn't
|
||||||
|
// a delete-able inline formset) so they remain in sequence:
|
||||||
|
forms.eq(i).find(childElementSelector).each(function() {
|
||||||
|
updateElementIndex($(this), options.prefix, i);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check if we've reached the minimum number of forms - hide all delete link(s)
|
||||||
|
if (!showDeleteLinks()){
|
||||||
|
$('a.' + delCssSelector).each(function(){$(this).hide();});
|
||||||
|
}
|
||||||
|
// Check if we need to show the add button:
|
||||||
|
if (buttonRow.is(':hidden') && showAddButton()) buttonRow.show();
|
||||||
|
// If a post-delete callback was provided, call it with the deleted form:
|
||||||
|
if (options.removed) options.removed(row);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$$.each(function(i) {
|
||||||
|
var row = $(this),
|
||||||
|
del = row.find('input:checkbox[id $= "-DELETE"]');
|
||||||
|
if (del.length) {
|
||||||
|
// If you specify "can_delete = True" when creating an inline formset,
|
||||||
|
// Django adds a checkbox to each form in the formset.
|
||||||
|
// Replace the default checkbox with a hidden field:
|
||||||
|
if (del.is(':checked')) {
|
||||||
|
// If an inline formset containing deleted forms fails validation, make sure
|
||||||
|
// we keep the forms hidden (thanks for the bug report and suggested fix Mike)
|
||||||
|
del.before('<input type="hidden" name="' + del.attr('name') +'" id="' + del.attr('id') +'" value="on" />');
|
||||||
|
row.hide();
|
||||||
|
} else {
|
||||||
|
del.before('<input type="hidden" name="' + del.attr('name') +'" id="' + del.attr('id') +'" />');
|
||||||
|
}
|
||||||
|
// Hide any labels associated with the DELETE checkbox:
|
||||||
|
$('label[for="' + del.attr('id') + '"]').hide();
|
||||||
|
del.remove();
|
||||||
|
}
|
||||||
|
if (hasChildElements(row)) {
|
||||||
|
row.addClass(options.formCssClass);
|
||||||
|
if (row.is(':visible')) {
|
||||||
|
insertDeleteLink(row);
|
||||||
|
applyExtraClasses(row, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if ($$.length) {
|
||||||
|
var hideAddButton = !showAddButton(),
|
||||||
|
addButton, template;
|
||||||
|
if (options.formTemplate) {
|
||||||
|
// If a form template was specified, we'll clone it to generate new form instances:
|
||||||
|
template = (options.formTemplate instanceof $) ? options.formTemplate : $(options.formTemplate);
|
||||||
|
template.removeAttr('id').addClass(options.formCssClass + ' formset-custom-template');
|
||||||
|
template.find(childElementSelector).each(function() {
|
||||||
|
updateElementIndex($(this), options.prefix, '__prefix__');
|
||||||
|
});
|
||||||
|
insertDeleteLink(template);
|
||||||
|
} else {
|
||||||
|
// Otherwise, use the last form in the formset; this works much better if you've got
|
||||||
|
// extra (>= 1) forms (thnaks to justhamade for pointing this out):
|
||||||
|
template = $('.' + options.formCssClass + ':last').clone(true).removeAttr('id');
|
||||||
|
template.find('input:hidden[id $= "-DELETE"]').remove();
|
||||||
|
// Clear all cloned fields, except those the user wants to keep (thanks to brunogola for the suggestion):
|
||||||
|
template.find(childElementSelector).not(options.keepFieldValues).each(function() {
|
||||||
|
var elem = $(this);
|
||||||
|
// If this is a checkbox or radiobutton, uncheck it.
|
||||||
|
// This fixes Issue 1, reported by Wilson.Andrew.J:
|
||||||
|
if (elem.is('input:checkbox') || elem.is('input:radio')) {
|
||||||
|
elem.attr('checked', false);
|
||||||
|
} else {
|
||||||
|
elem.val('');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// FIXME: Perhaps using $.data would be a better idea?
|
||||||
|
options.formTemplate = template;
|
||||||
|
|
||||||
|
if ($$.is('TR')) {
|
||||||
|
// If forms are laid out as table rows, insert the
|
||||||
|
// "add" button in a new table row:
|
||||||
|
var numCols = $$.eq(0).children().length, // This is a bit of an assumption :|
|
||||||
|
buttonRow = $('<tr><td colspan="' + numCols + '"><a class="' + options.addCssClass + '" href="javascript:void(0)">' + options.addText + '</a></tr>')
|
||||||
|
.addClass(options.formCssClass + '-add');
|
||||||
|
$$.parent().append(buttonRow);
|
||||||
|
if (hideAddButton) buttonRow.hide();
|
||||||
|
addButton = buttonRow.find('a');
|
||||||
|
} else {
|
||||||
|
// Otherwise, insert it immediately after the last form:
|
||||||
|
$$.filter(':last').after('<a class="' + options.addCssClass + '" href="javascript:void(0)">' + options.addText + '</a>');
|
||||||
|
addButton = $$.filter(':last').next();
|
||||||
|
if (hideAddButton) addButton.hide();
|
||||||
|
}
|
||||||
|
addButton.click(function() {
|
||||||
|
var formCount = parseInt(totalForms.val()),
|
||||||
|
row = options.formTemplate.clone(true).removeClass('formset-custom-template'),
|
||||||
|
buttonRow = $($(this).parents('tr.' + options.formCssClass + '-add').get(0) || this)
|
||||||
|
delCssSelector = $.trim(options.deleteCssClass).replace(/\s+/g, '.');
|
||||||
|
applyExtraClasses(row, formCount);
|
||||||
|
row.insertBefore(buttonRow).show();
|
||||||
|
row.find(childElementSelector).each(function() {
|
||||||
|
updateElementIndex($(this), options.prefix, formCount);
|
||||||
|
});
|
||||||
|
totalForms.val(formCount + 1);
|
||||||
|
// Check if we're above the minimum allowed number of forms -> show all delete link(s)
|
||||||
|
if (showDeleteLinks()){
|
||||||
|
$('a.' + delCssSelector).each(function(){$(this).show();});
|
||||||
|
}
|
||||||
|
// Check if we've exceeded the maximum allowed number of forms:
|
||||||
|
if (!showAddButton()) buttonRow.hide();
|
||||||
|
// If a post-add callback was supplied, call it with the added form:
|
||||||
|
if (options.added) options.added(row);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return $$;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Setup plugin defaults */
|
||||||
|
$.fn.formset.defaults = {
|
||||||
|
prefix: 'form', // The form prefix for your django formset
|
||||||
|
formTemplate: null, // The jQuery selection cloned to generate new form instances
|
||||||
|
addText: 'add another', // Text for the add link
|
||||||
|
deleteText: 'remove', // Text for the delete link
|
||||||
|
addCssClass: 'add-row', // CSS class applied to the add link
|
||||||
|
deleteCssClass: 'delete-row', // CSS class applied to the delete link
|
||||||
|
formCssClass: 'dynamic-form', // CSS class applied to each form in a formset
|
||||||
|
extraClasses: [], // Additional CSS classes, which will be applied to each form in turn
|
||||||
|
keepFieldValues: '', // jQuery selector for fields whose values should be kept when the form is cloned
|
||||||
|
added: null, // Function called each time a new form is added
|
||||||
|
removed: null // Function called each time a form is deleted
|
||||||
|
};
|
||||||
|
})(jQuery);
|
||||||
Reference in New Issue
Block a user