Browse Source

Merge remote-tracking branch 'Gimli/data_migrations' into data_migrations

middleware_test
Pavel 'LEdoian' Turinsky 5 years ago
parent
commit
34817eeeb1
  1. BIN
      Schema_new.dia
  2. 2
      galerie/models.py
  3. 73
      mamweb/static/css/mamweb.css
  4. 10
      mamweb/templates/base.html
  5. 24
      seminar/migrations/0081_auto_20200408_2221.py
  6. 45
      seminar/models.py
  7. 48
      seminar/templates/seminar/archiv/cisla.html
  8. 9
      seminar/templates/seminar/archiv/cislo.html
  9. 30
      seminar/templates/seminar/archiv/rocnik.html
  10. 2
      seminar/templates/seminar/archiv/rocnik_vysledkovka.tex
  11. 4
      seminar/templates/seminar/cojemam/organizatori.html
  12. 12
      seminar/templates/seminar/vysledkovka_rocnik.html
  13. 380
      seminar/views/views_all.py

BIN
Schema_new.dia

Binary file not shown.

2
galerie/models.py

@ -69,6 +69,8 @@ class Obrazek(models.Model):
ordering = ['nazev'] ordering = ['nazev']
def obrazek_maly_tag(self): def obrazek_maly_tag(self):
if not self.obrazek_maly:
return ''
return u'<img src="{}">'.format(self.obrazek_maly.url) return u'<img src="{}">'.format(self.obrazek_maly.url)
obrazek_maly_tag.short_description = "Náhled" obrazek_maly_tag.short_description = "Náhled"
obrazek_maly_tag.allow_tags = True obrazek_maly_tag.allow_tags = True

73
mamweb/static/css/mamweb.css

@ -14,8 +14,31 @@ div.container {
margin: auto; margin: auto;
} }
.org-logged-in div.container {
margin-top: 20px;
}
div.login-bar { div.login-bar {
background: #6a0043; 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 */ /* odkazy a nadpisy */
@ -74,16 +97,16 @@ h6 {
.org-logged-in .mam-text-plugin { .org-logged-in .mam-text-plugin {
border: dashed 1px #f77; border: dashed 1px #6a0043;
padding: 5px; padding: 5px;
margin: -5px; margin: -5px;
} }
.mam-org-only { .mam-org-only {
background: #fff0d7; background: #eee4ec;
padding: 10px; padding: 10px;
margin: 10px -10px; margin: 10px -10px;
border: orange 2px dashed; border: #6a0043 2px dashed;
} }
.mam-org-only .mam-org-only { .mam-org-only .mam-org-only {
@ -123,6 +146,8 @@ h1 {
margin-top: 0px; margin-top: 0px;
} }
/* Comments */ /* Comments */
#id_comment { #id_comment {
@ -191,7 +216,7 @@ vikendovka
#header.NOCsoustredeni { background-image: url("../images/header/vylet.jpg");} #header.NOCsoustredeni { background-image: url("../images/header/vylet.jpg");}
#header.NOCzadani { background-image: url("../images/header/stiny.jpg");} #header.NOCzadani { background-image: url("../images/header/stiny.jpg");}
#header.NOCclanky { background-image: url("../images/header/ohen.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 { #header img.logo {
@ -407,6 +432,10 @@ ul.submenu {
/* malý tablet, mobil */ /* malý tablet, mobil */
@media (max-width: 650px) { @media (max-width: 650px) {
#hide-if-small.login-bar-flatpage {
display: none;
}
#title { #title {
display: none; display: none;
} }
@ -558,7 +587,7 @@ div.seznam_orgu {
text-align: center; text-align: center;
} }
div.org_pole { div.org_pole, div.rocnik_pole {
display: inline-block; display: inline-block;
width: 30%; width: 30%;
min-width: 300px; min-width: 300px;
@ -576,16 +605,24 @@ div.org_email {
font-weight: bold; font-weight: bold;
} }
/*otáčecí karty organizátorů*/ /*otáčecí karty (orgové, archiv) */
.flip-card { .flip-card {
width: 200px;
height: 250px;
perspective: 1000px; /* Remove this if you don't want the 3D effect */ perspective: 1000px; /* Remove this if you don't want the 3D effect */
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
} }
#organizatori.flip-card {
width: 200px;
height: 250px;
}
#archiv.flip-card {
width: 210px;
height: 298px;
}
/* This container is needed to position the front and back side */ /* This container is needed to position the front and back side */
.flip-card-inner { .flip-card-inner {
position: relative; position: relative;
@ -614,9 +651,10 @@ div.org_email {
background-color: #bbb; background-color: #bbb;
} }
div.foto_org img { div.flip-card-foto img {
width: 100%; width: 100%;
height: 100%; height: 100%;
filter: drop-shadow(0px 3px 3px rgba(0, 0, 0, 0.4)); /* FIXME: obecně k obrázkům */
} }
/* Style the back side */ /* Style the back side */
@ -631,6 +669,23 @@ div.foto_org img {
padding-top: 20px; padding-top: 20px;
} }
/* karty archiv */
div.popis_rocniku {
text-align: left;
font-weight: bold;
margin: 20px;
}
div.popis_rocniku a{
color: black;
}
div.popis_rocniku a:hover{
color: #6f2509;
}
/* graf na úvodní stránce */ /* graf na úvodní stránce */
a span.popup { a span.popup {

10
mamweb/templates/base.html

@ -36,21 +36,21 @@
</head> </head>
<body class='{% if user.is_staff %}org-logged-in{% endif %}'> <body class='{% if user.is_staff %}org-logged-in{% endif %}'>
<div class="container">
{% if user.is_staff %} {% if user.is_staff %}
<div class="login-bar" > <div class="login-bar" >
{% if view.object %} {% if view.object %}
Objekt {{ view.object }}: {{ view.object }} Objekt {{ view.object }}: {{ view.object }}
{% if view.object.admin_url %}<a href='{{ view.object.admin_url }}'>[admin]</a>{% endif %} {% if view.object.admin_url %}<a class="login-ref-admin" href='{{ view.object.admin_url }}'>[admin]</a>{% endif %}
{% endif %} {% endif %}
{% if flatpage %} {% if flatpage %}
Stránka <tt>{{ flatpage.url }}</tt> ({{ flatpage.title }}) <div class="login-bar-flatpage" id="hide-if-small"> Stránka <tt>{{ flatpage.url }}</tt> ({{ flatpage.title }})</div>
<a href='{% url 'admin:flatpages_flatpage_change' flatpage.id %}'>[admin]</a> <a class="login-ref-admin" href='{% url 'admin:flatpages_flatpage_change' flatpage.id %}'>[admin]</a>
{% endif %} {% endif %}
<a class="login-ref-admin" href='/admin'>[admin mainpage]</a>
</div> </div>
{% endif %} {% endif %}
<div class="container">
<div class='row'> <div class='row'>
<div class='col-md-12'> <div class='col-md-12'>

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

45
seminar/models.py

@ -1,6 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import os import os
import random import random
import subprocess
import pathlib
import tempfile
from django.db import models from django.db import models
from django.contrib import auth from django.contrib import auth
@ -27,6 +30,7 @@ from unidecode import unidecode # Používám pro získání ID odkazu (ještě
from polymorphic.models import PolymorphicModel from polymorphic.models import PolymorphicModel
class SeminarModelBase(models.Model): class SeminarModelBase(models.Model):
class Meta: class Meta:
@ -407,7 +411,11 @@ class Rocnik(SeminarModelBase):
def cislo_pdf_filename(self, filename): def cislo_pdf_filename(self, filename):
rocnik = str(self.rocnik.rocnik) rocnik = str(self.rocnik.rocnik)
return os.path.join('cislo', 'pdf', rocnik, '{}-{}.pdf'.format(rocnik, self.poradi)) return pathlib.Path('cislo', 'pdf', rocnik, '{}-{}.pdf'.format(rocnik, self.poradi))
def cislo_png_filename(self, filename):
rocnik = str(self.rocnik.rocnik)
return pathlib.Path('cislo', 'png', rocnik, '{}-{}.png'.format(rocnik, self.poradi))
@reversion.register(ignore_duplicates=True) @reversion.register(ignore_duplicates=True)
class Cislo(SeminarModelBase): class Cislo(SeminarModelBase):
@ -451,7 +459,10 @@ class Cislo(SeminarModelBase):
help_text='Neveřejná poznámka k číslu (plain text)') help_text='Neveřejná poznámka k číslu (plain text)')
pdf = models.FileField('pdf', upload_to=cislo_pdf_filename, null=True, blank=True, pdf = models.FileField('pdf', upload_to=cislo_pdf_filename, null=True, blank=True,
help_text='Pdf čísla, které si mohou řešitelé stáhnout') help_text='PDF čísla, které si mohou řešitelé stáhnout')
titulka_nahled = models.ImageField('Obrázek titulní strany', upload_to=cislo_png_filename, null=True, blank=True,
help_text='Obrázek titulní strany, generuje se automaticky')
# má OneToOneField s: # má OneToOneField s:
# CisloNode # CisloNode
@ -488,6 +499,35 @@ class Cislo(SeminarModelBase):
return None return None
return cs[i] return cs[i]
def vygeneruj_nahled(self):
VYSKA = 594
sirka = int(VYSKA*210/297)
if not self.pdf:
return
# Pokud obrázek neexistuje nebo není aktuální, vytvoř jej
if not self.titulka_nahled or os.path.getmtime(self.titulka_nahled.path) < os.path.getmtime(self.pdf.path):
png_filename = pathlib.Path(tempfile.mkdtemp(), 'nahled.png')
subprocess.call([
"convert",
"-density", "300x300",
"-geometry", "{}x{}".format(VYSKA, sirka),
"-background", "white",
"-flatten",
"{}[0]".format(self.pdf.path), # titulní strana
png_filename
])
with open(png_filename,'rb') as f:
self.titulka_nahled.save('',f,True)
png_filename.unlink()
png_filename.parent.rmdir()
@classmethod @classmethod
def get(cls, rocnik, cislo): def get(cls, rocnik, cislo):
try: try:
@ -499,6 +539,7 @@ class Cislo(SeminarModelBase):
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
super().save(*args, **kwargs) super().save(*args, **kwargs)
self.vygeneruj_nahled()
# *Node.save() aktualizuje název *Nodu. # *Node.save() aktualizuje název *Nodu.
try: try:
self.cislonode.save() self.cislonode.save()

48
seminar/templates/seminar/archiv/cisla.html

@ -8,18 +8,56 @@
{% endblock %}{% endblock %} {% endblock %}{% endblock %}
</h2> </h2>
<div class='nahledy_cisel'> <!-- <div class='nahledy_cisel'>
{% autoescape off %}{{ nahledy }}{% endautoescape %} {% autoescape off %}{{ nahledy }}{% endautoescape %}
</div>-->
{% for rocnik, url_png in object_list.items %}
<div class="rocnik_pole">
<h3>
<a href='{{ rocnik.verejne_url }}'>Ročník {{ rocnik }}</a>
</h3>
{# karta ročníku - zepředu obrázek prvního čísla, zezadu odkaz na jednotlivá čísla a výsledkovku #}
<div class="flip-card" id="archiv">
<div class="flip-card-inner">
<div class="flip-card-front">
<div class="flip-card-foto">
<img src="{{ url_png }}" height=200px alt="{{ rocnik }}">
</div> </div>
</div>
<div class="flip-card-back">
<div class="popis_rocniku">
Jednotlivá čísla:
<ul> <ul>
{% for r in object_list %} {% for cislo in rocnik.cisla.all reversed %}
<li><a href='{{ r.verejne_url }}'>Ročník {{ r }}</a> <li><a href='{{ cislo.verejne_url }}'>{{ cislo.poradi }}. číslo</a> {% if cislo.pdf %}(<a href='{{ cislo.pdf.url }}'>pdf</a>) {% endif %}
{% empty %} {% empty %}
Nejsou žádné ročníky ---
{% endfor %} {% endfor %}
</ul> </ul>
<a href='{{ rocnik.verejne_url }}'>Výsledková listina</a> <!-- FIXME: url výsledkovky-->
</div>
</div>
</div>
</div>
{# konec karty ročníku #}
<hr>
</div>
{% empty %}
Nejsou žádné ročníky
{% endfor %}
</div> </div>
{% endblock content %} {% endblock content %}

9
seminar/templates/seminar/archiv/cislo.html

@ -49,12 +49,12 @@
{% endif %} {% endif %}
{% if cislo.verejna_vysledkovka %} {% if cislo.verejna_vysledkovka %}
<h2>Výsledkovka ({% now "jS F Y H:i" %})</h2> <h2>Výsledkovka</h2>
{% else %} {% else %}
{% if user.is_staff %} {% if user.is_staff %}
<div class='mam-org-only'> <div class='mam-org-only'>
<h2>Výsledkovka (neveřejná, {% now "jS F Y H:i:s" %})</h2> <h2>Výsledkovka (neveřejná)</h2>
{% endif %} {% endif %}
{% endif %} {% endif %}
@ -73,7 +73,7 @@
<tr> <tr>
<td class='border-r'>{% autoescape off %}{{ rv.poradi }}{% endautoescape %} <td class='border-r'>{% autoescape off %}{{ rv.poradi }}{% endautoescape %}
<th class='border-r'> <th class='border-r'>
{% if rv.titul is not '' %} {% if rv.titul %}
{{ rv.titul }}<sup>MM</sup> {{ rv.titul }}<sup>MM</sup>
{% endif %} {% endif %}
{{ rv.resitel.osoba.plne_jmeno }} {{ rv.resitel.osoba.plne_jmeno }}
@ -92,8 +92,5 @@
</div> </div>
{% endif %} {% endif %}
Čas: {% now "jS F Y H:i:s" %}
</div> </div>
{% endblock content %} {% endblock content %}

30
seminar/templates/seminar/archiv/rocnik.html

@ -2,13 +2,20 @@
{% block content %} {% block content %}
<div> <div>
<h1> <h2>
{% block nadpis1a %}{% block nadpis1b %} {% block nadpis1a %}{% block nadpis1b %}
Ročník {{ rocnik.roman }} Ročník {{ rocnik }}
{% endblock %}{% endblock %} {% endblock %}{% endblock %}
</h1> </h2>
<p>Ročník číslo {{ rocnik.rocnik }} ({{ rocnik.prvni_rok }}/{{ rocnik.druhy_rok }}) {% if temata_v_rocniku %}
<h2>Témata</h2>
<ul>
{% for tema in temata_v_rocniku %}
<li>{% if tema.text_zadani %}<a href="{{ tema.verejne_url }}">{% endif %}{{ tema.kod_v_rocniku }}: {{ tema.nazev }}{% if tema.text_zadani %}</a>{% endif %}
{% endfor %}
</ul>
{% endif %}
<ul> <ul>
{% for c in rocnik.verejna_cisla %} {% for c in rocnik.verejna_cisla %}
@ -19,15 +26,6 @@
{% endfor %} {% endfor %}
</ul> </ul>
{% if temata_v_rocniku %}
<h2>Témata</h2>
<ul>
{% for tema in temata_v_rocniku %}
<li>{% if tema.text_zadani %}<a href="{{ tema.verejne_url }}">{% endif %}{{ tema.kod_v_rocniku }}: {{ tema.nazev }}{% if tema.text_zadani %}</a>{% endif %}
{% endfor %}
</ul>
{% endif %}
{% if vysledkovka %} {% if vysledkovka %}
{% if user.is_staff %} {% if user.is_staff %}
<div class='mam-org-only'> <div class='mam-org-only'>
@ -42,7 +40,7 @@
{% if user.is_staff and vysledkovka_s_neverejnymi %} {% if user.is_staff and vysledkovka_s_neverejnymi %}
<div class='mam-org-only'> <div class='mam-org-only'>
<h2>Výsledková listina včetně neveřejných bodů</h2> <h2>Výsledková listina včetně neveřejných bodů</h2>
{% with vysledkovka_s_neverejnymi as vysledkovka %} {% with radky_vyledkovky_s_neverejnymi as radky_vysledkovky %}
{% include "seminar/vysledkovka_rocnik.html" %} {% include "seminar/vysledkovka_rocnik.html" %}
{% endwith %} {% endwith %}
</div> </div>
@ -50,7 +48,3 @@
</div> </div>
{% endblock content %} {% endblock content %}

2
seminar/templates/seminar/archiv/rocnik_vysledkovka.tex

@ -7,7 +7,7 @@
\endhead \endhead
\hline \hline
\endfoot \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.resitel.rocnik %}{{ rv.resitel.rocnik }}.{% endif %} & {{ rv.body_odjakziva }} {% for b in rv.body_cisla_sezn %} & {{ b }}{% endfor %} & {{ rv.body_rocnik }} \\
{% endfor %}\end{longtable} {% endfor %}\end{longtable}
{% endwith %} {% endwith %}
{% endwith %} {% endwith %}

4
seminar/templates/seminar/cojemam/organizatori.html

@ -46,12 +46,12 @@
{# karta organizátora - zepředu fotka, zezadu popis, u neaktivních data kdy organizovali #} {# karta organizátora - zepředu fotka, zezadu popis, u neaktivních data kdy organizovali #}
<div class="flip-card"> <div class="flip-card" id="organizatori">
<div class="flip-card-inner"> <div class="flip-card-inner">
<div class="flip-card-front"> <div class="flip-card-front">
<div class="foto_org"> <div class="flip-card-foto">
{% if org.osoba.foto %} {% if org.osoba.foto %}
<a href="{{org.osoba.foto.url}}"><img src="{{org.osoba.foto_male.url}}" height="{{org.osoba.foto_male.height}}" alt="{{org.osoba.jmeno}} {{org.osoba.prijmeni}}"></a> <a href="{{org.osoba.foto.url}}"><img src="{{org.osoba.foto_male.url}}" height="{{org.osoba.foto_male.height}}" alt="{{org.osoba.jmeno}} {{org.osoba.prijmeni}}"></a>
{% else %} {# pokud osoba nemá fotku, zobrazuje se defaultní obrázek #} {% else %} {# pokud osoba nemá fotku, zobrazuje se defaultní obrázek #}

12
seminar/templates/seminar/vysledkovka_rocnik.html

@ -4,23 +4,23 @@
<th class='border-r'>Jméno <th class='border-r'>Jméno
<th class='border-r'>R. <th class='border-r'>R.
<th class='border-r'>Odjakživa <th class='border-r'>Odjakživa
{% for c in vysledkovka.cisla %} {% for c in cisla %}
<th class='border-r'><a href="{{ c.verejne_url }}"> <th class='border-r'><a href="{{ c.verejne_url }}">
{{c.rocnik.rocnik}}.{{ c.cislo }}</a> {{c.rocnik.rocnik}}.{{ c.poradi }}</a>
{% endfor %} {% endfor %}
<th class='border-r'>Celkem <th class='border-r'>Celkem
{% for rv in vysledkovka.radky %} {% for rv in radky_vysledkovky %}
<tr> <tr>
<td class='border-r'>{% autoescape off %}{{ rv.poradi }}{% endautoescape %} <td class='border-r'>{% autoescape off %}{{ rv.poradi }}{% endautoescape %}
<th class='border-r'> <th class='border-r'>
{% if rv.titul %} {% if rv.titul %}
{{ rv.titul }}<sup>MM</sup> {{ rv.titul }}<sup>MM</sup>
{% endif %} {% endif %}
{{ rv.resitel.plne_jmeno }} {{ rv.resitel.osoba.plne_jmeno }}
<td class='border-r'>{{ rv.resitel.rocnik }} <td class='border-r'>{{ rv.resitel.rocnik }}
<td class='border-r'>{{ rv.body_odjakziva }} <td class='border-r'>{{ rv.body_celkem_odjakziva }}
{% for b in rv.body_cisla %} {% for b in rv.body_cisla_sezn %}
<td class='border-r'>{{ b }} <td class='border-r'>{{ b }}
{% endfor %} {% endfor %}
<td class='border-r'><b>{{ rv.body_rocnik }}</b> <td class='border-r'><b>{{ rv.body_rocnik }}</b>

380
seminar/views/views_all.py

@ -308,24 +308,51 @@ class ArchivView(generic.ListView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(ArchivView, self).get_context_data(**kwargs) context = super(ArchivView, self).get_context_data(**kwargs)
vyska = 297 # px vyska = 594 # px
sirka = 210 # px sirka = 420 # px
# nejnovějších 10 zveřejněných čísel # první číslo z každého ročníku
# cisla = Cislo.objects.filter(verejne_db=True)[:10] cisla = Cislo.objects.filter(poradi=1)
cisla = Cislo.objects.filter(poradi=1)[:10]
# op == os.path, udělá z argumentů cestu # op == os.path, udělá z argumentů cestu
png_dir = op.join(settings.MEDIA_ROOT, "cislo", "png") png_dir = op.join(settings.MEDIA_ROOT, "cislo", "png")
# seznam [(url obrázku, číslo)] # slovník {(ročník, url obrázku)}
urls = [] urls ={}
for i, c in enumerate(cisla): # for j, rocnik in enumerate(Rocnik.objects.all()):
# urls_rocnik = {}
# for i,c in enumerate(rocnik.cisla.all()):
# if not c.pdf:
# urls_rocnik[c.poradi] = op.join(settings.MEDIA_URL, "cislo", "png", "default.png")
# else:
# filename = os.path.split(c.pdf.file.name)[1].split(".")[0]
# png_filename = "{}.png".format(filename)
# # 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",
# "{}[0]".format(c.pdf.path), # titulní strana
# png_path
# ])
# urls_rocnik[c.poradi] = op.join(settings.MEDIA_URL, "cislo", "png", png_filename)
# urls[rocnik] = urls_rocnik
for i,c in enumerate(cisla):
if not c.pdf: if not c.pdf:
continue urls[c.rocnik] = op.join(settings.MEDIA_URL, "cislo", "png", "default.png")
else:
filename = os.path.split(c.pdf.file.name)[1].split(".")[0] filename = os.path.split(c.pdf.file.name)[1].split(".")[0]
png_filename = "{}-{}px.png".format(filename, vyska) png_filename = "{}.png".format(filename)
# Pokud obrázek neexistuje nebo není aktuální, vytvoř jej # Pokud obrázek neexistuje nebo není aktuální, vytvoř jej
png_path = op.join(png_dir, png_filename) png_path = op.join(png_dir, png_filename)
@ -338,63 +365,95 @@ class ArchivView(generic.ListView):
"-geometry", "{}x{}".format(vyska, sirka), "-geometry", "{}x{}".format(vyska, sirka),
"-background", "white", "-background", "white",
"-flatten", "-flatten",
"-rotate", str(90 * i),
"{}[0]".format(c.pdf.path), # titulní strana "{}[0]".format(c.pdf.path), # titulní strana
png_path png_path
]) ])
urls.append( urls[c.rocnik] = op.join(settings.MEDIA_URL, "cislo", "png", png_filename)
(op.join(settings.MEDIA_URL, "cislo", "png", png_filename), c)
) context["object_list"] = urls
vyska, sirka = sirka, vyska / 2
print(context)
tags = []
# for i, c in enumerate(cisla):
def spirala(urls, tags, idx): # if not c.pdf:
"""Rekurzivně prochází urls a generuje strom elementů do tags""" # continue
if idx >= len(urls): # filename = os.path.split(c.pdf.file.name)[1].split(".")[0]
return # png_filename = "{}-{}px.png".format(filename, vyska)
img_url, cislo = urls[idx] # # Pokud obrázek neexistuje nebo není aktuální, vytvoř jej
tags.append( # png_path = op.join(png_dir, png_filename)
"<div style='top:{}%;left:{}%;width:{}%;height:{}%;'>" # if not op.exists(png_path) or \
.format( # op.getmtime(png_path) < op.getmtime(c.pdf.path):
50 if idx % 4 == 2 else 0,
50 if idx % 4 == 1 else 0, # subprocess.call([
50 if idx % 2 == 1 else 100, # "convert",
50 if idx > 0 and idx % 2 == 0 else 100 # "-density", "300x300",
) # "-geometry", "{}x{}".format(vyska, sirka),
) # "-background", "white",
tags.append("<a href='{}' title='{}'>".format( # "-flatten",
cislo.verejne_url(), cislo.kod() # "-rotate", str(90 * i),
)) # "{}[0]".format(c.pdf.path), # titulní strana
tags.append( # png_path
"<img src='{}' style='top:{}%;left:{}%;width:{}%;height:{}%;'>" # ])
.format(
img_url, # urls.append(
50 if idx % 4 == 3 else 0, # (op.join(settings.MEDIA_URL, "cislo", "png", png_filename), c)
50 if idx % 4 == 2 else 0, # )
50 if idx % 2 == 0 else 100, # vyska, sirka = sirka, vyska / 2
50 if idx % 2 == 1 else 100
) # tags = []
)
tags.append("</a>") # def spirala(urls, tags, idx):
spirala(urls, tags, idx + 1) # """Rekurzivně prochází urls a generuje strom elementů do tags"""
tags.append("</div>") # if idx >= len(urls):
spirala(urls, tags, 0) # return
# img_url, cislo = urls[idx]
# tags.append(
# "<div style='top:{}%;left:{}%;width:{}%;height:{}%;'>"
# .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("<a href='{}' title='{}'>".format(
# cislo.verejne_url(), cislo.kod()
# ))
# tags.append(
# "<img src='{}' style='top:{}%;left:{}%;width:{}%;height:{}%;'>"
# .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("</a>")
# spirala(urls, tags, idx + 1)
# tags.append("</div>")
# spirala(urls, tags, 0)
# context["nahledy"] = "\n".join(tags)
context["nahledy"] = "\n".join(tags)
return context return context
### Výsledky ### 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.) # ze seznamu obsahujícího sestupně setřízené body řešitelů za daný ročník
def sloupec_s_poradim(seznam_s_body): # 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 aktualni_poradi = 1
sloupec_s_poradim = [] sloupec_s_poradim = []
# seskupíme seznam všech bodů podle hodnot # 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 # 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 # vypsat už jen prázdné místo, než dojdeme na správný řádek
if (index + 1) < aktualni_poradi: if (index + 1) < aktualni_poradi:
@ -402,10 +461,10 @@ def sloupec_s_poradim(seznam_s_body):
continue continue
velikost_skupiny = 0 velikost_skupiny = 0
# zjistíme počet po sobě jdoucích stejných hodnot # 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 velikost_skupiny = velikost_skupiny + 1
# na konci musíme ošetřit přetečení seznamu # 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 break
# pokud je velikost skupiny 1, vypíšu pořadí # pokud je velikost skupiny 1, vypíšu pořadí
if velikost_skupiny == 1: if velikost_skupiny == 1:
@ -418,28 +477,12 @@ def sloupec_s_poradim(seznam_s_body):
aktualni_poradi = aktualni_poradi + velikost_skupiny aktualni_poradi = aktualni_poradi + velikost_skupiny
return sloupec_s_poradim 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 # vrátí všechna čísla daného ročníku
#def __soucet_resitele_problemu(problem, resitel, cislo, soucet): def cisla_rocniku(rocnik, jen_verejne=True):
# # sečteme body za daný problém přes všechna řešení daného problému if jen_verejne:
# # od daného řešitele return rocnik.verejna_cisla()
# reseni_resitele = s.Reseni_Resitele.objects.filter(resitele=resitel) else:
# hodnoceni_resitele = problem.hodnoceni.filter(reseni__in=reseni_resitele, return rocnik.cisla.all()
# 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)
# pro daný problém vrátí jeho nejvyšší nadproblém # pro daný problém vrátí jeho nejvyšší nadproblém
def hlavni_problem(problem): def hlavni_problem(problem):
@ -447,6 +490,17 @@ def hlavni_problem(problem):
problem = problem.nadproblem problem = problem.nadproblem
return problem 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 # vrátí list všech problémů s body v daném čísle, které již nemají nadproblém
def hlavni_problemy_cisla(cislo): def hlavni_problemy_cisla(cislo):
hodnoceni = cislo.hodnoceni.select_related('problem', 'reseni').all() hodnoceni = cislo.hodnoceni.select_related('problem', 'reseni').all()
@ -476,27 +530,32 @@ def body_resitelu_odjakziva(rocnik, resitele):
for r in resitele: for r in resitele:
body_odjakziva[str(r.id)] = 0 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 # POZOR! Aktuálně počítá jen za posledních 10 let od zadaného ročníku #
# # s aktivními řešiteli) #########################################################################
# # Body za posledních 10 let je dobrá aproximace pro naše potřeby (výsledkovka
# body_pred_roky = [] # s aktivními řešiteli)
# for i in range(0, 10):
# body_pred_roky.append(body_resitelu_za_rocnik(rocnik-i, resitele)) body_pred_roky = []
# rok = rocnik.prvni_rok
# for r in resitele: rocniky = Rocnik.objects.filter(prvni_rok__gt=rok-11)
# for i in range(0,10): for roc in rocniky:
# body_odjakziva[str(r.id)] += body_pred_roky[i][str(r.id)] body_pred_roky.append(body_resitelu_za_rocnik(roc, 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(): # Nasledující řešení je sice správné, ale moc pomalé:
pricti_body(body_odjakziva, r, hodn.body) # (důsledek toho, že dotazy na joinování databázových tabulek jsou kvadratické)
# 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)
return body_odjakziva return body_odjakziva
# vrátí slovník řešitel:body obsahující počty bodů zadaných řešitelů za daný ročník # vrátí slovník řešitel:body obsahující počty bodů zadaných řešitelů za daný ročník
@ -514,13 +573,77 @@ def body_resitelu_za_rocnik(rocnik, aktivni_resitele):
pricti_body(body_za_rocnik, resitel, hodn.body) pricti_body(body_za_rocnik, resitel, hodn.body)
return body_za_rocnik return body_za_rocnik
# TODO: předělat na nový model class RadekVysledkovkyRocniku(object):
#def vysledkovka_rocniku(rocnik, jen_verejne=True): """Obsahuje věci, které se hodí vědět při konstruování výsledkovky.
# """Přebírá ročník (např. context["rocnik"]) a vrací výsledkovou listinu ve Umožňuje snazší práci v templatu (lepší, než seznam)."""
# formě vhodné pro šablonu "seminar/vysledkovka_rocniku.html"
# """ def __init__(self, poradi, resitel, body_cisla_sezn, body_rocnik, body_odjakziva):
# self.poradi = poradi
# #vyberu vsechny vysledky z rocniku self.resitel = resitel
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[str(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[str(cislo.id)][str(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
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
#vyberu vsechny vysledky z rocniku
# cisla_v_rocniku = VysledkyKCisluZaRocnik.objects.filter(cislo__rocnik=rocnik).order_by('cislo') # cisla_v_rocniku = VysledkyKCisluZaRocnik.objects.filter(cislo__rocnik=rocnik).order_by('cislo')
# if jen_verejne: # if jen_verejne:
# cisla_v_rocniku = cisla_v_rocniku.filter(cislo__verejna_vysledkovka=True) # cisla_v_rocniku = cisla_v_rocniku.filter(cislo__verejna_vysledkovka=True)
@ -594,10 +717,15 @@ class RocnikView(generic.DetailView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(RocnikView, self).get_context_data(**kwargs) context = super(RocnikView, self).get_context_data(**kwargs)
#context['vysledkovka'] = vysledkovka_rocniku(context["rocnik"]) # vysledkovka = True zajistí vykreslení,
#context['vysledkovka_s_neverejnymi'] = vysledkovka_rocniku(context["rocnik"], jen_verejne=False) # zkontrolovat, kdy se má a nemá vykreslovat
#context['temata_v_rocniku'] = verejna_temata(context["rocnik"]) context['vysledkovka'] = True
# FIXME: opravit vylistování témat v ročníku 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 return context
@ -621,7 +749,7 @@ class ProblemView(generic.DetailView):
return context return context
class RadekVysledkovky(object): class RadekVysledkovkyCisla(object):
"""Obsahuje věci, které se hodí vědět při konstruování výsledkovky. """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).""" Umožňuje snazší práci v templatu (lepší, než seznam)."""
@ -647,19 +775,22 @@ def pricti_body(slovnik, resitel, body):
slovnik[str(resitel.id)] += body slovnik[str(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 # 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ě # 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(), resitel_rocnikbody_sezn = sorted(resitel_rocnikbody_slov.items(),
key = lambda x: x[1], reverse = True) key = lambda x: x[1], reverse = True)
return resitel_rocnikbody_sezn return resitel_rocnikbody_sezn
# spočítá u řešitelů body za číslo a za jednotlivé hlavní problémy (témata) # 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é # 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 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ů) # 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 = {} hlavni_problemy_slovnik = {}
for hp in hlavni_problemy: for hp in hlavni_problemy:
hlavni_problemy_slovnik[str(hp.id)] = {} hlavni_problemy_slovnik[str(hp.id)] = {}
@ -697,12 +828,11 @@ def secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy):
return hlavni_problemy_slovnik, cislobody return hlavni_problemy_slovnik, cislobody
def spocti_vysledkovku_cisla(cislo, context=None): def vysledkovka_cisla(cislo, context=None):
if context is None: if context is None:
context = {} context = {}
hlavni_problemy = hlavni_problemy_cisla(cislo) 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 ## 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í # 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 # u alespoň jedné hodnoty něco jiného než NULL
@ -715,10 +845,10 @@ def spocti_vysledkovku_cisla(cislo, context=None):
hlavni_problemy_slovnik, cislobody = secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy) 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ě # 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 # 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) aktivni_resitele)
# řešitelé setřídění podle bodů za číslo sestupně # řešitelé setřídění podle bodů za číslo sestupně
@ -738,7 +868,7 @@ def spocti_vysledkovku_cisla(cislo, context=None):
for hp in hlavni_problemy: for hp in hlavni_problemy:
problemy.append(hlavni_problemy_slovnik[str(hp.id)][ar_id]) problemy.append(hlavni_problemy_slovnik[str(hp.id)][ar_id])
# vytáhneme informace pro daného řešitele # vytáhneme informace pro daného řešitele
radek = RadekVysledkovky( radek = RadekVysledkovkyCisla(
poradi[i], # pořadí poradi[i], # pořadí
Resitel.objects.get(id=ar_id), # řešitel (z id) Resitel.objects.get(id=ar_id), # řešitel (z id)
problemy, # seznam bodů za hlavní problémy čísla problemy, # seznam bodů za hlavní problémy čísla
@ -785,7 +915,7 @@ class CisloView(generic.DetailView):
cislo = context['cislo'] cislo = context['cislo']
# vrátíme context (aktuálně obsahuje jen věci ohledně výsledkovky # 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())) # 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 # #setridi problemy podle typu a poradi zadani
@ -844,13 +974,13 @@ class ArchivTemataView(generic.ListView):
# content_type = 'text/plain; charset=UTF8' # content_type = 'text/plain; charset=UTF8'
# #vypise na stranku textovy obsah vyTeXane vysledkovky k okopirovani # #vypise na stranku textovy obsah vyTeXane vysledkovky k okopirovani
# #
#class RocnikVysledkovkaView(RocnikView): class RocnikVysledkovkaView(RocnikView):
# model = Rocnik model = Rocnik
# template_name = 'seminar/archiv/rocnik_vysledkovka.tex' template_name = 'seminar/archiv/rocnik_vysledkovka.tex'
# #content_type = 'application/x-tex; charset=UTF8' #content_type = 'application/x-tex; charset=UTF8'
# #umozni rovnou stahnout TeXovsky dokument #umozni rovnou stahnout TeXovsky dokument
# content_type = 'text/plain; charset=UTF8' content_type = 'text/plain; charset=UTF8'
# #vypise na stranku textovy obsah vyTeXane vysledkovky k okopirovani #vypise na stranku textovy obsah vyTeXane vysledkovky k okopirovani
### Generovani obalek ### Generovani obalek
class CisloObalkyStruct: class CisloObalkyStruct:

Loading…
Cancel
Save