diff --git a/Makefile b/Makefile index e755aa55..17a4847f 100644 --- a/Makefile +++ b/Makefile @@ -117,10 +117,10 @@ sync_test: sync_test_media sync_test_db # Does not sync Galerie and CACHE (too huge). sync_local_media: rsync -ave ssh --exclude Galerie --exclude CACHE\ - atrey.karlin.mff.cuni.cz:/akce/MaM/WWW/mamweb-prod/media/ ./media/ + www-mam@atrey.karlin.mff.cuni.cz:/akce/MaM/WWW/mamweb-prod/media/ ./media/ # Downloads and restores production database to local database. PostgreSQL only. sync_local_db: - scp atrey.karlin.mff.cuni.cz:`ssh atrey.karlin.mff.cuni.cz 'ls -v /akce/MaM/WWW/backups/mam-prod-*\.pgdump | tail -n 1'` \ + scp www-mam@atrey.karlin.mff.cuni.cz:`ssh www-mam@atrey.karlin.mff.cuni.cz 'ls -v /akce/MaM/WWW/backups/mam-prod-*\.pgdump | tail -n 1'` \ ./last.pgdump pg_restore -c -d mam -U mam last.pgdump diff --git a/_git_hooks/README.md b/_git_hooks/README.md new file mode 100644 index 00000000..e74e5c53 --- /dev/null +++ b/_git_hooks/README.md @@ -0,0 +1,16 @@ +git hooks +========= + +Kontrola stylu pythoních zdrojáků pomocí flake8. Kontrolujeme jen změny, +abychom nenutili lidi dělat nesouvisející úpravy, které by rozbíjely historii +(git blame). + +pre-commit +---------- +* kontrola změn před commitnutím +* instalace: lokálně zkopírovat do .git/hooks (musí být spustitelný) + +update +------ +* kontrola změn přicházejících s pushem +* instalace: na atreyi zkopírovat do /akce/MaM/MaMweb/mamweb.git/hooks diff --git a/_git_hooks/pre-commit b/_git_hooks/pre-commit new file mode 100755 index 00000000..bdea5060 --- /dev/null +++ b/_git_hooks/pre-commit @@ -0,0 +1,6 @@ +#!/bin/sh +# +# Git hook script to verify what is about to be committed. +# Checks that the changes don't introduce new flake8 errors. + +git diff --unified=1 --cached HEAD -- '*py' | flake8 --diff diff --git a/_git_hooks/update b/_git_hooks/update new file mode 100755 index 00000000..2ac082c1 --- /dev/null +++ b/_git_hooks/update @@ -0,0 +1,53 @@ +#!/bin/sh + +# git update hook to check that pushed changes don't introduce new flake8 +# errors + +# --- Command line +refname="$1" +oldrev="$2" +newrev="$3" + +# --- Safety check +if [ -z "$GIT_DIR" ]; then + echo "Don't run this script from the command line." >&2 + echo " (if you want, you could supply GIT_DIR then run" >&2 + echo " $0 )" >&2 + exit 1 +fi + +if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then + echo "usage: $0 " >&2 + exit 1 +fi + + +TMPDIR=`mktemp -d` +TMPDIFF=`tempfile` + +[ $refname != "refs/heads/master" -a $refname != "refs/heads/stable" ] && exit 0 + +git diff --unified=1 $oldrev $newrev -- '*.py' >${TMPDIFF} + +# there is no working tree in bare git repository, so we recreate it for flake8 +git archive $newrev | tar -x -C ${TMPDIR} + +cd ${TMPDIR} +# report only errors on lines in diff +# (if threre was flake8 installed on atrey, we could just call flake8) +/akce/MaM/WWW/mamweb-test/bin/flake8 --diff <${TMPDIFF} +status=$? +if [ $status != 0 ] ; then + echo + echo -n "Změny, které se snažíte pushnout, obsahují kód v pythonu " + echo -n "nevyhovující flake8 (viz výše). Opravte je a zkuste to znovu. " + echo -n "Nezapomeňte, že můžete editovat historii (git commit --amend, " + echo -n "git rebase -i). Pokud byste chybu příště raději odhalili už při " + echo "commitu, zkopírujte si pre-commit hook z _git_hooks do .git/hooks." + echo +fi + +rm -rf ${TMPDIR} +rm -f ${TMPDIFF} + +exit $status diff --git a/korektury/migrations/0010_Pridani_odkazu_na_organizatora.py b/korektury/migrations/0010_Pridani_odkazu_na_organizatora.py new file mode 100644 index 00000000..e76d5058 --- /dev/null +++ b/korektury/migrations/0010_Pridani_odkazu_na_organizatora.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('seminar', '0041_konfery'), + ('korektury', '0009_trizeni_korektur_v_seznamu'), + ] + + operations = [ + migrations.AddField( + model_name='komentar', + name='autor_org', + field=models.ForeignKey(blank=True, to='seminar.Organizator', help_text='Autor koment\xe1\u0159e', null=True), + ), + migrations.AddField( + model_name='oprava', + name='autor_org', + field=models.ForeignKey(blank=True, to='seminar.Organizator', help_text=b'Autor opravy', null=True), + ), + ] diff --git a/korektury/migrations/0011_prevod_autora_z_charField_na_Organizator.py b/korektury/migrations/0011_prevod_autora_z_charField_na_Organizator.py new file mode 100644 index 00000000..dddb07be --- /dev/null +++ b/korektury/migrations/0011_prevod_autora_z_charField_na_Organizator.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + +def transform_autor(apps, schema_editor): + print + Organizator = apps.get_model('seminar', 'Organizator') + + # preorgovani oprav + Oprava = apps.get_model('korektury', 'Oprava') + for oprava in Oprava.objects.all(): + jmeno = oprava.autor.split() + if len(jmeno) == 2: + try: + org = Organizator.objects.get(user__first_name=jmeno[0], + user__last_name=jmeno[1]) + oprava.autor_org = org + oprava.save() + except: + print "Org nenalezen -- mažu korekturu" +# oprava.delete() + else: + print "Org nenalezen -- mažu korekturu" + oprava.delete() + + # preorgovani komentaru + Komentar = apps.get_model('korektury', 'Komentar') + for komentar in Komentar.objects.all(): + jmeno = komentar.autor.split() + if len(jmeno) == 2: + try: + org = Organizator.objects.get(user__first_name=jmeno[0], + user__last_name=jmeno[1]) + komentar.autor_org = org + komentar.save() + except: + print "Org nenalezen -- mažu korekturu" +# oprava.delete() + else: + print "Org nenalezen -- mažu korekturu" + komentar.delete() + +def back(apps, schema_editor): + pass + + +class Migration(migrations.Migration): + + dependencies = [ + ('korektury', '0010_Pridani_odkazu_na_organizatora'), + ] + + operations = [ + migrations.RunPython(transform_autor, back), + ] diff --git a/korektury/migrations/0012_delete_autor.py b/korektury/migrations/0012_delete_autor.py new file mode 100644 index 00000000..4f41a955 --- /dev/null +++ b/korektury/migrations/0012_delete_autor.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('korektury', '0011_prevod_autora_z_charField_na_Organizator'), + ] + + operations = [ + migrations.RemoveField( + model_name='komentar', + name='autor', + ), + migrations.RemoveField( + model_name='oprava', + name='autor', + ), + ] diff --git a/korektury/migrations/0013_rename_autor_org.py b/korektury/migrations/0013_rename_autor_org.py new file mode 100644 index 00000000..db0d3151 --- /dev/null +++ b/korektury/migrations/0013_rename_autor_org.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('korektury', '0012_delete_autor'), + ] + + operations = [ + migrations.RenameField( + model_name='komentar', + old_name='autor_org', + new_name='autor', + ), + migrations.RenameField( + model_name='oprava', + old_name='autor_org', + new_name='autor', + ), + ] diff --git a/korektury/models.py b/korektury/models.py index 311d5e20..a07c6945 100644 --- a/korektury/models.py +++ b/korektury/models.py @@ -6,13 +6,22 @@ from django.conf import settings from django.utils.encoding import python_2_unicode_compatible from django.utils.encoding import force_unicode from django.core.exceptions import ObjectDoesNotExist +from django.utils.text import get_valid_filename + +from seminar.models import Organizator import subprocess from reversion import revisions as reversion -# PrilohaReseni method +from unidecode import unidecode + + def generate_filename(self, filename): - clean = filename.replace('/','-').replace('\0', '').replace(":","_") + clean = get_valid_filename( + unidecode( + filename.replace('/', '-').replace('\0', '').replace(":", "_") + ) + ) fname = "%s_%s" % ( timezone.now().strftime('%Y-%m-%d-%H_%M'), clean) @@ -64,23 +73,18 @@ class KorekturovanePDF(models.Model): except ObjectDoesNotExist: pass super(KorekturovanePDF, self).save() - print("\nSaving") - print(self.pdf.path) - print(self.pdf.url) filename = os.path.split(self.pdf.file.name)[1].split(".")[0] - try: - os.listdir(settings.KOREKTURY_IMG_DIR) - except OSError: - os.mkdir(settings.KOREKTURY_IMG_DIR) + dirname = os.path.join(settings.MEDIA_ROOT, settings.KOREKTURY_IMG_DIR) + if not os.path.exists(dirname): + os.mkdir(dirname) while True: res = subprocess.call([ - "convert", - "-density","180x180", - "-geometry"," 1024x1448", - self.pdf.path+"[%d]"%self.stran, - os.path.join(settings.BASE_DIR, "media", - settings.KOREKTURY_IMG_DIR, - "%s-%d.png"%(filename,self.stran))]) + "convert", + "-density", "180x180", + "-geometry", " 1024x1448", + "%s[%d]" % (self.pdf.path, self.stran), + os.path.join(dirname, "%s-%d.png" % (filename, self.stran)) + ]) if res==1: break self.stran +=1 @@ -120,9 +124,9 @@ class Oprava(models.Model): status = models.CharField(u'stav opravy',max_length=16, choices=STATUS_CHOICES, blank=False, default = STATUS_K_OPRAVE) - - # TODO: Změnit na cizí klíč do orgů - autor = models.CharField(u'autor opravy',blank = True,max_length=20, help_text='Autor opravy') + autor = models.ForeignKey(Organizator, blank = True, + help_text='Autor opravy', + null = True) text = models.TextField(u'text opravy',blank = True, help_text='Text opravy') @@ -150,8 +154,9 @@ class Komentar(models.Model): cas = models.DateTimeField(u'čas komentáře',default=timezone.now,help_text = 'Čas zadání komentáře') oprava = models.ForeignKey(Oprava) - # TODO: Změnit na cizí klíč do orgů - autor = models.CharField(u'autor komentáře',blank = True,max_length=20, help_text='Autor komentáře') + autor = models.ForeignKey(Organizator, blank = True, + help_text = u'Autor komentáře', + null = True) text = models.TextField(u'text komentáře',blank = True, help_text='Text komentáře') diff --git a/korektury/templates/korektury/opraf.html b/korektury/templates/korektury/opraf.html index 83c997ab..04cfc2de 100644 --- a/korektury/templates/korektury/opraf.html +++ b/korektury/templates/korektury/opraf.html @@ -82,7 +82,10 @@

- Děkujeme opravovatelům: {% for autor,pocet in zasluhy.items %} {{autor}}({{pocet}}) {% endfor %}

+ Děkujeme opravovatelům: + {% for autor,pocet in zasluhy.items %} + {{autor}} ({{pocet}}){% if not forloop.last %},{% endif %} + {% endfor %}


{% for o in opravy %} diff --git a/korektury/views.py b/korektury/views.py index d6d126e4..931ab67c 100644 --- a/korektury/views.py +++ b/korektury/views.py @@ -3,8 +3,10 @@ from django.shortcuts import get_object_or_404, render from django.views import generic from django.utils.translation import ugettext as _ from django.conf import settings +from django.http import HttpResponseForbidden +from django.core.mail import send_mail -from .models import Oprava,Komentar,KorekturovanePDF +from .models import Oprava,Komentar,KorekturovanePDF, Organizator from .forms import OpravaForm import subprocess @@ -29,14 +31,17 @@ class KorekturyView(generic.TemplateView): form = self.form_class(request.POST) q = request.POST scroll = q.get('scroll') - autor = q.get('au') + # prirazeni autora podle prihlaseni + autor_user = request.user + # pokud existuje ucet (user), ale neni to organizator = 403 + autor = Organizator.objects.filter(user=autor_user).first() if not autor: - autor = 'anonym' + return HttpResponseForbidden() + if not scroll: scroll = 0 - action = q.get('action') if (action == u''): # Přidej x = int(q.get('x')) @@ -79,6 +84,7 @@ class KorekturyView(generic.TemplateView): text = q.get('txt') kom = Komentar(oprava=op,autor=autor,text=text) kom.save() + self.send_email_notification_komentar(op, autor, text) elif (action == u'update-comment'): id = int(q.get('id')) kom = Komentar.objects.get(id=id) @@ -112,6 +118,32 @@ class KorekturyView(generic.TemplateView): context['autor'] = autor return render(request, 'korektury/opraf.html',context) + def send_email_notification_komentar(self, oprava, autor, text): + ''' Rozesle e-mail pri pridani komentare, + ktery obsahuje text komentare. + ''' + + # parametry e-mailu + odkaz = "https://mam.mff.cuni.cz:1443/korektury/{}/".format(oprava.pdf.pk) + from_email = 'korekturovatko@mam.mff.cuni.cz' + subject = u'Nová korektura od {} v {}'.format(autor, + oprava.pdf.nazev) + text = u"Text komentáře:\n\n{}\n\n=== Konec textu komentáře ===\n\ + \nodkaz do korekturovátka: {}\n\ + \nVaše korekturovátko\n".format(text, odkaz) + + # Prijemci e-mailu + emails = set() + email = oprava.autor.user.email + if email: + emails.add(email) + for komentar in oprava.komentar_set.all(): + email = komentar.autor.user.email + if email: + emails.add(email) + + send_mail(subject, text, from_email, list(emails)) + def get_context_data(self, **kwargs): context = super(KorekturyView,self).get_context_data(**kwargs) pdf = get_object_or_404(KorekturovanePDF, id=self.kwargs['pdf']) @@ -141,6 +173,7 @@ class KorekturyView(generic.TemplateView): context['opravy'] = opravy context['zasluhy'] = zasluhy return context + def form_valid(self,form): return super(KorekturyView,self).form_valid(form) diff --git a/requirements.txt b/requirements.txt index 77d913ea..1800d357 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,6 +8,8 @@ pytz==2015.7 six==1.10.0 pexpect==4.0.1 traitlets==4.0.0 +Unidecode==0.4.19 +flake8==3.0.4 # Django and modules diff --git a/seminar/models.py b/seminar/models.py index 15367d3a..4c8fc3f0 100644 --- a/seminar/models.py +++ b/seminar/models.py @@ -12,15 +12,10 @@ from django.utils.text import slugify from django.core.urlresolvers import reverse from django.core.cache import cache from django.core.exceptions import ObjectDoesNotExist +from django.utils.text import get_valid_filename from imagekit.models import ImageSpecField, ProcessedImageField from imagekit.processors import ResizeToFit, Transpose -from PIL import Image -import os -#from functools import partial -from cStringIO import StringIO -from django.core.files.base import ContentFile - from django_countries.fields import CountryField from solo.models import SingletonModel from taggit.managers import TaggableManager @@ -29,6 +24,8 @@ from reversion import revisions as reversion from seminar.utils import roman +from unidecode import unidecode + class SeminarModelBase(models.Model): @@ -557,28 +554,35 @@ class Reseni(SeminarModelBase): super(Reseni, self).save(*args, **kwargs) -# PrilohaReseni method -# TODO vyresit partial, tak aby slo migrovat -#def generate_filename(self, filename, directory): -# Django 1.9 podporuje partial - -def generate_filename(self, filename): - clean = filename.replace('/','-').replace('\0', '') +def aux_generate_filename(self, filename): + """Pomocná funkce generující ošetřený název souboru v adresáři s datem""" + clean = get_valid_filename( + unidecode(filename.replace('/', '-').replace('\0', '')) + ) datedir = timezone.now().strftime('%Y-%m') fname = "%s_%s" % ( timezone.now().strftime('%Y-%m-%d-%H:%M'), clean) - return os.path.join(settings.SEMINAR_RESENI_DIR, datedir, fname) + return os.path.join(datedir, fname) + +# Django neumí jednoduše serializovat partial nebo třídu s __call__ +# (https://docs.djangoproject.com/en/1.8/topics/migrations/), +# neprojdou pak migrace. Takže rozlišení funkcí generujících názvy souboru +# podle adresáře řešíme takto. + def generate_filename_konfera(self, filename): - clean = filename.replace('/','-').replace('\0', '') - datedir = timezone.now().strftime('%Y-%m') - fname = "%s_%s" % ( - timezone.now().strftime('%Y-%m-%d-%H:%M'), - clean) - return os.path.join(settings.SEMINAR_KONFERY_DIR, datedir, fname) -# TODO vyresit partial tak, aby slo migrovat -# return os.path.join(directory, datedir, fname) + return os.path.join( + settings.SEMINAR_KONFERY_DIR, + aux_generate_filename(self, filename) + ) + + +def generate_filename(self, filename): + return os.path.join( + settings.SEMINAR_RESENI_DIR, + aux_generate_filename(self, filename) + ) @reversion.register(ignore_duplicate_revisions=True)