diff --git a/rowers/forms.py b/rowers/forms.py
index f029285d..5a7c4a45 100644
--- a/rowers/forms.py
+++ b/rowers/forms.py
@@ -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)
diff --git a/rowers/models.py b/rowers/models.py
index 7419ae54..2838451b 100644
--- a/rowers/models.py
+++ b/rowers/models.py
@@ -858,7 +858,9 @@ class GraphImage(models.Model):
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
diff --git a/rowers/rows.py b/rowers/rows.py
index 51de9769..20750819 100644
--- a/rowers/rows.py
+++ b/rowers/rows.py
@@ -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")
diff --git a/rowers/templates/image_form.html b/rowers/templates/image_form.html
new file mode 100644
index 00000000..0a77e5d2
--- /dev/null
+++ b/rowers/templates/image_form.html
@@ -0,0 +1,252 @@
+{% extends "base.html" %}
+{% load staticfiles %}
+{% load rowerfilters %}
+
+{% block title %}File loading{% endblock %}
+
+{% block meta %}
+
+
+
+{% endblock %}
+
+{% block content %}
+
+
Drag and drop files here
+
+
+{% endblock %}
+
+ {% block scripts %}
+
+
+
+ {% endblock %}
diff --git a/rowers/urls.py b/rowers/urls.py
index 0b93f9b2..f2d291f9 100644
--- a/rowers/urls.py
+++ b/rowers/urls.py
@@ -249,6 +249,7 @@ urlpatterns = [
url(r'^workout/(?P\d+)/otwsetpower$',views.workout_otwsetpower_view),
url(r'^workout/(?P\d+)/interactiveotwplot$',views.workout_otwpowerplot_view),
url(r'^workout/(?P\d+)/wind$',views.workout_wind_view),
+ url(r'^workout/(?P\d+)/image$',views.workout_uploadimage_view),
url(r'^workout/(?P\d+)/darkskywind$',views.workout_downloadwind_view),
url(r'^workout/(?P\d+)/metar/(?P\w+)$',views.workout_downloadmetar_view),
url(r'^workout/(?P\d+)/stream$',views.workout_stream_view),
diff --git a/rowers/views.py b/rowers/views.py
index a6d27deb..0429ecda 100644
--- a/rowers/views.py
+++ b/rowers/views.py
@@ -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
@@ -8836,6 +8836,92 @@ 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})
+ 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):
@@ -9508,8 +9594,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