diff --git a/mamweb/settings_common.py b/mamweb/settings_common.py index c7146455..718d0a3a 100644 --- a/mamweb/settings_common.py +++ b/mamweb/settings_common.py @@ -141,6 +141,7 @@ INSTALLED_APPS = ( 'odevzdavatko', 'vysledkovky', 'personalni', + 'soustredeni', # Admin upravy: diff --git a/mamweb/urls.py b/mamweb/urls.py index ef0aa449..3a0eb5d2 100644 --- a/mamweb/urls.py +++ b/mamweb/urls.py @@ -22,10 +22,13 @@ urlpatterns = [ # Korekturovaci aplikace (ma vlastni podadresare) path('', include('korektury.urls')), - + # Prednaskova aplikace (ma vlastni podadresare) path('', include('prednasky.urls')), + # Soustredkova aplikace (ma vlastni podadresare) + path('', include('soustredeni.urls')), + # Personalni aplikace (ma vlastni podadresare) # (profil, osobní údaje, ..., ne autentizace, viz dále) path('', include('personalni.urls')), diff --git a/seminar/admin.py b/seminar/admin.py index 98a65db2..15e60c28 100644 --- a/seminar/admin.py +++ b/seminar/admin.py @@ -150,41 +150,6 @@ class ResitelInline(admin.TabularInline): model = m.Resitel extra = 1 -class SoustredeniUcastniciInline(admin.TabularInline): - model = m.Soustredeni_Ucastnici - extra = 1 - fields = ['resitel','poznamka'] - autocomplete_fields = ['resitel'] - ordering = ['resitel__osoba__jmeno', 'resitel__osoba__prijmeni'] - formfield_overrides = { - models.TextField: {'widget': widgets.TextInput} - } - - def get_queryset(self,request): - qs = super().get_queryset(request) - return qs.select_related('resitel','soustredeni') - -class SoustredeniOrganizatoriInline(admin.TabularInline): - model = m.Soustredeni.organizatori.through - extra = 1 - fields = ['organizator','poznamka'] - autocomplete_fields = ['organizator'] - ordering = ['organizator__osoba__jmeno','organizator__prijmeni'] - formfield_overrides = { - models.TextField: {'widget': widgets.TextInput} - } - - def get_queryset(self,request): - qs = super().get_queryset(request) - return qs.select_related('organizator', 'soustredeni') - - -@admin.register(m.Soustredeni) -class SoustredeniAdmin(admin.ModelAdmin): - model = m.Soustredeni - inline_type = 'tabular' - inlines = [SoustredeniUcastniciInline, SoustredeniOrganizatoriInline] - admin.site.register(m.Pohadka) admin.site.register(m.Obrazek) diff --git a/seminar/models/__init__.py b/seminar/models/__init__.py index 9694f84c..714e2caa 100644 --- a/seminar/models/__init__.py +++ b/seminar/models/__init__.py @@ -2,3 +2,4 @@ from .models_all import * from .odevzdavatko import * from .base import * from .personalni import * +from .soustredeni import * diff --git a/seminar/models/models_all.py b/seminar/models/models_all.py index cb7c14e7..129b1d1b 100644 --- a/seminar/models/models_all.py +++ b/seminar/models/models_all.py @@ -317,68 +317,6 @@ class Cislo(SeminarModelBase): raise ValidationError({'datum_deadline_soustredeni': "Soustřeďkový deadline musí předcházet finálnímu deadlinu"}) -@reversion.register(ignore_duplicates=True) -class Soustredeni(SeminarModelBase): - - class Meta: - db_table = 'seminar_soustredeni' - verbose_name = 'Soustředění' - verbose_name_plural = 'Soustředění' - ordering = ['-rocnik__rocnik', '-datum_zacatku'] - - # Interní ID - id = models.AutoField(primary_key = True) - - rocnik = models.ForeignKey(Rocnik, verbose_name='ročník', related_name='soustredeni', - on_delete=models.PROTECT) - - datum_zacatku = models.DateField('datum začátku', blank=True, null=True, - help_text='První den soustředění') - - datum_konce = models.DateField('datum konce', blank=True, null=True, - help_text='Poslední den soustředění') - - verejne_db = models.BooleanField('soustředění zveřejněno', db_column='verejne', default=False) - - misto = models.CharField('místo soustředění', max_length=256, blank=True, default='', - help_text='Místo (název obce, volitelně též objektu') - - ucastnici = models.ManyToManyField(pm.Resitel, verbose_name='účastníci soustředění', - help_text='Seznam účastníků soustředění', through='Soustredeni_Ucastnici') - - organizatori = models.ManyToManyField(pm.Organizator, - verbose_name='Organizátoři soustředění', - help_text='Seznam organizátorů soustředění', - through='Soustredeni_Organizatori') - - text = models.TextField('text k soustředění (HTML)', blank=True, default='') - - TYP_JARNI = 'jarni' - TYP_PODZIMNI = 'podzimni' - TYP_VIKEND = 'vikend' - TYP_CHOICES = [ - (TYP_JARNI, 'Jarní soustředění'), - (TYP_PODZIMNI, 'Podzimní soustředění'), - (TYP_VIKEND, 'Víkendový sraz'), - ] - typ = models.CharField('typ akce', max_length=16, choices=TYP_CHOICES, blank=False, default=TYP_PODZIMNI) - - exportovat = models.BooleanField('export do AESOPa', db_column='exportovat', default=False, - help_text='Exportuje se jen podle tohoto flagu (ne veřejnosti)') - - def __str__(self): - return '{} ({})'.format(self.misto, self.datum_zacatku) - - def verejne(self): - return self.verejne_db - verejne.boolean = True - - def verejne_url(self): - #return reverse('seminar_soustredeni', kwargs={'pk': self.id}) - return reverse('seminar_seznam_soustredeni') - - - @reversion.register(ignore_duplicates=True) # Pozor na následující řádek. *Nekrmit, asi kouše!* class Problem(SeminarModelBase,PolymorphicModel): @@ -658,20 +596,6 @@ def aux_generate_filename(self, filename): clean) return os.path.join(datedir, fname) -# Django neumí jednoduše serializovat partial nebo třídu s __call__ -# (https://docs.djangoproject.com/en/1.8/topics/migrations/), -# neprojdou pak migrace. Takže rozlišení funkcí generujících názvy souboru -# podle adresáře řešíme takto. - -## -def generate_filename_konfera(self, filename): - return os.path.join( - settings.SEMINAR_KONFERY_DIR, - aux_generate_filename(self, filename) - ) - -## - class Pohadka(SeminarModelBase): """Kus pohádky před/za úlohou v čísle""" @@ -718,124 +642,6 @@ class Pohadka(SeminarModelBase): # Neexistující *Node nemá smysl aktualizovat. pass -@reversion.register(ignore_duplicates=True) -class Soustredeni_Ucastnici(SeminarModelBase): -# zmena dedicnosti z models.Model na SeminarModelBase, potencialni vznik bugu - - class Meta: - db_table = 'seminar_soustredeni_ucastnici' - verbose_name = 'Účast na soustředění' - verbose_name_plural = 'Účasti na soustředění' - ordering = ['soustredeni', 'resitel'] - - # Interní ID - id = models.AutoField(primary_key = True) - - resitel = models.ForeignKey(pm.Resitel, verbose_name='řešitel', on_delete=models.PROTECT) - - soustredeni = models.ForeignKey(Soustredeni, verbose_name='soustředění', - on_delete=models.PROTECT) - - poznamka = models.TextField('neveřejná poznámka', blank=True, - help_text='Neveřejná poznámka k účasti (plain text)') - - def __str__(self): - return '{} na {}'.format(self.resitel, self.soustredeni) - # NOTE: Poteciální DB HOG bez select_related - -@reversion.register(ignore_duplicates=True) -class Soustredeni_Organizatori(SeminarModelBase): -# zmena dedicnosti z models.Model na SeminarModelBase, potencialni vznik bugu - - class Meta: - db_table = 'seminar_soustredeni_organizatori' - verbose_name = 'Účast organizátorů na soustředění' - verbose_name_plural = 'Účasti organizátorů na soustředění' - ordering = ['soustredeni', 'organizator'] - - # Interní ID - id = models.AutoField(primary_key = True) - - organizator = models.ForeignKey(pm.Organizator, verbose_name='organizátor', - on_delete=models.PROTECT) - - soustredeni = models.ForeignKey(Soustredeni, verbose_name='soustředění', - on_delete=models.PROTECT) - - poznamka = models.TextField('neveřejná poznámka', blank=True, - help_text='Neveřejná poznámka k účasti organizátora (plain text)') - - def __str__(self): - return '{} na {}'.format(self.organizator, self.soustredeni) - # NOTE: Poteciální DB HOG bez select_related - - - -@reversion.register(ignore_duplicates=True) -class Konfera(Problem): - class Meta: - db_table = 'seminar_konfera' - verbose_name = 'Konfera' - verbose_name_plural = 'Konfery' - - anotace = models.TextField('anotace', blank=True, - help_text='Popis, o čem bude konfera.') - - abstrakt = models.TextField('abstrakt', blank=True, - help_text='Abstrakt konfery tak, jak byl uveden ve sborníku') - - # FIXME: Umíme omezit jen na účastníky daného soustřeďka? - ucastnici = models.ManyToManyField(pm.Resitel, verbose_name='účastníci konfery', - help_text='Seznam účastníků konfery', through='Konfery_Ucastnici') - - soustredeni = models.ForeignKey(Soustredeni, verbose_name='soustředění', - related_name='konfery', on_delete = models.SET_NULL, null=True) - - TYP_VELETRH = 'veletrh' - TYP_PREZENTACE = 'prezentace' - TYP_CHOICES = [ - (TYP_VELETRH, 'Veletrh (postery)'), - (TYP_PREZENTACE, 'Prezentace (přednáška)'), - ] - typ_prezentace = models.CharField('typ prezentace', max_length=16, choices=TYP_CHOICES, - blank=False, default=TYP_VELETRH) - - prezentace = models.FileField('prezentace',help_text = 'Prezentace nebo fotka posteru', - upload_to = generate_filename_konfera, blank=True) - - materialy = models.FileField('materialy', - help_text = 'Další materiály ke konfeře zabalené do jednoho souboru', - upload_to = generate_filename_konfera, blank=True) - - def __str__(self): - return "{}: ({})".format(self.nazev, self.soustredeni) - - def cislo_node(self): - return None - - -@reversion.register(ignore_duplicates=True) -class Konfery_Ucastnici(models.Model): - - class Meta: - db_table = 'seminar_konfery_ucastnici' - verbose_name = 'Účast na konfeře' - verbose_name_plural = 'Účasti na konfeře' - ordering = ['konfera', 'resitel'] - - # Interní ID - id = models.AutoField(primary_key = True) - - resitel = models.ForeignKey(pm.Resitel, verbose_name='řešitel', on_delete=models.PROTECT) - - konfera = models.ForeignKey(Konfera, verbose_name='konfera', on_delete=models.CASCADE) - - poznamka = models.TextField('neveřejná poznámka', blank=True, - help_text='Neveřejná poznámka k účasti (plain text)') - - def __str__(self): - return '{} na {}'.format(self.resitel, self.konfera) - # NOTE: Poteciální DB HOG bez select_related class Obrazek(SeminarModelBase): class Meta: diff --git a/seminar/models/soustredeni.py b/seminar/models/soustredeni.py new file mode 100644 index 00000000..06a87ece --- /dev/null +++ b/seminar/models/soustredeni.py @@ -0,0 +1,214 @@ +# -*- coding: utf-8 -*- +import logging +import os + +from django.db import models +from django.urls import reverse +from reversion import revisions as reversion + +from django.conf import settings + +from . import personalni as pm + +from .base import SeminarModelBase +from seminar.models import models_all as am + +logger = logging.getLogger(__name__) + + +@reversion.register(ignore_duplicates=True) +class Soustredeni(SeminarModelBase): + + class Meta: + db_table = 'seminar_soustredeni' + verbose_name = 'Soustředění' + verbose_name_plural = 'Soustředění' + ordering = ['-rocnik__rocnik', '-datum_zacatku'] + + # Interní ID + id = models.AutoField(primary_key = True) + + rocnik = models.ForeignKey(am.Rocnik, verbose_name='ročník', related_name='soustredeni', + on_delete=models.PROTECT) + + datum_zacatku = models.DateField('datum začátku', blank=True, null=True, + help_text='První den soustředění') + + datum_konce = models.DateField('datum konce', blank=True, null=True, + help_text='Poslední den soustředění') + + verejne_db = models.BooleanField('soustředění zveřejněno', db_column='verejne', default=False) + + misto = models.CharField('místo soustředění', max_length=256, blank=True, default='', + help_text='Místo (název obce, volitelně též objektu') + + ucastnici = models.ManyToManyField(pm.Resitel, verbose_name='účastníci soustředění', + help_text='Seznam účastníků soustředění', through='Soustredeni_Ucastnici') + + organizatori = models.ManyToManyField(pm.Organizator, + verbose_name='Organizátoři soustředění', + help_text='Seznam organizátorů soustředění', + through='Soustredeni_Organizatori') + + text = models.TextField('text k soustředění (HTML)', blank=True, default='') + + TYP_JARNI = 'jarni' + TYP_PODZIMNI = 'podzimni' + TYP_VIKEND = 'vikend' + TYP_CHOICES = [ + (TYP_JARNI, 'Jarní soustředění'), + (TYP_PODZIMNI, 'Podzimní soustředění'), + (TYP_VIKEND, 'Víkendový sraz'), + ] + typ = models.CharField('typ akce', max_length=16, choices=TYP_CHOICES, blank=False, default=TYP_PODZIMNI) + + exportovat = models.BooleanField('export do AESOPa', db_column='exportovat', default=False, + help_text='Exportuje se jen podle tohoto flagu (ne veřejnosti)') + + def __str__(self): + return '{} ({})'.format(self.misto, self.datum_zacatku) + + def verejne(self): + return self.verejne_db + verejne.boolean = True + + def verejne_url(self): + #return reverse('seminar_soustredeni', kwargs={'pk': self.id}) + return reverse('seminar_seznam_soustredeni') + + +@reversion.register(ignore_duplicates=True) +class Soustredeni_Ucastnici(SeminarModelBase): +# zmena dedicnosti z models.Model na SeminarModelBase, potencialni vznik bugu + + class Meta: + db_table = 'seminar_soustredeni_ucastnici' + verbose_name = 'Účast na soustředění' + verbose_name_plural = 'Účasti na soustředění' + ordering = ['soustredeni', 'resitel'] + + # Interní ID + id = models.AutoField(primary_key = True) + + resitel = models.ForeignKey(pm.Resitel, verbose_name='řešitel', on_delete=models.PROTECT) + + soustredeni = models.ForeignKey(Soustredeni, verbose_name='soustředění', + on_delete=models.PROTECT) + + poznamka = models.TextField('neveřejná poznámka', blank=True, + help_text='Neveřejná poznámka k účasti (plain text)') + + def __str__(self): + return '{} na {}'.format(self.resitel, self.soustredeni) + # NOTE: Poteciální DB HOG bez select_related + + +@reversion.register(ignore_duplicates=True) +class Soustredeni_Organizatori(SeminarModelBase): +# zmena dedicnosti z models.Model na SeminarModelBase, potencialni vznik bugu + + class Meta: + db_table = 'seminar_soustredeni_organizatori' + verbose_name = 'Účast organizátorů na soustředění' + verbose_name_plural = 'Účasti organizátorů na soustředění' + ordering = ['soustredeni', 'organizator'] + + # Interní ID + id = models.AutoField(primary_key = True) + + organizator = models.ForeignKey(pm.Organizator, verbose_name='organizátor', + on_delete=models.PROTECT) + + soustredeni = models.ForeignKey(Soustredeni, verbose_name='soustředění', + on_delete=models.PROTECT) + + poznamka = models.TextField('neveřejná poznámka', blank=True, + help_text='Neveřejná poznámka k účasti organizátora (plain text)') + + def __str__(self): + return '{} na {}'.format(self.organizator, self.soustredeni) + # NOTE: Poteciální DB HOG bez select_related + + +# FIXME cycle import + + +# Django neumí jednoduše serializovat partial nebo třídu s __call__ +# (https://docs.djangoproject.com/en/1.8/topics/migrations/), +# neprojdou pak migrace. Takže rozlišení funkcí generujících názvy souboru +# podle adresáře řešíme takto. + +## +def generate_filename_konfera(self, filename): + return os.path.join( + settings.SEMINAR_KONFERY_DIR, + am.aux_generate_filename(self, filename) + ) + +## + +@reversion.register(ignore_duplicates=True) +class Konfera(am.Problem): + class Meta: + db_table = 'seminar_konfera' + verbose_name = 'Konfera' + verbose_name_plural = 'Konfery' + + anotace = models.TextField('anotace', blank=True, + help_text='Popis, o čem bude konfera.') + + abstrakt = models.TextField('abstrakt', blank=True, + help_text='Abstrakt konfery tak, jak byl uveden ve sborníku') + + # FIXME: Umíme omezit jen na účastníky daného soustřeďka? + ucastnici = models.ManyToManyField(pm.Resitel, verbose_name='účastníci konfery', + help_text='Seznam účastníků konfery', through='Konfery_Ucastnici') + + soustredeni = models.ForeignKey(Soustredeni, verbose_name='soustředění', + related_name='konfery', on_delete = models.SET_NULL, null=True) + + TYP_VELETRH = 'veletrh' + TYP_PREZENTACE = 'prezentace' + TYP_CHOICES = [ + (TYP_VELETRH, 'Veletrh (postery)'), + (TYP_PREZENTACE, 'Prezentace (přednáška)'), + ] + typ_prezentace = models.CharField('typ prezentace', max_length=16, choices=TYP_CHOICES, + blank=False, default=TYP_VELETRH) + + prezentace = models.FileField('prezentace',help_text = 'Prezentace nebo fotka posteru', + upload_to = generate_filename_konfera, blank=True) + + materialy = models.FileField('materialy', + help_text = 'Další materiály ke konfeře zabalené do jednoho souboru', + upload_to = generate_filename_konfera, blank=True) + + def __str__(self): + return "{}: ({})".format(self.nazev, self.soustredeni) + + def cislo_node(self): + return None + + +@reversion.register(ignore_duplicates=True) +class Konfery_Ucastnici(models.Model): + + class Meta: + db_table = 'seminar_konfery_ucastnici' + verbose_name = 'Účast na konfeře' + verbose_name_plural = 'Účasti na konfeře' + ordering = ['konfera', 'resitel'] + + # Interní ID + id = models.AutoField(primary_key = True) + + resitel = models.ForeignKey(pm.Resitel, verbose_name='řešitel', on_delete=models.PROTECT) + + konfera = models.ForeignKey(Konfera, verbose_name='konfera', on_delete=models.CASCADE) + + poznamka = models.TextField('neveřejná poznámka', blank=True, + help_text='Neveřejná poznámka k účasti (plain text)') + + def __str__(self): + return '{} na {}'.format(self.resitel, self.konfera) + # NOTE: Poteciální DB HOG bez select_related diff --git a/seminar/urls.py b/seminar/urls.py index 8cc333e0..8f6a04e5 100644 --- a/seminar/urls.py +++ b/seminar/urls.py @@ -28,32 +28,6 @@ urlpatterns = [ #path('treenode/sirotcinec/', views.SirotcinecView.as_view(), name='seminar_treenode_sirotcinec'), #path('problem/(?P\d+)/(?P\d+)/', views.PrispevekView.as_view(), name='seminar_problem_prispevek'), - # Soustredeni - path( - 'soustredeni/probehlo/', - views.SoustredeniListView.as_view(), - name='seminar_seznam_soustredeni' - ), - path( - 'soustredeni//seznam_ucastniku', - org_required(views.SoustredeniUcastniciView.as_view()), - name='soustredeni_ucastnici' - ), - path( - 'soustredeni//maily_ucastniku', - org_required(views.SoustredeniMailyUcastnikuView.as_view()), - name='maily_ucastniku' - ), - path( - 'soustredeni//export_ucastniku', - org_required(views.soustredeniUcastniciExportView), - name='soustredeni_ucastnici_export' - ), - path( - 'soustredeni//fotogalerie/', - include('galerie.urls') - ), - # Zadani # path('aktualni/zadani/', views.AktualniZadaniView.as_view(), name='seminar_aktualni_zadani'), # Dočasně ad-hoc jednoduchá věc. path('aktualni/zadani/', views.AktualniZadaniView, name='seminar_aktualni_zadani'), @@ -101,11 +75,6 @@ urlpatterns = [ 'cislo/./odmeny/./', org_required(views.OdmenyView.as_view()), name="seminar_archiv_odmeny"), - path( - 'soustredeni//obalky.pdf', - org_required(views.soustredeniObalkyView), - name='seminar_soustredeni_obalky' - ), re_path(r'^temp/vue/.*$',views.VueTestView.as_view(),name='vue_test_view'), path('temp/image_upload/', views.NahrajObrazekKTreeNoduView.as_view()), diff --git a/seminar/views/views_all.py b/seminar/views/views_all.py index edc0b71a..8dc8c0ee 100644 --- a/seminar/views/views_all.py +++ b/seminar/views/views_all.py @@ -14,7 +14,7 @@ from django.core.exceptions import PermissionDenied import seminar.models as s import seminar.models as m -from seminar.models import Problem, Cislo, Reseni, Nastaveni, Rocnik, Soustredeni, Organizator, Resitel, Novinky, Soustredeni_Ucastnici, Tema, Clanek # Tohle je stare a chceme se toho zbavit. Pouzivejte s.ToCoChci +from seminar.models import Problem, Cislo, Reseni, Nastaveni, Rocnik, Organizator, Resitel, Novinky, Tema, Clanek # Tohle je stare a chceme se toho zbavit. Pouzivejte s.ToCoChci #from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva from seminar import utils, treelib import seminar.forms as f @@ -34,7 +34,6 @@ import os import os.path as op from django.conf import settings import unicodedata -import csv import logging import time @@ -850,53 +849,6 @@ def TitulyView(request, rocnik, cislo): return render(request, 'seminar/archiv/tituly.tex', {'resitele': resitele,'jmenovci':jmenovci},content_type="text/plain") -### Soustredeni - -class SoustredeniListView(generic.ListView): - model = Soustredeni - template_name = 'seminar/soustredeni/seznam_soustredeni.html' - -def soustredeniObalkyView(request,soustredeni): - soustredeni = get_object_or_404(Soustredeni,id = soustredeni) - return obalkyView(request,soustredeni.ucastnici.all()) - - -class SoustredeniUcastniciBaseView(generic.ListView): - model = Soustredeni_Ucastnici - - def get_queryset(self): - soustredeni = get_object_or_404( - Soustredeni, - pk=self.kwargs["soustredeni"] - ) - return Soustredeni_Ucastnici.objects.filter( - soustredeni=soustredeni).select_related('resitel') - - -class SoustredeniMailyUcastnikuView(SoustredeniUcastniciBaseView): - """ Seznam e-mailů řešitelů oddělených čárkami. """ - model = Soustredeni_Ucastnici - template_name = 'seminar/soustredeni/maily_ucastniku.txt' - - -class SoustredeniUcastniciView(SoustredeniUcastniciBaseView): - """ HTML tabulka účastníků pro tisk. """ - model = Soustredeni_Ucastnici - template_name = 'seminar/soustredeni/seznam_ucastniku.html' - -def soustredeniUcastniciExportView(request,soustredeni): - soustredeni = get_object_or_404(Soustredeni,id = soustredeni) - ucastnici = Resitel.objects.filter(soustredeni=soustredeni) - response = HttpResponse(content_type='text/csv') - response['Content-Disposition'] = 'attachment; filename="ucastnici.csv"' - - writer = csv.writer(response) - writer.writerow(["jmeno", "prijmeni", "rok_maturity", "telefon", "email", "ulice", "mesto", "psc","stat"]) - for u in ucastnici: - o = u.osoba - writer.writerow([o.jmeno, o.prijmeni, str(u.rok_maturity), o.telefon, o.email, o.ulice, o.mesto, o.psc, o.stat.name]) - return response - ### Články def group_by_rocnik(clanky): diff --git a/soustredeni/__init__.py b/soustredeni/__init__.py new file mode 100644 index 00000000..a9f1f263 --- /dev/null +++ b/soustredeni/__init__.py @@ -0,0 +1,5 @@ +""" +Obsahuje vše (až na přednášky) ohledně soustředění. + +TODO stvrzenky? +""" \ No newline at end of file diff --git a/soustredeni/admin.py b/soustredeni/admin.py new file mode 100644 index 00000000..11cb8d1d --- /dev/null +++ b/soustredeni/admin.py @@ -0,0 +1,43 @@ +from django.contrib import admin +from django.forms import widgets +from django.db import models + +from seminar.models import soustredeni as m + + +class SoustredeniUcastniciInline(admin.TabularInline): + model = m.Soustredeni_Ucastnici + extra = 1 + fields = ['resitel','poznamka'] + autocomplete_fields = ['resitel'] + ordering = ['resitel__osoba__jmeno', 'resitel__osoba__prijmeni'] + formfield_overrides = { + models.TextField: {'widget': widgets.TextInput} + } + + def get_queryset(self,request): + qs = super().get_queryset(request) + return qs.select_related('resitel','soustredeni') + + +class SoustredeniOrganizatoriInline(admin.TabularInline): + model = m.Soustredeni.organizatori.through + extra = 1 + fields = ['organizator','poznamka'] + autocomplete_fields = ['organizator'] + ordering = ['organizator__osoba__jmeno','organizator__prijmeni'] + formfield_overrides = { + models.TextField: {'widget': widgets.TextInput} + } + + def get_queryset(self,request): + qs = super().get_queryset(request) + return qs.select_related('organizator', 'soustredeni') + + +@admin.register(m.Soustredeni) +class SoustredeniAdmin(admin.ModelAdmin): + model = m.Soustredeni + inline_type = 'tabular' + inlines = [SoustredeniUcastniciInline, SoustredeniOrganizatoriInline] + diff --git a/soustredeni/apps.py b/soustredeni/apps.py new file mode 100644 index 00000000..5d282341 --- /dev/null +++ b/soustredeni/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class SoustredeniConfig(AppConfig): + name = 'soustredeni' diff --git a/soustredeni/migrations/__init__.py b/soustredeni/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/seminar/templates/seminar/soustredeni/maily_ucastniku.txt b/soustredeni/templates/soustredeni/maily_ucastniku.txt similarity index 100% rename from seminar/templates/seminar/soustredeni/maily_ucastniku.txt rename to soustredeni/templates/soustredeni/maily_ucastniku.txt diff --git a/seminar/templates/seminar/soustredeni/seznam_soustredeni.html b/soustredeni/templates/soustredeni/seznam_soustredeni.html similarity index 100% rename from seminar/templates/seminar/soustredeni/seznam_soustredeni.html rename to soustredeni/templates/soustredeni/seznam_soustredeni.html diff --git a/seminar/templates/seminar/soustredeni/seznam_ucastniku.html b/soustredeni/templates/soustredeni/seznam_ucastniku.html similarity index 100% rename from seminar/templates/seminar/soustredeni/seznam_ucastniku.html rename to soustredeni/templates/soustredeni/seznam_ucastniku.html diff --git a/seminar/templates/seminar/soustredeni/ucastnici.tex b/soustredeni/templates/soustredeni/ucastnici.tex similarity index 100% rename from seminar/templates/seminar/soustredeni/ucastnici.tex rename to soustredeni/templates/soustredeni/ucastnici.tex diff --git a/soustredeni/urls.py b/soustredeni/urls.py new file mode 100644 index 00000000..9cbd2e1d --- /dev/null +++ b/soustredeni/urls.py @@ -0,0 +1,35 @@ +from django.urls import path, include +from . import views +from seminar.utils import org_required + +urlpatterns = [ + path( + 'soustredeni/probehlo/', + views.SoustredeniListView.as_view(), + name='seminar_seznam_soustredeni' + ), + path( + 'soustredeni//seznam_ucastniku', + org_required(views.SoustredeniUcastniciView.as_view()), + name='soustredeni_ucastnici' + ), + path( + 'soustredeni//maily_ucastniku', + org_required(views.SoustredeniMailyUcastnikuView.as_view()), + name='maily_ucastniku' + ), + path( + 'soustredeni//export_ucastniku', + org_required(views.soustredeniUcastniciExportView), + name='soustredeni_ucastnici_export' + ), + path( + 'soustredeni//obalky.pdf', + org_required(views.soustredeniObalkyView), + name='seminar_soustredeni_obalky' + ), + path( + 'soustredeni//fotogalerie/', + include('galerie.urls') + ), +] diff --git a/soustredeni/views.py b/soustredeni/views.py new file mode 100644 index 00000000..99ec0b03 --- /dev/null +++ b/soustredeni/views.py @@ -0,0 +1,55 @@ +from django.shortcuts import get_object_or_404 +from django.http import HttpResponse +from django.views import generic +from seminar.models import Soustredeni, Resitel, Soustredeni_Ucastnici # Tohle je stare a chceme se toho zbavit. Pouzivejte s.ToCoChci +import csv + +from seminar.views import obalkyView + + +class SoustredeniListView(generic.ListView): + model = Soustredeni + template_name = 'soustredeni/seznam_soustredeni.html' + + +def soustredeniObalkyView(request, soustredeni): + soustredeni = get_object_or_404(Soustredeni, id=soustredeni) + return obalkyView(request, soustredeni.ucastnici.all()) + + +class SoustredeniUcastniciBaseView(generic.ListView): + model = Soustredeni_Ucastnici + + def get_queryset(self): + soustredeni = get_object_or_404( + Soustredeni, + pk=self.kwargs["soustredeni"] + ) + return Soustredeni_Ucastnici.objects.filter( + soustredeni=soustredeni).select_related('resitel') + + +class SoustredeniMailyUcastnikuView(SoustredeniUcastniciBaseView): + """ Seznam e-mailů řešitelů oddělených čárkami. """ + model = Soustredeni_Ucastnici + template_name = 'soustredeni/maily_ucastniku.txt' + + +class SoustredeniUcastniciView(SoustredeniUcastniciBaseView): + """ HTML tabulka účastníků pro tisk. """ + model = Soustredeni_Ucastnici + template_name = 'soustredeni/seznam_ucastniku.html' + + +def soustredeniUcastniciExportView(request, soustredeni): + soustredeni = get_object_or_404(Soustredeni, id=soustredeni) + ucastnici = Resitel.objects.filter(soustredeni=soustredeni) + response = HttpResponse(content_type='text/csv') + response['Content-Disposition'] = 'attachment; filename="ucastnici.csv"' + + writer = csv.writer(response) + writer.writerow(["jmeno", "prijmeni", "rok_maturity", "telefon", "email", "ulice", "mesto", "psc","stat"]) + for u in ucastnici: + o = u.osoba + writer.writerow([o.jmeno, o.prijmeni, str(u.rok_maturity), o.telefon, o.email, o.ulice, o.mesto, o.psc, o.stat.name]) + return response