diff --git a/Schema_new.dia b/Schema_new.dia index f09c0589..c212ec3b 100644 Binary files a/Schema_new.dia and b/Schema_new.dia differ diff --git a/mamweb/static/css/mamweb.css b/mamweb/static/css/mamweb.css index 357b5027..09b870f4 100644 --- a/mamweb/static/css/mamweb.css +++ b/mamweb/static/css/mamweb.css @@ -14,8 +14,31 @@ div.container { margin: auto; } +.org-logged-in div.container { + margin-top: 20px; +} + div.login-bar { background: #6a0043; + color: #f9d59e; + width: 100%; + + position: fixed; + margin-top: -20px; + min-height: 20px; + z-index: 20; + + padding-left: 5px; + padding-right: 5px; +} + +div.login-bar div { + display: inline; +} + +a.login-ref-admin { + display: inline; + color: #fffbf6; } /* odkazy a nadpisy */ @@ -28,7 +51,10 @@ a:focus, a:hover, a:active { color: #e84e10; text-decoration: none; } -a:focus a:hover + +img { + filter: drop-shadow(0px 3px 3px rgba(0, 0, 0, 0.4)); +} h1 { /*todo: odlišit 1 a 2 */ font-size: 200%; @@ -74,16 +100,16 @@ h6 { .org-logged-in .mam-text-plugin { - border: dashed 1px #f77; + border: dashed 1px #6a0043; padding: 5px; margin: -5px; } .mam-org-only { - background: #fff0d7; + background: #eee4ec; padding: 10px; margin: 10px -10px; - border: orange 2px dashed; + border: #6a0043 2px dashed; } .mam-org-only .mam-org-only { @@ -123,6 +149,8 @@ h1 { margin-top: 0px; } + + /* Comments */ #id_comment { @@ -191,7 +219,7 @@ vikendovka #header.NOCsoustredeni { background-image: url("../images/header/vylet.jpg");} #header.NOCzadani { background-image: url("../images/header/stiny.jpg");} #header.NOCclanky { background-image: url("../images/header/ohen.jpg");} -#header.NOCarchiv { background-image: url("../images/header/vikendovka.jpg");} +#header.NOCarchiv { background-image: url("../images/header/stiny.jpg");} #header img.logo { @@ -407,6 +435,10 @@ ul.submenu { /* malý tablet, mobil */ @media (max-width: 650px) { + #hide-if-small.login-bar-flatpage { + display: none; + } + #title { display: none; } @@ -558,13 +590,21 @@ div.seznam_orgu { text-align: center; } -div.org_pole { +div.org_pole, div.rocnik_pole { display: inline-block; width: 30%; min-width: 300px; text-align: center; } +div.cislo_pole { + display: inline-block; + width: 15%; + min-width: 165px; + text-align: center; + padding: 10px; +} + div.seznam_orgu h3 { text-align: center; margin-top: 10px; @@ -576,16 +616,29 @@ div.org_email { font-weight: bold; } -/*otáčecí karty organizátorů*/ +/*otáčecí karty (orgové, archiv) */ .flip-card { - width: 200px; - height: 250px; perspective: 1000px; /* Remove this if you don't want the 3D effect */ margin-left: auto; margin-right: auto; } +#organizatori.flip-card { + width: 200px; + height: 250px; +} + +#archiv.flip-card { + width: 210px; + height: 298px; +} + +#archiv-rocnik.flip-card { + width: 144px; + height: 205px; +} + /* This container is needed to position the front and back side */ .flip-card-inner { position: relative; @@ -614,35 +667,59 @@ div.org_email { background-color: #bbb; } -div.foto_org img { +div.flip-card-foto img { width: 100%; height: 100%; + } /* Style the back side */ .flip-card-back { - /*background-color: #e84e10; - color: #fffbf6; - background-color: #fdedd5;*/ background-color: #f9d59e; - color: black;/**/ + color: black; transform: rotateY(180deg); padding: 10px; padding-top: 20px; } -/* graf na úvodní stránce */ +/* karty archiv */ + +div.popis_rocniku { + text-align: left; + font-weight: bold; + margin: 20px; +} + -a span.popup { - position: absolute; - visibility: hidden; +div.popis_rocniku a, div.cislo_odkazy a { + font-weight: bold; + color: black; +} + +div.popis_rocniku a:hover, +div.cislo_odkazy a:hover { + color: #6f2509; +} + +div.cislo_odkazy ul { + margin: 0px; + padding: 0px; } -a span.popup:hover { - visibility:visible; - top:37px; left:37px; +/* archiv ročník +div.cisla-v-rocniku { + font-weight: bold; + color: #6f2509; } +div.cislo-v-rocniku-blok { + display: inline-block; + width: 150px; + height: 220px; + text-align: center; +}*/ + + /* galerie */ /* velká fotka */ diff --git a/mamweb/templates/base.html b/mamweb/templates/base.html index 18450af0..0d6b0618 100644 --- a/mamweb/templates/base.html +++ b/mamweb/templates/base.html @@ -37,21 +37,21 @@ -
- - {% if user.is_staff %} -
- {% if view.object %} - Objekt {{ view.object }}: {{ view.object }} - {% if view.object.admin_url %}[admin]{% endif %} - {% endif %} - {% if flatpage %} - Stránka {{ flatpage.url }} ({{ flatpage.title }}) - [admin] - {% endif %} -
- {% endif %} + {% if user.is_staff %} +
+ {% if view.object %} + Objekt {{ view.object }}: {{ view.object }} + {% if view.object.admin_url %}{% endif %} + {% endif %} + {% if flatpage %} + + + {% endif %} + +
+ {% endif %} +
diff --git a/seminar/admin.py b/seminar/admin.py index 6a9dd815..edfbb595 100644 --- a/seminar/admin.py +++ b/seminar/admin.py @@ -35,6 +35,7 @@ class ProblemAdmin(PolymorphicParentModelAdmin): m.Tema, m.Clanek, m.Uloha, + m.Konfera, ] @admin.register(m.Tema) @@ -52,6 +53,11 @@ class UlohaAdmin(PolymorphicChildModelAdmin): base_model = m.Uloha show_in_index = True +@admin.register(m.Konfera) +class KonferaAdmin(PolymorphicChildModelAdmin): + base_model = m.Konfera + show_in_index = True + class TextAdminInline(admin.TabularInline): model = m.Text exclude = ['text_zkraceny_set','text_zkraceny'] @@ -82,7 +88,6 @@ class ReseniAdmin(ReverseModelAdmin): admin.site.register(m.Hodnoceni) admin.site.register(m.Pohadka) -admin.site.register(m.Konfera) admin.site.register(m.Obrazek) @@ -97,8 +102,6 @@ class TreeNodeAdmin(PolymorphicParentModelAdmin): m.CisloNode, m.MezicisloNode, m.TemaVCisleNode, - m.KonferaNode, - m.ClanekNode, m.UlohaZadaniNode, m.PohadkaNode, m.UlohaVzorakNode, @@ -136,16 +139,6 @@ class TemaVCisleNodeAdmin(PolymorphicChildModelAdmin): base_model = m.TemaVCisleNode show_in_index = True -@admin.register(m.KonferaNode) -class KonferaNodeAdmin(PolymorphicChildModelAdmin): - base_model = m.KonferaNode - show_in_index = True - -@admin.register(m.ClanekNode) -class ClanekNodeAdmin(PolymorphicChildModelAdmin): - base_model = m.ClanekNode - show_in_index = True - @admin.register(m.UlohaZadaniNode) class UlohaZadaniNodeAdmin(PolymorphicChildModelAdmin): base_model = m.UlohaZadaniNode diff --git a/seminar/migrations/0080_zruseni_claneknode_a_konferanode.py b/seminar/migrations/0080_zruseni_claneknode_a_konferanode.py new file mode 100644 index 00000000..13d083f9 --- /dev/null +++ b/seminar/migrations/0080_zruseni_claneknode_a_konferanode.py @@ -0,0 +1,121 @@ +# Generated by Django 2.2.12 on 2020-04-01 20:54 +# Fixed by Pavel, 2020-01-04 20:56 UTC +# This is quite possibly a bug in Django. + +from django.db import migrations, models +import django.db.models.deletion + +def vyrob_dummy_problemy(apps, schema_editor): + Problem = apps.get_model('seminar', 'Problem') + Konfera = apps.get_model('seminar', 'Konfera') + for k in Konfera.objects.all(): + pr = Problem.objects.create(nazev=k.nazev, + garant=k.organizator) + pr.save() + k.problem_ptr = pr + k.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ('seminar', '0079_clanek_resitelsky'), + ] + + operations = [ + migrations.DeleteModel( + name='Konfery_Ucastnici', + ), + migrations.RemoveField( + model_name='konfera', + name='ucastnici', + ), + migrations.CreateModel( + name='OrgTextNode', + fields=[ + ('treenode_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='seminar.TreeNode')), + ('org_verejny', models.BooleanField(default=True, help_text='Pokud ano, bude org pod článkem podepsaný', verbose_name='Org je veřejný?')), + ('organizator', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='seminar.Organizator', verbose_name='Organizátor')), + ], + options={ + 'verbose_name': 'Organizátorský článek (Node)', + 'verbose_name_plural': 'Organizátorské články (Node)', + 'db_table': 'seminar_nodes_orgtextnode', + }, + bases=('seminar.treenode',), + ), + migrations.RemoveField( + model_name='konfera', + name='id', + ), + migrations.RenameModel( + old_name='OtisteneReseniNode', + new_name='ReseniNode', + ), + migrations.RemoveField( + model_name='clanek', + name='cislo', + ), + migrations.RemoveField( + model_name='clanek', + name='resitelsky', + ), + migrations.RemoveField( + model_name='reseni', + name='text_zkraceny', + ), + migrations.AddField( + model_name='konfera', + name='problem_ptr', + field=models.OneToOneField(auto_created=True, null=False, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='seminar.Problem'), + preserve_default=False, + ), + migrations.RemoveField( + model_name='konfera', + name='nazev', + ), + migrations.RemoveField( + model_name='konfera', + name='organizator', + ), + migrations.RemoveField( + model_name='konfera', + name='poznamka', + ), + migrations.RemoveField( + model_name='konfera', + name='reseni', + ), + migrations.AlterField( + model_name='reseni', + name='text_cely', + field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='reseni_cely_set', to='seminar.ReseniNode', verbose_name='Plná verze textu řešení'), + ), + migrations.DeleteModel( + name='ClanekNode', + ), + migrations.DeleteModel( + name='KonferaNode', + ), + migrations.CreateModel( + name='Konfery_Ucastnici', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka k účasti (plain text)', verbose_name='neveřejná poznámka')), + ('konfera', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='seminar.Konfera', verbose_name='konfera')), + ('resitel', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='seminar.Resitel', verbose_name='řešitel')), + ], + options={ + 'verbose_name': 'Účast na konfeře', + 'verbose_name_plural': 'Účasti na konfeře', + 'db_table': 'seminar_konfery_ucastnici', + 'ordering': ['konfera', 'resitel'], + }, + ), + migrations.AddField( + model_name='konfera', + name='ucastnici', + field=models.ManyToManyField(help_text='Seznam účastníků konfery', through='seminar.Konfery_Ucastnici', to='seminar.Resitel', verbose_name='účastníci konfery'), + ), + ] diff --git a/seminar/migrations/0081_auto_20200408_2221.py b/seminar/migrations/0081_auto_20200408_2221.py new file mode 100644 index 00000000..6cf908bb --- /dev/null +++ b/seminar/migrations/0081_auto_20200408_2221.py @@ -0,0 +1,24 @@ +# Generated by Django 2.2.9 on 2020-04-08 20:21 + +from django.db import migrations, models +import seminar.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('seminar', '0080_zruseni_claneknode_a_konferanode'), + ] + + operations = [ + migrations.AddField( + model_name='cislo', + name='titulka_nahled', + field=models.ImageField(blank=True, help_text='Obrázek titulní strany, generuje se automaticky', null=True, upload_to='', verbose_name='Obrázek titulní strany'), + ), + migrations.AlterField( + model_name='cislo', + name='pdf', + field=models.FileField(blank=True, help_text='PDF čísla, které si mohou řešitelé stáhnout', null=True, upload_to=seminar.models.cislo_pdf_filename, verbose_name='pdf'), + ), + ] diff --git a/seminar/models.py b/seminar/models.py index 6419e73d..57d4304a 100644 --- a/seminar/models.py +++ b/seminar/models.py @@ -728,6 +728,9 @@ class Problem(SeminarModelBase,PolymorphicModel): return '' def verejne(self): + # FIXME: Tohle se liší podle typu problému, má se udělat polymorfně. + # Zatím je tu jen dummy fail-safe default: nic není veřejné. + return False return (self.cislo_zadani and self.cislo_zadani.verejne()) verejne.boolean = True @@ -735,10 +738,7 @@ class Problem(SeminarModelBase,PolymorphicModel): return reverse('seminar_problem', kwargs={'pk': self.id}) def admin_url(self): - if self.stav == Problem.STAV_ZADANY: - return reverse('admin:seminar_problemzadany_change', args=(self.id, )) - else: - return reverse('admin:seminar_problemnavrh_change', args=(self.id, )) + return reverse('admin:seminar_problem_change', args=(self.id, )) # FIXME - k úloze def body_v_zavorce(self): @@ -792,14 +792,6 @@ class Clanek(Problem): verbose_name = 'Článek' verbose_name_plural = 'Články' - cislo = models.ForeignKey(Cislo, verbose_name='číslo', blank=True, null=True, - on_delete=models.PROTECT) - - resitelsky = models.BooleanField('Jde o řešitelský článek?', default=True) - - # má OneToOneField s: - # ClanekNode - def kod_v_rocniku(self): if self.stav == 'zadany': # Nemělo by být potřeba @@ -808,15 +800,6 @@ class Clanek(Problem): return "c{}".format(self.kod) return '' - def save(self, *args, **kwargs): - super().save(*args, **kwargs) - # *Node.save() aktualizuje název *Nodu. - try: - self.claneknode.save() - except ObjectDoesNotExist: - # Neexistující *Node nemá smysl aktualizovat. - pass - class Text(SeminarModelBase): class Meta: db_table = 'seminar_texty' @@ -926,13 +909,10 @@ class Reseni(SeminarModelBase): forma = models.CharField('forma řešení', max_length=16, choices=FORMA_CHOICES, blank=False, default=FORMA_EMAIL) - text_cely = models.OneToOneField(Text, verbose_name='Plná verze textu řešení', + text_cely = models.OneToOneField('ReseniNode', verbose_name='Plná verze textu řešení', blank=True, null=True, related_name="reseni_cely_set", - on_delete=models.SET_NULL) + on_delete=models.PROTECT) - text_zkraceny = models.ManyToManyField(Text, verbose_name='zkrácené verze řešení', - help_text='Seznam úryvků z řešení',related_name="reseni_zkraceny_set") - poznamka = models.TextField('neveřejná poznámka', blank=True, help_text='Neveřejná poznámka k řešení (plain text)') @@ -1141,25 +1121,18 @@ class Soustredeni_Organizatori(SeminarModelBase): @reversion.register(ignore_duplicates=True) -class Konfera(models.Model): +class Konfera(Problem): class Meta: db_table = 'seminar_konfera' verbose_name = 'Konfera' verbose_name_plural = 'Konfery' - # Interní ID - id = models.AutoField(primary_key = True) - nazev = models.CharField('název konfery', max_length=100, help_text = 'Název 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') - organizator = models.ForeignKey(Organizator, verbose_name='organizátor', related_name='konfery', - on_delete = models.SET_NULL, null=True) - # FIXME: Umíme omezit jen na účastníky daného soustřeďka? ucastnici = models.ManyToManyField(Resitel, verbose_name='účastníci konfery', help_text='Seznam účastníků konfery', through='Konfery_Ucastnici') @@ -1167,13 +1140,6 @@ class Konfera(models.Model): soustredeni = models.ForeignKey(Soustredeni, verbose_name='soustředění', related_name='konfery', on_delete = models.SET_NULL, null=True) - poznamka = models.TextField('neveřejná poznámka', blank=True, - help_text='Neveřejná poznámka ke konfeře(plain text)') - # Jedno reseni se vztahuje nejvyse k jedne konfere - reseni = models.OneToOneField(Reseni, verbose_name='článek ke konfeře', related_name='konfery', - help_text='Účastnický přípěvek o konfeře', on_delete = models.SET_NULL, - null=True, blank=True) - TYP_VELETRH = 'veletrh' TYP_PREZENTACE = 'prezentace' TYP_CHOICES = [ @@ -1190,21 +1156,9 @@ class Konfera(models.Model): help_text = 'Další materiály ke konfeře zabalené do jednoho souboru', upload_to = generate_filename_konfera, blank=True) - # má OneToOneField s: - # KonferaNode - def __str__(self): return "{}: ({})".format(self.nazev, self.soustredeni) - def save(self, *args, **kwargs): - super().save(*args, **kwargs) - # *Node.save() aktualizuje název *Nodu. - try: - self.konferanode.save() - except ObjectDoesNotExist: - # Neexistující *Node nemá smysl aktualizovat. - pass - # Vazebna tabulka. Mozna se generuje automaticky. @reversion.register(ignore_duplicates=True) @@ -1417,36 +1371,30 @@ class TemaVCisleNode(TreeNode): def getOdkazStr(self): return str(self.tema) -class KonferaNode(TreeNode): - class Meta: - db_table = 'seminar_nodes_konfera' - verbose_name = 'Konfera (Node)' - verbose_name_plural = 'Konfery (Node)' - konfera = models.OneToOneField(Konfera, - on_delete=models.PROTECT, # Pokud chci mazat téma, musím si Node pořešit ručně - verbose_name = "konfera", - null=True, - blank=False) - - def aktualizuj_nazev(self): - self.nazev = "KonferaNode: "+str(self.konfera) - -class ClanekNode(TreeNode): +class OrgTextNode(TreeNode): class Meta: - db_table = 'seminar_nodes_clanek' - verbose_name = 'Článek (Node)' - verbose_name_plural = 'Články (Node)' - clanek = models.OneToOneField(Clanek, - on_delete=models.PROTECT, # Pokud chci mazat téma, musím si Node pořešit ručně - verbose_name = "článek", - null=True, - blank=False) + db_table = 'seminar_nodes_orgtextnode' + verbose_name = 'Organizátorský článek (Node)' + verbose_name_plural = 'Organizátorské články (Node)' + + organizator = models.ForeignKey(Organizator, + null=False, + blank=False, + on_delete=models.DO_NOTHING, + verbose_name="Organizátor", + ) + org_verejny = models.BooleanField(default = True, + verbose_name = "Org je veřejný?", + help_text = "Pokud ano, bude org pod článkem podepsaný", + null=False, + ) def aktualizuj_nazev(self): - self.nazev = "ClanekNode: "+str(self.clanek) + return f"OrgTextNode začínající následujícim: {self.first_child.nazev}" - def getOdkazStr(self): - return str(self.clanek) + # FIXME!!! + #def getOdkazStr(self): + # return str(self.clanek) class UlohaZadaniNode(TreeNode): @@ -1527,7 +1475,7 @@ class CastNode(TreeNode): def getOdkazStr(self): return str(self.nadpis) -class OtisteneReseniNode(TreeNode): +class ReseniNode(TreeNode): class Meta: db_table = 'seminar_nodes_otistene_reseni' verbose_name = 'Otištěné řešení (Node)' diff --git a/seminar/templates/seminar/archiv/cisla.html b/seminar/templates/seminar/archiv/cisla.html index fbb19263..f931ee49 100644 --- a/seminar/templates/seminar/archiv/cisla.html +++ b/seminar/templates/seminar/archiv/cisla.html @@ -8,18 +8,56 @@ {% endblock %}{% endblock %} -
+ + + {% for rocnik, url_png in object_list.items %} + +
+ +

+ Ročník {{ rocnik }} +

+ + + {# karta ročníku - zepředu obrázek prvního čísla, zezadu odkaz na jednotlivá čísla a výsledkovku #} + +
+ +
+
+ +
+ {{ rocnik }} +
+ +
+
+
+ Jednotlivá čísla: +
    + {% for cislo in rocnik.cisla.all reversed %} +
  • {{ cislo.poradi }}. číslo {% if cislo.pdf %}(pdf) {% endif %} + {% empty %} + Žádná čísla k zobrazení + {% endfor %} +
+ Výsledková listina +
+ +
+
+
+ + {# konec karty ročníku #} + +
+
-
    - {% for r in object_list %} -
  • Ročník {{ r }} {% empty %} Nejsou žádné ročníky {% endfor %} -
{% endblock content %} - diff --git a/seminar/templates/seminar/archiv/cislo.html b/seminar/templates/seminar/archiv/cislo.html index 955988f1..80905e00 100644 --- a/seminar/templates/seminar/archiv/cislo.html +++ b/seminar/templates/seminar/archiv/cislo.html @@ -46,15 +46,15 @@
  • Obálkování
  • - {% endif %} + {% endif %} {% if cislo.verejna_vysledkovka %} -

    Výsledkovka ({% now "jS F Y H:i" %})

    +

    Výsledkovka

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

    Výsledkovka (neveřejná, {% now "jS F Y H:i:s" %})

    +

    Výsledkovka (neveřejná)

    {% endif %} {% endif %} @@ -73,7 +73,7 @@ {% autoescape off %}{{ rv.poradi }}{% endautoescape %} - {% if rv.titul is not '' %} + {% if rv.titul %} {{ rv.titul }}MM {% endif %} {{ rv.resitel.osoba.plne_jmeno }} @@ -86,14 +86,11 @@ {% endfor %} - {% endif %} + {% endif %} {% if not cislo.verejna_vysledkovka and user.is_staff %}
    {% endif %} - Čas: {% now "jS F Y H:i:s" %} -
    {% endblock content %} - diff --git a/seminar/templates/seminar/archiv/cislo_vysledkovka.tex b/seminar/templates/seminar/archiv/cislo_vysledkovka.tex index 68baec9b..b374864c 100644 --- a/seminar/templates/seminar/archiv/cislo_vysledkovka.tex +++ b/seminar/templates/seminar/archiv/cislo_vysledkovka.tex @@ -1,9 +1,9 @@ \setlength{\tabcolsep}{3pt} \begin{longtable}{|r|l|c|r|{% for p in problemy %}c@{\hskip.5em}{% endfor %}|r|r|}\hline -& & & & \multicolumn{ {{ problemy|length}} }{c|}{\textbf{Úlohy}} & & \\\textbf{Poř.}& \textbf{Jméno}& \textbf{R.}& \raisebox{0.7mm}{$\sum_{-1}$}& {% for p in problemy %}{% if p.typ == "uloha" %}\textbf{r{{p.kod}}}&{% elif p.typ == "tema" %}\textbf{t{{p.kod}}}&{% else %}\textbf{ {{p.kod}} }&{% endif %}{% endfor %}\raisebox{0.7mm}{$\sum_0$}&\raisebox{0.7mm}{$\sum_1$}\\\hline +& & & & \multicolumn{ {{ problemy|length}} }{c|}{\textbf{Úlohy}} & & \\\textbf{Poř.}& \textbf{Jméno}& \textbf{R.}& \raisebox{0.7mm}{$\sum_{-1}$}& {% for p in problemy %}\textbf{ {{ p.kod_v_rocniku }} }&{% endfor %}\raisebox{0.7mm}{$\sum_0$}&\raisebox{0.7mm}{$\sum_1$}\\\hline \endhead \hline \endfoot -{% for rv in vysledkovka %}{{rv.poradi}}&{% if rv.titul %}\titul{ {{ rv.titul}}}{% endif %}{{rv.resitel.inicial_krestni}} {{rv.resitel.prijmeni}}&{{rv.resitel.rocnik|default:""}}&{{rv.body_celkem_odjakziva}}&{% for b in rv.body_ulohy %}{{b}}&{% endfor %}{{rv.body_cislo}}&{{rv.body_celkem_rocnik|default:0}}\\ +{% for rv in radky_vysledkovky %}{{rv.poradi}}&{% if rv.titul %}\titul{ {{ rv.titul}}}{% endif %}{{rv.resitel.osoba.jmeno|slice:":1"}}. {{rv.resitel.osoba.prijmeni}}&{{rv.rocnik_resitele|default:""}}&{{rv.body_celkem_odjakziva}}&{% for b in rv.body_problemy_sezn %}{{b}}&{% endfor %}{{rv.body_cislo}}&{{rv.body_rocnik|default:0}}\\ {% endfor %} \end{longtable} diff --git a/seminar/templates/seminar/archiv/rocnik.html b/seminar/templates/seminar/archiv/rocnik.html index 64c4029d..0f48b30c 100644 --- a/seminar/templates/seminar/archiv/rocnik.html +++ b/seminar/templates/seminar/archiv/rocnik.html @@ -2,22 +2,11 @@ {% block content %}
    -

    +

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

    - -

    Ročník číslo {{ rocnik.rocnik }} ({{ rocnik.prvni_rok }}/{{ rocnik.druhy_rok }}) - -

      - {% for c in rocnik.verejna_cisla %} -
    • Číslo {{ c.kod }} - {% if c.pdf %} - (pdf) - {% endif %} - {% endfor %} -
    + {% if temata_v_rocniku %}

    Témata

    @@ -28,6 +17,50 @@ {% endif %} +
    + {% for c in rocnik.verejna_cisla %} +
    + +
    Číslo {{ c.kod }}
    + +
    + +
    +
    + +
    + {% if c.titulka_nahled %} + {{ c.kod }} + {% else %} + no image + {% endif %} +
    + +
    +
    + +
    + +
    + + +
    +
    +
    +
    + + {% endfor %} +
    + {% if vysledkovka %} {% if user.is_staff %}
    @@ -42,7 +75,7 @@ {% if user.is_staff and vysledkovka_s_neverejnymi %}

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

    - {% with vysledkovka_s_neverejnymi as vysledkovka %} + {% with radky_vyledkovky_s_neverejnymi as radky_vysledkovky %} {% include "seminar/vysledkovka_rocnik.html" %} {% endwith %}
    @@ -50,7 +83,3 @@
    {% endblock content %} - - - - diff --git a/seminar/templates/seminar/archiv/rocnik_vysledkovka.tex b/seminar/templates/seminar/archiv/rocnik_vysledkovka.tex index 75d8eb79..61c9abbb 100644 --- a/seminar/templates/seminar/archiv/rocnik_vysledkovka.tex +++ b/seminar/templates/seminar/archiv/rocnik_vysledkovka.tex @@ -1,13 +1,12 @@ {% with lb="{" %} {% with rb="}" %} \setlength{\tabcolsep}{3pt} -\begin{longtable}{|r|l|c|r|{% for cislo in vysledkovka.cisla %}c{% if not forloop.last %}@{\hskip.5em}{% endif %}{% endfor %}|r|} -\hline -& & & & \multicolumn{{ lb }}{{ vysledkovka.cisla|length }}}{c|}{\textbf{Číslo}} & \\\textbf{Poř.} & \textbf{Jméno} & \textbf{R.} & \raisebox{0.7mm}{$\sum_{-1}$} & {% for cislo in vysledkovka.cisla %}\textbf{{ lb }}{{ cislo.cislo }}{{ rb }} & {% endfor %}\raisebox{0.7mm}{$\sum_1$} \\\hline +\begin{longtable}{|r|l|c|r|{% for cislo in cisla %}c{% if not forloop.last %}@{\hskip.5em}{% endif %}{% endfor %}|r|}\hline +& & & & \multicolumn{{ lb }}{{ cisla|length }}}{c|}{\textbf{Číslo}} & \\\textbf{Poř.} & \textbf{Jméno} & \textbf{R.} & \raisebox{0.7mm}{$\sum_{-1}$} & {% for cislo in cisla %}\textbf{{ lb }}{{ cislo.poradi }}{{ rb }} & {% endfor %}\raisebox{0.7mm}{$\sum_1$} \\\hline \endhead \hline \endfoot -{% for rv in vysledkovka.radky %}{{ rv.poradi }} & {% if rv.titul %}\titul{{ lb }}{{ rv.titul }}}~{% endif %}{{ rv.resitel.jmeno|slice:":1" }}.~{{ rv.resitel.prijmeni }} & {% if rv.resitel.rocnik %}{{ rv.resitel.rocnik }}.{% endif %} & {{ rv.body_odjakziva }} {% for b in rv.body_cisla %} & {{ b }}{% endfor %} & {{ rv.body_rocnik }} \\ +{% for rv in radky_vysledkovky %}{{ rv.poradi }} & {% if rv.titul %}\titul{{ lb }}{{ rv.titul }}}~{% endif %}{{ rv.resitel.osoba.jmeno|slice:":1" }}.~{{ rv.resitel.osoba.prijmeni }} & {% if rv.rocnik_resitele %}{{ rv.rocnik_resitele }}{% endif %} & {{ rv.body_celkem_odjakziva }} {% for b in rv.body_cisla_sezn %} & {{ b }}{% endfor %} & {{ rv.body_rocnik }} \\ {% endfor %}\end{longtable} {% endwith %} {% endwith %} diff --git a/seminar/templates/seminar/archiv/tituly.tex b/seminar/templates/seminar/archiv/tituly.tex index 72b1cdde..aca444df 100644 --- a/seminar/templates/seminar/archiv/tituly.tex +++ b/seminar/templates/seminar/archiv/tituly.tex @@ -1,6 +1,5 @@ {% if broken %} -%% POZOR! %% Dva resitele maji stejne makro!!! -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +POZOR! Kolize jmen! Dva řešitelé mají stejné makro! {% endif %} {% autoescape off %} diff --git a/seminar/templates/seminar/cojemam/organizatori.html b/seminar/templates/seminar/cojemam/organizatori.html index 8e8f1aaa..a3957101 100644 --- a/seminar/templates/seminar/cojemam/organizatori.html +++ b/seminar/templates/seminar/cojemam/organizatori.html @@ -46,12 +46,12 @@ {# karta organizátora - zepředu fotka, zezadu popis, u neaktivních data kdy organizovali #} -
    +
    -
    +
    {% if org.osoba.foto %} {{org.osoba.jmeno}} {{org.osoba.prijmeni}} {% else %} {# pokud osoba nemá fotku, zobrazuje se defaultní obrázek #} diff --git a/seminar/templates/seminar/vysledkovka_rocnik.html b/seminar/templates/seminar/vysledkovka_rocnik.html index f1dc1d79..db9e848a 100644 --- a/seminar/templates/seminar/vysledkovka_rocnik.html +++ b/seminar/templates/seminar/vysledkovka_rocnik.html @@ -4,23 +4,23 @@ Jméno R. Odjakživa - {% for c in vysledkovka.cisla %} + {% for c in cisla %} - {{c.rocnik.rocnik}}.{{ c.cislo }} + {{c.rocnik.rocnik}}.{{ c.poradi }} {% endfor %} Celkem -{% for rv in vysledkovka.radky %} +{% for rv in radky_vysledkovky %} {% autoescape off %}{{ rv.poradi }}{% endautoescape %} {% if rv.titul %} {{ rv.titul }}MM {% endif %} - {{ rv.resitel.plne_jmeno }} - {{ rv.resitel.rocnik }} - {{ rv.body_odjakziva }} - {% for b in rv.body_cisla %} + {{ rv.resitel.osoba.plne_jmeno }} + {{ rv.rocnik_resitele }} + {{ rv.body_celkem_odjakziva }} + {% for b in rv.body_cisla_sezn %} {{ b }} {% endfor %} {{ rv.body_rocnik }} diff --git a/seminar/testutils.py b/seminar/testutils.py index 9aec9eba..65ffbfcd 100644 --- a/seminar/testutils.py +++ b/seminar/testutils.py @@ -9,7 +9,7 @@ from django.db import transaction import unidecode import logging -from seminar.models import Skola, Resitel, Rocnik, Cislo, Problem, Reseni, PrilohaReseni, Nastaveni, Soustredeni, Soustredeni_Ucastnici, Soustredeni_Organizatori, Osoba, Organizator, Prijemce, Tema, Uloha, Konfera, KonferaNode, TextNode, UlohaVzorakNode, RocnikNode, CisloNode, TemaVCisleNode, Text, Hodnoceni, UlohaZadaniNode, Novinky, TreeNode +from seminar.models import Skola, Resitel, Rocnik, Cislo, Problem, Reseni, PrilohaReseni, Nastaveni, Soustredeni, Soustredeni_Ucastnici, Soustredeni_Organizatori, Osoba, Organizator, Prijemce, Tema, Uloha, Konfera, TextNode, UlohaVzorakNode, RocnikNode, CisloNode, TemaVCisleNode, Text, Hodnoceni, UlohaZadaniNode, Novinky, TreeNode from django.contrib.flatpages.models import FlatPage from django.contrib.sites.models import Site @@ -270,9 +270,12 @@ def gen_ulohy_do_cisla(rnd, organizatori, resitele, rocnik_cisla, rocniky, size) #print("Generuji {}-té řešení".format(ri)) if rnd.randint(1, 10) == 6: # cca desetina řešení od více řešitelů - res_vyber = rnd.sample(resitele_cisla, rnd.randint(2, 5)) + res_vyber = rnd.sample(resitele_cisla, + rnd.randint(2, 5)) else: res_vyber = rnd.sample(resitele_cisla, 1) + if resitele[0] in res_vyber: + res_vyber.remove(resitele[0]) res = Reseni.objects.create(forma=rnd.choice(Reseni.FORMA_CHOICES)[0]) # problem a resitele přiřadíme později, ManyToManyField # se nedá vyplnit v create() @@ -336,7 +339,7 @@ def gen_konfery(size, rnd, organizatori, resitele, soustredeni): nazev=rnd.choice(['Pozorování', 'Zkoumání', 'Modelování', 'Počítání', 'Zkoušení']) + rnd.choice([' vlastností', ' jevů', ' charakteristik']) + rnd.choice([' vektorových prostorů', ' kinetické terorie látek', ' molekulární biologie', ' syntentických stromů']), anotace=lorem.paragraph(), abstrakt=lorem.paragraph(), - organizator=rnd.choice(organizatori), + garant=rnd.choice(organizatori), soustredeni=rnd.choice(soustredeni), typ_prezentace=rnd.choice(['veletrh', 'prezentace'])) ucastnici_sous = list(konfera.soustredeni.ucastnici.all()) @@ -346,9 +349,6 @@ def gen_konfery(size, rnd, organizatori, resitele, soustredeni): # Konfery_Ucastnici.objects.create(resitel=res, konfera=konfera) konfera.save() konfery.append(konfera) - - konferanode = KonferaNode.objects.create(konfera=konfera) - konferanode.save() return konfery def gen_cisla(rnd, rocniky): @@ -407,7 +407,8 @@ def gen_temata(rnd, rocniky, rocnik_cisla, organizatori): poc_oboru = rnd.randint(1, 2) rocnik_temata = [] - # Věříme, že rocnik_cisla je pole polí čísel podle ročníků, tak si necháme dát vždycky jeden ročník a k němu příslušná čísla. + # Věříme, že rocnik_cisla je pole polí čísel podle ročníků, tak si necháme dát + # vždycky jeden ročník a k němu příslušná čísla. for rocnik, cisla in zip(rocniky, rocnik_cisla): kod = 1 letosni_temata = [] diff --git a/seminar/urls.py b/seminar/urls.py index 6fa9a559..8f47c7dc 100644 --- a/seminar/urls.py +++ b/seminar/urls.py @@ -81,18 +81,20 @@ urlpatterns = [ path('aesop-export/index.csv', export.ExportIndexView.as_view(), name='seminar_export_index'), # Stranky viditelne pouze pro orgy: - #path( - # 'rocnik/(?P\d+)/vysledkovka.tex', - # staff_member_required(views.RocnikVysledkovkaView.as_view()), - # name='seminar_cislo_vysledkovka' - #), - #path('cislo/(?P\d+).(?P[0-9-]+)/vysledkovka.tex', - # staff_member_required(views.CisloVysledkovkaView.as_view()), name='seminar_cislo_vysledkovka'), + path( + 'rocnik//vysledkovka.tex', + staff_member_required(views.RocnikVysledkovkaView.as_view()), + name='seminar_rocnik_vysledkovka' + ), + path('cislo/./vysledkovka.tex', + staff_member_required(views.CisloVysledkovkaView.as_view()), + name='seminar_cislo_vysledkovka' + ), path('cislo/./obalky.pdf', staff_member_required(views.cisloObalkyView), name='seminar_cislo_obalky'), - #path('cislo/(?P\d+).(?P[0-9-]+)/tituly.tex', - # staff_member_required(views.TitulyView), name='seminar_cislo_titul'), + path('cislo/./tituly.tex', + staff_member_required(views.TitulyView), name='seminar_cislo_titul'), path('stav', staff_member_required(views.StavDatabazeView), name='stav_databaze'), path('cislo/./obalkovani', diff --git a/seminar/views/views_all.py b/seminar/views/views_all.py index 32956c35..9bb5276e 100644 --- a/seminar/views/views_all.py +++ b/seminar/views/views_all.py @@ -40,6 +40,7 @@ import traceback import sys import csv import logging +import time def verejna_temata(rocnik): @@ -340,93 +341,32 @@ class ArchivView(generic.ListView): def get_context_data(self, **kwargs): context = super(ArchivView, self).get_context_data(**kwargs) - vyska = 297 # px - sirka = 210 # px - - # nejnovějších 10 zveřejněných čísel - # cisla = Cislo.objects.filter(verejne_db=True)[:10] - cisla = Cislo.objects.filter(poradi=1)[:10] - - # op == os.path, udělá z argumentů cestu - png_dir = op.join(settings.MEDIA_ROOT, "cislo", "png") - - # seznam [(url obrázku, číslo)] - urls = [] + cisla = Cislo.objects.filter(poradi=1) + urls ={} for i, c in enumerate(cisla): - if not c.pdf: - continue - filename = os.path.split(c.pdf.file.name)[1].split(".")[0] - png_filename = "{}-{}px.png".format(filename, vyska) - - # Pokud obrázek neexistuje nebo není aktuální, vytvoř jej - png_path = op.join(png_dir, png_filename) - if not op.exists(png_path) or \ - op.getmtime(png_path) < op.getmtime(c.pdf.path): - - subprocess.call([ - "convert", - "-density", "300x300", - "-geometry", "{}x{}".format(vyska, sirka), - "-background", "white", - "-flatten", - "-rotate", str(90 * i), - "{}[0]".format(c.pdf.path), # titulní strana - png_path - ]) - - urls.append( - (op.join(settings.MEDIA_URL, "cislo", "png", png_filename), c) - ) - vyska, sirka = sirka, vyska / 2 - - tags = [] - - def spirala(urls, tags, idx): - """Rekurzivně prochází urls a generuje strom elementů do tags""" - if idx >= len(urls): - return - - img_url, cislo = urls[idx] - tags.append( - "
    " - .format( - 50 if idx % 4 == 2 else 0, - 50 if idx % 4 == 1 else 0, - 50 if idx % 2 == 1 else 100, - 50 if idx > 0 and idx % 2 == 0 else 100 - ) - ) - tags.append("".format( - cislo.verejne_url(), cislo.kod() - )) - tags.append( - "" - .format( - img_url, - 50 if idx % 4 == 3 else 0, - 50 if idx % 4 == 2 else 0, - 50 if idx % 2 == 0 else 100, - 50 if idx % 2 == 1 else 100 - ) - ) - tags.append("") - spirala(urls, tags, idx + 1) - tags.append("
    ") - spirala(urls, tags, 0) + if c.titulka_nahled: + urls[c.rocnik] = c.titulka_nahled.url + else: + urls[c.rocnik] = op.join(settings.MEDIA_URL, "cislo", "png", "default.png") - context["nahledy"] = "\n".join(tags) + context["object_list"] = urls + return context ### Výsledky -# ze setřízeného(!) seznamu všech bodů vytvoří seznam s pořadími (včetně 3.-5. a pak 2 volná místa atp.) -def sloupec_s_poradim(seznam_s_body): +# ze seznamu obsahujícího sestupně setřízené body řešitelů za daný ročník +# vytvoří seznam s pořadími (včetně 3.-5. a pak 2 volná místa atp.), +# podle toho, jak jdou za sebou ve výsledkovce +def sloupec_s_poradim(setrizene_body): + + # ze seznamu obsahujícího setřízené body spočítáme sloupec s pořadím aktualni_poradi = 1 sloupec_s_poradim = [] # seskupíme seznam všech bodů podle hodnot - for index in range(0, len(seznam_s_body)): + for index in range(0, len(setrizene_body)): # pokud je pořadí větší než číslo řádku, tak jsme vypsali větší rozsah a chceme # vypsat už jen prázdné místo, než dojdeme na správný řádek if (index + 1) < aktualni_poradi: @@ -434,10 +374,10 @@ def sloupec_s_poradim(seznam_s_body): continue velikost_skupiny = 0 # zjistíme počet po sobě jdoucích stejných hodnot - while seznam_s_body[index] == seznam_s_body[index + velikost_skupiny]: + while setrizene_body[index] == setrizene_body[index + velikost_skupiny]: velikost_skupiny = velikost_skupiny + 1 # na konci musíme ošetřit přetečení seznamu - if (index + velikost_skupiny) > len(seznam_s_body) - 1: + if (index + velikost_skupiny) > len(setrizene_body) - 1: break # pokud je velikost skupiny 1, vypíšu pořadí if velikost_skupiny == 1: @@ -450,28 +390,12 @@ def sloupec_s_poradim(seznam_s_body): aktualni_poradi = aktualni_poradi + velikost_skupiny return sloupec_s_poradim -## spočítá součet bodů získaných daným řešitelem za zadaný problém a všechny jeho podproblémy -#def __soucet_resitele_problemu(problem, resitel, cislo, soucet): -# # sečteme body za daný problém přes všechna řešení daného problému -# # od daného řešitele -# reseni_resitele = s.Reseni_Resitele.objects.filter(resitele=resitel) -# hodnoceni_resitele = problem.hodnoceni.filter(reseni__in=reseni_resitele, -# cislo_body=cislo) -# # XXX chyba na řádku výše - řešení může mít více řešitelů, asi chceme contains -# # nebo in -# for r in hodnoceni_resitele: -# soucet += r.body -# -# # a přičteme k tomu hodnocení všech podproblémů -# for p in problem.podproblem.all(): -# # i přes jméno by to měla být množina jeho podproblémů -# soucet += __soucet_resitele_problemu(p, resitel, soucet) -# return soucet - -## spočítá součet všech bodů ze všech podproblémů daného problému daného řešitele -#def body_resitele_problemu_v_cisle(problem, resitel, cislo): -# # probably FIXED: nezohledňuje číslo, do kterého se body počítají -# return __soucet_resitele_problemu(problem, resitel, cislo, 0) +# vrátí všechna čísla daného ročníku +def cisla_rocniku(rocnik, jen_verejne=True): + if jen_verejne: + return rocnik.verejna_cisla() + else: + return rocnik.cisla.all() # pro daný problém vrátí jeho nejvyšší nadproblém def hlavni_problem(problem): @@ -479,6 +403,17 @@ def hlavni_problem(problem): problem = problem.nadproblem return problem +def hlavni_problemy_rocniku(rocnik, jen_verejne=True): + hlavni_problemy = [] + for cislo in cisla_rocniku(rocnik, jen_verejne): + for problem in hlavni_problemy_cisla(cislo): + hlavni_problemy.append(problem) + hlavni_problemy_set = set(hlavni_problemy) + hlavni_problemy = list(hlavni_problemy_set) + hlavni_problemy.sort(key=lambda k:k.kod_v_rocniku()) # setřídit podle pořadí + + return hlavni_problemy + # vrátí list všech problémů s body v daném čísle, které již nemají nadproblém def hlavni_problemy_cisla(cislo): hodnoceni = cislo.hodnoceni.select_related('problem', 'reseni').all() @@ -504,31 +439,12 @@ def hlavni_problemy_cisla(cislo): # vrátí slovník řešitel:body obsahující počty bodů zadaných řešitelů za daný ročník def body_resitelu_odjakziva(rocnik, resitele): - body_odjakziva = {} - - for r in resitele: - body_odjakziva[str(r.id)] = 0 -# POZOR! Aktuálně počítá jen za posledních 10 let od zadaného ročníku -# # Body za posledních 10 let je dobrá aproximace pro naše potřeby (výsledkovka -# # s aktivními řešiteli) -# -# body_pred_roky = [] -# for i in range(0, 10): -# body_pred_roky.append(body_resitelu_za_rocnik(rocnik-i, resitele)) -# -# for r in resitele: -# for i in range(0,10): -# body_odjakziva[str(r.id)] += body_pred_roky[i][str(r.id)] - - -# Nasledující řešení je sice správné, ale moc pomalé: - for res in Reseni.objects.prefetch_related('resitele', 'hodnoceni_set').all(): - for r in res.resitele.all(): - # daný řešitel nemusí být v naší podmnožině - if r not in resitele: continue - - for hodn in res.hodnoceni_set.all(): - pricti_body(body_odjakziva, r, hodn.body) + # Následující řádek přidá ke každému řešiteli údaj ".body" se součtem jejich bodů + resitele_s_body = Resitel.objects.annotate(body=Sum('reseni__hodnoceni__body')) + # Teď jen z QuerySetu řešitelů anotovaných body vygenerujeme slovník indexovaný řešitelským id obsahující body + # ... ale jen ro řešitele, které dostaneme jako parametr. + # TODO: Zjistit, co ten parametr říká a proč je potřeba + body_odjakziva = {int(res.id) : res.body for res in resitele_s_body if res in resitele} return body_odjakziva # vrátí slovník řešitel:body obsahující počty bodů zadaných řešitelů za daný ročník @@ -536,74 +452,90 @@ def body_resitelu_za_rocnik(rocnik, aktivni_resitele): body_za_rocnik = {} # inicializujeme na 0 pro všechny aktivní řešitele for ar in aktivni_resitele: - body_za_rocnik[str(ar.id)] = 0 + body_za_rocnik[ar.id] = 0 # spočítáme body řešitelům přes všechna řešení s hodnocením v daném ročníku + print("Před dotazem:{}".format(time.time())) reseni = Reseni.objects.prefetch_related('resitele', 'hodnoceni_set').filter(hodnoceni__cislo_body__rocnik=rocnik) + print("Po dotazu:{}".format(time.time())) for res in reseni: for resitel in res.resitele.all(): for hodn in res.hodnoceni_set.all(): pricti_body(body_za_rocnik, resitel, hodn.body) + print("Po for-cyklu:{}".format(time.time())) return body_za_rocnik -# TODO: předělat na nový model -#def vysledkovka_rocniku(rocnik, jen_verejne=True): -# """Přebírá ročník (např. context["rocnik"]) a vrací výsledkovou listinu ve -# formě vhodné pro šablonu "seminar/vysledkovka_rocniku.html" -# """ -# -# #vyberu vsechny vysledky z rocniku -# cisla_v_rocniku = VysledkyKCisluZaRocnik.objects.filter(cislo__rocnik=rocnik).order_by('cislo') -# if jen_verejne: -# cisla_v_rocniku = cisla_v_rocniku.filter(cislo__verejna_vysledkovka=True) -# -# #pokud žádné nejsou, výsledkovka se nezobrazí -# if not cisla_v_rocniku: -# return None -# -# #vybere vsechny vysledky z posledniho (verejneho) cisla a setridi sestupne dle bodu -# vysledky = list(cisla_v_rocniku.filter(cislo = cisla_v_rocniku[0].poradi).order_by('-body', 'resitel__prijmeni', 'resitel__jmeno').select_related('resitel')) -# -# class Vysledkovka: -# def __init__(self): -# self.rocnik = rocnik.rocnik -# self.radky = [] -# self.cisla = [] -# -# vysledkovka = Vysledkovka() -# vysledkovka.cisla = (rocnik.verejne_vysledkovky_cisla() if jen_verejne else rocnik.cisla.all().order_by('cislo')) -# -# # doplníme některé údaje do řádků výsledkovky pro řešitele ve skupině -# for poradi, v in zip(sloupec_s_poradim(vysledky), vysledky): -# v.poradi = poradi -# v.resitel.rocnik = v.resitel.rocnik(rocnik) -# -# verejne_vysl_odjakziva = VysledkyKCisluOdjakziva.objects.filter(cislo__rocnik=rocnik, cislo=cisla_v_rocniku[0].poradi) -# if jen_verejne: -# verejne_vysl_odjakziva = verejne_vysl_odjakziva.filter(cislo__verejna_vysledkovka=True) -# -# v.body_odjakziva = verejne_vysl_odjakziva.filter(resitel = v.resitel)[0].body -# v.titul = v.resitel.get_titul(v.body_odjakziva) -# v.body_rocnik = v.body -# v.body_cisla = [] -# -# #pokud pro dany rok a cislo nema resitel vysledky, ma defaultne 0 -# for cis in vysledkovka.cisla: -# if not jen_verejne or cis.verejna_vysledkovka: -# #seznam vysledku se spravnym rocnikem a cislem pro resitele -# #zobrazim jen je-li vysledkovka verejna -# body_za_cislo = VysledkyZaCislo.objects.filter(cislo__rocnik=rocnik).filter(cislo = cis).filter(resitel = v.resitel) -# if body_za_cislo: -# #neprazdne vysledky by mely obsahovat prave jeden vysledek -# v.body_cisla.append(body_za_cislo[0].body) -# else: -# #resitel nema za cislo body -# v.body_cisla.append(0) -# -# vysledkovka.radky.append(v) -# -# return vysledkovka +class RadekVysledkovkyRocniku(object): + """Obsahuje věci, které se hodí vědět při konstruování výsledkovky. + Umožňuje snazší práci v templatu (lepší, než seznam).""" + def __init__(self, poradi, resitel, body_cisla_sezn, body_rocnik, body_odjakziva, rok): + self.poradi = poradi + self.resitel = resitel + self.rocnik_resitele = resitel.rocnik(rok) + self.body_rocnik = body_rocnik + self.body_celkem_odjakziva = body_odjakziva + self.body_cisla_sezn = body_cisla_sezn + self.titul = resitel.get_titul(body_odjakziva) + +def vysledkovka_rocniku(rocnik, jen_verejne=True): + """Přebírá ročník (např. context["rocnik"]) a vrací výsledkovou listinu ve + formě vhodné pro šablonu "seminar/vysledkovka_rocniku.html" + """ + + ## TODO možná chytřeji vybírat aktivní řešitele + # aktivní řešitelé - chceme letos něco poslal, TODO později vyfiltrujeme ty, kdo mají + # u alespoň jedné hodnoty něco jiného než NULL + aktivni_resitele = list(Resitel.objects.filter( + rok_maturity__gte=rocnik.druhy_rok())) + # TODO: zkusit hodnoceni__rocnik... + #.filter(hodnoceni_set__rocnik__eq=cislo_rocnik) + cisla = cisla_rocniku(rocnik, jen_verejne) + body_cisla_slov = {} + print("Jen veřejná: {}, čísla: {}".format(jen_verejne, cisla)) + for cislo in cisla: + # získáme body za číslo + _, cislobody = secti_body_za_cislo(cislo, aktivni_resitele) + body_cisla_slov[cislo.id] = cislobody + + # získáme body za ročník, seznam obsahuje dvojice (řešitel_id, body) setřízené sestupně + resitel_rocnikbody_sezn = secti_body_za_rocnik(rocnik, aktivni_resitele) + + # setřídíme řešitele podle počtu bodů a získáme seznam s body od nejvyšších po nenižší + setrizeni_resitele_id = [dvojice[0] for dvojice in resitel_rocnikbody_sezn] + setrizeni_resitele = [Resitel.objects.get(id=i) for i in setrizeni_resitele_id] + setrizene_body = [dvojice[1] for dvojice in resitel_rocnikbody_sezn] + poradi = sloupec_s_poradim(setrizene_body) + + # získáme body odjakživa + resitel_odjakzivabody_slov = body_resitelu_odjakziva(rocnik, aktivni_resitele) + + # vytvoříme jednotlivé sloupce výsledkovky + radky_vysledkovky = [] + i = 0 + for ar_id in setrizeni_resitele_id: + # seznam počtu bodů daného řešitele pro jednotlivá čísla + body_cisla_sezn = [] + for cislo in cisla: + body_cisla_sezn.append(body_cisla_slov[cislo.id][ar_id]) + + # vytáhneme informace pro daného řešitele + radek = RadekVysledkovkyRocniku( + poradi[i], # pořadí + Resitel.objects.get(id=ar_id), # řešitel (z id) + body_cisla_sezn, # seznam bodů za čísla + setrizene_body[i], # body za ročník (spočítané výše s pořadím) + resitel_odjakzivabody_slov[ar_id], # body odjakživa + rocnik) # ročník semináře pro získání ročníku řešitele + print("{}: číslobody - {}, ročníkbody - {}," + "odjakživabody - {}".format(radek.resitel, radek.body_cisla_sezn, + radek.body_rocnik, radek.body_celkem_odjakziva)) + radky_vysledkovky.append(radek) + print("Přikládám {}-tý řádek.".format(i)) + i += 1 + + return radky_vysledkovky + class RocnikView(generic.DetailView): model = Rocnik @@ -626,10 +558,16 @@ class RocnikView(generic.DetailView): def get_context_data(self, **kwargs): context = super(RocnikView, self).get_context_data(**kwargs) - #context['vysledkovka'] = vysledkovka_rocniku(context["rocnik"]) - #context['vysledkovka_s_neverejnymi'] = vysledkovka_rocniku(context["rocnik"], jen_verejne=False) - #context['temata_v_rocniku'] = verejna_temata(context["rocnik"]) - # FIXME: opravit vylistování témat v ročníku + # vysledkovka = True zajistí vykreslení, + # zkontrolovat, kdy se má a nemá vykreslovat + context['vysledkovka'] = True + context['cisla_s_neverejnymi'] = cisla_rocniku(context["rocnik"], jen_verejne=False) + context['cisla'] = cisla_rocniku(context["rocnik"]) + context['radky_vysledkovky'] = vysledkovka_rocniku(context["rocnik"]) + context['radky_vysledkovky_s_neverejnymi'] = vysledkovka_rocniku( + context["rocnik"], jen_verejne=False) + context['hlavni_problemy_v_rocniku'] = hlavni_problemy_rocniku(context["rocnik"]) + context['hlavni_problemy_v_rocniku_s_neverejnymi'] = hlavni_problemy_rocniku(context["rocnik"], jen_verejne=False) return context @@ -637,29 +575,36 @@ class RocnikView(generic.DetailView): class ProblemView(generic.DetailView): model = Problem - def _je_clanek(self, problem): - return problem.typ in [Problem.TYP_ORG_CLANEK, Problem.TYP_RES_CLANEK] - + # Používáme funkci, protože přímo template_name neumí mít v přiřazení dost logiky. Ledaže by se to udělalo polymorfně... def get_template_names(self, **kwargs): - context = super(ProblemView, self).get_context_data(**kwargs) - return ['seminar/archiv/problem_' + ('clanek.html' if self._je_clanek(context['problem']) else 'uloha_tema.html')] + # FIXME: Switch podle typu není hezký, ale nechtělo se mi to přepisovat celé. Správně by se tohle mělo řešit polymorfismem. + spravne_templaty = { + s.Uloha: "uloha", + s.Tema: "tema", + s.Konfera: "konfera", + s.Clanek: "clanek", + } + context = super().get_context_data(**kwargs) + return ['seminar/archiv/problem_' + spravne_templaty[context['object'].__class__] + '.html'] 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: + context = super().get_context_data(**kwargs) + # Musí se používat context['object'], protože nevíme, jestli dostaneme úložku, téma, článek, .... a tyhle věci vyrábějí různé klíče. + if not context['object'].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') + if isinstance(context['object'], Clanek): + context['reseni'] = Reseni.objects.filter(problem=context['object']).select_related('resitel').order_by('resitel__prijmeni') return context -class RadekVysledkovky(object): +class RadekVysledkovkyCisla(object): """Obsahuje věci, které se hodí vědět při konstruování výsledkovky. Umožňuje snazší práci v templatu (lepší, než seznam).""" def __init__(self, poradi, resitel, body_problemy_sezn, - body_cislo, body_rocnik, body_odjakziva): + body_cislo, body_rocnik, body_odjakziva, rok): self.resitel = resitel + self.rocnik_resitele = resitel.rocnik(rok) self.body_cislo = body_cislo self.body_rocnik = body_rocnik self.body_celkem_odjakziva = body_odjakziva @@ -674,36 +619,39 @@ def pricti_body(slovnik, resitel, body): # daného řešitele, předěláme na 0 # (v dalším kroku přičteme reálný počet bodů), # rozlišujeme tím mezi 0 a neodevzdaným řešením - if slovnik[str(resitel.id)] == "": - slovnik[str(resitel.id)] = 0 + if slovnik[resitel.id] == "": + slovnik[resitel.id] = 0 - slovnik[str(resitel.id)] += body + slovnik[resitel.id] += body -def secti_body_za_rocnik(cislo, aktivni_resitele): +def secti_body_za_rocnik(rocnik, aktivni_resitele): # spočítáme všem řešitelům jejich body za ročník - resitel_rocnikbody_slov = body_resitelu_za_rocnik(cislo.rocnik, aktivni_resitele) + resitel_rocnikbody_slov = body_resitelu_za_rocnik(rocnik, aktivni_resitele) # zeptáme se na dvojice (řešitel, body) za ročník a setřídíme sestupně resitel_rocnikbody_sezn = sorted(resitel_rocnikbody_slov.items(), key = lambda x: x[1], reverse = True) return resitel_rocnikbody_sezn # spočítá u řešitelů body za číslo a za jednotlivé hlavní problémy (témata) -def secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy): +def secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy=None): # TODO setřídit hlavní problémy čísla podle id, ať jsou ve stejném pořadí pokaždé # pro každý hlavní problém zavedeme slovník s body za daný hlavní problém # pro jednotlivé řešitele (slovník slovníků hlavních problémů) + if hlavni_problemy is None: + hlavni_problemy = hlavni_problemy_cisla(cislo) + hlavni_problemy_slovnik = {} for hp in hlavni_problemy: - hlavni_problemy_slovnik[str(hp.id)] = {} + hlavni_problemy_slovnik[hp.id] = {} # zakládání prázdných záznamů pro řešitele cislobody = {} for ar in aktivni_resitele: # řešitele převedeme na řetězec pomocí unikátního id - cislobody[str(ar.id)] = "" + cislobody[ar.id] = "" for hp in hlavni_problemy: - slovnik = hlavni_problemy_slovnik[str(hp.id)] - slovnik[str(ar.id)] = "" + slovnik = hlavni_problemy_slovnik[hp.id] + slovnik[ar.id] = "" # vezmeme všechna řešení s body do daného čísla reseni_do_cisla = Reseni.objects.prefetch_related('problem', 'resitele', @@ -716,7 +664,7 @@ def secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy): # řešení může řešit více problémů for prob in list(reseni.problem.all()): nadproblem = hlavni_problem(prob) - nadproblem_slovnik = hlavni_problemy_slovnik[str(nadproblem.id)] + nadproblem_slovnik = hlavni_problemy_slovnik[nadproblem.id] # a mít více hodnocení for hodn in list(reseni.hodnoceni_set.all()): @@ -727,14 +675,12 @@ def secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy): pricti_body(cislobody, resitel, body) pricti_body(nadproblem_slovnik, resitel, body) return hlavni_problemy_slovnik, cislobody - -def spocti_vysledkovku_cisla(cislo, context=None): +def vysledkovka_cisla(cislo, context=None): if context is None: context = {} hlavni_problemy = hlavni_problemy_cisla(cislo) - ## TODO dostat pro tyto problémy součet v daném čísle pro daného řešitele ## TODO možná chytřeji vybírat aktivní řešitele # aktivní řešitelé - chceme letos něco poslal, TODO později vyfiltrujeme ty, kdo mají # u alespoň jedné hodnoty něco jiného než NULL @@ -747,10 +693,10 @@ def spocti_vysledkovku_cisla(cislo, context=None): hlavni_problemy_slovnik, cislobody = secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy) # získáme body za ročník, seznam obsahuje dvojice (řešitel_id, body) setřízené sestupně - resitel_rocnikbody_sezn = secti_body_za_rocnik(cislo, aktivni_resitele) + resitel_rocnikbody_sezn = secti_body_za_rocnik(cislo.rocnik, aktivni_resitele) # získáme body odjakživa - resitel_odjakzivabody_slov = body_resitelu_odjakziva(cislo.rocnik.druhy_rok(), + resitel_odjakzivabody_slov = body_resitelu_odjakziva(cislo.rocnik, aktivni_resitele) # řešitelé setřídění podle bodů za číslo sestupně @@ -768,22 +714,22 @@ def spocti_vysledkovku_cisla(cislo, context=None): # získáme seznam bodů za problémy pro daného řešitele problemy = [] for hp in hlavni_problemy: - problemy.append(hlavni_problemy_slovnik[str(hp.id)][ar_id]) + problemy.append(hlavni_problemy_slovnik[hp.id][ar_id]) # vytáhneme informace pro daného řešitele - radek = RadekVysledkovky( + radek = RadekVysledkovkyCisla( poradi[i], # pořadí Resitel.objects.get(id=ar_id), # řešitel (z id) problemy, # seznam bodů za hlavní problémy čísla cislobody[ar_id], # body za číslo setrizeni_resitele_body[i], # body za ročník (spočítané výše s pořadím) - resitel_odjakzivabody_slov[ar_id]) # body odjakživa + resitel_odjakzivabody_slov[ar_id], # body odjakživa + cislo.rocnik) # ročník semináře pro zjištění ročníku řešitele print("{}: body za problémy - {}, číslobody - {}, ročníkbody - {}, odjakživabody - {}".format(radek.resitel, radek.body_problemy_sezn, radek.body_cislo, radek.body_rocnik, radek.body_celkem_odjakziva)) radky_vysledkovky.append(radek) print("Přikládám {}-tý řádek.".format(i)) i += 1 - print("Následuje předávání do kontextu.") # vytahané informace předáváme do kontextu context['cislo'] = cislo context['radky_vysledkovky'] = radky_vysledkovky @@ -817,45 +763,8 @@ class CisloView(generic.DetailView): cislo = context['cislo'] # vrátíme context (aktuálně obsahuje jen věci ohledně výsledkovky - return spocti_vysledkovku_cisla(cislo, context) + return vysledkovka_cisla(cislo, context) -# problemy = sorted(set(r.problem for r in reseni), key=lambda x:(poradi_typu[x.typ], x.kod_v_rocniku())) -# #setridi problemy podle typu a poradi zadani -# problem_index = {} -# for i in range(len(problemy)): -# #umoznuje zjistit index podle id problemu -# -# vysledky_resitele = {} -# vysledkovka = [] -# -# # doplníme některé údaje do řádků výsledkovky pro řešitele ve skupině -# for poradi, v in zip(sloupec_s_poradim(vysledky), vysledky): -# v.poradi = poradi -# v.body_celkem_rocnik = v.body -# v.body_celkem_odjakziva = VysledkyKCisluOdjakziva.objects.get(resitel=v.resitel, cislo=context['cislo']).body -# v.resitel.rocnik = v.resitel.rocnik(v.cislo.rocnik) -# -# # je tady '', aby se nezobrazovala 0, pokud se řešitel o řešení úlohy ani nepokusil -# v.body_ulohy = [''] * len(problemy) -# -# v.titul = v.resitel.get_titul(v.body_celkem_odjakziva) -# -# body_cislo_q = VysledkyZaCislo.objects.filter(resitel=v.resitel, cislo=context['cislo']) -# v.body_cislo = body_cislo_q[0].body if body_cislo_q else 0 -# -# vysledkovka.append(v) -# -# # připravíme si odkaz na řádek, abychom do něj mohli doplnit body za jednotlivé úlohy -# vysledky_resitele[v.resitel.id] = v -# -# # 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 -# context['resene_problemy'] = resene_problemy class ArchivTemataView(generic.ListView): model = Problem @@ -864,25 +773,24 @@ class ArchivTemataView(generic.ListView): ### Generovani vysledkovky -#class CisloVysledkovkaView(CisloView):i -# poradi | titul. jmeno prijmeni | ulohy | za cislo | celkem | odjakziva -# -# -# -# model = Cislo -# template_name = 'seminar/archiv/cislo_vysledkovka.tex' -# #content_type = 'application/x-tex; charset=UTF8' -# #umozni rovnou stahnout TeXovsky dokument -# content_type = 'text/plain; charset=UTF8' -# #vypise na stranku textovy obsah vyTeXane vysledkovky k okopirovani -# -#class RocnikVysledkovkaView(RocnikView): -# model = Rocnik -# template_name = 'seminar/archiv/rocnik_vysledkovka.tex' -# #content_type = 'application/x-tex; charset=UTF8' -# #umozni rovnou stahnout TeXovsky dokument -# content_type = 'text/plain; charset=UTF8' -# #vypise na stranku textovy obsah vyTeXane vysledkovky k okopirovani +class CisloVysledkovkaView(CisloView): + "View vytvořené pro stránku zobrazující výsledkovku čísla v TeXu." + + model = Cislo + template_name = 'seminar/archiv/cislo_vysledkovka.tex' + #content_type = 'application/x-tex; charset=UTF8' + #umozni rovnou stahnout TeXovsky dokument + content_type = 'text/plain; charset=UTF8' + #vypise na stranku textovy obsah vyTeXane vysledkovky k okopirovani + +class RocnikVysledkovkaView(RocnikView): + "View vytvořené pro stránku zobrazující výsledkovku ročníku v TeXu." + model = Rocnik + template_name = 'seminar/archiv/rocnik_vysledkovka.tex' + #content_type = 'application/x-tex; charset=UTF8' + #umozni rovnou stahnout TeXovsky dokument + content_type = 'text/plain; charset=UTF8' + #vypise na stranku textovy obsah vyTeXane vysledkovky k okopirovani ### Generovani obalek class CisloObalkyStruct: @@ -961,30 +869,31 @@ def oldObalkovaniView(request, rocnik, cislo): ### Tituly -# TODO udelat neco jako get_objects_or_404 -# FIXME: prepsat, aby nepouzivalo VysledkyK... -#def TitulyView(request, rocnik, cislo): -# rocnik_obj = Rocnik.objects.get(rocnik = rocnik) -# resitele = Resitel.objects.filter(rok_maturity__gte = rocnik_obj.prvni_rok) -# cislo_obj = Cislo.objects.get(rocnik = rocnik_obj, cislo = cislo) -# -# asciijmena = [] -# broken = False -# -# for resitel in resitele: -# try: -# vys = VysledkyKCisluOdjakziva.objects.get(resitel = resitel, cislo = cislo_obj) -# body = vys.body -# except ObjectDoesNotExist: -# body = 0 -# resitel.titul = resitel.get_titul(body) -# resitel.ascii = unicodedata.normalize('NFKD',resitel.jmeno+resitel.prijmeni).encode("ascii","ignore").replace(" ","") -# if resitel.ascii not in asciijmena: -# asciijmena.append(resitel.ascii) -# else: -# broken = True -# -# return render(request, 'seminar/archiv/tituly.tex',{'resitele': resitele,'broken':broken},content_type="text/plain") +def TitulyView(request, rocnik, cislo): + rocnik_obj = Rocnik.objects.get(rocnik = rocnik) + resitele = Resitel.objects.filter(rok_maturity__gte = rocnik_obj.prvni_rok) + cislo_obj = Cislo.objects.get(rocnik = rocnik_obj, poradi = cislo) + + asciijmena = [] + jmenovci = False # detekuje, zda jsou dva řešitelé jmenovci (modulo nabodeníčka), pokud ano, vrátí se jako true + slovnik_s_body = body_resitelu_odjakziva(rocnik_obj, resitele) + + for resitel in resitele: + resitel.titul = resitel.get_titul(slovnik_s_body[resitel.id]) + jmeno = resitel.osoba.jmeno+resitel.osoba.prijmeni + # převedeme jména a příjmení řešitelů do ASCII + ascii_jmeno_bytes = unicodedata.normalize('NFKD', jmeno).encode("ascii","ignore") + # vrátí se byte string, převedeme na standardní string + ascii_jmeno_divnoznaky = str(ascii_jmeno_bytes, "utf-8", "ignore").replace(" ","") + resitel.ascii = ''.join(a for a in ascii_jmeno_divnoznaky if a.isalnum()) + if resitel.ascii not in asciijmena: + asciijmena.append(resitel.ascii) + else: + jmenovci = True + + return render(request, 'seminar/archiv/tituly.tex', + {'resitele': resitele,'jmenovci':jmenovci},content_type="text/plain") + ### Soustredeni