Private
Public Access
1
0

Merge branch 'release/v5.71'

This commit is contained in:
Sander Roosendaal
2018-01-23 15:24:57 +01:00
11 changed files with 418 additions and 36 deletions

View File

@@ -1,7 +1,7 @@
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 rowers.rows import validate_file_extension,must_be_csv,validate_image_extension
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from django.contrib.admin.widgets import AdminDateWidget
@@ -46,6 +46,16 @@ class TeamInviteCodeForm(forms.Form):
class StrokeDataForm(forms.Form):
strokedata = forms.CharField(label='payload',widget=forms.Textarea)
# The form used for uploading images
class ImageForm(forms.Form):
file = forms.FileField(required=False,
validators=[validate_image_extension])
def __init__(self, *args, **kwargs):
from django.forms.widgets import HiddenInput
super(ImageForm, self).__init__(*args, **kwargs)
# The form used for uploading files
class DocumentsForm(forms.Form):
title = forms.CharField(required=False)

View File

@@ -479,10 +479,6 @@ class Rower(models.Model):
runkeepertoken = models.CharField(default='',max_length=200,
blank=True,null=True)
# runkeepertokenexpirydate = models.DateTimeField(blank=True,null=True)
# runkeeperrefreshtoken = models.CharField(default='',max_length=200,
# blank=True,null=True)
# Plan
plans = (
('basic','basic'),
@@ -856,12 +852,15 @@ class GraphImage(models.Model):
def __str__(self):
return self.filename
# delete related file object when image is deleted
@receiver(models.signals.post_delete,sender=GraphImage)
def auto_delete_image_on_delete(sender,instance, **kwargs):
if instance.filename:
if os.path.isfile(instance.filename):
os.remove(instance.filename)
others = GraphImage.objects.filter(filename=instance.filename)
if len(others) == 0:
os.remove(instance.filename)
else:
print "couldn't find the file "+instance.filename

View File

@@ -1,11 +1,11 @@
import time
import gzip
import shutil
import hashlib
from django.core.exceptions import ValidationError
def format_pace_tick(x,pos=None):
min=int(x/60)
min=int(x/60)
sec=int(x-min*60.)
sec_str=str(sec).zfill(2)
template='%d:%s'
@@ -45,6 +45,14 @@ def format_time(x,pos=None):
return str1
def validate_image_extension(value):
import os
ext = os.path.splitext(value.name)[1].lower()
valid_extension = ['.jpg','.jpeg','.png','.gif']
if not ext in valid_extension:
raise ValidationError(u'File not supported')
def validate_file_extension(value):
import os
ext = os.path.splitext(value.name)[1]
@@ -62,6 +70,34 @@ def must_be_csv(value):
raise ValidationError(u'File not supported!')
def handle_uploaded_image(i):
import StringIO
from PIL import Image, ImageOps
import os
from django.core.files import File
image_str = ""
for c in i.chunks():
image_str += c
imagefile = StringIO.StringIO(image_str)
image = Image.open(imagefile)
if image.mode not in ("L", "RGB"):
image = image.convert("RGB")
basewidth = 600
wpercent = (basewidth/float(image.size[0]))
hsize = int((float(image.size[1])*float(wpercent)))
image = image.resize((basewidth,hsize), Image.ANTIALIAS)
filename = hashlib.md5(imagefile.getvalue()).hexdigest()+'.jpg'
filename2 = os.path.join('static/plots/',filename)
image.save(filename2,'JPEG')
return filename,filename2
def handle_uploaded_file(f):
fname = f.name
timestr = time.strftime("%Y%m%d-%H%M%S")

View File

@@ -0,0 +1 @@
E408191@CZ27LT9RCGN72.1800:1516641451

View File

@@ -0,0 +1,216 @@
{% extends "base.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% block title %}File loading{% endblock %}
{% block meta %}
<script type='text/javascript'
src='https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js'>
</script>
<script type='text/javascript'
src='https://ajax.aspnetcdn.com/ajax/jquery.validate/1.14.0/jquery.validate.min.js'>
</script>
<script>
</script>
{% endblock %}
{% block content %}
<div id="id_dropregion" class="grid_12 alpha watermark invisible">
<p>Drag and drop files here </p>
</div>
<div id="id_drop-files" class="grid_12 alpha drop-files">
<form id="file_form" enctype="multipart/form-data" action="{{ formloc }}" method="post">
<div id="left" class="grid_6 alpha">
<h1>Upload Image</h1>
{% if form.errors %}
<p style="color: red;">
Please correct the error{{ form.errors|pluralize }} below.
</p>
{% endif %}
<table>
{{ form.as_table }}
</table>
{% csrf_token %}
<div id="formbutton" class="grid_1 prefix_4 suffix_1">
<input class="button green" type="submit" value="Submit">
</div>
</div>
<div id="right" class="grid_6 omega">
&nbsp;
</div>
</form>
</div>
{% endblock %}
{% block scripts %}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script>
var td = new FormData();
var formdatasetok = false;
try {
td.set('aap','noot');
formdatasetok = true;
console.log('FormData.set OK');
}
catch(err) {
console.log('FormData.set not OK');
formdatasetok = false;
}
if (!formdatasetok) {
$("#id_dropregion").remove();
}
if (formdatasetok) {
$(document).ready(function() {
var csrftoken = jQuery("[name=csrfmiddlewaretoken]").val();
console.log("CSRF token",csrftoken);
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
console.log("Loading dropper");
jQuery.event.props.push('dataTransfer');
$(window).on('dragenter', function() {
$("#id_drop-files").css("background-color","#E9E9E4");
$("#id_dropregion").addClass("watermark").removeClass("invisible");})
$(window).on('dragleave', function() {
$("#id_drop-files").css("background-color","#FFFFFF");
$("#id_dropregion").removeClass("watermark").addClass("invisible");})
var frm = $("#file_form");
if( window.FormData === undefined ) {
console.log('no formdata');
alert("No FormData");
} else {
console.log('we have formdata');
}
var data = new FormData(frm[0]);
$('#id_file').on('change', function(evt) {
var f = this.files[0];
console.log(f);
var istcx = false;
var isgzip = false;
var size1 = 10485760;
var size2 = 1048576;
if ((/\.(tcx|TCX)/i).test(f.name)) {
istcx = true;
console.log('tcx');
if ((/\.(gz|GZ)/i).test(f.name)) {
isgzip = true;
console.log('gzip');
size1 /= 5;
size2 /= 5;
}
}
console.log(size1)
console.log(size2)
if (f.size > size1) {
alert("File Size must be smaller than 10 MB");
this.value = null;
} else {
if (f.size > size2) {
$('#id_offline').val('True');
$('#id_offline').prop('checked','True');
data.set($('#id_offline').attr('name'),$('#id_offline').prop('checked'));
console.log("Set offline to True");
}
}
});
$('input').each(function( i ) {
$(this).change(function() {
if ($(this).attr('type') == 'checkbox') {
data.set($(this).attr('name'),$(this).prop('checked'));
console.log($(this).attr('id'),$(this).attr('name'),$(this).prop('checked'));
} else {
data.set($(this).attr('name'),$(this).val());
if ($(this).attr('id') == 'id_file') {
data.set("file",this.files[0]);
}
console.log($(this).attr('name'),$(this).val());
};
});});
$('select').each(function( i ) {
console.log($(this).attr('name'),$(this).val());
$(this).change(function() {
data.set($(this).attr('name'),$(this).val());
console.log($(this).attr('id'),$(this).attr('name'),$(this).val());
});
});
$('#id_drop-files').bind({
drop: function(e) {
e.preventDefault();
console.log("you dropped something");
var files = e.dataTransfer.files;
console.log(files[0]);
var f = files[0];
var istcx = false;
var isgzip = false;
var size1 = 10485760;
var size2 = 1048576;
if ((/\.(tcx|TCX)/i).test(f.name)) {
istcx = true;
console.log('tcx');
if ((/\.(gz|GZ)/i).test(f.name)) {
isgzip = true;
console.log('gzip');
size1 /= 5;
size2 /= 5;
}
}
console.log(f);
console.log(size1)
console.log(size2)
if (f.size > size1) {
alert("File Size must be smaller than 10 MB");
$("#id_file").value = 0;
return false;
}
data.set("file",f);
// data.append("file",f);
$("#id_file").replaceWith('<div id="id_file">'+files[0].name+'&nbsp; <a class="remove" href="javascript:void(0);"><b><font color="red">X</font></b></a></div>');
},
mouseenter:function(){$("#id_drop-files").css("background-color","#E9E9E4");},
mouseleave:function(){$("#id_drop-files").css("background-color","#FFFFFF");},
dragover:function(e){
e.preventDefault();
$("#id_drop-files").css("background-color","#E9E9E4");},
dragleave:function(e){ e.preventDefault();},
});
$(document).on("click", "a.remove", function() {
$(this).parent().replaceWith('<td><input id="id_file" name="file" type="file" /></td>');
});
});
};
</script>
{% endblock %}

View File

@@ -0,0 +1,5 @@
<div class="grid_2 alpha">
<p>
<a class="button blue small" href="/rowers/workout/{{ workout.id }}/image">Attach Image</a>
</p>
</div>

View File

@@ -137,8 +137,12 @@
</p>
</div>
<div class="grid_6 alpha">
<div class="grid_2 prefix_4 alpha">
<div class="grid_2 alpha">
<p>
<a class="button blue small" href="/rowers/workout/{{ workout.id }}/image">Attach Image</a>
</p>
</div>
<div class="grid_2 prefix_2 omega">
<p>
<a class="button blue small" href="/rowers/workout/{{ workout.id }}/addpowerpiechart">Power Pie Chart</a>
</p>

View File

@@ -280,11 +280,16 @@ def make_plot(r,w,f1,f2,plottype,title,imagename='',plotnr=0):
width = 1200
height = 600
i = GraphImage(workout=w,
creationdatetime=timezone.now(),
filename=fullpathimagename,
width=width,height=height)
i.save()
imgs = GraphImage.objects.filter(workout=w)
if len(imgs) < 7:
i = GraphImage(workout=w,
creationdatetime=timezone.now(),
filename=fullpathimagename,
width=width,height=height)
i.save()
else:
return 0,'You have reached the maximum number of static images for this workout. Delete an image first'
return i.id,job.id

View File

@@ -249,6 +249,7 @@ urlpatterns = [
url(r'^workout/(?P<id>\d+)/otwsetpower$',views.workout_otwsetpower_view),
url(r'^workout/(?P<id>\d+)/interactiveotwplot$',views.workout_otwpowerplot_view),
url(r'^workout/(?P<id>\d+)/wind$',views.workout_wind_view),
url(r'^workout/(?P<id>\d+)/image$',views.workout_uploadimage_view),
url(r'^workout/(?P<id>\d+)/darkskywind$',views.workout_downloadwind_view),
url(r'^workout/(?P<id>\d+)/metar/(?P<airportcode>\w+)$',views.workout_downloadmetar_view),
url(r'^workout/(?P<id>\d+)/stream$',views.workout_stream_view),

View File

@@ -42,6 +42,7 @@ workflowleftpanel = (
('panel_stats.html','Workout Statistics Button'),
('panel_flexchart.html','Flex Chart'),
('panel_staticchart.html','Create Static Charts Buttons'),
('panel_uploadimage.html','Attach Image'),
('panel_geekyheader.html','Geeky Header'),
('panel_editwind.html','Edit Wind Data'),
('panel_editstream.html','Edit Stream Data'),
@@ -57,6 +58,7 @@ defaultleft = [
'panel_editintervals.html',
'panel_stats.html',
'panel_staticchart.html',
'panel_uploadimage.html',
]
coxes_calls = [
@@ -65,6 +67,9 @@ coxes_calls = [
"Almost there. Give me ten strokes on the legs!",
"Let it run!",
"Don't rush the slides!",
"Quick hands.",
"You are clearing the puddles.",
"Let's push through now. Get me that open water.",
"We're going for the line now. Power ten on the next.",
]

View File

@@ -27,7 +27,7 @@ from django.http import (
)
from django.contrib.auth import authenticate, login, logout
from rowers.forms import (
LoginForm,DocumentsForm,UploadOptionsForm,
LoginForm,DocumentsForm,UploadOptionsForm,ImageForm,
TeamUploadOptionsForm,WorkFlowLeftPanelForm,WorkFlowMiddlePanelForm,
WorkFlowLeftPanelElement,WorkFlowMiddlePanelElement,
LandingPageForm,
@@ -103,7 +103,7 @@ import requests
import json
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
from rowers.rows import handle_uploaded_file
from rowers.rows import handle_uploaded_file,handle_uploaded_image
from rowers.tasks import handle_makeplot,handle_otwsetpower,handle_sendemailtcx,handle_sendemailcsv
from rowers.tasks import (
handle_sendemail_unrecognized,handle_sendemailnewcomment,
@@ -116,7 +116,7 @@ from rowers.tasks import (
from scipy.signal import savgol_filter
from django.shortcuts import render_to_response
from Cookie import SimpleCookie
from shutil import copyfile
from shutil import copyfile,move
import types
from rowingdata import rower as rrower
from rowingdata import main as rmain
@@ -7112,13 +7112,17 @@ def instroke_chart(request,id=0,metric=''):
width = 1200
height = 600
i = GraphImage(workout=w,
creationdatetime=timezone.now(),
filename=fullpathimagename,
width=width,height=height)
i.save()
print i.id,'aap'
imgs = GraphImage.objects.filter(workout=w)
if len(imgs) < 7:
i = GraphImage(workout=w,
creationdatetime=timezone.now(),
filename=fullpathimagename,
width=width,height=height)
i.save()
else:
messages.error(request,'You have reached the maximum number of static images for this workout. Delete an image first')
r = getrower(request.user)
url = reverse(r.defaultlandingpage,
@@ -8832,6 +8836,96 @@ def workout_edit_view_navionics(request,id=0,message="",successmessage=""):
return HttpResponseRedirect(url)
# Image upload
@login_required()
def workout_uploadimage_view(request,id):
is_ajax = False
if request.is_ajax():
is_ajax = True
r = getrower(request.user)
try:
w = Workout.objects.get(id=id)
except Workout.DoesNotExist:
raise Http404("Workout doesn't exist")
if not checkworkoutuser(request.user,w):
raise PermissionDenied("You are not allowed to edit this workout")
images = GraphImage.objects.filter(workout=w)
if len(images) >= 6:
message = "You have reached the maximum number of static images for this workout"
messages.error(request,message)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':int(id),
})
return HttpResponseRedirect(url)
if request.method == 'POST':
form = ImageForm(request.POST,request.FILES)
if form.is_valid():
f = form.cleaned_data['file']
if f is not None:
filename,path_and_filename = handle_uploaded_image(f)
print path_and_filename,'aap'
try:
width,height = Image.open(path_and_filename).size
except:
message = "Not a valid image"
messages.error(request,message)
os.remove(path_and_filename)
url = reverse(workout_uploadimage_view,
kwargs = {'id':id})
if is_ajax:
return JSONResponse({'result':0,'url':0})
else:
return HttpResponseRedirect(url)
i = GraphImage(workout=w,
creationdatetime=timezone.now(),
filename = path_and_filename,
width=width,height=height)
i.save()
url = reverse(r.defaultlandingpage,
kwargs = {'id':id})
if is_ajax:
return JSONResponse({'result':1,'url':0})
else:
return HttpResponseRedirect(url)
else:
messages.error(request,'Something went wrong - no file attached')
url = reverse(workout_uploadimage_view,
kwargs = {'id':id})
if is_ajax:
return JSONResponse({'result':0,'url':0})
else:
return HttpResponseRedirect(url)
else:
return HttpResponse("Form is not valid")
else:
if not is_ajax:
form = ImageForm()
return render(request,'image_form.html',
{'form':form,
'teams':get_my_teams(request.user),
'workout': w,
})
else:
return {'result':0}
# Generic chart creation
@login_required()
def workout_add_chart_view(request,id,plotnr=1):
@@ -8855,10 +8949,13 @@ def workout_add_chart_view(request,id,plotnr=1):
r,w,f1,w.csvfilename,'timeplot',title,plotnr=plotnr,
imagename=imagename
)
try:
request.session['async_tasks'] += [(jobid,'make_plot')]
except KeyError:
request.session['async_tasks'] = [(jobid,'make_plot')]
if res == 0:
messages.error(request,jobid)
else:
try:
request.session['async_tasks'] += [(jobid,'make_plot')]
except KeyError:
request.session['async_tasks'] = [(jobid,'make_plot')]
try:
url = request.session['referer']
@@ -9501,8 +9598,7 @@ def workout_toggle_ranking(request,id=0):
raise Http404("Workout doesn't exist")
if not checkworkoutuser(request.user,row):
message = "You are not allowed to change this workout"
messages.error(request,message)
raise PermissionDenied("You are not allowed to change this workout")
# we are still here - we own the workout
row.rankingpiece = not row.rankingpiece
@@ -9739,10 +9835,13 @@ def workout_upload_view(request,
r = getrower(request.user)
if (make_plot):
res,jobid = uploads.make_plot(r,w,f1,f2,plottype,t)
try:
request.session['async_tasks'] += [(jobid,'make_plot')]
except KeyError:
request.session['async_tasks'] = [(jobid,'make_plot')]
if res == 0:
messages.error(request,jobid)
else:
try:
request.session['async_tasks'] += [(jobid,'make_plot')]
except KeyError:
request.session['async_tasks'] = [(jobid,'make_plot')]
# upload to C2
if (upload_to_c2):
@@ -9988,7 +10087,8 @@ def team_workout_upload_view(request,message="",
r = getrower(request.user)
if (make_plot):
id = uploads.make_plot(r,w,f1,f2,plottype,t)
id,jobid = uploads.make_plot(r,w,f1,f2,plottype,t)