Browse Source

Merge remote-tracking branch 'origin/master' into stable

remotes/origin/temata v1.5
Bc. Petr Pecha 9 years ago
parent
commit
3b07e861c0
  1. 3
      .gitignore
  2. 14
      korektury/TODO
  3. 0
      korektury/__init__.py
  4. 16
      korektury/admin.py
  5. 13
      korektury/forms.py
  6. 63
      korektury/migrations/0001_initial.py
  7. 39
      korektury/migrations/0002_auto_20151202_2351.py
  8. 27
      korektury/migrations/0003_auto_20151204_1855.py
  9. 26
      korektury/migrations/0004_auto_20151204_2240.py
  10. 32
      korektury/migrations/0005_auto_20151204_2244.py
  11. 20
      korektury/migrations/0006_oprava_pdf.py
  12. 33
      korektury/migrations/0007_auto_20151225_1237.py
  13. 0
      korektury/migrations/__init__.py
  14. 176
      korektury/models.py
  15. BIN
      korektury/static/korektury/imgs/check.png
  16. BIN
      korektury/static/korektury/imgs/comment.png
  17. BIN
      korektury/static/korektury/imgs/cross.png
  18. BIN
      korektury/static/korektury/imgs/delete-gr.png
  19. BIN
      korektury/static/korektury/imgs/delete.png
  20. BIN
      korektury/static/korektury/imgs/edit-gr.png
  21. BIN
      korektury/static/korektury/imgs/edit.png
  22. BIN
      korektury/static/korektury/imgs/link.png
  23. BIN
      korektury/static/korektury/imgs/next-gr.png
  24. BIN
      korektury/static/korektury/imgs/next.png
  25. BIN
      korektury/static/korektury/imgs/undo.png
  26. 145
      korektury/static/korektury/opraf.css
  27. 256
      korektury/static/korektury/opraf.js
  28. 1
      korektury/static/korektury/png/.gitignore
  29. 1
      korektury/static/korektury/tmp/.gitignore
  30. 9
      korektury/templates/korektury/base.html
  31. 47
      korektury/templates/korektury/help.html
  32. 187
      korektury/templates/korektury/opraf.html
  33. 22
      korektury/templates/korektury/seznam.html
  34. 3
      korektury/tests.py
  35. 12
      korektury/urls.py
  36. 165
      korektury/views.py
  37. 6
      mamweb/settings_common.py
  38. 7
      mamweb/settings_local.py
  39. 3
      mamweb/urls.py
  40. 17
      seminar/admin.py
  41. 34
      seminar/autocomplete_light_registry.py
  42. 36
      seminar/migrations/0036_add_org_to_soustredeni.py
  43. 112
      seminar/models.py
  44. 2
      seminar/templates/seminar/archiv/problem.html
  45. 10
      seminar/templates/seminar/archiv/problem_uloha_tema.html
  46. 19
      seminar/templates/seminar/soustredeni/seznam_soustredeni.html
  47. 7
      seminar/views.py

3
.gitignore

@ -20,3 +20,6 @@
# secrets
/django.secret
# vim tmp files
*~

14
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?

0
korektury/__init__.py

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

13
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)

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

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

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

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

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

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

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

0
korektury/migrations/__init__.py

176
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))

BIN
korektury/static/korektury/imgs/check.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 697 B

BIN
korektury/static/korektury/imgs/comment.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 726 B

BIN
korektury/static/korektury/imgs/cross.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 717 B

BIN
korektury/static/korektury/imgs/delete-gr.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 B

BIN
korektury/static/korektury/imgs/delete.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 500 B

BIN
korektury/static/korektury/imgs/edit-gr.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 973 B

BIN
korektury/static/korektury/imgs/edit.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
korektury/static/korektury/imgs/link.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
korektury/static/korektury/imgs/next-gr.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 881 B

BIN
korektury/static/korektury/imgs/next.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 557 B

BIN
korektury/static/korektury/imgs/undo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 617 B

145
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;
}

256
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(/&amp;/g,'&').
replace(/&gt;/g,'>').
replace(/&lt;/g,'<').
replace(/&quot;/g,'"')
);
};

1
korektury/static/korektury/png/.gitignore

@ -0,0 +1 @@
*

1
korektury/static/korektury/tmp/.gitignore

@ -0,0 +1 @@
*

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

47
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 %}
<h1> Nápověda ke korigovátku</h1>
<p> 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é.
<h2> Použití </h2>
<p>
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.
</p><p>
Pokud chci korekturu okomentovat, kliknu na ikonu <img src="{% static "korektury/imgs/comment.png" %}"/>,
napíši komentář a kliknu na Oprav! (nebo Ctrl-Enter). Komentář se zobrazí pod
původní korekturou.
</p>
<h2> Tlačítka u korektury </h2>
<ul>
<li> <img src="{% static "korektury/imgs/delete.png" %}"/> - smazat korekturu
<li> <img src="{% static "korektury/imgs/check.png" %}"/> - označt koreturu jako zanesenou
<li> <img src="{% static "korektury/imgs/cross.png" %}"/> - označit korekturu jako irelevantní
(není to chyba, nebude zaneseno)
<li> <img src="{% static "korektury/imgs/edit.png" %}"/> - upravit text korektury
<li> <img src="{% static "korektury/imgs/comment.png" %}"/> - okomentovat korekturu
</ul>
<h2> Stavy </h2>
<h3> Korektura </h3>
<ul>
<li> K opravě - zadaná, čeká na zanesení / zahození
<li> Zanesená - zanesená v TeXu
<li> Irelevantní - není to chyba, nebude zanesena
<li> K reakci - vyžaduje reakci od autora <i>(zatím není
implementováno)</i>
</ul>
<h3> PDF </h3>
<ul>
<li> Přidávání - probíhá přidávání korektur
<li> Zanášení - probíhá zanášení korektur do TeXu
<li> Zastaralé - PDF je zastaralé, nepřidávat nové korektury
</ul>
{% endblock content %}

187
korektury/templates/korektury/opraf.html

@ -0,0 +1,187 @@
{% load staticfiles %}
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" type="text/css" media="screen, projection" href="{% static "korektury/opraf.css"%}" />
<script src="{% static "korektury/opraf.js"%}"></script>
<title>Korektury {{pdf.nazev}}</title>
</head>
<body {% if pdf.status = 'zanaseni'%} class="comitting" {% elif pdf.status = 'zastarale' %} class="deprecated" {% endif %}>
<h1>Korektury {{pdf.nazev}}</h1>
{% if pdf.status = 'zanaseni' %} <h2> Probíhá zanášení korektur, zvažte, zda chcete přidávat nové </h2> {% endif %}
{% if pdf.status = 'zastarale' %} <h2> Toto PDF je již zastaralé, nepřidávejte nové korektury </h2> {% endif %}
<i>{{pdf.komentar}}</i>
<br>
<i>Klikni na chybu, napiš komentář</i> |
<a href="../">seznam souborů</a> |
<a href="../help">nápověda</a> |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|
<a href="/">hlavní stránka</a> |
<a href="https://mam.mff.cuni.cz/wiki">wiki</a> |
<hr/>
<div id="commform-div">
<!-- Pridat korekturu / komentar !-->
<form action='' onsubmit='save_scroll(this)' id="commform" method="POST">
{% csrf_token %}
<input size="8" name="au" value="{{user.username}}"/>
<input type=submit value="Oprav!"/>
<button type="button" onclick="close_commform()">Zavřít</button>
<br/>
<textarea onkeypress="textarea_onkey(event);" id="commform-text" cols=40 rows=10 name="txt"></textarea>
<br/>
<input type="hidden" size="3" name="pdf" value='{{pdf.id}}'/>
<input type="hidden" size="3" id="commform-x" name="x"/>
<input type="hidden" size="3" id="commform-y" name="y"/>
<input type="hidden" size="3" id="commform-img-id" name="img-id"/>
<input type="hidden" size="3" id="commform-id" name="id"/>
<input type="hidden" size="3" id="commform-action" name="action"/>
<input type="hidden" size="3" id="commform-action" name="scroll"/>
</form>
<!-- /Pridat korekturu / komentar !-->
</div>
{% for i in img_indexes %}
<div class='imgdiv'>
<img width='1021' height='1448'
onclick='img_click(this,event)' id='img-{{i}}'
src='/media/korektury/img/{{img_name}}-{{i}}.png'/>
</div>
<hr/>
{% endfor %}
<!-- Smazat vsechny komentare !-->
<form method="post">
{% csrf_token %}
<input type='hidden' name='action' value='delall'/>
<input type='submit' value='Smazat všechny komentáře'/>
<input type='hidden' name='pdf' value='{{pdf.id}}'/>
<input type='checkbox' name='yes'/> Souhlasím se smazáním všech kometářů
</form>
<!-- /Smazat vsechny komentare !-->
<hr/>
<h4>Změnit stav PDF:</h4>
<i>Aktuální: {{pdf.status}}</i>
<br>
<!-- Zmenit stav PDF !-->
<form method="post">
{% csrf_token %}
<input type='hidden' name='action' value='set-state'/>
<input type='hidden' name='pdf' value='{{pdf.id}}'/>
<input type="radio" name="state" value="adding" {% if pdf.status = 'pridavani' %} checked {% endif %}>Přidávání korektur
<br>
<input type="radio" name="state" value="comitting" {% if pdf.status = 'zanaseni' %} checked {% endif %}>Zanášení korektur
<br>
<input type="radio" name="state" value="deprecated" {% if pdf.status = 'zastarale' %} checked {% endif %}>Zastaralé, nekorigovat
<br>
<input type='submit' value='Změnit stav PDF'/>
</form>
<!-- /Zmenit stav PDF !-->
<hr/>
<p>
Děkujeme opravovatelům: {% for autor,pocet in zasluhy.items %} {{autor}}({{pocet}}) {% endfor %}</p>
<hr>
{% for o in opravy %}
<div onclick='img_click(this,event)'
id='op{{o.id}}-pointer'
class='pointer{%if o.status = 'opraveno' %}-done{% elif o.status = 'neni_chyba' %}-wontfix{% endif %}'>
</div>
<div name='op{{o.id}}' id='op{{o.id}}'
class='box{%if o.status = 'opraveno' %}-done{% elif o.status = 'neni_chyba' %}-wontfix{% endif %}'
onmouseover='box_onmouseover(this,{% if o.status = 'opraveno' %}"done"{% elif o.status = 'neni_chyba' %}"wontfix"{%else%}""{% endif %})'
onmouseout='box_onmouseout(this,{% if o.status = 'opraveno' %}"done"{% elif o.status = 'neni_chyba' %}"wontfix"{%else%}""{% endif %})'>
<div class='corr-header'>
<div class='author' id='op{{o.id}}-autor'>{{o.autor}}</div>
<div class='float-right'>
<!-- Existujici korektura !-->
<form action='' onsubmit='save_scroll(this)' method='POST'>
{% csrf_token %}
<input type='hidden' name="au" value="{{o.autor}}"/>
<input type='hidden' name='pdf' value='{{pdf.id}}'>
<input type='hidden' name='id' value='{{o.id}}'>
<input type='hidden' name='scroll'>
{% if o.komentare %}
<button name='action' value='del' type='button'
title="Opravu nelze smazat &ndash; už ji někdo okomentoval">
<img src="{% static "korektury/imgs/delete-gr.png"%}"/>
</button>
{% else %}
<button type='submit' name='action' value='del' title='Smaž opravu'>
<img src="{% static "korektury/imgs/delete.png"%}"/>
</button>
{% endif %}
{% if o.status = 'opraveno' or o.status = 'neni_chyba' %}
<button type='submit' name='action' value='undone' title='Označ jako neopravené'>
<img src="{% static "korektury/imgs/undo.png"%}"/>
</button>
{% else %}
<button type='submit' name='action' value='done' title='Označ jako opravené'>
<img src="{% static "korektury/imgs/check.png"%}"/>
</button>
<button type='submit' name='action' value='wontfix' title='Označ jako irelevantní '>
<img src="{% static "korektury/imgs/cross.png" %}"/>
</button>
{% endif %}
</form>
<!-- /Existujici korektura !-->
{% if o.komentare %}
<button type='button' title="Korekturu nelze upravit &ndash; už ji někdo okomentoval">
<img src="{% static "korektury/imgs/edit-gr.png" %}"/>
</button>
{% else %}
<button type='button' onclick='box_edit(this,"update");' title='Oprav opravu'>
<img src="{% static "korektury/imgs/edit.png" %}"/>
</button>
{% endif %}
<button type='button' onclick='box_edit(this, "comment");' title='Komentovat'>
<img src="{% static "korektury/imgs/comment.png" %}"/>
</button>
</div>
</div>
<div id='op{{o.id}}-text'>{{o.text}}</div>
{% for k in o.komentare %}
<hr>
<div class='comment' id='k{{k.id}}'>
<div class='corr-header'>
<div class='author'>{{k.autor}}</div>
<div class="float-right">
<!-- Komentar !-->
<form action='' onsubmit='save_scroll(this)' method='POST'>
{% csrf_token %}
<input type='hidden' name='pdf' value='{{pdf.id}}'>
<input type='hidden' name='id' value='{{k.id}}'>
<input type='hidden' name='scroll'>
<button type='submit' name='action' value='del-comment' title='Smaž komentář'
onclick='return confirm("Opravdu smazat komentář?")'>
<img src="{% static "korektury/imgs/delete.png" %}"/>
</button>
</form>
<!-- /Komentar !-->
<button type='button' onclick='update_comment(this);' title='Uprav komentář'>
<img src="{% static "korektury/imgs/edit.png"%}"/>
</button>
</div>
</div>
<div id='kt{{k.id}}'>{{k.text}}</div>
</div>
{% endfor %}
</div>
{% endfor %}
<script>
{% for s in opravy_strany %}
place_comments_one_div("img-{{s.strana}}", [{% for o in s.op_id %}["op{{o.id}}",{{o.x}},{{o.y}}],{% endfor %}[]]);
{% endfor %}
{% if scroll %}
window.scrollTo(0,{{scroll}});
{% endif %}
</script>
</body>
</html>

22
korektury/templates/korektury/seznam.html

@ -0,0 +1,22 @@
{% extends "korektury/base.html" %}
{% block content %}
<h1>
{% block nadpis1a %}
{% block title %}
Korektury
{% endblock title %}
{% endblock nadpis1a %}
</h1>
<ul>
{% for pdf in object_list %}
<li> <b>{{ pdf.nazev }}</b> <i>{{pdf.komentar}}</i> <a href="/korektury/{{pdf.id}}">{{pdf.pdf.url}}</a> </li>
{% empty %}
<li> Nejsou žádné dokumenty ke korekturování.
{% endfor %}
</ul>
{% endblock content %}

3
korektury/tests.py

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

12
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<pdf>\d+)/$', staff_member_required(views.KorekturyView.as_view()), name='korektury'),
url(r'^korektury/help/', staff_member_required(views.KorekturyHelpView.as_view()), name='korektury-help'),
)

165
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)

6
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')

7
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

3
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')),

17
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 = [

34
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):

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

112
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'

2
seminar/templates/seminar/archiv/problem.html

@ -3,7 +3,7 @@
{% load comments %}
{% block content %}
<div>
<div {% if not problem.verejne and user.is_staff %}class="mam-org-only"{% endif %}>
{% block problem %}
{% endblock %}

10
seminar/templates/seminar/archiv/problem_uloha_tema.html

@ -8,14 +8,10 @@
</h2>
{% if problem.cislo_zadani %}
<p>Zadáno v čísle <a href='{{ problem.cislo_zadani.verejne_url }}'>{{ problem.cislo_zadani.kod }}</a>.
{% if problem.cislo_reseni %}
<p>Řešeno v čísle <a href='{{ problem.cislo_reseni.verejne_url }}'>{{ problem.cislo_reseni.kod }}</a>.
{% endif %}
{% else %}
{# TODO ? #}
<h2>Problém {{ problem.nazev }}</h2>
{% endif %}
{% if problem.cislo_reseni %}
<p>Řešeno v čísle <a href='{{ problem.cislo_reseni.verejne_url }}'>{{ problem.cislo_reseni.kod }}</a>.
{% endif %}
<h3>Zadání</h3>
{{ problem.text_zadani |safe }}

19
seminar/templates/seminar/soustredeni/seznam_soustredeni.html

@ -60,16 +60,27 @@
{% autoescape off %}{{soustredeni.text}}{% endautoescape %}
{% endif %}
{% if user.is_authenticated %}
<div class="mam-org-only">
{# Účastníci #}
<h3>Soustředění se zúčastnili tito účastníci:</h3>
<ul>
<p>
{% for i in soustredeni.soustredeni_ucastnici_set.all %}
<li>{{i.resitel}}
{{i.resitel}}{% if forloop.last %}.{% else %},{% endif %}
{% empty %}
<li>Nic!
Nic!
{% endfor %}
</ul>
</p>
<h3>Soustředění se účastnili tito organizátoři:</h3>
<p>
{% for i in soustredeni.soustredeni_organizatori_set.all %}
{{i.organizator}}{% if forloop.last %}.{% else %},{% endif %}
{% empty %}
Nic!
{% endfor %}
</p>
{% endif %}
</div>
{% if not soustredeni.verejne_db and user.is_staff %}
</div> {# class="mam-org-only" #}
{% endif %}

7
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

Loading…
Cancel
Save