diff --git a/.gitignore b/.gitignore index aefe158b..6b8b5c02 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,6 @@ # secrets /django.secret + +# vim tmp files +*~ diff --git a/korektury/TODO b/korektury/TODO new file mode 100644 index 00000000..92c4b8d5 --- /dev/null +++ b/korektury/TODO @@ -0,0 +1,14 @@ +- korektura potrebuje reakci ++ komentáře fixně na username + - používat skutečné jméno? +- vyžádat pozornost autora obsahu +- zvednout upload limit na 5MB +- sbalit a rozbalit korekturu +- nápověda +- nahrávání jiných věcí než PDF - kontrolovat? +- stylování +- vylepšení hlavičky + - seznam PDF, homepage M&M, admin, wiki +- seznam PDF - co zobrazovat? + + diff --git a/korektury/__init__.py b/korektury/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/korektury/admin.py b/korektury/admin.py new file mode 100644 index 00000000..564e2cd0 --- /dev/null +++ b/korektury/admin.py @@ -0,0 +1,16 @@ +from django.contrib import admin +from reversion.admin import VersionAdmin +from korektury.models import KorekturovanePDF + +# Register your models here. +class KorekturovanePDFAdmin(VersionAdmin): + readonly_fields = ['cas', 'stran'] + fieldsets = [ + (None, {'fields': ['pdf', 'cas', 'stran', 'nazev', 'komentar']}), +# (u'PDF', {'fields': ['pdf']}), + ] + list_display = ['pdf', 'cas', 'stran'] + list_filter = [] + search_fields = [] + +admin.site.register(KorekturovanePDF, KorekturovanePDFAdmin) diff --git a/korektury/forms.py b/korektury/forms.py new file mode 100644 index 00000000..676d6b3a --- /dev/null +++ b/korektury/forms.py @@ -0,0 +1,13 @@ +from django import forms + +class OpravaForm(forms.Form): + text = forms.CharField(max_length=256) + autor = forms.CharField(max_length=20) + x = forms.IntegerField() + y = forms.IntegerField() + scroll = forms.CharField(max_length=256) + pdf = forms.CharField(max_length=256) + img_id = forms.CharField(max_length=256) + id = forms.CharField(max_length=256) + action = forms.CharField(max_length=256) + diff --git a/korektury/migrations/0001_initial.py b/korektury/migrations/0001_initial.py new file mode 100644 index 00000000..22643689 --- /dev/null +++ b/korektury/migrations/0001_initial.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import korektury.models + + +class Migration(migrations.Migration): + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='KorekturovanePDF', + fields=[ + ('id', models.AutoField(serialize=False, primary_key=True)), + ('pdf', models.FileField(upload_to=korektury.models.generate_filename, verbose_name='pdf')), + ], + options={ + 'db_table': 'korekturovane_cislo', + 'verbose_name': 'PDF k oprav\xe1m', + 'verbose_name_plural': 'PDF k oprav\xe1m', + }, + bases=(models.Model,), + ), + migrations.CreateModel( + name='Oprava', + fields=[ + ('id', models.AutoField(serialize=False, primary_key=True)), + ('strana', models.IntegerField(help_text=b'Strana s opravou (od 0)', verbose_name='strana s opravou')), + ('x', models.IntegerField(verbose_name='x-ov\xe1 sou\u0159adnice bugu')), + ('y', models.IntegerField(verbose_name='y-ov\xe1 sou\u0159adnice bugu')), + ('status', models.CharField(default=b'k_oprave', max_length=16, verbose_name='stav opravy', choices=[(b'k_oprave', 'K oprav\u011b'), (b'opraveno', 'Opraveno'), (b'smazano', 'Smaz\xe1no')])), + ('autor', models.TextField(help_text=b'Autor opravy', verbose_name='autor opravy', blank=True)), + ('text', models.TextField(help_text=b'Text opravy', verbose_name='text opravy', blank=True)), + ], + options={ + 'ordering': ['y', 'x'], + 'db_table': 'opravy', + 'verbose_name': 'Oprava', + 'verbose_name_plural': 'Opravy', + }, + bases=(models.Model,), + ), + migrations.CreateModel( + name='OpravaKomentar', + fields=[ + ('id', models.AutoField(serialize=False, primary_key=True)), + ('cas', models.DateTimeField(help_text=b'\xc4\x8cas zad\xc3\xa1n\xc3\xad koment\xc3\xa1\xc5\x99e', verbose_name='\u010das koment\xe1\u0159e')), + ('autor', models.TextField(help_text=b'Autor koment\xc3\xa1\xc5\x99e', verbose_name='autor koment\xe1\u0159e', blank=True)), + ('text', models.TextField(help_text=b'Text koment\xc3\xa1\xc5\x99e', verbose_name='text koment\xe1\u0159e', blank=True)), + ('oprava', models.ForeignKey(to='korektury.Oprava')), + ], + options={ + 'ordering': ['cas'], + 'db_table': 'opravy_komentare', + 'verbose_name': 'Koment\xe1\u0159 k oprav\u011b', + 'verbose_name_plural': 'Koment\xe1\u0159e k oprav\u011b', + }, + bases=(models.Model,), + ), + ] diff --git a/korektury/migrations/0002_auto_20151202_2351.py b/korektury/migrations/0002_auto_20151202_2351.py new file mode 100644 index 00000000..ba88f7c7 --- /dev/null +++ b/korektury/migrations/0002_auto_20151202_2351.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('korektury', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Komentar', + fields=[ + ('id', models.AutoField(serialize=False, primary_key=True)), + ('cas', models.DateTimeField(default=django.utils.timezone.now, help_text=b'\xc4\x8cas zad\xc3\xa1n\xc3\xad koment\xc3\xa1\xc5\x99e', verbose_name='\u010das koment\xe1\u0159e')), + ('autor', models.TextField(help_text=b'Autor koment\xc3\xa1\xc5\x99e', verbose_name='autor koment\xe1\u0159e', blank=True)), + ('text', models.TextField(help_text=b'Text koment\xc3\xa1\xc5\x99e', verbose_name='text koment\xe1\u0159e', blank=True)), + ('oprava', models.ForeignKey(to='korektury.Oprava')), + ], + options={ + 'ordering': ['cas'], + 'db_table': 'komentare', + 'verbose_name': 'Koment\xe1\u0159 k oprav\u011b', + 'verbose_name_plural': 'Koment\xe1\u0159e k oprav\u011b', + }, + bases=(models.Model,), + ), + migrations.RemoveField( + model_name='opravakomentar', + name='oprava', + ), + migrations.DeleteModel( + name='OpravaKomentar', + ), + ] diff --git a/korektury/migrations/0003_auto_20151204_1855.py b/korektury/migrations/0003_auto_20151204_1855.py new file mode 100644 index 00000000..8f8cb70b --- /dev/null +++ b/korektury/migrations/0003_auto_20151204_1855.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('korektury', '0002_auto_20151202_2351'), + ] + + operations = [ + migrations.AddField( + model_name='korekturovanepdf', + name='cas', + field=models.DateTimeField(default=django.utils.timezone.now, help_text=b'\xc4\x8cas vlo\xc5\xbeen\xc3\xad PDF', verbose_name='\u010das vlo\u017een\xed PDF'), + preserve_default=True, + ), + migrations.AddField( + model_name='korekturovanepdf', + name='stran', + field=models.IntegerField(default=0, help_text=b'Po\xc4\x8det stran PDF', verbose_name='po\u010det stran'), + preserve_default=True, + ), + ] diff --git a/korektury/migrations/0004_auto_20151204_2240.py b/korektury/migrations/0004_auto_20151204_2240.py new file mode 100644 index 00000000..ed47ccaf --- /dev/null +++ b/korektury/migrations/0004_auto_20151204_2240.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('korektury', '0003_auto_20151204_1855'), + ] + + operations = [ + migrations.AddField( + model_name='korekturovanepdf', + name='komentar', + field=models.TextField(help_text=b'Koment\xc3\xa1\xc5\x99 ke korekturovan\xc3\xa9mu PDF (nap\xc5\x99. na co se zam\xc4\x9b\xc5\x99it)', verbose_name='koment\xe1\u0159 k PDF', blank=True), + preserve_default=True, + ), + migrations.AddField( + model_name='korekturovanepdf', + name='nazev', + field=models.TextField(help_text=b'N\xc3\xa1zev (nap\xc5\x99. 22.1 verze 4) korekturovan\xc3\xa9ho PDF', verbose_name='n\xe1zev PDF', blank=True), + preserve_default=True, + ), + ] diff --git a/korektury/migrations/0005_auto_20151204_2244.py b/korektury/migrations/0005_auto_20151204_2244.py new file mode 100644 index 00000000..52ebdfb6 --- /dev/null +++ b/korektury/migrations/0005_auto_20151204_2244.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('korektury', '0004_auto_20151204_2240'), + ] + + operations = [ + migrations.AlterField( + model_name='komentar', + name='autor', + field=models.CharField(help_text=b'Autor koment\xc3\xa1\xc5\x99e', max_length=20, verbose_name='autor koment\xe1\u0159e', blank=True), + preserve_default=True, + ), + migrations.AlterField( + model_name='korekturovanepdf', + name='nazev', + field=models.CharField(help_text=b'N\xc3\xa1zev (nap\xc5\x99. 22.1 verze 4) korekturovan\xc3\xa9ho PDF', max_length=50, verbose_name='n\xe1zev PDF', blank=True), + preserve_default=True, + ), + migrations.AlterField( + model_name='oprava', + name='autor', + field=models.CharField(help_text=b'Autor opravy', max_length=20, verbose_name='autor opravy', blank=True), + preserve_default=True, + ), + ] diff --git a/korektury/migrations/0006_oprava_pdf.py b/korektury/migrations/0006_oprava_pdf.py new file mode 100644 index 00000000..2adf04e7 --- /dev/null +++ b/korektury/migrations/0006_oprava_pdf.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('korektury', '0005_auto_20151204_2244'), + ] + + operations = [ + migrations.AddField( + model_name='oprava', + name='pdf', + field=models.ForeignKey(default=-1, to='korektury.KorekturovanePDF'), + preserve_default=True, + ), + ] diff --git a/korektury/migrations/0007_auto_20151225_1237.py b/korektury/migrations/0007_auto_20151225_1237.py new file mode 100644 index 00000000..805e3510 --- /dev/null +++ b/korektury/migrations/0007_auto_20151225_1237.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import korektury.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('korektury', '0006_oprava_pdf'), + ] + + operations = [ + migrations.AddField( + model_name='korekturovanepdf', + name='status', + field=models.CharField(default=b'pridavani', max_length=16, verbose_name='stav PDF', choices=[(b'pridavani', 'P\u0159id\xe1v\xe1n\xed korektur'), (b'zanaseni', 'Korektury jsou zan\xe1\u0161eny'), (b'zastarale', 'Star\xe1 verze, nekorigovat')]), + preserve_default=True, + ), + migrations.AlterField( + model_name='korekturovanepdf', + name='pdf', + field=models.FileField(upload_to=korektury.models.generate_filename, verbose_name='PDF'), + preserve_default=True, + ), + migrations.AlterField( + model_name='oprava', + name='status', + field=models.CharField(default=b'k_oprave', max_length=16, verbose_name='stav opravy', choices=[(b'k_oprave', 'K oprav\u011b'), (b'opraveno', 'Opraveno'), (b'neni_chyba', 'Nen\xed chyba'), (b'k_reakci', 'K reakci autora textu'), (b'smazano', 'Smaz\xe1no')]), + preserve_default=True, + ), + ] diff --git a/korektury/migrations/__init__.py b/korektury/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/korektury/models.py b/korektury/models.py new file mode 100644 index 00000000..ee873616 --- /dev/null +++ b/korektury/models.py @@ -0,0 +1,176 @@ +# -*- coding: utf-8 -*- +import os +import random +from django.db import models +from django.contrib import auth +from django.utils import timezone +from django.conf import settings +from django.utils.encoding import python_2_unicode_compatible +from django.utils.encoding import force_unicode +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 imagekit.models import ImageSpecField, ProcessedImageField +from imagekit.processors import ResizeToFit, Transpose + +import os +import subprocess +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 + +import reversion + +# PrilohaReseni method +def generate_filename(self, filename): + clean = filename.replace('/','-').replace('\0', '').replace(":","_") + fname = "%s_%s" % ( + timezone.now().strftime('%Y-%m-%d-%H_%M'), + clean) + return os.path.join(settings.KOREKTURY_PDF_DIR, fname) + + +#@reversion.register(ignore_duplicate_revision=True) +#@python_2_unicode_compatible +class KorekturovanePDF(models.Model): + class Meta: + db_table = 'korekturovane_cislo' + verbose_name = u'PDF k opravám' + verbose_name_plural = u'PDF k opravám' + + #Interní ID + id = models.AutoField(primary_key = True) + + cas = models.DateTimeField(u'čas vložení PDF',default=timezone.now,help_text = 'Čas vložení PDF') + + nazev = models.CharField(u'název PDF',blank = True,max_length=50, help_text='Název (např. 22.1 verze 4) korekturovaného PDF') + + komentar = models.TextField(u'komentář k PDF',blank = True, help_text='Komentář ke korekturovanému PDF (např. na co se zaměřit)') + + pdf = models.FileField(u'PDF', upload_to = generate_filename) + + stran = models.IntegerField(u'počet stran', help_text = 'Počet stran PDF', default = 0) + STATUS_PRIDAVANI = 'pridavani' + STATUS_ZANASENI = 'zanaseni' + STATUS_ZASTARALE = 'zastarale' + STATUS_CHOICES = ( + (STATUS_PRIDAVANI, u'Přidávání korektur'), + (STATUS_ZANASENI, u'Korektury jsou zanášeny'), + (STATUS_ZASTARALE, u'Stará verze, nekorigovat'), + ) + status = models.CharField(u'stav PDF',max_length=16, choices=STATUS_CHOICES, blank=False, + default = STATUS_PRIDAVANI) + + + #TODO Nepovinný foreign key k číslu + + def save(self): + # Pokud se nezmenilo PDF, tak nepregenerovavej nahledy + try: + original = KorekturovanePDF.objects.get(pk=self.pk) + if original.pdf == self.pdf: + super(KorekturovanePDF, self).save() + return + 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) + while True: + res = subprocess.call([ + "convert", + "-density","180x180", + "-geometry"," 1024x1448", + self.pdf.path+"[%d]"%self.stran, + os.path.join(settings.KOREKTURY_IMG_DIR, "%s-%d.png"%(filename,self.stran))]) + if res==1: + break + self.stran +=1 + super(KorekturovanePDF, self).save() + + + +@reversion.register(ignore_duplicate_revision=True) +@python_2_unicode_compatible +class Oprava(models.Model): + class Meta: + db_table = 'opravy' + verbose_name = u'Oprava' + verbose_name_plural = u'Opravy' + ordering = ['y','x'] + + #Interní ID + id = models.AutoField(primary_key = True) + + pdf = models.ForeignKey(KorekturovanePDF, default=-1) + + strana = models.IntegerField(u'strana s opravou', help_text='Strana s opravou (od 0)') + + x = models.IntegerField(u'x-ová souřadnice bugu') + y = models.IntegerField(u'y-ová souřadnice bugu') + + STATUS_K_OPRAVE = 'k_oprave' + STATUS_OPRAVENO = 'opraveno' + STATUS_NENI_CHYBA = 'neni_chyba' + STATUS_K_REAKCI = 'k_reakci' + STATUS_SMAZANO = 'smazano' + STATUS_CHOICES = ( + (STATUS_K_OPRAVE, u'K opravě'), + (STATUS_OPRAVENO, u'Opraveno'), + (STATUS_NENI_CHYBA, u'Není chyba'), + (STATUS_K_REAKCI, u'K reakci autora textu'), + (STATUS_SMAZANO, u'Smazáno'), + ) + 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') + + text = models.TextField(u'text opravy',blank = True, help_text='Text opravy') + +# def __init__(self,dictionary): +# for k,v in dictionary.items(): +# setattr(self,k,v) + + def __str__(self): + return force_unicode(u'%s od %s: %s'%(self.status,self.autor,self.text)) + + + +@reversion.register(ignore_duplicate_revision=True) +@python_2_unicode_compatible +class Komentar(models.Model): + class Meta: + db_table = 'komentare' + verbose_name = u'Komentář k opravě' + verbose_name_plural = u'Komentáře k opravě' + ordering = ['cas'] + + #Interní ID + id = models.AutoField(primary_key = True) + + 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') + + text = models.TextField(u'text komentáře',blank = True, help_text='Text komentáře') + + def __str__(self): + return force_unicode(u'%s od %s: %s'%(self.cas,self.autor,self.text)) + + + diff --git a/korektury/static/korektury/imgs/check.png b/korektury/static/korektury/imgs/check.png new file mode 100644 index 00000000..c4d5504e Binary files /dev/null and b/korektury/static/korektury/imgs/check.png differ diff --git a/korektury/static/korektury/imgs/comment.png b/korektury/static/korektury/imgs/comment.png new file mode 100644 index 00000000..4a648425 Binary files /dev/null and b/korektury/static/korektury/imgs/comment.png differ diff --git a/korektury/static/korektury/imgs/cross.png b/korektury/static/korektury/imgs/cross.png new file mode 100644 index 00000000..f3add6aa Binary files /dev/null and b/korektury/static/korektury/imgs/cross.png differ diff --git a/korektury/static/korektury/imgs/delete-gr.png b/korektury/static/korektury/imgs/delete-gr.png new file mode 100644 index 00000000..d0ed643c Binary files /dev/null and b/korektury/static/korektury/imgs/delete-gr.png differ diff --git a/korektury/static/korektury/imgs/delete.png b/korektury/static/korektury/imgs/delete.png new file mode 100644 index 00000000..9b77ca3b Binary files /dev/null and b/korektury/static/korektury/imgs/delete.png differ diff --git a/korektury/static/korektury/imgs/edit-gr.png b/korektury/static/korektury/imgs/edit-gr.png new file mode 100644 index 00000000..e7ca04b8 Binary files /dev/null and b/korektury/static/korektury/imgs/edit-gr.png differ diff --git a/korektury/static/korektury/imgs/edit.png b/korektury/static/korektury/imgs/edit.png new file mode 100644 index 00000000..95cabe7d Binary files /dev/null and b/korektury/static/korektury/imgs/edit.png differ diff --git a/korektury/static/korektury/imgs/link.png b/korektury/static/korektury/imgs/link.png new file mode 100644 index 00000000..cfa343c7 Binary files /dev/null and b/korektury/static/korektury/imgs/link.png differ diff --git a/korektury/static/korektury/imgs/next-gr.png b/korektury/static/korektury/imgs/next-gr.png new file mode 100644 index 00000000..885c2cd7 Binary files /dev/null and b/korektury/static/korektury/imgs/next-gr.png differ diff --git a/korektury/static/korektury/imgs/next.png b/korektury/static/korektury/imgs/next.png new file mode 100644 index 00000000..e8b15f66 Binary files /dev/null and b/korektury/static/korektury/imgs/next.png differ diff --git a/korektury/static/korektury/imgs/undo.png b/korektury/static/korektury/imgs/undo.png new file mode 100644 index 00000000..973b5939 Binary files /dev/null and b/korektury/static/korektury/imgs/undo.png differ diff --git a/korektury/static/korektury/opraf.css b/korektury/static/korektury/opraf.css new file mode 100644 index 00000000..18da5f5a --- /dev/null +++ b/korektury/static/korektury/opraf.css @@ -0,0 +1,145 @@ +body{background: #f3f3f3; color: black;} +body.comitting { + background: yellow; +} +body.deprecated { + background: red; +} + +img{background:white;} + +.pointer-hi, +.pointer, +.pointer-wontfix, +.pointer-wontfix-hi, +.pointer-done, +.pointer-done-hi { + position:absolute; + /*border-bottom-left-radius: 10px; */ + border-left: 2px solid yellow; + border-bottom: 2px solid yellow; +} + +.pointer-done-hi, +.pointer-wontfix-hi, +.pointer-hi { + border-width: 3px; +} + +.pointer { + border-color: #F00; /*IE*/ + border-color: rgba(255, 0, 0, 0.35); +} +.pointer-hi { + border-color: #F00; /*IE*/ + border-color: rgba(255, 0, 0, 1); +} +.pointer-done { + border-color: #00F; /*IE*/ + border-color: rgba(0, 0, 255, 0.2); +} +.pointer-done-hi { + border-color: #00F; /*IE*/ + border-color: rgba(0, 0, 255, 1); +} +.pointer-wontfix { + border-color: #00F; /*IE*/ + border-color: rgba(128, 128, 128, 0.2); +} +.pointer-wontfix-hi { + border-color: #00F; /*IE*/ + border-color: rgba(128, 128, 128, 1); +} + + +.box:hover, +.box-done:hover, +.box-wontfix:hover{ + border-width:3px; + margin: 0px; +} +.box, .box-done, .box-wontfix { + margin: 1px; + background-color: white; + width:300px; + /*position:absolute;*/ + padding: 3px; + border: 2px solid black; + border-radius: 10px; +} +.box { + border-color: red; +} +.box-done { + border-color: blue; +} +.box-wontfix { + border-color: grey; +} +form { + display:inline; +} + +.float-right{ + float:right; +} + +.imgdiv { + position:relative; + left:0px; + top:0px; +} +#commform-div { + display: none; + position: absolute; + background-color: white; + border: 1px solid; + padding: 3px; + /* + width: 310; + height: 220; + */ + z-index: 10; + border: 4px solid red; + border-radius: 10px; + background-color: white; +} +.close-button{ + background-color: yellow; +} + + +.box button, +.box img, +.box-done button, +.box-done img, +.box-wontfix button, +.box-wontfix img{ + border: 1px solid white; + background-color:transparent; + margin:0; + padding: 1px; +} +.box button:hover, +.box img:hover, +.box-done img:hover, +.box-done button:hover, +.box-wontfix img:hover, +.box-wontfix button:hover{ + border: 1px solid black; +} + +.comment hr { + height: 0px; +} + +.corr-header { + overflow: auto; +} + +.author { + font-weight: bold; + float: left; + margin-top: 3px; +} + diff --git a/korektury/static/korektury/opraf.js b/korektury/static/korektury/opraf.js new file mode 100644 index 00000000..d39c0797 --- /dev/null +++ b/korektury/static/korektury/opraf.js @@ -0,0 +1,256 @@ +function place_comments_one_div(img_id, comments) +{ + var img = document.getElementById(img_id); + if( img == null ) { + return; + } + var par = img.parentNode; + var w = img.clientWidth; + var h = img.clientHeight; + var w_skip = 10; + var h_skip = 5; + var pointer_min_h = 30; + + var bott_max = 0; + var comments_sorted = comments.sort(function (a,b) { + return a[2] - b[2]; + //pokus o hezci kladeni poiteru, ale nic moc + if( a[3] < b[3] ) { + return (a[2] + pointer_min_h)- b[2]; + } else { + return (a[2] - pointer_min_h)- b[2]; + } + + }); + //console.log("w:" + w); + for (c in comments_sorted) { + var id = comments_sorted[c][0]; + var x = comments_sorted[c][1]; + var y = comments_sorted[c][2]; + + var el = document.getElementById(id); + var elp = document.getElementById(id + "-pointer"); + + if( el == null || elp == null ) { + continue; + } + + par.appendChild(elp); + par.appendChild(el); + + var delta_y = (y > bott_max) ? 0: bott_max - y + h_skip; + + elp.style.left = x; + elp.style.top = y ; + elp.style.width = w - x + w_skip; + elp.style.height = pointer_min_h + delta_y; + elp.img_id = img_id; + el.img_id = img_id; + + el.style.position = 'absolute'; + el.style.left = w + w_skip; + el.style.top = y + delta_y; + + var bott = el.offsetTop + el.offsetHeight; + bott_max = ( bott_max > bott ) ? bott_max : bott; + + //console.log( "par.w:" + par.style.width); + + } + if( par.offsetHeight < bott_max ) { + //par.style.height = bott_max; + //alert("preteklo to:"+ par.offsetHeight +",mx:" + bott_max ); + par.style.height = bott_max; + + } +} + +// ctrl-enter submits form +function textarea_onkey(ev) +{ + //console.log("ev:" + ev.keyCode + "," + ev.ctrlKey); + if( (ev.keyCode == 13 || ev.keyCode == 10 ) && ev.ctrlKey ) { + var form = document.getElementById('commform'); + if( form ) { + save_scroll(form); + //form.action =''; + form.submit(); + } + return true; + } + return false; +} + +//hide comment form +function close_commform() { + var formdiv = document.getElementById('commform-div'); + if( formdiv == null ) { + alert("form null"); + return true; + } + formdiv.style.display = 'none'; + return false; +} + +// show comment form, when clicked to image +function img_click(element, ev) { + var body_class = document.body.className; + switch(body_class){ + case "comitting": + if (!confirm("Právě jsou zanášeny korektury, opravdu chcete přidat novou?")) + return; + break; + case "deprecated": + if (!confirm("Toto PDF je již zastaralé, opravdu chcete vytvořit korekturu?")) + return; + break; + } + + var dx, dy; + var par = element.parentNode; + if( ev.pageX != null ) { + dx = ev.pageX - par.offsetLeft; + dy = ev.pageY - par.offsetTop; + } else { //IE + dx = ev.offsetX; + dy = ev.offsetY; + } + var img_id = element.id; + if( element.img_id != null ) { + // click was to '-pointer' + img_id = element.img_id; + } + return show_form(img_id, dx, dy, '', '', '', ''); +} + +// show comment form, when 'edit' or 'comment' button pressed +function box_edit(button, action) +{ + var divbox = button.parentNode.parentNode.parentNode; + var id = divbox.id; + var divpointer = document.getElementById(divbox.id + '-pointer'); + + var text; + if (action == 'update') { + var text_el = document.getElementById(divbox.id + '-text'); + text = text_el.innerHTML.unescapeHTML(); + + } else { + text = ''; + } + + var dx = parseInt(divpointer.style.left); + var dy = parseInt(divpointer.style.top); + //alert('not yet 2:' + text + text_el); // + divpointer.style.top "x" + divpo ); + id = id.substring(2); + return show_form(divbox.img_id, dx, dy, id, text, action); +} + +// show comment form when 'update-comment' button pressed +function update_comment(button) +{ + var divbox = button.parentNode.parentNode.parentNode.parentNode; + var id = divbox.id; + var divpointer = document.getElementById(divbox.id + '-pointer'); + var dx = parseInt(divpointer.style.left); + var dy = parseInt(divpointer.style.top); + + var commentdiv = button.parentNode.parentNode.parentNode; + var id = commentdiv.id.substring(1); + var text = document.getElementById('kt' + id).innerHTML.unescapeHTML(); + + return show_form(divbox.img_id, dx, dy, id, text, 'update-comment'); +} + +//fill up comment form and show him +function show_form(img_id, dx, dy, id, text, action) { + var form = document.getElementById('commform'); + var formdiv = document.getElementById('commform-div'); + var textarea = document.getElementById('commform-text'); + var inputX = document.getElementById('commform-x'); + var inputY = document.getElementById('commform-y'); + var inputImgId = document.getElementById('commform-img-id'); + var inputId = document.getElementById('commform-id'); + var inputAction = document.getElementById('commform-action'); + var img = document.getElementById(img_id); + + if( formdiv == null || textarea == null ) { + alert("form null"); + return 1; + } + + //form.action = "#" + img_id; + + // set hidden values + inputX.value = dx; + inputY.value = dy; + inputImgId.value = img_id; + inputId.value = id; + inputAction.value = action; + textarea.value = text; + + //textarea.value = "dxy:"+ dx + "x" + dy + "\n" + 'id:' + img_id; + + // show form + formdiv.style.display = 'block'; + formdiv.style.left = dx; + formdiv.style.top = dy; + + img.parentNode.appendChild(formdiv); + + textarea.focus(); + + return true; + +} + +function box_onmouseover(box, stat) +{ + var id = box.id; + var pointer = document.getElementById(box.id + '-pointer'); + switch (stat){ + case 'done': + pointer.className = 'pointer-done-hi'; + break; + case 'wontfix': + pointer.className = 'pointer-wontfix-hi'; + break; + default: + pointer.className = 'pointer-hi'; + } +} + +function box_onmouseout(box, stat) +{ + var id = box.id; + var pointer = document.getElementById(box.id + '-pointer'); + switch (stat){ + case 'done': + pointer.className = 'pointer-done'; + break; + case 'wontfix': + pointer.className = 'pointer-wontfix'; + break; + default: + pointer.className = 'pointer'; + } +} + +function save_scroll(form) +{ + //alert('save_scroll:' + document.body.scrollTop); + form.scroll.value = document.body.scrollTop; + //alert('save_scroll:' + form.scroll.value); + return true; +} + + +String.prototype.unescapeHTML = function () { + return( + this.replace(/&/g,'&'). + replace(/>/g,'>'). + replace(/</g,'<'). + replace(/"/g,'"') + ); +}; + diff --git a/korektury/static/korektury/png/.gitignore b/korektury/static/korektury/png/.gitignore new file mode 100644 index 00000000..72e8ffc0 --- /dev/null +++ b/korektury/static/korektury/png/.gitignore @@ -0,0 +1 @@ +* diff --git a/korektury/static/korektury/tmp/.gitignore b/korektury/static/korektury/tmp/.gitignore new file mode 100644 index 00000000..72e8ffc0 --- /dev/null +++ b/korektury/static/korektury/tmp/.gitignore @@ -0,0 +1 @@ +* diff --git a/korektury/templates/korektury/base.html b/korektury/templates/korektury/base.html new file mode 100644 index 00000000..b31d5d47 --- /dev/null +++ b/korektury/templates/korektury/base.html @@ -0,0 +1,9 @@ +{% extends "base.html" %} + +{% block content %} +{# blok do kterého se nacita text, v pripade jinyhc templatu obalit vlastnim blokem #} +{% endblock %} + +{% block title %} +{# blok pro titulek stranky #} +{% endblock %} diff --git a/korektury/templates/korektury/help.html b/korektury/templates/korektury/help.html new file mode 100644 index 00000000..0024848e --- /dev/null +++ b/korektury/templates/korektury/help.html @@ -0,0 +1,47 @@ +{% extends "korektury/base.html" %} +{% load staticfiles %} + +{% block title %} Nápověda ke korigovátku {% endblock title %} + +{% block content %} +

Nápověda ke korigovátku

+

Korigovátko slouží ke korigování PDF souborů. Umožňuje přidávat a komentovat +korektury a označovat je jako zanesené / irelevantní. Rovněž umožňuje o PDF +říci, že jsou právě zanášeny korektury nebo že je zastaralé. +

Použití

+

+Kliknu do PDF tam, kam chci zadat korekturu, napíši text a kliknu na Oprav! +(nebo Ctrl-Enter). +Korektura se zobrazí na pravé straně červeně. Korektura nelze smazat ani +upravit. +

+Pokud chci korekturu okomentovat, kliknu na ikonu , +napíši komentář a kliknu na Oprav! (nebo Ctrl-Enter). Komentář se zobrazí pod +původní korekturou. +

+

Tlačítka u korektury

+ +

Stavy

+

Korektura

+ +

PDF

+ + +{% endblock content %} diff --git a/korektury/templates/korektury/opraf.html b/korektury/templates/korektury/opraf.html new file mode 100644 index 00000000..f840e203 --- /dev/null +++ b/korektury/templates/korektury/opraf.html @@ -0,0 +1,187 @@ +{% load staticfiles %} + + + + + + + Korektury {{pdf.nazev}} + + +

Korektury {{pdf.nazev}}

+ {% if pdf.status = 'zanaseni' %}

Probíhá zanášení korektur, zvažte, zda chcete přidávat nové

{% endif %} + {% if pdf.status = 'zastarale' %}

Toto PDF je již zastaralé, nepřidávejte nové korektury

{% endif %} + {{pdf.komentar}} +
+ Klikni na chybu, napiš komentář | + seznam souborů | + nápověda |          | + hlavní stránka | + wiki | +
+ +
+ +
+ {% csrf_token %} + + + +
+ +
+ + + + + + + +
+ +
+ + {% for i in img_indexes %} +
+ +
+
+ {% endfor %} + + +
+ {% csrf_token %} + + + + Souhlasím se smazáním všech kometářů +
+ +
+

Změnit stav PDF:

+ Aktuální: {{pdf.status}} +
+ +
+ {% csrf_token %} + + + Přidávání korektur +
+ Zanášení korektur +
+ Zastaralé, nekorigovat +
+ +
+ + +
+

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

+
+ + {% for o in opravy %} +
+
+
+ +
+
{{o.autor}}
+
+ +
+ {% csrf_token %} + + + + + + {% if o.komentare %} + + {% else %} + + {% endif %} + + {% if o.status = 'opraveno' or o.status = 'neni_chyba' %} + + {% else %} + + + {% endif %} +
+ + + {% if o.komentare %} + + {% else %} + + {% endif %} + +
+
+
{{o.text}}
+ + {% for k in o.komentare %} +
+
+
+
{{k.autor}}
+
+ +
+ {% csrf_token %} + + + + +
+ + +
+
+
{{k.text}}
+
+ {% endfor %} +
+ {% endfor %} + + + + diff --git a/korektury/templates/korektury/seznam.html b/korektury/templates/korektury/seznam.html new file mode 100644 index 00000000..3e1f01e8 --- /dev/null +++ b/korektury/templates/korektury/seznam.html @@ -0,0 +1,22 @@ +{% extends "korektury/base.html" %} + +{% block content %} + +

+{% block nadpis1a %} +{% block title %} + Korektury +{% endblock title %} +{% endblock nadpis1a %} +

+ + + + +{% endblock content %} diff --git a/korektury/tests.py b/korektury/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/korektury/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/korektury/urls.py b/korektury/urls.py new file mode 100644 index 00000000..ab6024a4 --- /dev/null +++ b/korektury/urls.py @@ -0,0 +1,12 @@ +from django.conf.urls import * # NOQA +from django.conf.urls import patterns, url +from django.contrib.auth.decorators import user_passes_test +from . import views + +staff_member_required = user_passes_test(lambda u: u.is_staff) + +urlpatterns = patterns('', + url(r'^korektury/$', staff_member_required(views.KorekturyListView.as_view()), name='korektury-list'), + url(r'^korektury/(?P\d+)/$', staff_member_required(views.KorekturyView.as_view()), name='korektury'), + url(r'^korektury/help/', staff_member_required(views.KorekturyHelpView.as_view()), name='korektury-help'), +) diff --git a/korektury/views.py b/korektury/views.py new file mode 100644 index 00000000..6b77f61e --- /dev/null +++ b/korektury/views.py @@ -0,0 +1,165 @@ +# -*- coding: utf-8 -*- +from django.shortcuts import render +from django.shortcuts import get_object_or_404, render +from django.http import HttpResponseRedirect +from django.http import HttpResponse +from django.core.urlresolvers import reverse +from django.views import generic +from django.utils.translation import ugettext as _ +from django.http import Http404 +from django.http import HttpResponseRedirect + +from .models import Oprava,Komentar,KorekturovanePDF +from .forms import OpravaForm + +from datetime import timedelta, date, datetime +from itertools import groupby +import tempfile +import subprocess +import shutil +import os +from django.conf import settings +import unicodedata + +class KorekturyHelpView(generic.TemplateView): + template_name = 'korektury/help.html' + +class KorekturyListView(generic.ListView): + model = KorekturovanePDF + template_name = 'korektury/seznam.html' + +### Korektury +class KorekturyView(generic.TemplateView): + model = Oprava + template_name = 'korektury/opraf.html' + form_class = OpravaForm + + def post(self, request, *args, **kwargs): + form = self.form_class(request.POST) + q = request.POST + scroll = q.get('scroll') + autor = q.get('au') + print "autor:" + str(autor) + if not autor: + autor = 'anonym' + if not scroll: + scroll = 0 + + + action = q.get('action') + if (action == u''): # Přidej + x = int(q.get('x')) + y = int(q.get('y')) + text = q.get('txt') + strana = int(q.get('img-id')[4:]) + pdf = KorekturovanePDF.objects.filter(id=q.get('pdf')).first() + + op = Oprava(x=x,y=y, autor=autor, text=text, strana=strana,pdf = pdf) + op.save() + elif (action == u'del'): + id = int(q.get('id')) + op = Oprava.objects.filter(id=id).first() + op.delete() + elif (action == u'update'): + id = int(q.get('id')) + op = Oprava.objects.filter(id=id).first() + text = q.get('txt') + op.autor = autor + op.text = text + op.save() + elif (action == u'undone'): + id = int(q.get('id')) + op = Oprava.objects.filter(id=id).first() + op.status = op.STATUS_K_OPRAVE + op.save() + elif (action == u'done'): + id = int(q.get('id')) + op = Oprava.objects.filter(id=id).first() + op.status = op.STATUS_OPRAVENO + op.save() + elif (action == u'wontfix'): + id = int(q.get('id')) + op = Oprava.objects.filter(id=id).first() + op.status = op.STATUS_NENI_CHYBA + op.save() + elif (action == u'comment'): + id = int(q.get('id')) + op = Oprava.objects.filter(id=id).first() + text = q.get('txt') + kom = Komentar(oprava=op,autor=autor,text=text) + kom.save() + elif (action == u'update-comment'): + id = int(q.get('id')) + kom = Komentar.objects.filter(id=id).first() + text = q.get('txt') + kom.text = text + kom.autor = autor + kom.save() + elif (action == u'del-comment'): + id = int(q.get('id')) + kom = Komentar.objects.filter(id=id).first() + kom.delete() + elif (action == u'delall'): + pdf = KorekturovanePDF.objects.filter(id=q.get('pdf')) + checked = q.get('yes') + if checked: + opravy = Oprava.objects.filter(pdf=pdf) + komentare = Komentar.objects.filter(oprava=opravy) + opravy.delete() + komentare.delete() + elif (action == u'set-state'): + pdf = KorekturovanePDF.objects.get(id=q.get('pdf')) + if (q.get('state') == u'adding'): + pdf.status = pdf.STATUS_PRIDAVANI + elif (q.get('state') == u'comitting'): + pdf.status = pdf.STATUS_ZANASENI + elif (q.get('state') == u'deprecated'): + pdf.status = pdf.STATUS_ZASTARALE + pdf.save() + + + + +# return HttpResponse(u'Keys: %s '%(q.iteitems())) +# return HttpResponse(u'Oprav: %d, akce: %s'%( +# len(Oprava.objects.all()),action)) + +# return HttpResponseRedirect(reverse('korektury')+"?scroll=%s"%(scroll)) + context = self.get_context_data() + context['scroll'] = scroll + context['autor'] = autor + return render(request, 'korektury/opraf.html',context) +# return HttpResponse(u'Oprav: %d,x: %d y: %d, autor: %s, text: %s, strana: %d'%( +# len(Oprava.objects.all()),x,y,autor,text,strana)) + def get_context_data(self, **kwargs): + context = super(KorekturyView,self).get_context_data(**kwargs) + pdf = KorekturovanePDF.objects.filter(id=self.kwargs['pdf']).first() + context['pdf'] = pdf + context['img_name'] = os.path.split(pdf.pdf.path)[1].split('.')[0] + context['img_path'] = settings.KOREKTURY_IMG_DIR + context['img_indexes'] = range(pdf.stran) + context['form_oprava'] = OpravaForm() + opravy = Oprava.objects.filter(pdf=self.kwargs['pdf']) + zasluhy = {} + for o in opravy: + if o.autor in zasluhy: + zasluhy[o.autor]+=1 + else: + zasluhy[o.autor]=1 + o.komentare = o.komentar_set.all() + for k in o.komentare: + if k.autor in zasluhy: + zasluhy[k.autor]+=1 + else: + zasluhy[k.autor]=1 + + strany = opravy.values('strana') + opravy_na_stranu = [{'strana':s['strana'],'op_id':opravy.filter(strana=s['strana'])} for s in strany] + context['opravy_strany'] = opravy_na_stranu + + context['opravy'] = opravy + context['zasluhy'] = zasluhy + return context + def form_valid(self,form): + return super(KorekturyView,self).form_valid(form) + diff --git a/mamweb/settings_common.py b/mamweb/settings_common.py index 67072e59..d3eaabb5 100644 --- a/mamweb/settings_common.py +++ b/mamweb/settings_common.py @@ -42,6 +42,9 @@ STATICFILES_FINDERS = ( 'django.contrib.staticfiles.finders.FileSystemFinder', ) +# Where redirect for login required services +LOGIN_URL = '/admin/login' + # Modules configuration AUTHENTICATION_BACKENDS = ( @@ -117,6 +120,7 @@ INSTALLED_APPS = ( 'mamweb', 'seminar', 'galerie', + 'korektury', # Admin upravy: @@ -236,3 +240,5 @@ LOGGING = { # MaM specific SEMINAR_RESENI_DIR = os.path.join(BASE_DIR, 'media', 'reseni') +KOREKTURY_PDF_DIR = os.path.join(BASE_DIR, 'media', 'korektury','pdf') +KOREKTURY_IMG_DIR = os.path.join(BASE_DIR, 'media', 'korektury','img') diff --git a/mamweb/settings_local.py b/mamweb/settings_local.py index 5ac59cd0..68030cdd 100644 --- a/mamweb/settings_local.py +++ b/mamweb/settings_local.py @@ -35,6 +35,13 @@ DATABASES = { 'NAME': os.path.join(BASE_DIR, 'db-local.sqlite3'), } } +#DATABASES = { +# 'default': { +# 'ENGINE': 'django.db.backends.postgresql_psycopg2', +# 'NAME': 'mam_local', +# 'USER': 'mam', +# }, +#} # LOGGING diff --git a/mamweb/urls.py b/mamweb/urls.py index 999ffc90..223bdae8 100644 --- a/mamweb/urls.py +++ b/mamweb/urls.py @@ -14,6 +14,9 @@ urlpatterns = patterns('', # Seminarova aplikace (ma vlastni podadresare) url(r'^', include('seminar.urls')), + + # Korekturovaci aplikace (ma vlastni podadresare) + url(r'^', include('korektury.urls')), # Comments (interni i verejne) url(r'^comments_dj/', include('django_comments.urls')), diff --git a/seminar/admin.py b/seminar/admin.py index b86d933a..8fd5aa61 100644 --- a/seminar/admin.py +++ b/seminar/admin.py @@ -11,7 +11,7 @@ from django.db import models from django.contrib.auth.models import User -from seminar.models import Skola, Resitel, Rocnik, Cislo, Problem, Reseni, PrilohaReseni, Nastaveni, Soustredeni, Soustredeni_Ucastnici, Novinky, Organizator +from seminar.models import Skola, Resitel, Rocnik, Cislo, Problem, Reseni, PrilohaReseni, Nastaveni, Soustredeni, Soustredeni_Ucastnici, Soustredeni_Organizatori, Novinky, Organizator import autocomplete_light @@ -163,6 +163,19 @@ class Soustredeni_UcastniciInline(admin.TabularInline): qs = super(Soustredeni_UcastniciInline, self).get_queryset(request) return qs.select_related('resitel', 'soustredeni') +class Soustredeni_OrganizatoriInline(admin.TabularInline): + form = autocomplete_light.modelform_factory(Soustredeni_Organizatori, autocomplete_fields=['organizator'], fields=['organizator'],) + model = Soustredeni_Organizatori + fields = ['organizator', 'poznamka', ] + extra = 0 + formfield_overrides = { + models.TextField: {'widget': forms.TextInput}, + } + + def get_queryset(self, request): + qs = super(Soustredeni_OrganizatoriInline, self).get_queryset(request) + return qs.select_related('organizator', 'soustredeni') + ### Resitel class ResitelAdmin(VersionAdmin): @@ -362,7 +375,7 @@ class SoustredeniAdmin(VersionAdmin): (u'Data', {'fields': ['datum_zacatku', 'datum_konce']}), ] list_display = ['rocnik', 'misto', 'datum_zacatku', 'typ', 'exportovat', 'verejne'] - inlines = [Soustredeni_UcastniciInline] + inlines = [Soustredeni_UcastniciInline, Soustredeni_OrganizatoriInline] list_filter = ['typ', 'rocnik'] view_on_site = Soustredeni.verejne_url actions = [ diff --git a/seminar/autocomplete_light_registry.py b/seminar/autocomplete_light_registry.py index c7762054..76b9f6ba 100644 --- a/seminar/autocomplete_light_registry.py +++ b/seminar/autocomplete_light_registry.py @@ -2,7 +2,7 @@ import autocomplete_light -from models import Skola, Resitel, Problem +from models import Skola, Resitel, Problem, Organizator from taggit.models import Tag @@ -64,6 +64,38 @@ class ResitelAutocomplete(autocomplete_light.AutocompleteModelBase): autocomplete_light.register(ResitelAutocomplete) +class OrganizatorAutocomplete(autocomplete_light.AutocompleteModelBase): + + model = Organizator + + search_fields=['user__first_name', 'user__last_name', 'prezdivka'] + + split_words = False + + limit_choices = 15 + + def choice_label(self, organizator): + return u"%s '%s' %s" % (organizator.user.first_name, + organizator.prezdivka, + organizator.user.last_name) + + attrs={ + # This will set the input placeholder attribute: + 'placeholder': u'Organizátor', + # This will set the yourlabs.Autocomplete.minimumCharacters + # options, the naming conversion is handled by jQuery + 'data-autocomplete-minimum-characters': 1, + } + + widget_attrs={ + 'data-widget-maximum-values': 15, + # Enable modern-style widget ! + 'class': 'modern-style', + } + +autocomplete_light.register(OrganizatorAutocomplete) + + class ProblemAutocomplete(autocomplete_light.AutocompleteModelBase): diff --git a/seminar/migrations/0036_add_org_to_soustredeni.py b/seminar/migrations/0036_add_org_to_soustredeni.py new file mode 100644 index 00000000..cd6a2aed --- /dev/null +++ b/seminar/migrations/0036_add_org_to_soustredeni.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('seminar', '0035_django_imagekit'), + ] + + operations = [ + migrations.CreateModel( + name='Soustredeni_Organizatori', + fields=[ + ('id', models.AutoField(serialize=False, primary_key=True)), + ('poznamka', models.TextField(help_text='Neve\u0159ejn\xe1 pozn\xe1mka k \xfa\u010dasti organiz\xe1tora (plain text)', verbose_name='neve\u0159ejn\xe1 pozn\xe1mka', blank=True)), + ('organizator', models.ForeignKey(verbose_name='organiz\xe1tor', to='seminar.Organizator')), + ('soustredeni', models.ForeignKey(verbose_name='soust\u0159ed\u011bn\xed', to='seminar.Soustredeni')), + ], + options={ + 'ordering': ['soustredeni', 'organizator'], + 'db_table': 'seminar_soustredeni_organizatori', + 'verbose_name': '\xda\u010dast organiz\xe1tor\u016f na soust\u0159ed\u011bn\xed', + 'verbose_name_plural': '\xda\u010dasti organiz\xe1tor\u016f na soust\u0159ed\u011bn\xed', + }, + bases=(models.Model,), + ), + migrations.AddField( + model_name='soustredeni', + name='organizatori', + field=models.ManyToManyField(help_text='Seznam organiz\xe1tor\u016f soust\u0159ed\u011bn\xed', to='seminar.Organizator', verbose_name='Organiz\xe1to\u0159i soust\u0159ed\u011bn\xed', through='seminar.Soustredeni_Organizatori'), + preserve_default=True, + ), + ] diff --git a/seminar/models.py b/seminar/models.py index fbb2a9d5..d54a5868 100644 --- a/seminar/models.py +++ b/seminar/models.py @@ -579,6 +579,51 @@ class PrilohaReseni(SeminarModelBase): return force_unicode(self.soubor) +@reversion.register(ignore_duplicate_revisions=True) +@python_2_unicode_compatible +class Organizator(models.Model): + user = models.OneToOneField(settings.AUTH_USER_MODEL, verbose_name='Osoba', + help_text = 'Vyber účet spřažený s organizátorem.') + prezdivka = models.CharField('Přezdívka', max_length = 32, + null = True, blank = True) + organizuje_od_roku = models.IntegerField('Organizuje od roku', + null = True, blank = True) + organizuje_do_roku = models.IntegerField('Organizuje do roku', + null = True, blank = True) + studuje = models.CharField('Studium aj.', max_length = 256, + null = True, blank = True, + help_text="Např. 'Studuje Obecnou fyziku (Bc.), 3. ročník', " + "'Vystudovala Diskrétní modely a algoritmy (Mgr.)' nebo " + "'Přednáší na MFF'") + strucny_popis_organizatora = models.TextField('Stručný popis organizátora', + null = True, blank = True) + foto = ProcessedImageField(verbose_name='Fotografie organizátora', + upload_to='image_organizatori/velke/%Y/', null = True, blank = True, + help_text = 'Vlož fotografii organizátora o libovolné velikosti', + processors=[ + Transpose(Transpose.AUTO), + ResizeToFit(500, 500, upscale=False) + ], + options={'quality': 95}) + foto_male = ImageSpecField(source='foto', + processors=[ + ResizeToFit(200, 200, upscale=False) + ], + options={'quality': 95}) + + def __str__(self): + if self.prezdivka: + return u"%s '%s' %s" % (self.user.first_name, + self.prezdivka, + self.user.last_name) + else: + return u"%s %s" % (self.user.first_name, self.user.last_name) + + class Meta: + verbose_name = 'Organizátor' + verbose_name_plural = 'Organizátoři' + + @reversion.register(ignore_duplicate_revisions=True) @python_2_unicode_compatible class Soustredeni(SeminarModelBase): @@ -608,6 +653,11 @@ class Soustredeni(SeminarModelBase): ucastnici = models.ManyToManyField(Resitel, verbose_name=u'účastníci soustředění', help_text=u'Seznam účastníků soustředění', through='Soustredeni_Ucastnici') + organizatori = models.ManyToManyField(Organizator, + verbose_name=u'Organizátoři soustředění', + help_text=u'Seznam organizátorů soustředění', + through='Soustredeni_Organizatori') + text = models.TextField(u'text k soustředění (HTML)', blank=True, default='') TYP_JARNI = 'jarni' @@ -659,6 +709,30 @@ class Soustredeni_Ucastnici(models.Model): return force_unicode(u'%s na %s' % (self.resitel, self.soustredeni, )) # NOTE: Poteciální DB HOG bez select_related +@reversion.register(ignore_duplicate_revisions=True) +@python_2_unicode_compatible +class Soustredeni_Organizatori(models.Model): + + class Meta: + db_table = 'seminar_soustredeni_organizatori' + verbose_name = u'Účast organizátorů na soustředění' + verbose_name_plural = u'Účasti organizátorů na soustředění' + ordering = ['soustredeni', 'organizator'] + + # Interní ID + id = models.AutoField(primary_key = True) + + organizator = models.ForeignKey(Organizator, verbose_name=u'organizátor') + + soustredeni = models.ForeignKey(Soustredeni, verbose_name=u'soustředění') + + poznamka = models.TextField(u'neveřejná poznámka', blank=True, + help_text=u'Neveřejná poznámka k účasti organizátora (plain text)') + + def __str__(self): + return force_unicode(u'%s na %s' % (self.organizator, self.soustredeni, )) + # NOTE: Poteciální DB HOG bez select_related + @python_2_unicode_compatible class VysledkyBase(SeminarModelBase): @@ -778,41 +852,3 @@ class Novinky(models.Model): verbose_name_plural = 'Novinky' -@reversion.register(ignore_duplicate_revisions=True) -@python_2_unicode_compatible -class Organizator(models.Model): - user = models.OneToOneField(settings.AUTH_USER_MODEL, verbose_name='Osoba', - help_text = 'Vyber účet spřažený s organizátorem.') - prezdivka = models.CharField('Přezdívka', max_length = 32, - null = True, blank = True) - organizuje_od_roku = models.IntegerField('Organizuje od roku', - null = True, blank = True) - organizuje_do_roku = models.IntegerField('Organizuje do roku', - null = True, blank = True) - studuje = models.CharField('Studium aj.', max_length = 256, - null = True, blank = True, - help_text="Např. 'Studuje Obecnou fyziku (Bc.), 3. ročník', " - "'Vystudovala Diskrétní modely a algoritmy (Mgr.)' nebo " - "'Přednáší na MFF'") - strucny_popis_organizatora = models.TextField('Stručný popis organizátora', - null = True, blank = True) - foto = ProcessedImageField(verbose_name='Fotografie organizátora', - upload_to='image_organizatori/velke/%Y/', null = True, blank = True, - help_text = 'Vlož fotografii organizátora o libovolné velikosti', - processors=[ - Transpose(Transpose.AUTO), - ResizeToFit(500, 500, upscale=False) - ], - options={'quality': 95}) - foto_male = ImageSpecField(source='foto', - processors=[ - ResizeToFit(200, 200, upscale=False) - ], - options={'quality': 95}) - - def __str__(self): - return str(self.user) - - class Meta: - verbose_name = 'Organizátor' - verbose_name_plural = 'Organizátoři' diff --git a/seminar/templates/seminar/archiv/problem.html b/seminar/templates/seminar/archiv/problem.html index 67071dea..6fdc5fac 100644 --- a/seminar/templates/seminar/archiv/problem.html +++ b/seminar/templates/seminar/archiv/problem.html @@ -3,7 +3,7 @@ {% load comments %} {% block content %} -
+
{% block problem %} {% endblock %} diff --git a/seminar/templates/seminar/archiv/problem_uloha_tema.html b/seminar/templates/seminar/archiv/problem_uloha_tema.html index 12ee40ac..bd68a6fb 100644 --- a/seminar/templates/seminar/archiv/problem_uloha_tema.html +++ b/seminar/templates/seminar/archiv/problem_uloha_tema.html @@ -8,14 +8,10 @@ {% if problem.cislo_zadani %}

Zadáno v čísle {{ problem.cislo_zadani.kod }}. - {% if problem.cislo_reseni %} -

Řešeno v čísle {{ problem.cislo_reseni.kod }}. - {% endif %} - {% else %} - {# TODO ? #} -

Problém {{ problem.nazev }}

{% endif %} - + {% if problem.cislo_reseni %} +

Řešeno v čísle {{ problem.cislo_reseni.kod }}. + {% endif %}

Zadání

{{ problem.text_zadani |safe }} diff --git a/seminar/templates/seminar/soustredeni/seznam_soustredeni.html b/seminar/templates/seminar/soustredeni/seznam_soustredeni.html index f7bc73a8..4c863fc4 100644 --- a/seminar/templates/seminar/soustredeni/seznam_soustredeni.html +++ b/seminar/templates/seminar/soustredeni/seznam_soustredeni.html @@ -60,16 +60,27 @@ {% autoescape off %}{{soustredeni.text}}{% endautoescape %} {% endif %} {% if user.is_authenticated %} +
{# Účastníci #}

Soustředění se zúčastnili tito účastníci:

-
    +

    {% for i in soustredeni.soustredeni_ucastnici_set.all %} -

  • {{i.resitel}} + {{i.resitel}}{% if forloop.last %}.{% else %},{% endif %} {% empty %} -
  • Nic! + Nic! {% endfor %} -
+

+

Soustředění se účastnili tito organizátoři:

+

+ {% for i in soustredeni.soustredeni_organizatori_set.all %} + {{i.organizator}}{% if forloop.last %}.{% else %},{% endif %} + {% empty %} + Nic! + {% endfor %} +

{% endif %} +
+ {% if not soustredeni.verejne_db and user.is_staff %}
{# class="mam-org-only" #} {% endif %} diff --git a/seminar/views.py b/seminar/views.py index 4bef203a..93945778 100644 --- a/seminar/views.py +++ b/seminar/views.py @@ -1,9 +1,9 @@ # coding:utf-8 from django.shortcuts import get_object_or_404, render -from django.http import HttpResponseRedirect -from django.http import HttpResponse +from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden from django.core.urlresolvers import reverse +from django.core.exceptions import PermissionDenied from django.views import generic from django.utils.translation import ugettext as _ from django.http import Http404 @@ -235,11 +235,14 @@ class ProblemView(generic.DetailView): def get_context_data(self, **kwargs): context = super(ProblemView, self).get_context_data(**kwargs) + if not context['problem'].verejne() and not self.request.user.is_staff: + raise PermissionDenied() if context['problem'].typ == Problem.TYP_RES_CLANEK: context['reseni'] = Reseni.objects.filter(problem=context['problem']).select_related('resitel').order_by('resitel__prijmeni') return context + class RadekVysledkovky(object): pass