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 %} + +
+
+
+

Upload Image

+ {% if form.errors %} +

+ Please correct the error{{ form.errors|pluralize }} below. +

+ {% endif %} + + + {{ form.as_table }} +
+ {% csrf_token %} +
+ +
+
+ + + + +
+
+{% 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