Merge branch 'release/v4.29'
This commit is contained in:
+14
-1
@@ -828,7 +828,13 @@ def new_workout_from_file(r,f2,
|
||||
makeprivate=False,
|
||||
notes=''):
|
||||
message = None
|
||||
fileformat = get_file_type(f2)
|
||||
try:
|
||||
fileformat = get_file_type(f2)
|
||||
except IOError:
|
||||
os.remove(f2)
|
||||
message = "Rowsandall could not process this file. The extension is supported but the file seems corrupt. Contact info@rowsandall.com if you think this is incorrect."
|
||||
return (0,message,f2)
|
||||
|
||||
summary = ''
|
||||
oarlength = 2.89
|
||||
inboard = 0.88
|
||||
@@ -860,6 +866,13 @@ def new_workout_from_file(r,f2,
|
||||
os.remove(f2)
|
||||
message = "It looks like this file doesn't contain stroke data."
|
||||
return (0,message,f2)
|
||||
|
||||
# Some people upload corrupted zip files
|
||||
if fileformat == 'notgzip':
|
||||
os.remove(f2)
|
||||
message = "Rowsandall could not process this file. The extension is supported but the file seems corrupt. Contact info@rowsandall.com if you think this is incorrect."
|
||||
return (0,message,f2)
|
||||
|
||||
|
||||
# Some people try to upload RowPro summary logs
|
||||
if fileformat == 'rowprolog':
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from django import forms
|
||||
from django.contrib.admin.widgets import FilteredSelectMultiple
|
||||
from rowers.models import Workout
|
||||
from rowers.rows import validate_file_extension,must_be_csv
|
||||
from django.contrib.auth.forms import UserCreationForm
|
||||
@@ -57,7 +58,77 @@ class DocumentsForm(forms.Form):
|
||||
class Meta:
|
||||
fields = ['title','file','workouttype','fileformat']
|
||||
|
||||
from utils import (
|
||||
workflowleftpanel,workflowmiddlepanel,
|
||||
defaultleft,defaultmiddle
|
||||
)
|
||||
|
||||
|
||||
# Form to change Workflow page layout
|
||||
class WorkFlowLeftPanelForm(forms.Form):
|
||||
panels = defaultleft
|
||||
|
||||
leftpanel = forms.MultipleChoiceField(
|
||||
label='',
|
||||
choices=workflowleftpanel,
|
||||
initial=panels,
|
||||
widget=FilteredSelectMultiple(
|
||||
('elements'),
|
||||
False
|
||||
)
|
||||
)
|
||||
|
||||
class Media:
|
||||
css = {
|
||||
'all':['admin/css/widgets.css',]
|
||||
# 'css/uid-manage-form.css'],
|
||||
}
|
||||
js = ['/admin/jsi18n/']
|
||||
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
if 'instance' in kwargs:
|
||||
r = kwargs.pop('instance')
|
||||
panels = r.workflowleftpanel
|
||||
self.base_fields['leftpanel'].initial=panels
|
||||
else:
|
||||
panels = defaultleft
|
||||
|
||||
|
||||
super(WorkFlowLeftPanelForm,self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class WorkFlowMiddlePanelForm(forms.Form):
|
||||
panels = defaultmiddle
|
||||
|
||||
|
||||
middlepanel = forms.MultipleChoiceField(
|
||||
label='',
|
||||
choices=workflowmiddlepanel,
|
||||
initial=panels,
|
||||
widget=FilteredSelectMultiple(
|
||||
('elements'),
|
||||
False
|
||||
)
|
||||
)
|
||||
|
||||
class Media:
|
||||
css = {
|
||||
'all':['admin/css/widgets.css',]
|
||||
# 'css/uid-manage-form.css'],
|
||||
}
|
||||
js = ['/admin/jsi18n/']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
if 'instance' in kwargs:
|
||||
r = kwargs.pop('instance')
|
||||
panels = r.workflowmiddlepanel
|
||||
self.base_fields['middlepanel'].initial=panels
|
||||
else:
|
||||
panels = defaultmiddle
|
||||
|
||||
super(WorkFlowMiddlePanelForm,self).__init__(*args, **kwargs)
|
||||
|
||||
# The form to indicate additional actions to be performed immediately
|
||||
# after a successful upload
|
||||
class UploadOptionsForm(forms.Form):
|
||||
|
||||
@@ -235,7 +235,7 @@ def interactive_activitychart(workouts,startdate,enddate,stack='type'):
|
||||
dates.append(dd)
|
||||
durations.append(du)
|
||||
types.append(w.workouttype)
|
||||
rowers.append(w.user.user.first_name)
|
||||
rowers.append(w.user.user.first_name[0]+w.user.user.last_name[0])
|
||||
|
||||
try:
|
||||
d = utc.localize(startdate)
|
||||
|
||||
+51
-1
@@ -61,6 +61,47 @@ timezones = (
|
||||
class UserFullnameChoiceField(forms.ModelChoiceField):
|
||||
def label_from_instance(self,obj):
|
||||
return obj.get_full_name()
|
||||
|
||||
# model for configurable template field
|
||||
class TemplateListField(models.TextField):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.token = kwargs.pop('token',',')
|
||||
super(TemplateListField, self).__init__(*args, **kwargs)
|
||||
|
||||
def to_python(self, value):
|
||||
if not value: return
|
||||
if isinstance(value, list):
|
||||
return value
|
||||
# remove double quotes and brackets
|
||||
value = re.sub(r'u\"','',value)
|
||||
value = re.sub(r'u\'','',value)
|
||||
value = re.sub(r'\\','',value)
|
||||
value = re.sub(r'\"','',value)
|
||||
value = re.sub(r'\'','',value)
|
||||
value = re.sub(r'\[','',value)
|
||||
value = re.sub(r'\]','',value)
|
||||
value = re.sub(r'\[\[','[',value)
|
||||
value = re.sub(r'\]\]',']',value)
|
||||
value = re.sub(r'\ \ ',' ',value)
|
||||
value = re.sub(r', ',',',value)
|
||||
|
||||
return value.split(self.token)
|
||||
|
||||
def from_db_value(self,value, expression, connection, context):
|
||||
if value is None:
|
||||
return value
|
||||
if isinstance(value, list):
|
||||
return value
|
||||
return value.split(self.token)
|
||||
|
||||
def get_db_prep_value(self, value, connection, prepared=False):
|
||||
if not value: return
|
||||
assert(isinstance(value, list) or isinstance(value, tuple))
|
||||
return self.token.join([unicode(s) for s in value])
|
||||
|
||||
def value_to_string(self, obj):
|
||||
value = self._get_val_from_obj(obj)
|
||||
return self.get_deb_prep_value(value)
|
||||
|
||||
# model for Power Zone names
|
||||
class PowerZonesField(models.TextField):
|
||||
@@ -155,7 +196,11 @@ class TeamRequest(models.Model):
|
||||
issuedate = models.DateField(default=timezone.now)
|
||||
code = models.CharField(max_length=150,unique=True)
|
||||
|
||||
|
||||
from utils import (
|
||||
workflowleftpanel,workflowmiddlepanel,
|
||||
defaultleft,defaultmiddle
|
||||
)
|
||||
|
||||
# Extension of User with rowing specific data
|
||||
class Rower(models.Model):
|
||||
weightcategories = (
|
||||
@@ -234,6 +279,11 @@ class Rower(models.Model):
|
||||
'Pwr TR',
|
||||
'Pwr AN'])
|
||||
|
||||
# Site Settings
|
||||
workflowleftpanel = TemplateListField(default=defaultleft)
|
||||
|
||||
workflowmiddlepanel = TemplateListField(default=defaultmiddle)
|
||||
|
||||
# Access tokens
|
||||
c2token = models.CharField(default='',max_length=200,blank=True,null=True)
|
||||
tokenexpirydate = models.DateTimeField(blank=True,null=True)
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<div class="grid_2 alpha">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/advanced">Advanced</a>
|
||||
</p>
|
||||
</div>
|
||||
@@ -1,18 +1,5 @@
|
||||
<div class="grid_2 alpha">
|
||||
<h2>Navigation</h2>
|
||||
</div>
|
||||
<div class="grid_2 alpha">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/edit">Edit Workout</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid_2 alpha">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/advanced">Advanced</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid_2 alpha">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/editintervals">Edit Intervals</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<div class="grid_2 alpha">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/editintervals">Edit Intervals</a>
|
||||
</p>
|
||||
</div>
|
||||
@@ -0,0 +1,5 @@
|
||||
<div class="grid_2 alpha">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/stream">Edit Stream Data</a>
|
||||
</p>
|
||||
</div>
|
||||
@@ -0,0 +1,5 @@
|
||||
<div class="grid_2 alpha">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/wind">Edit Wind Data</a>
|
||||
</p>
|
||||
</div>
|
||||
@@ -0,0 +1,3 @@
|
||||
<div class="grid_2 alpha">
|
||||
<h2>Geeky Stuff</h2>
|
||||
</div>
|
||||
@@ -0,0 +1,3 @@
|
||||
<div class="grid_2 alpha">
|
||||
<h2>Navigation</h2>
|
||||
</div>
|
||||
@@ -0,0 +1,5 @@
|
||||
<div class="grid_2 alpha">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/otwsetpower">OTW Power</a>
|
||||
</p>
|
||||
</div>
|
||||
@@ -31,6 +31,11 @@
|
||||
</div>
|
||||
<div id="leftpanel" class="grid_2 alpha">
|
||||
{% block left_panel %}
|
||||
<div class="grid_2 alpha">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/me/workflowconfig">Configure this page</a>
|
||||
</p>
|
||||
</div>
|
||||
{% for templateName in leftTemplates %}
|
||||
{% include templateName %}
|
||||
{% endfor %}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Change Rower Export Settings{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<script type="text/javascript" src="/static/js/reorderSelect.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/jquery.init.js"></script>
|
||||
{{ formmiddle.media }}
|
||||
|
||||
<div class="grid_12 alpha">
|
||||
{% if workoutid %}
|
||||
<div class="grid_2 suffix_10 alpha">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/workout/{{ workoutid }}/workflow">Return to Workout</a>
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
<h1>Workflow Page Configuration</h1>
|
||||
<p>On this page, you can add and remove elements from the left and middle
|
||||
panels of your Workflow page.
|
||||
Use the arrows on the right to move selected elements up and down to
|
||||
determine the order.
|
||||
In this way, you can configure the page to
|
||||
streamline your regular workout analysis workflow.</p>
|
||||
<h2>Left Panel</h2>
|
||||
<form enctype="multipart/form-data" action="" method="post">
|
||||
<div class="grid_11 alpha">
|
||||
<p>
|
||||
{% if formleft.errors %}
|
||||
<p style="color: red;">
|
||||
Please correct the error{{ formleft.errors|pluralize }} below.
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<table>
|
||||
{{ formleft.as_table }}
|
||||
</table>
|
||||
{% csrf_token %}
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid_1 omega">
|
||||
<img src="/static/img/moveup.gif" alt="Move Up" onclick="moveUp('id_leftpanel_to')" style="cursor:pointer" /><br />
|
||||
<img src="/static/img/movedn.gif" alt="Move Down" onclick="moveDown('id_leftpanel_to')" style="cursor:pointer" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid_12 alpha">
|
||||
<div class="grid_4 prefix_4 suffix_4">
|
||||
<input class="grid_4 button green" type="submit" value="Save">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="grid_12 alpha">
|
||||
<h2>Middle Panel</h2>
|
||||
<form enctype="multipart/form-data" action="" method="post">
|
||||
<div class="grid_11 alpha">
|
||||
<p>
|
||||
{% if formmiddle.errors %}
|
||||
<p style="color: red;">
|
||||
Please correct the error{{ formmiddle.errors|pluralize }} below.
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<table>
|
||||
{{ formmiddle.as_table }}
|
||||
</table>
|
||||
{% csrf_token %}
|
||||
</div>
|
||||
<div class="grid_1 omega">
|
||||
<img src="/static/img/moveup.gif" alt="Move Up" onclick="moveUp('id_middlepanel_to')" style="cursor:pointer" /><br />
|
||||
<img src="/static/img/movedn.gif" alt="Move Down" onclick="moveDown('id_middlepanel_to')" style="cursor:pointer" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid_12 alpha">
|
||||
<div class="grid_4 prefix_4 suffix_4">
|
||||
<input class="grid_4 button green" type="submit" value="Save">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
{% endblock %}
|
||||
@@ -312,6 +312,7 @@ urlpatterns = [
|
||||
url(r'^me/tprefresh/$',views.rower_tp_token_refresh),
|
||||
url(r'^me/c2refresh/$',views.rower_c2_token_refresh),
|
||||
url(r'^me/favoritecharts/$',views.rower_favoritecharts_view),
|
||||
url(r'^me/workflowconfig$',views.workout_workflow_config_view),
|
||||
url(r'^email/send/$', views.sendmail),
|
||||
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'),
|
||||
|
||||
@@ -5,6 +5,39 @@ import colorsys
|
||||
|
||||
lbstoN = 4.44822
|
||||
|
||||
workflowmiddlepanel = (
|
||||
('panel_statcharts.html','Static Charts'),
|
||||
('flexthumbnails.html','Flex Charts'),
|
||||
('panel_summary.html','Summary'),
|
||||
)
|
||||
|
||||
defaultmiddle = ['panel_statcharts.html',
|
||||
'flexthumbnails.html',
|
||||
'panel_summary.html']
|
||||
|
||||
workflowleftpanel = (
|
||||
('panel_navigationheader.html','Navigation Header'),
|
||||
('panel_editbuttons.html','Edit Workout Button'),
|
||||
('panel_advancededit.html','Advanced Workout Edit Button'),
|
||||
('panel_editintervals.html','Edit Intervals Button'),
|
||||
('panel_stats.html','Workout Statistics Button'),
|
||||
('panel_staticchart.html','Create Static Charts Buttons'),
|
||||
('panel_geekyheader.html','Geeky Header'),
|
||||
('panel_editwind.html','Edit Wind Data'),
|
||||
('panel_editstream.html','Edit Stream Data'),
|
||||
('panel_otwpower.html','Run OTW Power Calculations')
|
||||
)
|
||||
|
||||
defaultleft = [
|
||||
'panel_navigationheader.html',
|
||||
'panel_editbuttons.html',
|
||||
'panel_advancededit.html',
|
||||
'panel_editintervals.html'
|
||||
'panel_stats.html',
|
||||
'panel_staticchart.html',
|
||||
]
|
||||
|
||||
|
||||
def absolute(request):
|
||||
urls = {
|
||||
'ABSOLUTE_ROOT': request.build_absolute_uri('/')[:-1].strip("/"),
|
||||
|
||||
+44
-9
@@ -22,7 +22,7 @@ from django.http import (
|
||||
from django.contrib.auth import authenticate, login, logout
|
||||
from rowers.forms import (
|
||||
LoginForm,DocumentsForm,UploadOptionsForm,
|
||||
TeamUploadOptionsForm,
|
||||
TeamUploadOptionsForm,WorkFlowLeftPanelForm,WorkFlowMiddlePanelForm,
|
||||
)
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core.exceptions import PermissionDenied
|
||||
@@ -5852,11 +5852,50 @@ def workout_comparison_view2(request,id1=0,id2=0,xparam='distance',
|
||||
'promember':promember,
|
||||
})
|
||||
|
||||
# Workflow Configuration
|
||||
@login_required()
|
||||
def workout_workflow_config_view(request):
|
||||
request.session['referer'] = absolute(request)['PATH']
|
||||
request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE
|
||||
try:
|
||||
workoutid = request.session['lastworkout']
|
||||
except KeyError:
|
||||
workoutid = 0
|
||||
|
||||
|
||||
r = getrower(request.user)
|
||||
if request.method == 'POST' and 'leftpanel' in request.POST:
|
||||
formleft = WorkFlowLeftPanelForm(request.POST)
|
||||
if formleft.is_valid():
|
||||
leftpanel = formleft.cleaned_data['leftpanel']
|
||||
r.workflowleftpanel = leftpanel
|
||||
r.save()
|
||||
if request.method == 'POST' and 'middlepanel' in request.POST:
|
||||
formmiddle = WorkFlowMiddlePanelForm(request.POST)
|
||||
if formmiddle.is_valid():
|
||||
middlepanel = formmiddle.cleaned_data['middlepanel']
|
||||
r.workflowmiddlepanel = middlepanel
|
||||
r.save()
|
||||
|
||||
# Flex thumbnails
|
||||
formleft = WorkFlowLeftPanelForm(instance=r)
|
||||
formmiddle = WorkFlowMiddlePanelForm(instance=r)
|
||||
|
||||
|
||||
# Add processing of POST data
|
||||
|
||||
return render(request,'workflowconfig.html',
|
||||
{
|
||||
'rower':r,
|
||||
'formleft':formleft,
|
||||
'formmiddle':formmiddle,
|
||||
'workoutid': workoutid,
|
||||
})
|
||||
|
||||
# Workflow View
|
||||
@login_required()
|
||||
def workout_workflow_view(request,id):
|
||||
request.session['referer'] = absolute(request)['PATH']
|
||||
request.session['lastworkout'] = id
|
||||
request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE
|
||||
try:
|
||||
row = Workout.objects.get(id=id)
|
||||
@@ -5892,13 +5931,9 @@ def workout_workflow_view(request,id):
|
||||
statcharts = GraphImage.objects.filter(workout=row)
|
||||
|
||||
# This will be user configurable in the future
|
||||
middleTemplates = ['panel_statcharts.html','flexthumbnails.html',
|
||||
'panel_summary.html']
|
||||
leftTemplates = [
|
||||
'panel_editbuttons.html',
|
||||
'panel_stats.html',
|
||||
'panel_staticchart.html',
|
||||
]
|
||||
middleTemplates = r.workflowmiddlepanel
|
||||
|
||||
leftTemplates = r.workflowleftpanel
|
||||
|
||||
return render(request,
|
||||
'workflow.html',
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 183 B |
Binary file not shown.
|
After Width: | Height: | Size: 178 B |
@@ -0,0 +1,27 @@
|
||||
|
||||
function moveUp(selectId) {
|
||||
var selectList = document.getElementById(selectId);
|
||||
var selectOptions = selectList.getElementsByTagName('option');
|
||||
for (var i = 1; i < selectOptions.length; i++) {
|
||||
var opt = selectOptions[i];
|
||||
if (opt.selected) {
|
||||
selectList.removeChild(opt);
|
||||
selectList.insertBefore(opt, selectOptions[i - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function moveDown(selectId) {
|
||||
var selectList = document.getElementById(selectId);
|
||||
var selectOptions = selectList.getElementsByTagName('option');
|
||||
for (var i = selectOptions.length - 2; i >= 0; i--) {
|
||||
var opt = selectOptions[i];
|
||||
if (opt.selected) {
|
||||
var nextOpt = selectOptions[i + 1];
|
||||
opt = selectList.removeChild(opt);
|
||||
nextOpt = selectList.replaceChild(opt, nextOpt);
|
||||
selectList.insertBefore(nextOpt, opt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user