diff --git a/Makefile b/Makefile index afe1221d..a397f44b 100644 --- a/Makefile +++ b/Makefile @@ -44,7 +44,7 @@ clean_virtualenv: rm -rf virtualenv-*.tar.gz run: - ./manage.py runserver_plus + ./manage.py runserver test: ./manage.py test -v2 seminar mamweb @@ -93,3 +93,16 @@ deploy_prod: touch mamweb/wsgi.py @echo Done. +# Sync media directory with atrey. Useful for local development with production database +# Does not sync Galerie and CACHE (too huge). +sync_media: + rsync -ave ssh --exclude Galerie --exclude CACHE\ + atrey.karlin.mff.cuni.cz:/akce/MaM/WWW/mamweb-prod/media/ ./media/ +# Downloads and restores production database to local database. PostgreSQL only. +sync_db: + scp atrey.karlin.mff.cuni.cz:`ssh atrey.karlin.mff.cuni.cz 'ls -v /akce/MaM/WWW/backups/mam-prod-*\.pgdump | tail -n 1'` \ + ./last.pgdump + pg_restore -c -d mam -U mam last.pgdump + +# Sync database and media. See above lines +sync: sync_media sync_db diff --git a/korektury/admin.py b/korektury/admin.py index 564e2cd0..58e460d3 100644 --- a/korektury/admin.py +++ b/korektury/admin.py @@ -9,7 +9,7 @@ class KorekturovanePDFAdmin(VersionAdmin): (None, {'fields': ['pdf', 'cas', 'stran', 'nazev', 'komentar']}), # (u'PDF', {'fields': ['pdf']}), ] - list_display = ['pdf', 'cas', 'stran'] + list_display = ['nazev', 'cas', 'stran'] list_filter = [] search_fields = [] diff --git a/korektury/models.py b/korektury/models.py index cccc4a74..311d5e20 100644 --- a/korektury/models.py +++ b/korektury/models.py @@ -78,7 +78,9 @@ class KorekturovanePDF(models.Model): "-density","180x180", "-geometry"," 1024x1448", self.pdf.path+"[%d]"%self.stran, - os.path.join(settings.KOREKTURY_IMG_DIR, "%s-%d.png"%(filename,self.stran))]) + os.path.join(settings.BASE_DIR, "media", + settings.KOREKTURY_IMG_DIR, + "%s-%d.png"%(filename,self.stran))]) if res==1: break self.stran +=1 diff --git a/korektury/static/korektury/opraf.js b/korektury/static/korektury/opraf.js index d79e17ec..a6d6244c 100644 --- a/korektury/static/korektury/opraf.js +++ b/korektury/static/korektury/opraf.js @@ -126,7 +126,7 @@ function img_click(element, ev) { function toggle_visibility(button){ var divbox = button.parentNode.parentNode.parentNode; var id = divbox.id; - var text = document.getElementById(id+'-text'); + var text = document.getElementById(id+'-body'); if (text.style.display == 'none'){ text.style.display = 'block'; }else { diff --git a/korektury/templates/korektury/opraf.html b/korektury/templates/korektury/opraf.html index 6f83a127..83c997ab 100644 --- a/korektury/templates/korektury/opraf.html +++ b/korektury/templates/korektury/opraf.html @@ -150,6 +150,7 @@ +
{{o.text}}
{% for k in o.komentare %} @@ -178,6 +179,7 @@
{{k.text}}
{% endfor %} + {% endfor %} diff --git a/korektury/views.py b/korektury/views.py index 99f71e00..d6d126e4 100644 --- a/korektury/views.py +++ b/korektury/views.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from django.shortcuts import render +from django.shortcuts import get_object_or_404, render from django.views import generic from django.utils.translation import ugettext as _ from django.conf import settings @@ -114,7 +114,7 @@ class KorekturyView(generic.TemplateView): def get_context_data(self, **kwargs): context = super(KorekturyView,self).get_context_data(**kwargs) - pdf = KorekturovanePDF.objects.get(id=self.kwargs['pdf']) + pdf = get_object_or_404(KorekturovanePDF, id=self.kwargs['pdf']) context['pdf'] = pdf context['img_name'] = os.path.split(pdf.pdf.path)[1].split('.')[0] context['img_path'] = settings.KOREKTURY_IMG_DIR diff --git a/mamweb/settings_common.py b/mamweb/settings_common.py index 07cf6d61..a1025b07 100644 --- a/mamweb/settings_common.py +++ b/mamweb/settings_common.py @@ -240,7 +240,8 @@ LOGGING = { # MaM specific -SEMINAR_RESENI_DIR = os.path.join(BASE_DIR, 'media', 'reseni') -SEMINAR_KONFERY_DIR = os.path.join(BASE_DIR, 'media', 'konfery') -KOREKTURY_PDF_DIR = os.path.join(BASE_DIR, 'media', 'korektury','pdf') -KOREKTURY_IMG_DIR = os.path.join(BASE_DIR, 'media', 'korektury','img') +SEMINAR_RESENI_DIR = os.path.join('reseni') +SEMINAR_KONFERY_DIR = os.path.join('konfery') +KOREKTURY_PDF_DIR = os.path.join('korektury','pdf') +KOREKTURY_IMG_DIR = os.path.join('korektury','img') +CISLO_IMG_DIR = os.path.join('cislo','img') diff --git a/mamweb/static/css/mamweb.css b/mamweb/static/css/mamweb.css index a8f48643..0d23829f 100644 --- a/mamweb/static/css/mamweb.css +++ b/mamweb/static/css/mamweb.css @@ -52,7 +52,7 @@ div.content { padding: 15px 30px; } -h2 { +h1 { margin-top: 0px; } @@ -92,6 +92,7 @@ h2 { top: 40px; left: 55px; } +/* #header h1 { font-size: 130%; position: absolute; @@ -99,6 +100,7 @@ h2 { left: 30px; height: 110px; } +*/ #header img.header { margin: -25px 20px 0px 0px; height: 145px; @@ -160,7 +162,7 @@ div.menu li.selected a { z-index: 15px; } -h2 a:hover { +h1 a:hover { text-decoration: none; } @@ -263,7 +265,7 @@ div.zadani_azad_termin { text-align: center; } -#seznam_orgu h2 { +#seznam_orgu h1 { text-align: center; } @@ -495,7 +497,7 @@ div.zadani_azad_termin { margin: 20px auto 0 auto; } -.galerie h2 { +.galerie h1 { text-align: center; } @@ -679,3 +681,7 @@ div.zadani_azad_termin { div.novinka_obrazek { width: 100%; } + +div.org-text { + font-style: italic; +} diff --git a/seminar/admin.py b/seminar/admin.py index e2ec58cd..8d6e758a 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, Soustredeni_Organizatori, Novinky, Organizator +from seminar.models import Skola, Resitel, Rocnik, Cislo, Problem, Reseni, PrilohaReseni, Nastaveni, Soustredeni, Soustredeni_Ucastnici, Soustredeni_Organizatori, Novinky, Organizator, Prispevek, Pohadka from autocomplete_light import shortcuts as autocomplete_light @@ -296,6 +296,39 @@ class ReseniAdmin(VersionAdmin): admin.site.register(Reseni, ReseniAdmin) +### Pohádka + +class PohadkaAdminForm(forms.ModelForm): + class Meta: + model = Pohadka + exclude = [] + autor = UserModelChoiceField(User.objects.filter(is_staff=True)) + uloha = forms.ModelChoiceField(Problem.objects.filter(typ=Problem.TYP_ULOHA)) + +class PohadkaAdmin(VersionAdmin): + form = PohadkaAdminForm + + def get_kod_ulohy(self, obj): + return obj.uloha.kod_v_rocniku() + get_kod_ulohy.short_description = u'Kód úlohy' + + def get_rocnik(self, obj): + return obj.uloha.cislo_zadani.rocnik.rocnik + get_rocnik.short_description = u'Ročník' + + list_display = ['__str__', 'get_rocnik', 'get_kod_ulohy', 'uloha', 'autor', 'timestamp'] + + get_form = get_form_predvypln_autora + +class PohadkaKProblemuInline(admin.TabularInline): + form = PohadkaAdminForm + model = Pohadka + exclude = [] + extra = 0 + +admin.site.register(Pohadka, PohadkaAdmin) + + ### Problem from autocomplete_light.contrib.taggit_field import TaggitField, TaggitWidget @@ -348,7 +381,14 @@ create_modeladmin(ProblemNavrhAdmin, Problem, 'ProblemNavrh', verbose_name=u'Pro class ProblemZadanyAdmin(ProblemAdmin): list_display = ['nazev', 'typ', 'autor', 'opravovatel', 'kod', 'cislo_zadani', 'pocet_reseni', 'verejne'] list_filter = ['typ', 'zamereni', 'cislo_zadani__cislo', 'cislo_zadani__rocnik'] - inlines = [ReseniKProblemuInline] + + def get_inline_instances(self, request, obj=None): + if obj and obj.typ == Problem.TYP_ULOHA: + inlines = [ReseniKProblemuInline, PohadkaKProblemuInline] + else: + inlines = [ReseniKProblemuInline] + + return [inline(self.model, self.admin_site) for inline in inlines] def get_queryset(self, request): qs = super(ProblemZadanyAdmin, self).get_queryset(request) @@ -360,6 +400,24 @@ create_modeladmin(ProblemZadanyAdmin, Problem, 'ProblemZadany', verbose_name=u'P #admin.site.register(Problem, ProblemAdmin) +### Prispevek (k tematkum) + +class PrispevekAdminForm(forms.ModelForm): + text_org = forms.CharField(widget=CKEditorWidget(), required=False, + **field_labels(Prispevek, 'text_org')) + text_resitel = forms.CharField(widget=CKEditorWidget(), required=False, + **field_labels(Prispevek, 'text_resitel')) + + class Meta: + model = Prispevek + exclude = [] + +class PrispevekAdmin(VersionAdmin): + form = PrispevekAdminForm + list_display = ['nazev', 'problem', 'reseni', 'zverejnit'] + +admin.site.register(Prispevek, PrispevekAdmin) + ### Soustredeni class SoustredeniAdminForm(forms.ModelForm): diff --git a/seminar/migrations/0037_prispevek.py b/seminar/migrations/0037_prispevek.py new file mode 100644 index 00000000..fd3df05a --- /dev/null +++ b/seminar/migrations/0037_prispevek.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('seminar', '0036_add_org_to_soustredeni'), + ] + + operations = [ + migrations.CreateModel( + name='Prispevek', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('nazev', models.CharField(max_length=200, verbose_name=b'N\xc3\xa1zev')), + ('text_org', models.TextField(null=True, verbose_name=b'Orgovsk\xc3\xbd text', blank=True)), + ('text_resitel', models.TextField(null=True, verbose_name=b'\xc5\x98e\xc5\xa1itelsk\xc3\xbd text', blank=True)), + ('zverejnit', models.BooleanField(verbose_name=b'Zve\xc5\x99ejnit?')), + ('problem', models.ForeignKey(verbose_name=b'Probl\xc3\xa9m', to='seminar.Problem')), + ('reseni', models.OneToOneField(null=True, blank=True, to='seminar.Reseni', verbose_name=b'\xc5\x98e\xc5\xa1en\xc3\xad')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/seminar/migrations/0038_change_meta_prispevek.py b/seminar/migrations/0038_change_meta_prispevek.py new file mode 100644 index 00000000..5378e57e --- /dev/null +++ b/seminar/migrations/0038_change_meta_prispevek.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('seminar', '0037_prispevek'), + ] + + operations = [ + migrations.AlterModelOptions( + name='prispevek', + options={'verbose_name': 'P\u0159\xedsp\u011bvek k probl\xe9mu', 'verbose_name_plural': 'P\u0159\xedsp\u011bvky k probl\xe9m\u016fm'}, + ), + ] diff --git a/seminar/migrations/0039_pohadka.py b/seminar/migrations/0039_pohadka.py new file mode 100644 index 00000000..800be0e8 --- /dev/null +++ b/seminar/migrations/0039_pohadka.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +import django.utils.timezone +from django.conf import settings + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('seminar', '0038_change_meta_prispevek'), + ] + + operations = [ + migrations.CreateModel( + name='Pohadka', + fields=[ + ('id', models.AutoField(serialize=False, primary_key=True)), + ('text', models.TextField(verbose_name='Text poh\xe1dky')), + ('pred', models.BooleanField(default=True, verbose_name='P\u0159ed \xfalohou')), + ('timestamp', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Vytvo\u0159eno', editable=False, blank=True)), + ('autor', models.ForeignKey(verbose_name=b'Autor poh\xc3\xa1dky', to=settings.AUTH_USER_MODEL)), + ('uloha', models.ForeignKey(related_name='pohadky', verbose_name='\xdaloha', to='seminar.Problem')), + ], + options={ + 'ordering': ['uloha__cislo_zadani', 'uloha__kod', '-pred'], + 'db_table': 'seminar_pohadky', + 'verbose_name': 'Poh\xe1dka', + 'verbose_name_plural': 'Poh\xe1dky', + }, + ), + ] diff --git a/seminar/migrations/0040_pohadka_nepovinny_autor.py b/seminar/migrations/0040_pohadka_nepovinny_autor.py new file mode 100644 index 00000000..092cb578 --- /dev/null +++ b/seminar/migrations/0040_pohadka_nepovinny_autor.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +from django.conf import settings + + +class Migration(migrations.Migration): + + dependencies = [ + ('seminar', '0039_pohadka'), + ] + + operations = [ + migrations.AlterField( + model_name='pohadka', + name='autor', + field=models.ForeignKey(verbose_name=b'Autor poh\xc3\xa1dky', to=settings.AUTH_USER_MODEL, null=True), + ), + ] diff --git a/seminar/migrations/0041_konfery.py b/seminar/migrations/0041_konfery.py new file mode 100644 index 00000000..e0adaa2d --- /dev/null +++ b/seminar/migrations/0041_konfery.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import seminar.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('seminar', '0040_pohadka_nepovinny_autor'), + ] + + operations = [ + migrations.CreateModel( + name='Konfera', + fields=[ + ('id', models.AutoField(serialize=False, primary_key=True)), + ('nazev', models.CharField(help_text='N\xe1zev konfery', max_length=40, verbose_name='n\xe1zev konfery')), + ('popis', models.TextField(help_text='Popis konfery k zobrazen\xed na webu', verbose_name='popis konfery', blank=True)), + ('abstrakt', models.TextField(help_text='Abstrakt konfery tak, jak byl uveden ve sborn\xedku', verbose_name='abstrakt', blank=True)), + ('org_poznamka', models.TextField(help_text='Neve\u0159ejn\xe1 pozn\xe1mka ke konfe\u0159e(plain text)', verbose_name='neve\u0159ejn\xe1 pozn\xe1mka', blank=True)), + ('typ_prezentace', models.CharField(default=b'veletrh', max_length=16, verbose_name='typ prezentace', choices=[(b'veletrh', 'Veletrh (postery)'), (b'prezentace', 'Prezentace (p\u0159edn\xe1\u0161ka)')])), + ('prezentace', models.FileField(help_text='Prezentace nebo fotka posteru', upload_to=seminar.models.generate_filename_konfera, verbose_name='prezentace')), + ('materialy', models.FileField(help_text='Dal\u0161\xed materi\xe1ly ke konfe\u0159e zabalen\xe9 do jednoho souboru', upload_to=seminar.models.generate_filename_konfera, verbose_name='materialy')), + ('organizator', models.ForeignKey(related_name='konfery', on_delete=django.db.models.deletion.SET_NULL, verbose_name='organiz\xe1tor', to='seminar.Organizator', null=True)), + ], + options={ + 'db_table': 'seminar_konfera', + 'verbose_name': 'Konfera', + 'verbose_name_plural': 'Konfery', + }, + ), + migrations.CreateModel( + name='Konfery_Ucastnici', + fields=[ + ('id', models.AutoField(serialize=False, primary_key=True)), + ('poznamka', models.TextField(help_text='Neve\u0159ejn\xe1 pozn\xe1mka k \xfa\u010dasti (plain text)', verbose_name='neve\u0159ejn\xe1 pozn\xe1mka', blank=True)), + ('konfera', models.ForeignKey(verbose_name='konfera', to='seminar.Konfera')), + ('resitel', models.ForeignKey(verbose_name='\u0159e\u0161itel', to='seminar.Resitel')), + ], + options={ + 'ordering': ['konfera', 'resitel'], + 'db_table': 'seminar_konfery_ucastnici', + 'verbose_name': '\xda\u010dast na konfe\u0159e', + 'verbose_name_plural': '\xda\u010dasti na konfe\u0159e', + }, + ), + migrations.AlterField( + model_name='problem', + name='typ', + field=models.CharField(default=b'uloha', max_length=32, verbose_name='typ probl\xe9mu', choices=[(b'uloha', '\xdaloha'), (b'tema', 'T\xe9ma'), (b'serial', 'Seri\xe1l'), (b'konfera', 'Konfera'), (b'org-clanek', 'Organiz\xe1torsk\xfd \u010dl\xe1nek'), (b'res-clanek', '\u0158e\u0161itelsk\xfd \u010dl\xe1nek')]), + ), + migrations.AddField( + model_name='konfera', + name='prispevek', + field=models.ForeignKey(related_name='konfery', on_delete=django.db.models.deletion.SET_NULL, verbose_name='p\u0159\xedsp\u011bvek do \u010d\xedsla', to='seminar.Problem', help_text='\xda\u010dastnick\xfd p\u0159\xedp\u011bvek o konfe\u0159e', null=True), + ), + migrations.AddField( + model_name='konfera', + name='soustredeni', + field=models.ForeignKey(related_name='konfery', on_delete=django.db.models.deletion.SET_NULL, verbose_name='soust\u0159ed\u011bn\xed', to='seminar.Soustredeni', null=True), + ), + migrations.AddField( + model_name='konfera', + name='ucastnici', + field=models.ManyToManyField(help_text='Seznam \xfa\u010dastn\xedk\u016f konfery', to='seminar.Resitel', verbose_name='\xfa\u010dastn\xedci konfery', through='seminar.Konfery_Ucastnici'), + ), + ] diff --git a/seminar/models.py b/seminar/models.py index b617dad4..566b9d8d 100644 --- a/seminar/models.py +++ b/seminar/models.py @@ -16,7 +16,7 @@ from imagekit.processors import ResizeToFit, Transpose from PIL import Image import os -from functools import partial +#from functools import partial from cStringIO import StringIO from django.core.files.base import ContentFile @@ -402,12 +402,14 @@ class Problem(SeminarModelBase): TYP_ULOHA = 'uloha' TYP_TEMA = 'tema' TYP_SERIAL = 'serial' + TYP_KONFERA = 'konfera' TYP_ORG_CLANEK = 'org-clanek' TYP_RES_CLANEK = 'res-clanek' TYP_CHOICES = [ (TYP_ULOHA, u'Úloha'), (TYP_TEMA, u'Téma'), (TYP_SERIAL, u'Seriál'), + (TYP_KONFERA, u'Konfera'), (TYP_ORG_CLANEK, u'Organizátorský článek'), (TYP_RES_CLANEK, u'Řešitelský článek'), ] @@ -545,6 +547,10 @@ class Reseni(SeminarModelBase): # PrilohaReseni method +# TODO vyresit partial, tak aby slo migrovat +#def generate_filename(self, filename, directory): +# Django 1.9 podporuje partial + def generate_filename(self, filename): clean = filename.replace('/','-').replace('\0', '') datedir = timezone.now().strftime('%Y-%m') @@ -553,6 +559,16 @@ def generate_filename(self, filename): clean) return os.path.join(settings.SEMINAR_RESENI_DIR, datedir, fname) +def generate_filename_konfera(self, filename): + clean = filename.replace('/','-').replace('\0', '') + datedir = timezone.now().strftime('%Y-%m') + fname = "%s_%s" % ( + timezone.now().strftime('%Y-%m-%d-%H:%M'), + clean) + return os.path.join(settings.SEMINAR_KONFERY_DIR, datedir, fname) +# TODO vyresit partial tak, aby slo migrovat +# return os.path.join(directory, datedir, fname) + @reversion.register(ignore_duplicate_revisions=True) @python_2_unicode_compatible @@ -580,6 +596,75 @@ class PrilohaReseni(SeminarModelBase): return force_unicode(self.soubor) +@python_2_unicode_compatible +class Pohadka(SeminarModelBase): + u"""Kus pohádky před/za úlohou v čísle""" + + class Meta: + db_table = 'seminar_pohadky' + verbose_name = u'Pohádka' + verbose_name_plural = u'Pohádky' + ordering = ['uloha__cislo_zadani', 'uloha__kod', '-pred'] + + # Interní ID + id = models.AutoField(primary_key=True) + + text = models.TextField(u'Text pohádky') + uloha = models.ForeignKey( + Problem, + verbose_name=u'Úloha', + related_name='pohadky' + ) + + # Kusů pohádky je v čísle obvykle o 1 více, než úloh. Jeden bude za úlohou + # místo před ní. + pred = models.BooleanField(u'Před úlohou', default=True) + + autor = models.ForeignKey( + settings.AUTH_USER_MODEL, + verbose_name="Autor pohádky", + + # Při nahrávání z TeXu není vyplnění vyžadováno, v adminu je + null=True, + blank=False + ) + + timestamp = models.DateTimeField( + u'Vytvořeno', + default=timezone.now, + blank=True, + editable=False + ) + + def __str__(self): + uryvek = self.text if len(self.text) < (50-3) else self.text[:50]+"..." + return force_unicode(uryvek) + + +@reversion.register(ignore_duplicate_revisions=True) +class Prispevek(SeminarModelBase): + problem = models.ForeignKey(Problem, verbose_name='Problém') # TODO autokompleet + nazev = models.CharField('Název', max_length=200) + reseni = models.OneToOneField(Reseni, verbose_name='Řešení', + blank = True, null = True) + text_org = models.TextField('Orgovský text', blank = True, null = True) + text_resitel = models.TextField('Řešitelský text', blank = True, null = True) + zverejnit = models.BooleanField('Zveřejnit?') + + class Meta: + verbose_name = 'Příspěvek k problému' + verbose_name_plural = 'Příspěvky k problémům' + + def __unicode__(self): + if self.reseni: + return force_unicode(self.nazev) + ' (' + \ + force_unicode(self.reseni.resitel) + ') ' + else: + return force_unicode(self.nazev) + ' ' + + @reversion.register(ignore_duplicate_revisions=True) @python_2_unicode_compatible class Organizator(models.Model): @@ -736,69 +821,70 @@ class Soustredeni_Organizatori(models.Model): -#@reversion.register(ignore_duplicate_revisions=True) -#@python_2_unicode_compatible -#class Konfera(models.Model): -# class Meta: -# db_table = 'seminar_konfera' -# verbose_name = u'Konfera' -# verbose_name_plural = u'Konfery' -# # Interní ID -# id = models.AutoField(primary_key = True) -# nazev = models.CharField(u'název konfery', max_length=40, help_text = u'Název konfery') -# popis = models.TextField(u'popis konfery', blank=True, -# help_text=u'Popis konfery k zobrazení na webu') -# abstrakt = models.TextField(u'abstrakt', blank=True, -# help_text=u'Abstrakt konfery tak, jak byl uveden ve sborníku') -# organizator = models.ForeignKey(Organizator, verbose_name=u'organizátor', related_name='konfery', -# on_delete = models.SET_NULL, null=True) -# ucastnici = models.ManyToManyField(Resitel, verbose_name=u'účastníci konfery', -# help_text=u'Seznam účastníků konfery', through='Konfery_Ucastnici') -# soustredeni = models.ForeignKey(Soustredeni, verbose_name=u'soustředění', related_name='konfery', -# on_delete = models.SET_NULL, null=True) -# org_poznamka = models.TextField(u'neveřejná poznámka', blank=True, -# help_text=u'Neveřejná poznámka ke konfeře(plain text)') -# #prispevek #TODO -# TYP_VELETRH = 'veletrh' -# TYP_PREZENTACE = 'prezentace' -# TYP_CHOICES = [ -# (TYP_VELETRH, u'Veletrh (postery)'), -# (TYP_PREZENTACE, u'Prezentace (přednáška)'), -# ] -# typ_prezentace = models.CharField(u'typ prezentace', max_length=16, choices=TYP_CHOICES, blank=False, default=TYP_VELETRH) -# prezentace = models.FileField(u'prezentace',help_text = u'Prezentace nebo fotka posteru', -# upload_to = partial(generate_filename,directory=settings.SEMINAR_KONFERY_DIR)) -# materialy = models.FileField(u'materialy',help_text = u'Další materiály ke konfeře zabalené do jednoho souboru', -# upload_to = partial(generate_filename,directory=settings.SEMINAR_KONFERY_DIR)) -# -# def __str__(self): -# return force_unicode(u"%s: (%s)" % (self.nazev, self.soustredeni)) -# -# -# -#@reversion.register(ignore_duplicate_revisions=True) -#@python_2_unicode_compatible -#class Konfery_Ucastnici(models.Model): -# -# class Meta: -# db_table = 'seminar_konfery_ucastnici' -# verbose_name = u'Účast na konfeře' -# verbose_name_plural = u'Účasti na konfeře' -# ordering = ['konfera', 'resitel'] -# -# # Interní ID -# id = models.AutoField(primary_key = True) -# -# resitel = models.ForeignKey(Resitel, verbose_name=u'řešitel') -# -# konfera = models.ForeignKey(Konfera, verbose_name=u'konfera') -# -# poznamka = models.TextField(u'neveřejná poznámka', blank=True, -# help_text=u'Neveřejná poznámka k účasti (plain text)') -# -# def __str__(self): -# return force_unicode(u'%s na %s' % (self.resitel, self.konfera, )) -# # NOTE: Poteciální DB HOG bez select_related +@reversion.register(ignore_duplicate_revisions=True) +@python_2_unicode_compatible +class Konfera(models.Model): + class Meta: + db_table = 'seminar_konfera' + verbose_name = u'Konfera' + verbose_name_plural = u'Konfery' + # Interní ID + id = models.AutoField(primary_key = True) + nazev = models.CharField(u'název konfery', max_length=40, help_text = u'Název konfery') + popis = models.TextField(u'popis konfery', blank=True, + help_text=u'Popis konfery k zobrazení na webu') + abstrakt = models.TextField(u'abstrakt', blank=True, + help_text=u'Abstrakt konfery tak, jak byl uveden ve sborníku') + organizator = models.ForeignKey(Organizator, verbose_name=u'organizátor', related_name='konfery', + on_delete = models.SET_NULL, null=True) + ucastnici = models.ManyToManyField(Resitel, verbose_name=u'účastníci konfery', + help_text=u'Seznam účastníků konfery', through='Konfery_Ucastnici') + soustredeni = models.ForeignKey(Soustredeni, verbose_name=u'soustředění', related_name='konfery', + on_delete = models.SET_NULL, null=True) + org_poznamka = models.TextField(u'neveřejná poznámka', blank=True, + help_text=u'Neveřejná poznámka ke konfeře(plain text)') + prispevek = models.ForeignKey(Problem, verbose_name=u'příspěvek do čísla', related_name='konfery', + help_text=u'Účastnický přípěvek o konfeře',on_delete = models.SET_NULL, null=True) + TYP_VELETRH = 'veletrh' + TYP_PREZENTACE = 'prezentace' + TYP_CHOICES = [ + (TYP_VELETRH, u'Veletrh (postery)'), + (TYP_PREZENTACE, u'Prezentace (přednáška)'), + ] + typ_prezentace = models.CharField(u'typ prezentace', max_length=16, choices=TYP_CHOICES, blank=False, default=TYP_VELETRH) + prezentace = models.FileField(u'prezentace',help_text = u'Prezentace nebo fotka posteru', + upload_to = generate_filename_konfera) + materialy = models.FileField(u'materialy',help_text = u'Další materiály ke konfeře zabalené do jednoho souboru', + upload_to = generate_filename_konfera) + + def __str__(self): + return force_unicode(u"%s: (%s)" % (self.nazev, self.soustredeni)) + + + +@reversion.register(ignore_duplicate_revisions=True) +@python_2_unicode_compatible +class Konfery_Ucastnici(models.Model): + + class Meta: + db_table = 'seminar_konfery_ucastnici' + verbose_name = u'Účast na konfeře' + verbose_name_plural = u'Účasti na konfeře' + ordering = ['konfera', 'resitel'] + + # Interní ID + id = models.AutoField(primary_key = True) + + resitel = models.ForeignKey(Resitel, verbose_name=u'řešitel') + + konfera = models.ForeignKey(Konfera, verbose_name=u'konfera') + + poznamka = models.TextField(u'neveřejná poznámka', blank=True, + help_text=u'Neveřejná poznámka k účasti (plain text)') + + def __str__(self): + return force_unicode(u'%s na %s' % (self.resitel, self.konfera, )) + # NOTE: Poteciální DB HOG bez select_related @python_2_unicode_compatible diff --git a/seminar/templates/seminar/archiv/cislo.html b/seminar/templates/seminar/archiv/cislo.html index 63038aac..8d6495ab 100644 --- a/seminar/templates/seminar/archiv/cislo.html +++ b/seminar/templates/seminar/archiv/cislo.html @@ -2,34 +2,38 @@ {% block content %}
-

+

{% block nadpis1a %}{% block nadpis1b %} Číslo {{ cislo }} {% endblock %}{% endblock %} -

+ {% if cislo.pdf %}

Číslo v pdf {% endif %}

Ročník {{ cislo.rocnik }} -

Zadané problémy

+

Zadané problémy

-

Řešené problémy

+

Řešené problémy

{% if user.is_staff %}
-

Orgovské odkazy

+

Orgovské odkazy

  • Obálky (PDF)
  • Tituly (TeX)
  • @@ -40,11 +44,11 @@ {% endif %} {% if cislo.verejna_vysledkovka %} -

    Výsledkovka

    +

    Výsledkovka

    {% else %} {% if user.is_staff %}
    -

    Výsledkovka (neveřejná)

    +

    Výsledkovka (neveřejná)

    {% endif %} {% endif %} diff --git a/seminar/templates/seminar/archiv/prispevek.html b/seminar/templates/seminar/archiv/prispevek.html new file mode 100644 index 00000000..fdf2b3f2 --- /dev/null +++ b/seminar/templates/seminar/archiv/prispevek.html @@ -0,0 +1,24 @@ +{% extends "seminar/archiv/base_temata.html" %} + +{% block title %} + {{prispevek.nazev}} +{% endblock title %} + +{% block content %} + {% if not prispevek.zverejnit %}
    {% endif %} +

    {{prispevek.nazev}}

    +

    {{titul}} {{prispevek.reseni.resitel}}

    +

    Příspěvek k tématu {{prispevek.problem.nazev}}

    + {% if prispevek.reseni.body %} +
    ({{prispevek.reseni.body}} b)
    + {% endif %} + + {% if prispevek.text_org %} +
    {% autoescape off %}{{prispevek.text_org}}{% endautoescape %}
    + {% endif %} + + {% if prispevek.text_resitel %} + {% autoescape off %}{{prispevek.text_resitel}}{% endautoescape %} + {% endif %} + {% if not prispevek.zverejnit %}
    {% endif %} +{% endblock content %} diff --git a/seminar/templates/seminar/archiv/problem.html b/seminar/templates/seminar/archiv/problem.html index 6fdc5fac..bc921203 100644 --- a/seminar/templates/seminar/archiv/problem.html +++ b/seminar/templates/seminar/archiv/problem.html @@ -10,10 +10,10 @@ {% if user.is_staff %}
    -

    Text - org

    +

    Text - org

    {{ problem.text_org |safe }} -

    Diskuse - org

    +

    Diskuse - org

    {% render_comment_list for object %} {% render_comment_form for object %} diff --git a/seminar/templates/seminar/archiv/problem_clanek.html b/seminar/templates/seminar/archiv/problem_clanek.html index c7beba99..ec9b4767 100644 --- a/seminar/templates/seminar/archiv/problem_clanek.html +++ b/seminar/templates/seminar/archiv/problem_clanek.html @@ -3,12 +3,12 @@ {% block problem %} {% if problem.cislo_zadani %} -

    +

    {{ problem.nazev_typu }}: {% block nadpis1a %}{% block nadpis1b %} {{ problem.nazev }} {% endblock %}{% endblock %} -

    +
    {% if problem.typ == 'res-clanek' %} @@ -30,7 +30,7 @@ {% else %} {# TODO ? #} -

    Problém {{ problem.nazev }}

    +

    Problém {{ problem.nazev }}

    {% endif %} {{ problem.text_zadani |safe }} diff --git a/seminar/templates/seminar/archiv/problem_uloha_tema.html b/seminar/templates/seminar/archiv/problem_uloha_tema.html index bd68a6fb..df5e97f7 100644 --- a/seminar/templates/seminar/archiv/problem_uloha_tema.html +++ b/seminar/templates/seminar/archiv/problem_uloha_tema.html @@ -1,11 +1,11 @@ {% extends "seminar/archiv/problem.html" %} {% block problem %} -

    +

    {% block nadpis1a %}{% block nadpis1b %} {{ problem.nazev_typu }} {{ problem.kod_v_rocniku }}: {{ problem.nazev }} {{ problem.body_v_zavorce }} {% endblock %}{% endblock %} -

    + {% if problem.cislo_zadani %}

    Zadáno v čísle {{ problem.cislo_zadani.kod }}. {% endif %} @@ -13,10 +13,10 @@

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

    Zadání

    +

    Zadání

    {{ problem.text_zadani |safe }} {% if problem.text_reseni %} -

    Řešení

    +

    Řešení

    {{ problem.text_reseni |safe }} {% endif %} diff --git a/seminar/templates/seminar/archiv/rocnik.html b/seminar/templates/seminar/archiv/rocnik.html index 8fcc01cd..a595f465 100644 --- a/seminar/templates/seminar/archiv/rocnik.html +++ b/seminar/templates/seminar/archiv/rocnik.html @@ -2,11 +2,11 @@ {% block content %}
    -

    +

    {% block nadpis1a %}{% block nadpis1b %} Ročník {{ rocnik.roman }} {% endblock %}{% endblock %} -

    +

    Ročník číslo {{ rocnik.rocnik }} ({{ rocnik.prvni_rok }}/{{ rocnik.druhy_rok }}) @@ -20,7 +20,7 @@

{% if temata_v_rocniku %} -

Témata

+

Témata

    {% for tema in temata_v_rocniku %}
  • {# TODO zprovoznit odkaz až bude na co #}{{ tema.kod_v_rocniku }}: {{ tema.nazev }}{# TODO #} @@ -29,13 +29,13 @@ {% endif %} {% if vysledkovka %} -

    Výsledková listina

    +

    Výsledková listina

    {% include "seminar/vysledkovka_rocnik.html" %} {% endif %} {% if user.is_staff and vysledkovka_s_neverejnymi %}
    -

    Výsledková listina včetně neveřejných bodů

    +

    Výsledková listina včetně neveřejných bodů

    {% with vysledkovka_s_neverejnymi as vysledkovka %} {% include "seminar/vysledkovka_rocnik.html" %} {% endwith %} diff --git a/seminar/templates/seminar/archiv/temata.html b/seminar/templates/seminar/archiv/temata.html index 44e40ef2..21aebb22 100644 --- a/seminar/templates/seminar/archiv/temata.html +++ b/seminar/templates/seminar/archiv/temata.html @@ -1,17 +1,17 @@ {% extends "seminar/archiv/base_temata.html" %} {% block content %} -

    +

    {% block nadpis1a %}{% block nadpis1b %} Archiv témat {% endblock %}{% endblock%} -

    + {% for tema in object_list %} {% with tema.cislo_zadani.rocnik.rocnik as rocnik %} {% ifchanged rocnik %} {% if not forloop.first %}
{% endif %} -

{{ rocnik }}. ročník

+

{{ rocnik }}. ročník

    {% endifchanged %}
  • diff --git a/seminar/templates/seminar/clanky/resitelske_clanky.html b/seminar/templates/seminar/clanky/resitelske_clanky.html index b588c1f2..1e15fcbc 100644 --- a/seminar/templates/seminar/clanky/resitelske_clanky.html +++ b/seminar/templates/seminar/clanky/resitelske_clanky.html @@ -10,17 +10,17 @@ {% block content %} -

    +

    {% block nadpis1a %}{% block nadpis1b %} Řešitelské články {% endblock %}{% endblock %} -

    + {% for clanek in object_list %} {% with clanek.cislo_zadani.rocnik.rocnik as rocnik %} {% ifchanged rocnik %} {% if not forloop.first %}
{% endif %} -

{{ rocnik }}. ročník

+

{{ rocnik }}. ročník

    {% endifchanged %}
  • diff --git a/seminar/templates/seminar/cojemam/organizatori.html b/seminar/templates/seminar/cojemam/organizatori.html index 0111cd20..4f3709a4 100644 --- a/seminar/templates/seminar/cojemam/organizatori.html +++ b/seminar/templates/seminar/cojemam/organizatori.html @@ -24,13 +24,13 @@
    {% endif %} {% for org in object_list %} -

    +

    {{org.user.first_name}} {% if org.prezdivka %} „{{org.prezdivka}}“ {% endif %} {{org.user.last_name}} -

    +
    diff --git a/seminar/templates/seminar/novinky.html b/seminar/templates/seminar/novinky.html index 0ac99356..ccf40831 100644 --- a/seminar/templates/seminar/novinky.html +++ b/seminar/templates/seminar/novinky.html @@ -3,6 +3,9 @@
    {% if not novinka.zverejneno and user.is_staff %}
    + {% endif %} {% if novinka.zverejneno or user.is_staff %} {# datum #} diff --git a/seminar/templates/seminar/pracuje_se.html b/seminar/templates/seminar/pracuje_se.html index 5349707d..f081edea 100644 --- a/seminar/templates/seminar/pracuje_se.html +++ b/seminar/templates/seminar/pracuje_se.html @@ -1,11 +1,11 @@ {% load staticfiles %} {% block content %} -

    +

    {% block nadpis1a %}{% block nadpis1b %} Tady se pracuje {% endblock %}{% endblock %} -

    +

    Na této stránce velmi intenzivně pracujeme. diff --git a/seminar/templates/seminar/soustredeni/seznam_soustredeni.html b/seminar/templates/seminar/soustredeni/seznam_soustredeni.html index 636ad01b..fcbcde26 100644 --- a/seminar/templates/seminar/soustredeni/seznam_soustredeni.html +++ b/seminar/templates/seminar/soustredeni/seznam_soustredeni.html @@ -14,10 +14,6 @@ {% endblock %}{% endblock %} {% block content %} - - Na galeriích ze soustředění a dalších informacích ještě pracujeme. -

    - {# Projdi vsechna soustredeni #} {% for soustredeni in object_list %} {# Kdyz je verejne -> zobraz #} @@ -27,10 +23,10 @@ {% endif %} {# misto soustredeni TODO upravit#} -

    +

    {{soustredeni.get_typ_display}} {{soustredeni.misto}} -

    +
    • {{soustredeni.datum_zacatku}} – {{soustredeni.datum_konce}} @@ -62,7 +58,7 @@ {% if user.is_staff %}
      {# Účastníci #} -

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

      +

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

      {% for i in soustredeni.soustredeni_ucastnici_set.all %} {{i.resitel}}{% if forloop.last %}.{% else %},{% endif %} @@ -70,7 +66,7 @@ Nic! {% endfor %}

      -

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

      +

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

      {% for i in soustredeni.soustredeni_organizatori_set.all %} {{i.organizator}}{% if forloop.last %}.{% else %},{% endif %} diff --git a/seminar/templates/seminar/stare_novinky.html b/seminar/templates/seminar/stare_novinky.html index 821889bf..af4c017d 100644 --- a/seminar/templates/seminar/stare_novinky.html +++ b/seminar/templates/seminar/stare_novinky.html @@ -2,11 +2,11 @@ {% block content %} -

      +

      {% block nadpis1a %}{% block nadpis1b %} Archiv novinek {% endblock %}{% endblock %} -

      + {% include 'seminar/novinky.html' %} diff --git a/seminar/templates/seminar/stav_databaze.html b/seminar/templates/seminar/stav_databaze.html index a8170290..66ea364f 100644 --- a/seminar/templates/seminar/stav_databaze.html +++ b/seminar/templates/seminar/stav_databaze.html @@ -2,13 +2,13 @@ {% block content %}
      -

      +

      {% block nadpis1a %}{% block nadpis1b %} Stav databáze {% endblock %}{% endblock %} -

      + -

      Řešitelé

      +

      Řešitelé

      Řešitelů: {{ resitele |length}} ({{ muzi |length}} mužů, {{ zeny |length}} žen)
      Křestní jména mužů:
      @@ -16,9 +16,9 @@
      Křestní jména žen:

      {% for n, c in jmena_zen.items %}{{ n }} ({{ c }}), {% endfor %} -

      Nastavení

      +

      Nastavení

      -

      Problémy

      +

      Problémy

      {% for p in problemy %}
      {{ p |safe }}
      {% endfor %} diff --git a/seminar/templates/seminar/titulnistrana.html b/seminar/templates/seminar/titulnistrana.html index ae949d7e..2031779a 100644 --- a/seminar/templates/seminar/titulnistrana.html +++ b/seminar/templates/seminar/titulnistrana.html @@ -6,11 +6,11 @@ {% block content %} {# Uvitaci text #} -

      +

      {% block nadpis1a %}{% block nadpis1b %} Vítej! {% endblock %}{% endblock %} -

      +

      M&M je korespondeční seminář. Několikrát do roka zdarma vydáváme časopis a v něm zajímavé podněty k přemýšlení. Ty na ně můžeš reagovat.
      @@ -26,7 +26,7 @@ M&M je korespondeční seminář. Několikrát do roka zdarma vydáváme ča {% endif %} {# Novinky #} -

      Novinky

      +

      Novinky

      {% include 'seminar/novinky.html' %} Archiv novinek @@ -59,7 +59,7 @@ M&M je korespondeční seminář. Několikrát do roka zdarma vydáváme ča -

      Zjistit víc!

      +

      Zjistit víc!

      diff --git a/seminar/templates/seminar/zadani/AktualniVysledkovka.html b/seminar/templates/seminar/zadani/AktualniVysledkovka.html index 8e0898ca..341e7c00 100644 --- a/seminar/templates/seminar/zadani/AktualniVysledkovka.html +++ b/seminar/templates/seminar/zadani/AktualniVysledkovka.html @@ -10,11 +10,11 @@ {% block content %} {% with nastaveni.aktualni_rocnik as rocnik %} -

      +

      {% block nadpis1a %}{% block nadpis1b %} Výsledky {% endblock %}{% endblock %} -

      + {% if vysledkovka %} {% include "seminar/vysledkovka_rocnik.html" %} @@ -24,7 +24,7 @@ {% if user.is_staff and vysledkovka_s_neverejnymi %}
      -

      Výsledky včetně neveřejných

      +

      Výsledky včetně neveřejných

      {% with vysledkovka_s_neverejnymi as vysledkovka %} {% include "seminar/vysledkovka_rocnik.html" %} {% endwith %} diff --git a/seminar/templates/seminar/zadani/AktualniZadani.html b/seminar/templates/seminar/zadani/AktualniZadani.html index a9f84a64..ab870c2b 100644 --- a/seminar/templates/seminar/zadani/AktualniZadani.html +++ b/seminar/templates/seminar/zadani/AktualniZadani.html @@ -32,7 +32,7 @@ {% for sada in jednorazove_problemy %} {# podnadpisy, kdyz neni zakomentuje se nadpis #} {% if not sada %}{% endif %} {# publikace jednotlivych zadani #} {% for problem in sada %} @@ -41,7 +41,7 @@ {% endfor %} {# TODO použít {{problem.kod_v_rocniku}} ? vrací 4.u1 místo 4.1 #} -

      {{problem.cislo_zadani.cislo}}.{{problem.kod}} {{problem.nazev}} {{ problem.body_v_zavorce }}

      +

      {{problem.cislo_zadani.cislo}}.{{problem.kod}} {{problem.nazev}} {{ problem.body_v_zavorce }}

      {% autoescape off %}{{problem.text_zadani}}{% endautoescape %}
      {% endfor %} @@ -51,11 +51,11 @@ {% if user.is_staff and not verejne%}
      {% endif %} {% else %} -

      Aktuálně nejsou zveřejněny žádné úlohy

      +

      Aktuálně nejsou zveřejněny žádné úlohy

      {% endif %} -

      Témata

      +

      Témata

        {% for problem in temata %} {# TODO použít {{problem.kod_v_rocniku}} ? vrací t4 místo 4 #} diff --git a/seminar/templates/seminar/zadani/Temata.html b/seminar/templates/seminar/zadani/Temata.html index d39d2c34..ec700a1f 100644 --- a/seminar/templates/seminar/zadani/Temata.html +++ b/seminar/templates/seminar/zadani/Temata.html @@ -10,11 +10,11 @@ {% block content %} {% with nastaveni.aktualni_rocnik as ar %}
        -

        +

        {% block nadpis1a %}{% block nadpis1b %} Témata {% endblock %}{% endblock %} -

        +

        Témata jsou hlavním obsahem časopisu M&M. Obvykle představují @@ -30,6 +30,7 @@ Letos jsme pro tebe připravili tato témata:

        {% endif %} + {# seznam temat s odkazy doprostred teto stranky #}
          {% for problem in temata %} {# TODO použít {{problem.kod_v_rocniku}} ? vrací t4 místo 4 #} @@ -38,11 +39,35 @@ {% endfor %}
        + {# nahled kazdeho tematu #} {% for problem in temata %} {# TODO použít {{problem.kod_v_rocniku}} ? vrací t4 místo 4 #} -

        Téma {{problem.kod}}: {{problem.nazev}}

        +

        Téma {{problem.kod}}: {{problem.nazev}}

        {% autoescape off %}{{problem.text_zadani}}{% endautoescape %} + {# kdyz je reseni pridej odkaz na samostatnou stranku s tematem #} + {% if problem.text_reseni %} +

        Více

        + {% else %} +

        Stejně

        + {% endif%} + {# prime odkazy na prispevky #} + {% if problem.prispevky %} +

        K tématu přišly a zveřejnili jsme následující příspěvky

        +
        + +
        + {% endif %}
        {% empty %} Aktuálně nejsou zadána žádná témata k řešení. diff --git a/seminar/urls.py b/seminar/urls.py index 545d0d02..9da2858e 100644 --- a/seminar/urls.py +++ b/seminar/urls.py @@ -4,38 +4,49 @@ from django.contrib.auth.decorators import user_passes_test from . import views, export from utils import staff_member_required from prednasky.views import newPrednaska, Prednaska_hotovo +from django.views.generic.base import RedirectView staff_member_required = user_passes_test(lambda u: u.is_staff) urlpatterns = [ # prednasky - url(r'^prednasky/$', newPrednaska), - url(r'^prednasky/hotovo$', Prednaska_hotovo), + #url(r'^prednasky/$', newPrednaska), + #url(r'^prednasky/hotovo$', Prednaska_hotovo), + # REDIRECTy + url(r'^jak-resit/$', RedirectView.as_view(url='/co-je-MaM/jak-resit/')), + + # Organizatori url(r'^co-je-MaM/organizatori/$', views.CojemamOrganizatoriView.as_view(), name='organizatori'), url(r'^co-je-MaM/organizatori/organizovali/$', views.CojemamOrganizatoriStariView.as_view(), name='stari_organizatori'), + # Archiv url(r'^archiv/cisla/$', views.CislaView.as_view()), url(r'^archiv/temata/$', views.ArchivTemataView.as_view()), url(r'^rocnik/(?P\d+)/$', views.RocnikView.as_view(), name='seminar_rocnik'), url(r'^cislo/(?P\d+)\.(?P[0-9-]+)/$', views.CisloView.as_view(), name='seminar_cislo'), url(r'^problem/(?P\d+)/$', views.ProblemView.as_view(), name='seminar_problem'), + url(r'^problem/(?P\d+)/(?P\d+)/$', views.PrispevekView.as_view(), name='seminar_problem_prispevek'), + # Soustredeni url(r'^soustredeni/probehlo/$', views.SoustredeniListView.as_view(), name = 'seminar_seznam_soustredeni'), url(r'^soustredeni/probehlo/(?P\d+)/$', views.SoustredeniView.as_view(), name='seminar_soustredeni'), url(r'^soustredeni/(?P\d+)/fotogalerie/', include('galerie.urls')), + # Zadani url(r'^zadani/aktualni/$', views.AktualniZadaniView, name='seminar_aktualni_zadani'), url(r'^zadani/temata/$', views.ZadaniTemataView, name='seminar_temata'), url(r'^zadani/vysledkova-listina/$', views.ZadaniAktualniVysledkovkaView, name='seminar_vysledky'), url(r'^$', views.TitulniStranaView.as_view(), name='titulni_strana'), url(r'^stare-novinky/$', views.StareNovinkyView.as_view(), name='stare_novinky'), + # Clanky url(r'^clanky/resitel/$', views.ClankyResitelView.as_view(), name='clanky_resitel'), url(r'^clanky/org/$', views.ClankyOrganizatorView.as_view(), name='clanky_organizator'), + # Aesop url(r'^aesop-export/mam-rocnik-(?P\d+)\.csv$', export.ExportRocnikView.as_view(), name='seminar_export_rocnik'), url(r'^aesop-export/mam-sous-(?P[\d-]+)\.csv$', export.ExportSousView.as_view(), name='seminar_export_sous'), url(r'^aesop-export/index.csv$', export.ExportIndexView.as_view(), name='seminar_export_index'), @@ -52,6 +63,11 @@ urlpatterns = [ staff_member_required(views.StavDatabazeView), name='stav_databaze'), url(r'^cislo/(?P\d+).(?P[0-9-]+)/obalkovani$', staff_member_required(views.obalkovaniView), name='seminar_cislo_resitel_obalkovani'), + url(r'^cislo/(?P\d+).(?P[0-9-]+)/tex-download.json$', + staff_member_required(views.texDownloadView), name='seminar_tex_download'), url(r'^soustredeni/(?P\d+)/obalky.pdf', staff_member_required(views.soustredeniObalkyView), name='seminar_soustredeni_obalky'), + + url(r'^tex-upload/login/$', views.LoginView, name='seminar_login'), + url(r'^tex-upload/$', staff_member_required(views.texUploadView), name='seminar_tex_upload'), ] diff --git a/seminar/views.py b/seminar/views.py index 0f4c6e50..aa320a57 100644 --- a/seminar/views.py +++ b/seminar/views.py @@ -1,15 +1,17 @@ # coding:utf-8 from django.shortcuts import get_object_or_404, render -from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden +from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, JsonResponse from django.core.urlresolvers import reverse from django.core.exceptions import PermissionDenied, ObjectDoesNotExist from django.views import generic from django.utils.translation import ugettext as _ from django.http import Http404 from django.db.models import Q +from django.views.decorators.csrf import ensure_csrf_cookie +from django.contrib.auth import authenticate, login -from .models import Problem, Cislo, Reseni, Nastaveni, Rocnik, Soustredeni, Organizator, Resitel, Novinky, Soustredeni_Ucastnici +from .models import Problem, Cislo, Reseni, Nastaveni, Rocnik, Soustredeni, Organizator, Resitel, Novinky, Soustredeni_Ucastnici, Pohadka, Prispevek from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva from . import utils @@ -21,6 +23,10 @@ import shutil import os from django.conf import settings import unicodedata +import json +import traceback +import sys + def verejna_temata(rocnik): """Vrací queryset zveřejněných témat v daném ročníku. @@ -45,9 +51,15 @@ def AktualniZadaniView(request): def ZadaniTemataView(request): nastaveni = get_object_or_404(Nastaveni) + temata = verejna_temata(nastaveni.aktualni_rocnik) + for t in temata: + if request.user.is_staff: + t.prispevky = t.prispevek_set.filter(problem=t) + else: + t.prispevky = t.prispevek_set.filter(problem=t, zverejnit=True) return render(request, 'seminar/zadani/Temata.html', { - 'temata': verejna_temata(nastaveni.aktualni_rocnik) + 'temata': temata, } ) @@ -241,6 +253,45 @@ class ProblemView(generic.DetailView): context['reseni'] = Reseni.objects.filter(problem=context['problem']).select_related('resitel').order_by('resitel__prijmeni') return context +class PrispevekView(generic.DetailView): + model = Prispevek + template_name = 'seminar/archiv/prispevek.html' + + # Vlastni ziskavani objektu z databaze podle prispevku + # pokud je prispevek neverejny zobrazi se jenom orgum + def get_object(self, queryset=None): + if queryset is None: + queryset = self.get_queryset() + problem_arg = self.kwargs.get('pk') + prispevek_arg = self.kwargs.get('prispevek') + queryset = queryset.filter(pk=prispevek_arg) + + try: + obj = queryset.get() + except queryset.model.DoesNotExist: + raise Http404(_("No %(verbose_name)s found matching the query") % + {'verbose_name': queryset.model._meta.verbose_name}) + + if self.request.user.is_staff or obj.zverejnit: + return obj + else: + raise Http404() + + def get_context_data(self, **kwargs): + context = super(PrispevekView, self).get_context_data(**kwargs) + # snaho o ziskani titulu + titul = '' + try: + resitel = context['prispevek'].reseni.resitel + cislo = context['prispevek'].reseni.cislo_body + body = VysledkyKCisluOdjakziva.objects.get(resitel=resitel, + cislo=cislo).body + titul = resitel.get_titul(body) + except: + pass + context['titul'] = titul + return context + class RadekVysledkovky(object): @@ -318,7 +369,7 @@ class CisloView(generic.DetailView): # za každé řešení doplníme k příslušnému řešiteli a úloze body for r in reseni: vysledky_resitele[r.resitel.id].body_ulohy[problem_index[r.problem.id]] = r.body - + context['vysledkovka'] = vysledkovka context['problemy'] = problemy context['v_cisle_zadane'] = v_cisle_zadane @@ -360,9 +411,9 @@ def aktualniResitele(rocnik): return Resitel.objects.filter(rok_maturity__gt = letos.prvni_rok) # # ALERT: pokud nekdo nema vypleny rok maturity, tak neni aktualni, protoze Karel Tesar a jini # return Resitel.objects.filter(Q(rok_maturity__gt = letos.prvni_rok)|Q(rok_maturity = None)) - -# Vraci QuerySet aktivnich resitelu = -# jeste neodmaturovali && + +# Vraci QuerySet aktivnich resitelu = +# jeste neodmaturovali && # (pokud je aktualni cislo mensi nez 3, pak (letos || loni) neco poslali # jinak letos neco poslali) def aktivniResitele(rocnik,cislo): @@ -370,11 +421,11 @@ def aktivniResitele(rocnik,cislo): loni = CisloObalkyStruct() aktualni_resitele = aktualniResitele(rocnik) - + letos.rocnik = Rocnik.objects.get(rocnik = rocnik) loni.rocnik = Rocnik.objects.get(rocnik = int(rocnik)-1) letos.cisla = Cislo.objects.filter(rocnik=letos.rocnik,cislo__lte = cislo) - loni.cisla = Cislo.objects.filter(rocnik=loni.rocnik) + loni.cisla = Cislo.objects.filter(rocnik=loni.rocnik) if int(cislo) > 3: problemy = Problem.objects.filter(cislo_zadani = letos.cisla) else: @@ -382,7 +433,7 @@ def aktivniResitele(rocnik,cislo): resitele = aktualni_resitele.filter(reseni = Reseni.objects.filter(problem=problemy)).distinct() return resitele - + def cisloObalkyView(request,rocnik,cislo): return obalkyView(request,aktivniResitele(rocnik,cislo)) @@ -408,11 +459,22 @@ def obalkovaniView(request, rocnik, cislo): rocnik = Rocnik.objects.get(rocnik=rocnik) cislo = Cislo.objects.get(rocnik=rocnik, cislo=cislo) - reseni = Reseni.objects.filter(cislo_body=cislo) - serazena_reseni = sorted(reseni, key=lambda r: (r.resitel.prijmeni, r.resitel.jmeno, r.problem.typ, r.problem.kod)) + reseni = ( + Reseni.objects.filter(cislo_body=cislo) + .order_by( + 'resitel__prijmeni', + 'resitel__jmeno', + 'problem__typ', + 'problem__kod' + ) + ) problemy = sorted(set(r.problem for r in reseni), key=lambda p: (p.typ, p.kod)) - return render(request, 'seminar/archiv/cislo_obalkovani.html', {'cislo': cislo, 'problemy': problemy, 'reseni': serazena_reseni}) + return render( + request, + 'seminar/archiv/cislo_obalkovani.html', + {'cislo': cislo, 'problemy': problemy, 'reseni': reseni} + ) ### Tituly @@ -438,7 +500,7 @@ def TitulyView(request, rocnik, cislo): else: broken = True - return render(request, 'seminar/archiv/tituly.tex',{'resitele': resitele,'broken':broken}) + return render(request, 'seminar/archiv/tituly.tex',{'resitele': resitele,'broken':broken},content_type="text/plain") ### Soustredeni @@ -487,3 +549,177 @@ def StavDatabazeView(request): 'jmena_muzu': utils.histogram([r.jmeno for r in muzi]), 'jmena_zen': utils.histogram([r.jmeno for r in zeny]), }) + + + +@ensure_csrf_cookie +def LoginView(request): + """Pro přihlášení při nahrávání z texu""" + q = request.POST + # nastavení cookie csrftoken + if not q: + return JsonResponse({"ok": 1}) + + if "username" in q: + username = q["username"] + password = q["password"] + user = authenticate(username=username, password=password) + if user is not None and user.is_staff: + login(request, user) + return JsonResponse({"ok": 1}) + else: + return JsonResponse({"error": "Neplatné uživatelské jméno nebo heslo"}) + + +@ensure_csrf_cookie +def texUploadView(request): + + def uloz_soubory(files, rocnik, cislo): + for filename, f in files: + path = os.path.join( + settings.MEDIA_ROOT, + settings.CISLO_IMG_DIR, + rocnik, + cislo, + filename + ) + + adresar = os.path.dirname(path) + if not os.path.exists(adresar): + os.makedirs(adresar) + + with open(path, "wb+") as fout: + for chunk in f.chunks(): + fout.write(chunk) + + q = request.POST + # nastavení cookie csrftoken + if not q: + return JsonResponse({"ok": 1}) + + # Odchytíme všechny výjimky a traceback pošleme v odpovědi + try: + meta = json.loads(q["meta"]) + html = q["html"] + + if meta["typ"] in ["uloha", "serial", "reseni"]: + + # Uložíme soubory + if meta["typ"] != "reseni": + c = meta["cislo"] + else: + # Řešení má nastavené číslo svojí úlohy, ale obrázky jsou + # ukládány do čísla, kde řešení vyšlo + c = meta["cislo_reseni"] + uloz_soubory(request.FILES.items(), meta["rocnik"], c) + + # Zjistíme typ ukládaného problému + typy = { + "uloha": Problem.TYP_ULOHA, + "serial": Problem.TYP_SERIAL, + "reseni": Problem.TYP_ULOHA, + } + problem_typ = typy[meta["typ"]] + + + # Pokud už problém existuje, vytáhneme jej z db a upravíme + + # Pokud ročník/číslo ještě neexistuje, vyhodí to výjimku -> + # číslo/ročník se musí založit ručně v adminu + rocnik = Rocnik.objects.get(rocnik=meta["rocnik"]) + cislo = Cislo.objects.get(rocnik=rocnik, cislo=meta["cislo"]) + + existujici = Problem.objects.filter( + typ=problem_typ, + stav=Problem.STAV_ZADANY, + cislo_zadani=cislo, + kod=meta["kod"] + ) + + problem = None + if existujici: + problem = existujici[0] + # Jinak vytvoříme nový + else: + problem = Problem( + typ=problem_typ, + stav=Problem.STAV_ZADANY, + kod=meta["kod"], + cislo_zadani=cislo + ) + + if meta["typ"] == "reseni": + problem.text_reseni = html + #při nahrávání řešení už původní zadání atd. neměníme + else: + problem.text_zadani = html + problem.nazev = meta["nazev"] + problem.body = meta["body"] + + problem.save() + + # Vrátíme id dané úlohy, aby se k ní dala případně připojit pohádka + return JsonResponse({"db_id": problem.id}) + + elif meta["typ"] == "pohadka": + uloha = Problem.objects.get(typ=Problem.TYP_ULOHA, pk=meta["uloha"]) + + # Pokud už příslušná pohádka existuje, jen ji upravíme + existujici = Pohadka.objects.filter(uloha=uloha, pred=meta["pred"]) + pohadka = None + if existujici: + pohadka = existujici[0] + else: + pohadka = Pohadka(uloha=uloha, pred=meta["pred"]) + pohadka.text = q["html"] + pohadka.save() + + return JsonResponse({"db_id": pohadka.id}) + + except Exception, e: + # Pošleme zpátky traceback, ať uživatel ví, v čem je problém + tb = "".join(traceback.format_exception(type(e), e, sys.exc_info()[2])) + return JsonResponse({"error": tb}) + + +def texDownloadView(request, rocnik, cislo): + """View posílající JSON se zadanými a řešenými problémy pro založení čísla + """ + cislo = Cislo.objects.get(rocnik__rocnik=rocnik, cislo=cislo) + zadane = Problem.objects.filter( + cislo_zadani=cislo, + stav=Problem.STAV_ZADANY + ) + resene = Problem.objects.filter( + cislo_reseni=cislo, + stav=Problem.STAV_ZADANY, + typ=Problem.TYP_ULOHA + ) + pred_pohadky = Pohadka.objects.filter(uloha__cislo_zadani=cislo, pred=True) + po_pohadky = Pohadka.objects.filter(uloha__cislo_zadani=cislo, pred=False) + + response = { + "zadane": [ + { + "nazev": p.nazev, + "typ": p.typ, + "kod": p.kod, + "body": p.body, + "zadani": p.text_zadani, + "pred_pohadky": [x.text for x in pred_pohadky.filter(uloha=p)], + "po_pohadky": [x.text for x in po_pohadky.filter(uloha=p)], + } for p in zadane + ], + "resene": [ + { + "nazev": p.nazev, + "typ": p.typ, + "kod": p.kod, + "body": p.body, + "zadani": p.text_zadani, + "reseni": p.text_reseni, + "cislo_zadani": p.cislo_zadani.cislo, + } for p in resene + ], + } + return JsonResponse(response)