Private
Public Access
1
0

Merge branch 'release/v4.29'

This commit is contained in:
Sander Roosendaal
2017-10-18 16:42:08 +02:00
20 changed files with 364 additions and 25 deletions
+14 -1
View File
@@ -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':
+71
View File
@@ -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):
+1 -1
View File
@@ -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
View File
@@ -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)
+5
View File
@@ -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>
-13
View File
@@ -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>
+5
View File
@@ -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>
+5
View File
@@ -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>
+3
View File
@@ -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>
+5
View File
@@ -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>
+5
View File
@@ -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 %}
+86
View File
@@ -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 %}
+1
View File
@@ -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'),
+33
View File
@@ -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
View File
@@ -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

+27
View File
@@ -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);
}
}
}