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 e37e35e9..2838451b 100644
--- a/rowers/models.py
+++ b/rowers/models.py
@@ -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
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..7ae08c3c
--- /dev/null
+++ b/rowers/templates/.#image_form.html
@@ -0,0 +1 @@
+E408191@CZ27LT9RCGN72.1800:1516641451
\ No newline at end of file
diff --git a/rowers/templates/image_form.html b/rowers/templates/image_form.html
new file mode 100644
index 00000000..32f7c725
--- /dev/null
+++ b/rowers/templates/image_form.html
@@ -0,0 +1,216 @@
+{% 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/templates/panel_uploadimage.html b/rowers/templates/panel_uploadimage.html
new file mode 100644
index 00000000..f99652cc
--- /dev/null
+++ b/rowers/templates/panel_uploadimage.html
@@ -0,0 +1,5 @@
+
diff --git a/rowers/templates/workout_form.html b/rowers/templates/workout_form.html
index d4e2985b..e0f87339 100644
--- a/rowers/templates/workout_form.html
+++ b/rowers/templates/workout_form.html
@@ -137,8 +137,12 @@
-
-
+
+
Power Pie Chart
diff --git a/rowers/uploads.py b/rowers/uploads.py
index e263b7bf..02ac1837 100644
--- a/rowers/uploads.py
+++ b/rowers/uploads.py
@@ -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
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/utils.py b/rowers/utils.py
index 300e95fa..108a670a 100644
--- a/rowers/utils.py
+++ b/rowers/utils.py
@@ -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.",
]
diff --git a/rowers/views.py b/rowers/views.py
index ca4cc7db..eabc67fe 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
@@ -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)
+