Browse Source

Merge branch 'master' into stable

remotes/origin/posli-mail-autorovi-korektury v1.8
Bc. Petr Pecha 8 years ago
parent
commit
661a940756
  1. 4
      Makefile
  2. 16
      _git_hooks/README.md
  3. 6
      _git_hooks/pre-commit
  4. 53
      _git_hooks/update
  5. 25
      korektury/migrations/0010_Pridani_odkazu_na_organizatora.py
  6. 56
      korektury/migrations/0011_prevod_autora_z_charField_na_Organizator.py
  7. 22
      korektury/migrations/0012_delete_autor.py
  8. 24
      korektury/migrations/0013_rename_autor_org.py
  9. 41
      korektury/models.py
  10. 5
      korektury/templates/korektury/opraf.html
  11. 41
      korektury/views.py
  12. 2
      requirements.txt
  13. 48
      seminar/models.py

4
Makefile

@ -117,10 +117,10 @@ sync_test: sync_test_media sync_test_db
# Does not sync Galerie and CACHE (too huge). # Does not sync Galerie and CACHE (too huge).
sync_local_media: sync_local_media:
rsync -ave ssh --exclude Galerie --exclude CACHE\ 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. # Downloads and restores production database to local database. PostgreSQL only.
sync_local_db: 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 ./last.pgdump
pg_restore -c -d mam -U mam last.pgdump pg_restore -c -d mam -U mam last.pgdump

16
_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

6
_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

53
_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 <ref> <oldrev> <newrev>)" >&2
exit 1
fi
if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
echo "usage: $0 <ref> <oldrev> <newrev>" >&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

25
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),
),
]

56
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),
]

22
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',
),
]

24
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',
),
]

41
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 python_2_unicode_compatible
from django.utils.encoding import force_unicode from django.utils.encoding import force_unicode
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.utils.text import get_valid_filename
from seminar.models import Organizator
import subprocess import subprocess
from reversion import revisions as reversion from reversion import revisions as reversion
# PrilohaReseni method from unidecode import unidecode
def generate_filename(self, filename): def generate_filename(self, filename):
clean = filename.replace('/','-').replace('\0', '').replace(":","_") clean = get_valid_filename(
unidecode(
filename.replace('/', '-').replace('\0', '').replace(":", "_")
)
)
fname = "%s_%s" % ( fname = "%s_%s" % (
timezone.now().strftime('%Y-%m-%d-%H_%M'), timezone.now().strftime('%Y-%m-%d-%H_%M'),
clean) clean)
@ -64,23 +73,18 @@ class KorekturovanePDF(models.Model):
except ObjectDoesNotExist: except ObjectDoesNotExist:
pass pass
super(KorekturovanePDF, self).save() 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] filename = os.path.split(self.pdf.file.name)[1].split(".")[0]
try: dirname = os.path.join(settings.MEDIA_ROOT, settings.KOREKTURY_IMG_DIR)
os.listdir(settings.KOREKTURY_IMG_DIR) if not os.path.exists(dirname):
except OSError: os.mkdir(dirname)
os.mkdir(settings.KOREKTURY_IMG_DIR)
while True: while True:
res = subprocess.call([ res = subprocess.call([
"convert", "convert",
"-density", "180x180", "-density", "180x180",
"-geometry", " 1024x1448", "-geometry", " 1024x1448",
self.pdf.path+"[%d]"%self.stran, "%s[%d]" % (self.pdf.path, self.stran),
os.path.join(settings.BASE_DIR, "media", os.path.join(dirname, "%s-%d.png" % (filename, self.stran))
settings.KOREKTURY_IMG_DIR, ])
"%s-%d.png"%(filename,self.stran))])
if res==1: if res==1:
break break
self.stran +=1 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, status = models.CharField(u'stav opravy',max_length=16, choices=STATUS_CHOICES, blank=False,
default = STATUS_K_OPRAVE) default = STATUS_K_OPRAVE)
autor = models.ForeignKey(Organizator, blank = True,
# TODO: Změnit na cizí klíč do orgů help_text='Autor opravy',
autor = models.CharField(u'autor opravy',blank = True,max_length=20, help_text='Autor opravy') null = True)
text = models.TextField(u'text opravy',blank = True, help_text='Text opravy') 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') cas = models.DateTimeField(u'čas komentáře',default=timezone.now,help_text = 'Čas zadání komentáře')
oprava = models.ForeignKey(Oprava) oprava = models.ForeignKey(Oprava)
# TODO: Změnit na cizí klíč do orgů autor = models.ForeignKey(Organizator, blank = True,
autor = models.CharField(u'autor komentáře',blank = True,max_length=20, help_text='Autor komentáře') help_text = u'Autor komentáře',
null = True)
text = models.TextField(u'text komentáře',blank = True, help_text='Text komentáře') text = models.TextField(u'text komentáře',blank = True, help_text='Text komentáře')

5
korektury/templates/korektury/opraf.html

@ -82,7 +82,10 @@
<!-- /Zmenit stav PDF !--> <!-- /Zmenit stav PDF !-->
<hr/> <hr/>
<p> <p>
Děkujeme opravovatelům: {% for autor,pocet in zasluhy.items %} {{autor}}({{pocet}}) {% endfor %}</p> Děkujeme opravovatelům:
{% for autor,pocet in zasluhy.items %}
{{autor}} ({{pocet}}){% if not forloop.last %},{% endif %}
{% endfor %}</p>
<hr> <hr>
{% for o in opravy %} {% for o in opravy %}

41
korektury/views.py

@ -3,8 +3,10 @@ from django.shortcuts import get_object_or_404, render
from django.views import generic from django.views import generic
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.conf import settings 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 from .forms import OpravaForm
import subprocess import subprocess
@ -29,14 +31,17 @@ class KorekturyView(generic.TemplateView):
form = self.form_class(request.POST) form = self.form_class(request.POST)
q = request.POST q = request.POST
scroll = q.get('scroll') 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: if not autor:
autor = 'anonym' return HttpResponseForbidden()
if not scroll: if not scroll:
scroll = 0 scroll = 0
action = q.get('action') action = q.get('action')
if (action == u''): # Přidej if (action == u''): # Přidej
x = int(q.get('x')) x = int(q.get('x'))
@ -79,6 +84,7 @@ class KorekturyView(generic.TemplateView):
text = q.get('txt') text = q.get('txt')
kom = Komentar(oprava=op,autor=autor,text=text) kom = Komentar(oprava=op,autor=autor,text=text)
kom.save() kom.save()
self.send_email_notification_komentar(op, autor, text)
elif (action == u'update-comment'): elif (action == u'update-comment'):
id = int(q.get('id')) id = int(q.get('id'))
kom = Komentar.objects.get(id=id) kom = Komentar.objects.get(id=id)
@ -112,6 +118,32 @@ class KorekturyView(generic.TemplateView):
context['autor'] = autor context['autor'] = autor
return render(request, 'korektury/opraf.html',context) 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): def get_context_data(self, **kwargs):
context = super(KorekturyView,self).get_context_data(**kwargs) context = super(KorekturyView,self).get_context_data(**kwargs)
pdf = get_object_or_404(KorekturovanePDF, id=self.kwargs['pdf']) pdf = get_object_or_404(KorekturovanePDF, id=self.kwargs['pdf'])
@ -141,6 +173,7 @@ class KorekturyView(generic.TemplateView):
context['opravy'] = opravy context['opravy'] = opravy
context['zasluhy'] = zasluhy context['zasluhy'] = zasluhy
return context return context
def form_valid(self,form): def form_valid(self,form):
return super(KorekturyView,self).form_valid(form) return super(KorekturyView,self).form_valid(form)

2
requirements.txt

@ -8,6 +8,8 @@ pytz==2015.7
six==1.10.0 six==1.10.0
pexpect==4.0.1 pexpect==4.0.1
traitlets==4.0.0 traitlets==4.0.0
Unidecode==0.4.19
flake8==3.0.4
# Django and modules # Django and modules

48
seminar/models.py

@ -12,15 +12,10 @@ from django.utils.text import slugify
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.core.cache import cache from django.core.cache import cache
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.utils.text import get_valid_filename
from imagekit.models import ImageSpecField, ProcessedImageField from imagekit.models import ImageSpecField, ProcessedImageField
from imagekit.processors import ResizeToFit, Transpose 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 django_countries.fields import CountryField
from solo.models import SingletonModel from solo.models import SingletonModel
from taggit.managers import TaggableManager from taggit.managers import TaggableManager
@ -29,6 +24,8 @@ from reversion import revisions as reversion
from seminar.utils import roman from seminar.utils import roman
from unidecode import unidecode
class SeminarModelBase(models.Model): class SeminarModelBase(models.Model):
@ -557,28 +554,35 @@ class Reseni(SeminarModelBase):
super(Reseni, self).save(*args, **kwargs) super(Reseni, self).save(*args, **kwargs)
# PrilohaReseni method def aux_generate_filename(self, filename):
# TODO vyresit partial, tak aby slo migrovat """Pomocná funkce generující ošetřený název souboru v adresáři s datem"""
#def generate_filename(self, filename, directory): clean = get_valid_filename(
# Django 1.9 podporuje partial unidecode(filename.replace('/', '-').replace('\0', ''))
)
def generate_filename(self, filename):
clean = filename.replace('/','-').replace('\0', '')
datedir = timezone.now().strftime('%Y-%m') datedir = timezone.now().strftime('%Y-%m')
fname = "%s_%s" % ( fname = "%s_%s" % (
timezone.now().strftime('%Y-%m-%d-%H:%M'), timezone.now().strftime('%Y-%m-%d-%H:%M'),
clean) 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): def generate_filename_konfera(self, filename):
clean = filename.replace('/','-').replace('\0', '') return os.path.join(
datedir = timezone.now().strftime('%Y-%m') settings.SEMINAR_KONFERY_DIR,
fname = "%s_%s" % ( aux_generate_filename(self, filename)
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 def generate_filename(self, filename):
# return os.path.join(directory, datedir, fname) return os.path.join(
settings.SEMINAR_RESENI_DIR,
aux_generate_filename(self, filename)
)
@reversion.register(ignore_duplicate_revisions=True) @reversion.register(ignore_duplicate_revisions=True)

Loading…
Cancel
Save