From 0ee4ac877e5bcaddeb2ff735cb791f924cd3fdd4 Mon Sep 17 00:00:00 2001 From: Pavel 'LEdoian' Turinsky Date: Wed, 1 Apr 2020 21:59:34 +0200 Subject: [PATCH 01/26] =?UTF-8?q?Models:=20Dneska=20jsme=20zase=20p=C5=99e?= =?UTF-8?q?kopali=20model.=20U=C5=BE=20zase=20n=C3=A1m=20d=C3=A1v=C3=A1=20?= =?UTF-8?q?smysl?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seminar/models.py | 92 +++++++++++------------------------------------ 1 file changed, 21 insertions(+), 71 deletions(-) diff --git a/seminar/models.py b/seminar/models.py index 2a1c60d6..b7a09734 100644 --- a/seminar/models.py +++ b/seminar/models.py @@ -751,14 +751,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 @@ -767,15 +759,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' @@ -885,13 +868,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)') @@ -1100,7 +1080,7 @@ 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' @@ -1108,17 +1088,12 @@ class Konfera(models.Model): # 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') @@ -1126,13 +1101,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 = [ @@ -1149,21 +1117,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) @@ -1376,36 +1332,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) + + 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): @@ -1486,7 +1436,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)' From 732a2273db52db389bf6655c7d976209c717e8ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kate=C5=99ina=20=C4=8C=C3=AD=C5=BEkov=C3=A1?= Date: Wed, 1 Apr 2020 23:09:58 +0200 Subject: [PATCH 02/26] =?UTF-8?q?pokusy=20s=20archivem,=20pokou=C5=A1?= =?UTF-8?q?=C3=ADm=20se=20do=20kontextu=20k=20ro=C4=8Dn=C3=ADku=20p=C5=99i?= =?UTF-8?q?dat=20cestu=20k=20png=201.=20=C4=8D=C3=ADsla,=20te=C4=8F=20to?= =?UTF-8?q?=20nefunguje?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seminar/templates/seminar/archiv/cisla.html | 2 +- seminar/views/views_all.py | 166 ++++++++++++-------- 2 files changed, 101 insertions(+), 67 deletions(-) diff --git a/seminar/templates/seminar/archiv/cisla.html b/seminar/templates/seminar/archiv/cisla.html index fbb19263..c58a3a33 100644 --- a/seminar/templates/seminar/archiv/cisla.html +++ b/seminar/templates/seminar/archiv/cisla.html @@ -15,6 +15,7 @@
    {% for r in object_list %}
  • Ročník {{ r }} + {% empty %} Nejsou žádné ročníky {% endfor %} @@ -22,4 +23,3 @@ {% endblock content %} - diff --git a/seminar/views/views_all.py b/seminar/views/views_all.py index 01546843..47444ed4 100644 --- a/seminar/views/views_all.py +++ b/seminar/views/views_all.py @@ -311,79 +311,113 @@ class ArchivView(generic.ListView): 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] + # první číslo z každého ročníku + cisla = Cislo.objects.filter(poradi=1) # op == os.path, udělá z argumentů cestu png_dir = op.join(settings.MEDIA_ROOT, "cislo", "png") # seznam [(url obrázku, číslo)] - urls = [] + urls ={} - for i, c in enumerate(cisla): + 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) - - context["nahledy"] = "\n".join(tags) + urls[c.rocnik] = op.join(settings.MEDIA_URL, "cislo", "png", "default.png") + # urls.append( + # (op.join(settings.MEDIA_URL, "cislo", "png", "default.png"), c.rocnik) + # ) + 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[c.rocnik] = op.join(settings.MEDIA_URL, "cislo", "png", png_filename) + #urls.append( + #(op.join(settings.MEDIA_URL, "cislo", "png", png_filename), c.rocnik) + #) + + context["object_list"] = 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) + + # context["nahledy"] = "\n".join(tags) + print(context) + for i in context["object_list"]: + print(context["object_list"][i]) return context ### Výsledky From 33ff9715c9448d1a6f6cc7c44ff176f34b1bcc4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kate=C5=99ina=20=C4=8C=C3=AD=C5=BEkov=C3=A1?= Date: Wed, 1 Apr 2020 23:28:26 +0200 Subject: [PATCH 03/26] =?UTF-8?q?archiv=20obr=C3=A1zky=20u=20ro=C4=8Dn?= =?UTF-8?q?=C3=ADk=C5=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seminar/templates/seminar/archiv/cisla.html | 6 +++--- seminar/views/views_all.py | 12 ++---------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/seminar/templates/seminar/archiv/cisla.html b/seminar/templates/seminar/archiv/cisla.html index c58a3a33..d6981ff3 100644 --- a/seminar/templates/seminar/archiv/cisla.html +++ b/seminar/templates/seminar/archiv/cisla.html @@ -13,9 +13,9 @@
      - {% for r in object_list %} -
    • Ročník {{ r }} - + {% for rocnik, url_png in object_list.items %} +
    • Ročník {{ rocnik }} + {% empty %} Nejsou žádné ročníky {% endfor %} diff --git a/seminar/views/views_all.py b/seminar/views/views_all.py index 47444ed4..89830574 100644 --- a/seminar/views/views_all.py +++ b/seminar/views/views_all.py @@ -317,15 +317,12 @@ class ArchivView(generic.ListView): # op == os.path, udělá z argumentů cestu png_dir = op.join(settings.MEDIA_ROOT, "cislo", "png") - # seznam [(url obrázku, číslo)] + # slovník {(ročník, url obrázku)} urls ={} for i,c in enumerate(cisla): if not c.pdf: urls[c.rocnik] = op.join(settings.MEDIA_URL, "cislo", "png", "default.png") - # urls.append( - # (op.join(settings.MEDIA_URL, "cislo", "png", "default.png"), c.rocnik) - # ) else: filename = os.path.split(c.pdf.file.name)[1].split(".")[0] png_filename = "{}.png".format(filename) @@ -346,9 +343,6 @@ class ArchivView(generic.ListView): ]) urls[c.rocnik] = op.join(settings.MEDIA_URL, "cislo", "png", png_filename) - #urls.append( - #(op.join(settings.MEDIA_URL, "cislo", "png", png_filename), c.rocnik) - #) context["object_list"] = urls @@ -415,9 +409,7 @@ class ArchivView(generic.ListView): # spirala(urls, tags, 0) # context["nahledy"] = "\n".join(tags) - print(context) - for i in context["object_list"]: - print(context["object_list"][i]) + return context ### Výsledky From 90648aedae381001e3d8432969d5d9e112beb9ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kate=C5=99ina=20=C4=8C=C3=AD=C5=BEkov=C3=A1?= Date: Thu, 2 Apr 2020 21:43:58 +0200 Subject: [PATCH 04/26] vzhled archivu flipcards --- mamweb/static/css/mamweb.css | 10 ++++- seminar/templates/seminar/archiv/cisla.html | 42 ++++++++++++++++--- .../seminar/cojemam/organizatori.html | 2 +- seminar/views/views_all.py | 4 +- 4 files changed, 47 insertions(+), 11 deletions(-) diff --git a/mamweb/static/css/mamweb.css b/mamweb/static/css/mamweb.css index 357b5027..033e0c4e 100644 --- a/mamweb/static/css/mamweb.css +++ b/mamweb/static/css/mamweb.css @@ -191,7 +191,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 { @@ -586,6 +586,11 @@ div.org_email { margin-right: auto; } +#archiv.flip-card { + width: 210px; + height: 298px; +} + /* This container is needed to position the front and back side */ .flip-card-inner { position: relative; @@ -614,9 +619,10 @@ div.org_email { background-color: #bbb; } -div.foto_org img { +div.flip-card-foto img { width: 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 */ diff --git a/seminar/templates/seminar/archiv/cisla.html b/seminar/templates/seminar/archiv/cisla.html index d6981ff3..60b61e6e 100644 --- a/seminar/templates/seminar/archiv/cisla.html +++ b/seminar/templates/seminar/archiv/cisla.html @@ -8,18 +8,48 @@ {% endblock %}{% endblock %} -
      + -
        {% for rocnik, url_png in object_list.items %} -
      • Ročník {{ rocnik }} - + +
        + +

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

        + + + {# karta ročníku - zepředu obrázek prvního čísla, zezadu odkaz na jednotlivá čísla a výsledkovku #} + +
        + +
        +
        + +
        + {{ rocnik }} +
        + +
        +
        + +
        + +
        + +
        +
        +
        + {# konec karty organizátora #} + +
        +
        + {% empty %} Nejsou žádné ročníky {% endfor %} -
      {% endblock content %} diff --git a/seminar/templates/seminar/cojemam/organizatori.html b/seminar/templates/seminar/cojemam/organizatori.html index 8e8f1aaa..58d13dde 100644 --- a/seminar/templates/seminar/cojemam/organizatori.html +++ b/seminar/templates/seminar/cojemam/organizatori.html @@ -51,7 +51,7 @@
      -
      +
      {% if org.osoba.foto %} {{org.osoba.jmeno}} {{org.osoba.prijmeni}} {% else %} {# pokud osoba nemá fotku, zobrazuje se defaultní obrázek #} diff --git a/seminar/views/views_all.py b/seminar/views/views_all.py index 89830574..9fe65ca0 100644 --- a/seminar/views/views_all.py +++ b/seminar/views/views_all.py @@ -308,8 +308,8 @@ class ArchivView(generic.ListView): def get_context_data(self, **kwargs): context = super(ArchivView, self).get_context_data(**kwargs) - vyska = 297 # px - sirka = 210 # px + vyska = 594 # px + sirka = 420 # px # první číslo z každého ročníku cisla = Cislo.objects.filter(poradi=1) From fded4e4e36b7284b3f9f81adde40aa54597e90b0 Mon Sep 17 00:00:00 2001 From: "Tomas \"Jethro\" Pokorny" Date: Thu, 2 Apr 2020 22:07:09 +0200 Subject: [PATCH 05/26] Schema | Aktualizovane a zjednodusene schema. --- Schema_new.dia | Bin 15640 -> 14779 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Schema_new.dia b/Schema_new.dia index f09c0589bd3e2de024ed79fd1c89b52b5e1e2bb4..c212ec3bfd493dffca658f7d328a3033f7d801cb 100644 GIT binary patch literal 14779 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 he*xuCr0M_w From 3e79ad6d543b908efc6165fe751e33d27968f0ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kate=C5=99ina=20=C4=8C=C3=AD=C5=BEkov=C3=A1?= Date: Fri, 3 Apr 2020 12:08:32 +0200 Subject: [PATCH 06/26] =?UTF-8?q?archiv=20flashcards=20-=20odkazy=20na=20z?= =?UTF-8?q?adn=C3=AD=20stran=C4=9B=20karet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mamweb/static/css/mamweb.css | 28 ++++++++++++++++--- seminar/templates/seminar/archiv/cisla.html | 13 +++++++-- .../seminar/cojemam/organizatori.html | 2 +- 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/mamweb/static/css/mamweb.css b/mamweb/static/css/mamweb.css index 033e0c4e..5089f46e 100644 --- a/mamweb/static/css/mamweb.css +++ b/mamweb/static/css/mamweb.css @@ -558,7 +558,7 @@ div.seznam_orgu { text-align: center; } -div.org_pole { +div.org_pole, div.rocnik_pole { display: inline-block; width: 30%; min-width: 300px; @@ -576,16 +576,19 @@ 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; @@ -637,6 +640,23 @@ div.flip-card-foto img { 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 */ a span.popup { diff --git a/seminar/templates/seminar/archiv/cisla.html b/seminar/templates/seminar/archiv/cisla.html index 60b61e6e..be860f81 100644 --- a/seminar/templates/seminar/archiv/cisla.html +++ b/seminar/templates/seminar/archiv/cisla.html @@ -14,7 +14,7 @@ {% for rocnik, url_png in object_list.items %} -

      +

      Ročník {{ rocnik }} @@ -34,9 +34,16 @@

      -
      - + Jednotlivá čísla: +
        + {% for cislo in rocnik.cisla.all reversed %} +
      • {{ cislo.poradi }}. číslo {% if cislo.pdf %}(pdf) {% endif %} + {% empty %} + --- + {% endfor %} +
      + Výsledková listina
      diff --git a/seminar/templates/seminar/cojemam/organizatori.html b/seminar/templates/seminar/cojemam/organizatori.html index 58d13dde..a3957101 100644 --- a/seminar/templates/seminar/cojemam/organizatori.html +++ b/seminar/templates/seminar/cojemam/organizatori.html @@ -46,7 +46,7 @@ {# karta organizátora - zepředu fotka, zezadu popis, u neaktivních data kdy organizovali #} -
      +
      From 835f35a44c3c867192db3bf8d9a3f70b61f66aca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kate=C5=99ina=20=C4=8C=C3=AD=C5=BEkov=C3=A1?= Date: Sat, 4 Apr 2020 10:54:20 +0200 Subject: [PATCH 07/26] =?UTF-8?q?pr=C3=A1ce=20na=20archivu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seminar/templates/seminar/archiv/cisla.html | 5 +- seminar/templates/seminar/archiv/cislo.html | 9 ++- seminar/templates/seminar/archiv/rocnik.html | 28 +++----- seminar/views/views_all.py | 73 ++++++++++++++------ 4 files changed, 69 insertions(+), 46 deletions(-) diff --git a/seminar/templates/seminar/archiv/cisla.html b/seminar/templates/seminar/archiv/cisla.html index be860f81..3c4a38b4 100644 --- a/seminar/templates/seminar/archiv/cisla.html +++ b/seminar/templates/seminar/archiv/cisla.html @@ -38,7 +38,7 @@ Jednotlivá čísla:
      - {# konec karty organizátora #} + + {# konec karty ročníku #}
      diff --git a/seminar/templates/seminar/archiv/cislo.html b/seminar/templates/seminar/archiv/cislo.html index 955988f1..3d3dae9e 100644 --- a/seminar/templates/seminar/archiv/cislo.html +++ b/seminar/templates/seminar/archiv/cislo.html @@ -46,7 +46,7 @@
    • Obálkování
    - {% endif %} + {% endif %} {% if cislo.verejna_vysledkovka %}

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

    @@ -86,7 +86,7 @@ {% endfor %} - {% endif %} + {% endif %} {% if not cislo.verejna_vysledkovka and user.is_staff %} @@ -94,6 +94,5 @@ Čas: {% now "jS F Y H:i:s" %} - -{% endblock content %} - + +{% endblock content %} diff --git a/seminar/templates/seminar/archiv/rocnik.html b/seminar/templates/seminar/archiv/rocnik.html index 64c4029d..c6165941 100644 --- a/seminar/templates/seminar/archiv/rocnik.html +++ b/seminar/templates/seminar/archiv/rocnik.html @@ -2,13 +2,20 @@ {% 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 }}) + {% if temata_v_rocniku %} +

    Témata

    + + {% endif %}
      {% for c in rocnik.verejna_cisla %} @@ -19,15 +26,6 @@ {% endfor %}
    - {% if temata_v_rocniku %} -

    Témata

    - - {% endif %} - {% if vysledkovka %} {% if user.is_staff %}
    @@ -50,7 +48,3 @@
    {% endblock content %} - - - - diff --git a/seminar/views/views_all.py b/seminar/views/views_all.py index 9fe65ca0..81c8d55e 100644 --- a/seminar/views/views_all.py +++ b/seminar/views/views_all.py @@ -320,32 +320,61 @@ class ArchivView(generic.ListView): # slovník {(ročník, url obrázku)} urls ={} + # 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: - urls[c.rocnik] = 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[c.rocnik] = op.join(settings.MEDIA_URL, "cislo", "png", png_filename) + if not c.pdf: + urls[c.rocnik] = 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[c.rocnik] = op.join(settings.MEDIA_URL, "cislo", "png", png_filename) context["object_list"] = urls + print(context) + # for i, c in enumerate(cisla): # if not c.pdf: # continue From 45d857f27f5057983a181453f77ad5cead2816fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kate=C5=99ina=20=C4=8C=C3=AD=C5=BEkov=C3=A1?= Date: Wed, 8 Apr 2020 20:23:37 +0200 Subject: [PATCH 08/26] =?UTF-8?q?li=C5=A1ta=20p=C5=99ihl=C3=A1=C5=A1en?= =?UTF-8?q?=C3=A9ho=20orga?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mamweb/static/css/mamweb.css | 25 +++++++++++++++++++++++++ mamweb/templates/base.html | 28 ++++++++++++++-------------- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/mamweb/static/css/mamweb.css b/mamweb/static/css/mamweb.css index 5089f46e..3cc88707 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: #fffbf6; + 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-block; +} + +a.login-ref-admin { + display: inline; + color: inherit; } /* odkazy a nadpisy */ @@ -123,6 +146,8 @@ h1 { margin-top: 0px; } + + /* Comments */ #id_comment { diff --git a/mamweb/templates/base.html b/mamweb/templates/base.html index a53fa3df..bfc14023 100644 --- a/mamweb/templates/base.html +++ b/mamweb/templates/base.html @@ -36,21 +36,21 @@ -
    - - {% if user.is_staff %} - - {% endif %} + {% if user.is_staff %} + + {% endif %} +
    From 3cb2b76f15a66d92111635ef96c1b86ebda989d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kate=C5=99ina=20=C4=8C=C3=AD=C5=BEkov=C3=A1?= Date: Wed, 8 Apr 2020 21:02:37 +0200 Subject: [PATCH 09/26] =?UTF-8?q?li=C5=A1ta=20p=C5=99ihl=C3=A1=C5=A1en?= =?UTF-8?q?=C3=A9ho=20orga=20(mal=C3=BD=20displej)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mamweb/static/css/mamweb.css | 10 +++++++--- mamweb/templates/base.html | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/mamweb/static/css/mamweb.css b/mamweb/static/css/mamweb.css index 3cc88707..d64cc878 100644 --- a/mamweb/static/css/mamweb.css +++ b/mamweb/static/css/mamweb.css @@ -20,7 +20,7 @@ div.container { div.login-bar { background: #6a0043; - color: #fffbf6; + color: #f9d59e; width: 100%; position: fixed; @@ -33,12 +33,12 @@ div.login-bar { } div.login-bar div { - display: inline-block; + display: inline; } a.login-ref-admin { display: inline; - color: inherit; + color: #fffbf6; } /* odkazy a nadpisy */ @@ -432,6 +432,10 @@ ul.submenu { /* malý tablet, mobil */ @media (max-width: 650px) { + #hide-if-small.login-bar-flatpage { + display: none; + } + #title { display: none; } diff --git a/mamweb/templates/base.html b/mamweb/templates/base.html index bfc14023..a7616627 100644 --- a/mamweb/templates/base.html +++ b/mamweb/templates/base.html @@ -43,7 +43,7 @@ {% if view.object.admin_url %}{% endif %} {% endif %} {% if flatpage %} - + {% endif %} From baeb1cd2f78f4dcf33d06c3b50e4605457b87edc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kate=C5=99ina=20=C4=8C=C3=AD=C5=BEkov=C3=A1?= Date: Wed, 8 Apr 2020 22:58:41 +0200 Subject: [PATCH 10/26] =?UTF-8?q?orgovsk=C3=A9=20=C4=8D=C3=A1sti=20po=20p?= =?UTF-8?q?=C5=99ihl=C3=A1=C5=A1en=C3=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mamweb/static/css/mamweb.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mamweb/static/css/mamweb.css b/mamweb/static/css/mamweb.css index d64cc878..7e7adbfd 100644 --- a/mamweb/static/css/mamweb.css +++ b/mamweb/static/css/mamweb.css @@ -97,16 +97,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 { From 9d42584dd15cae4e0569ed3ee44961ee8ee10861 Mon Sep 17 00:00:00 2001 From: Anet Date: Wed, 8 Apr 2020 23:44:06 +0200 Subject: [PATCH 11/26] Runkcni vysledkovka rocniku + texova, ale pomala a mozna potreba osetrit poradi cisel. --- seminar/templates/seminar/archiv/rocnik.html | 2 +- .../seminar/archiv/rocnik_vysledkovka.tex | 2 +- .../templates/seminar/vysledkovka_rocnik.html | 12 +- seminar/views/views_all.py | 227 ++++++++++++------ 4 files changed, 159 insertions(+), 84 deletions(-) diff --git a/seminar/templates/seminar/archiv/rocnik.html b/seminar/templates/seminar/archiv/rocnik.html index 64c4029d..c047a5b1 100644 --- a/seminar/templates/seminar/archiv/rocnik.html +++ b/seminar/templates/seminar/archiv/rocnik.html @@ -42,7 +42,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 %}
    diff --git a/seminar/templates/seminar/archiv/rocnik_vysledkovka.tex b/seminar/templates/seminar/archiv/rocnik_vysledkovka.tex index 75d8eb79..4218de57 100644 --- a/seminar/templates/seminar/archiv/rocnik_vysledkovka.tex +++ b/seminar/templates/seminar/archiv/rocnik_vysledkovka.tex @@ -7,7 +7,7 @@ \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.resitel.rocnik %}{{ rv.resitel.rocnik }}.{% endif %} & {{ rv.body_odjakziva }} {% for b in rv.body_cisla_sezn %} & {{ b }}{% endfor %} & {{ rv.body_rocnik }} \\ {% endfor %}\end{longtable} {% endwith %} {% endwith %} diff --git a/seminar/templates/seminar/vysledkovka_rocnik.html b/seminar/templates/seminar/vysledkovka_rocnik.html index f1dc1d79..424f805b 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.osoba.plne_jmeno }} {{ rv.resitel.rocnik }} - {{ rv.body_odjakziva }} - {% for b in rv.body_cisla %} + {{ rv.body_celkem_odjakziva }} + {% for b in rv.body_cisla_sezn %} {{ b }} {% endfor %} {{ rv.body_rocnik }} diff --git a/seminar/views/views_all.py b/seminar/views/views_all.py index 01546843..771c204c 100644 --- a/seminar/views/views_all.py +++ b/seminar/views/views_all.py @@ -388,13 +388,17 @@ class ArchivView(generic.ListView): ### 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: @@ -402,10 +406,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: @@ -418,28 +422,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): @@ -447,6 +435,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() @@ -476,27 +475,32 @@ def body_resitelu_odjakziva(rocnik, resitele): 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) +######################################################################### +# 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 = [] + rok = rocnik.prvni_rok + rocniky = Rocnik.objects.filter(prvni_rok__gt=rok-11) + for roc in rocniky: + 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é: +# (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 # vrátí slovník řešitel:body obsahující počty bodů zadaných řešitelů za daný ročník @@ -514,13 +518,77 @@ def body_resitelu_za_rocnik(rocnik, aktivni_resitele): pricti_body(body_za_rocnik, resitel, hodn.body) 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 +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): + self.poradi = poradi + 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') # if jen_verejne: # cisla_v_rocniku = cisla_v_rocniku.filter(cislo__verejna_vysledkovka=True) @@ -594,10 +662,15 @@ 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 @@ -621,7 +694,7 @@ class ProblemView(generic.DetailView): 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).""" @@ -647,19 +720,22 @@ def pricti_body(slovnik, resitel, 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 - 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)] = {} @@ -697,12 +773,11 @@ def secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy): 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 @@ -715,10 +790,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ě @@ -738,7 +813,7 @@ def spocti_vysledkovku_cisla(cislo, context=None): for hp in hlavni_problemy: problemy.append(hlavni_problemy_slovnik[str(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 @@ -785,7 +860,7 @@ 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 @@ -844,13 +919,13 @@ class ArchivTemataView(generic.ListView): # 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 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 ### Generovani obalek class CisloObalkyStruct: From 5f550b06c3f03006a46194f9e25745e8c29b1b5a Mon Sep 17 00:00:00 2001 From: Anet Date: Wed, 8 Apr 2020 23:45:27 +0200 Subject: [PATCH 12/26] emplate pro vysledkovku cisla. --- seminar/templates/seminar/archiv/cislo.html | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/seminar/templates/seminar/archiv/cislo.html b/seminar/templates/seminar/archiv/cislo.html index 955988f1..0e2e8a71 100644 --- a/seminar/templates/seminar/archiv/cislo.html +++ b/seminar/templates/seminar/archiv/cislo.html @@ -49,12 +49,12 @@ {% 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 }} @@ -92,8 +92,6 @@
    {% endif %} - Čas: {% now "jS F Y H:i:s" %} -
    {% endblock content %} From 374a97e28a3aaf3d62c08fefd04c13acb4a1d30d Mon Sep 17 00:00:00 2001 From: "Tomas \"Jethro\" Pokorny" Date: Wed, 8 Apr 2020 21:45:55 +0200 Subject: [PATCH 13/26] Fix obrazek_maly_tag kdyz neni zadny obrazek. --- galerie/models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/galerie/models.py b/galerie/models.py index c6acc5df..2d9b8e56 100644 --- a/galerie/models.py +++ b/galerie/models.py @@ -69,6 +69,8 @@ class Obrazek(models.Model): ordering = ['nazev'] def obrazek_maly_tag(self): + if not self.obrazek_maly: + return '' return u''.format(self.obrazek_maly.url) obrazek_maly_tag.short_description = "Náhled" obrazek_maly_tag.allow_tags = True From 03696d79ab090e51b77e54a7abe685c2f61b544e Mon Sep 17 00:00:00 2001 From: "Tomas \"Jethro\" Pokorny" Date: Wed, 8 Apr 2020 23:09:25 +0200 Subject: [PATCH 14/26] Seminar | models | Pridan nahled titulky cisla. --- seminar/migrations/0080_auto_20200408_2221.py | 24 ++++++++++ seminar/models.py | 45 ++++++++++++++++++- 2 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 seminar/migrations/0080_auto_20200408_2221.py diff --git a/seminar/migrations/0080_auto_20200408_2221.py b/seminar/migrations/0080_auto_20200408_2221.py new file mode 100644 index 00000000..f095ee06 --- /dev/null +++ b/seminar/migrations/0080_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', '0079_clanek_resitelsky'), + ] + + 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 2a1c60d6..6419e73d 100644 --- a/seminar/models.py +++ b/seminar/models.py @@ -1,6 +1,9 @@ # -*- coding: utf-8 -*- import os import random +import subprocess +import pathlib +import tempfile from django.db import models 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 + class SeminarModelBase(models.Model): class Meta: @@ -407,7 +411,11 @@ class Rocnik(SeminarModelBase): def cislo_pdf_filename(self, filename): 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) class Cislo(SeminarModelBase): @@ -451,7 +459,10 @@ class Cislo(SeminarModelBase): help_text='Neveřejná poznámka k číslu (plain text)') 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: # CisloNode @@ -488,6 +499,35 @@ class Cislo(SeminarModelBase): return None 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 def get(cls, rocnik, cislo): try: @@ -499,6 +539,7 @@ class Cislo(SeminarModelBase): def save(self, *args, **kwargs): super().save(*args, **kwargs) + self.vygeneruj_nahled() # *Node.save() aktualizuje název *Nodu. try: self.cislonode.save() From 3b4daeb9a1166248a9caabe54e21995047dcbe7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kate=C5=99ina=20=C4=8C=C3=AD=C5=BEkov=C3=A1?= Date: Wed, 15 Apr 2020 18:49:23 +0200 Subject: [PATCH 15/26] =?UTF-8?q?pr=C3=A1ce=20na=20archivu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seminar/templates/seminar/archiv/rocnik.html | 3 ++ seminar/views/views_all.py | 56 -------------------- 2 files changed, 3 insertions(+), 56 deletions(-) diff --git a/seminar/templates/seminar/archiv/rocnik.html b/seminar/templates/seminar/archiv/rocnik.html index 2dd99ab4..da5187f5 100644 --- a/seminar/templates/seminar/archiv/rocnik.html +++ b/seminar/templates/seminar/archiv/rocnik.html @@ -23,6 +23,9 @@ {% if c.pdf %} (pdf) {% endif %} + {% if c.titulka_nahled %} + + {% endif %} {% endfor %}
diff --git a/seminar/views/views_all.py b/seminar/views/views_all.py index e72f23aa..5822fcd4 100644 --- a/seminar/views/views_all.py +++ b/seminar/views/views_all.py @@ -319,33 +319,6 @@ class ArchivView(generic.ListView): # slovník {(ročník, url obrázku)} urls ={} - - # 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: @@ -375,35 +348,6 @@ class ArchivView(generic.ListView): print(context) - # 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): From f01aed8af2a93ef71243b640e5168da9f5eb6973 Mon Sep 17 00:00:00 2001 From: Pavel 'LEdoian' Turinsky Date: Wed, 1 Apr 2020 23:28:21 +0200 Subject: [PATCH 16/26] =?UTF-8?q?=C3=9Apravy=20okol=C3=AD,=20aby=20reflekt?= =?UTF-8?q?ovaly=20zm=C4=9Bny=20modelu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seminar/admin.py | 19 +--- .../0080_zruseni_claneknode_a_konferanode.py | 103 ++++++++++++++++++ seminar/models.py | 10 +- seminar/testutils.py | 7 +- 4 files changed, 115 insertions(+), 24 deletions(-) create mode 100644 seminar/migrations/0080_zruseni_claneknode_a_konferanode.py 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..bfc5b1bd --- /dev/null +++ b/seminar/migrations/0080_zruseni_claneknode_a_konferanode.py @@ -0,0 +1,103 @@ +# 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.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.RenameModel( + old_name='OtisteneReseniNode', + new_name='ReseniNode', + ), + migrations.RemoveField( + model_name='konferanode', + name='konfera', + ), + migrations.RemoveField( + model_name='konferanode', + name='treenode_ptr', + ), + 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=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='seminar.Problem'), + preserve_default=False, + ), + migrations.RunPython(vyrob_dummy_problemy), + migrations.RemoveField( + model_name='konfera', + name='id', + ), + 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', + ), + ] diff --git a/seminar/models.py b/seminar/models.py index b7a09734..c22ab5d3 100644 --- a/seminar/models.py +++ b/seminar/models.py @@ -868,7 +868,7 @@ class Reseni(SeminarModelBase): forma = models.CharField('forma řešení', max_length=16, choices=FORMA_CHOICES, blank=False, default=FORMA_EMAIL) - text_cely = models.OneToOneField(ReseniNode, 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.PROTECT) @@ -1085,8 +1085,6 @@ class Konfera(Problem): db_table = 'seminar_konfera' verbose_name = 'Konfera' verbose_name_plural = 'Konfery' - # Interní ID - id = models.AutoField(primary_key = True) anotace = models.TextField('anotace', blank=True, help_text='Popis, o čem bude konfera.') @@ -1334,9 +1332,9 @@ class TemaVCisleNode(TreeNode): class OrgTextNode(TreeNode): class Meta: - db_table = 'seminar_nodes_clanek' - verbose_name = 'Článek (Node)' - verbose_name_plural = 'Články (Node)' + 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, diff --git a/seminar/testutils.py b/seminar/testutils.py index 9aec9eba..3c9972eb 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 @@ -336,7 +336,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 +346,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): From d2f7f4b5ff0831d8007c2ffd3d035ae695c082ae Mon Sep 17 00:00:00 2001 From: Pavel 'LEdoian' Turinsky Date: Wed, 15 Apr 2020 20:56:13 +0200 Subject: [PATCH 17/26] Fix migrace --- .../0080_zruseni_claneknode_a_konferanode.py | 46 +++++++++++++------ 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/seminar/migrations/0080_zruseni_claneknode_a_konferanode.py b/seminar/migrations/0080_zruseni_claneknode_a_konferanode.py index bfc5b1bd..13d083f9 100644 --- a/seminar/migrations/0080_zruseni_claneknode_a_konferanode.py +++ b/seminar/migrations/0080_zruseni_claneknode_a_konferanode.py @@ -24,6 +24,13 @@ class Migration(migrations.Migration): ] operations = [ + migrations.DeleteModel( + name='Konfery_Ucastnici', + ), + migrations.RemoveField( + model_name='konfera', + name='ucastnici', + ), migrations.CreateModel( name='OrgTextNode', fields=[ @@ -38,18 +45,14 @@ class Migration(migrations.Migration): }, bases=('seminar.treenode',), ), + migrations.RemoveField( + model_name='konfera', + name='id', + ), migrations.RenameModel( old_name='OtisteneReseniNode', new_name='ReseniNode', ), - migrations.RemoveField( - model_name='konferanode', - name='konfera', - ), - migrations.RemoveField( - model_name='konferanode', - name='treenode_ptr', - ), migrations.RemoveField( model_name='clanek', name='cislo', @@ -65,14 +68,9 @@ class Migration(migrations.Migration): migrations.AddField( model_name='konfera', name='problem_ptr', - field=models.OneToOneField(auto_created=True, null=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='seminar.Problem'), + 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.RunPython(vyrob_dummy_problemy), - migrations.RemoveField( - model_name='konfera', - name='id', - ), migrations.RemoveField( model_name='konfera', name='nazev', @@ -100,4 +98,24 @@ class Migration(migrations.Migration): 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'), + ), ] From 0ac67f1a81e92d731649ace8d102afc5d32e52bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kate=C5=99ina=20=C4=8C=C3=AD=C5=BEkov=C3=A1?= Date: Wed, 15 Apr 2020 21:03:10 +0200 Subject: [PATCH 18/26] =?UTF-8?q?archiv=20ro=C4=8Dn=C3=ADku=20vzhled?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mamweb/static/css/mamweb.css | 54 ++++++++++---- seminar/templates/seminar/archiv/cisla.html | 2 +- seminar/templates/seminar/archiv/rocnik.html | 50 ++++++++++--- seminar/views/views_all.py | 78 ++------------------ 4 files changed, 87 insertions(+), 97 deletions(-) diff --git a/mamweb/static/css/mamweb.css b/mamweb/static/css/mamweb.css index 7e7adbfd..09b870f4 100644 --- a/mamweb/static/css/mamweb.css +++ b/mamweb/static/css/mamweb.css @@ -51,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%; @@ -594,6 +597,14 @@ div.org_pole, div.rocnik_pole { 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; @@ -623,6 +634,11 @@ div.org_email { 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; @@ -654,16 +670,13 @@ div.org_email { div.flip-card-foto img { width: 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 */ .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; @@ -678,26 +691,35 @@ div.popis_rocniku { } -div.popis_rocniku a{ +div.popis_rocniku a, div.cislo_odkazy a { + font-weight: bold; color: black; } -div.popis_rocniku a:hover{ +div.popis_rocniku a:hover, +div.cislo_odkazy a:hover { color: #6f2509; } -/* graf na úvodní stránce */ - -a span.popup { - position: absolute; - visibility: hidden; +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/seminar/templates/seminar/archiv/cisla.html b/seminar/templates/seminar/archiv/cisla.html index 3c4a38b4..f931ee49 100644 --- a/seminar/templates/seminar/archiv/cisla.html +++ b/seminar/templates/seminar/archiv/cisla.html @@ -40,7 +40,7 @@ {% for cislo in rocnik.cisla.all reversed %}
  • {{ cislo.poradi }}. číslo {% if cislo.pdf %}(pdf) {% endif %} {% empty %} - --- + Žádná čísla k zobrazení {% endfor %} Výsledková listina diff --git a/seminar/templates/seminar/archiv/rocnik.html b/seminar/templates/seminar/archiv/rocnik.html index da5187f5..0f48b30c 100644 --- a/seminar/templates/seminar/archiv/rocnik.html +++ b/seminar/templates/seminar/archiv/rocnik.html @@ -17,17 +17,49 @@ {% endif %} -
      +
      {% for c in rocnik.verejna_cisla %} -
    • Číslo {{ c.kod }} - {% if c.pdf %} - (pdf) - {% endif %} - {% if c.titulka_nahled %} - - {% endif %} +
      + +
      Číslo {{ c.kod }}
      + +
      + +
      +
      + +
      + {% if c.titulka_nahled %} + {{ c.kod }} + {% else %} + no image + {% endif %} +
      + +
      +
      + +
      + +
      + + +
      +
      +
      +
      + {% endfor %} -
    + {% if vysledkovka %} {% if user.is_staff %} diff --git a/seminar/views/views_all.py b/seminar/views/views_all.py index 5822fcd4..2e833c98 100644 --- a/seminar/views/views_all.py +++ b/seminar/views/views_all.py @@ -308,80 +308,16 @@ class ArchivView(generic.ListView): def get_context_data(self, **kwargs): context = super(ArchivView, self).get_context_data(**kwargs) - vyska = 594 # px - sirka = 420 # px - - # první číslo z každého ročníku - cisla = Cislo.objects.filter(poradi=1) - - # op == os.path, udělá z argumentů cestu - png_dir = op.join(settings.MEDIA_ROOT, "cislo", "png") - - # slovník {(ročník, url obrázku)} + cisla = Cislo.objects.filter(poradi=1) urls ={} - - for i,c in enumerate(cisla): - if not c.pdf: - urls[c.rocnik] = 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[c.rocnik] = op.join(settings.MEDIA_URL, "cislo", "png", png_filename) - context["object_list"] = urls + for i, c in enumerate(cisla): + if c.titulka_nahled: + urls[c.rocnik] = c.titulka_nahled.url + else: + urls[c.rocnik] = op.join(settings.MEDIA_URL, "cislo", "png", "default.png") - print(context) - - # 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) - - # context["nahledy"] = "\n".join(tags) + context["object_list"] = urls return context From c499d823f9d2d2e28a3b4d8f443030e2800d0178 Mon Sep 17 00:00:00 2001 From: Anet Date: Wed, 15 Apr 2020 22:30:58 +0200 Subject: [PATCH 19/26] =?UTF-8?q?Dokon=C4=8Dena=20oprava=20v=C3=BDsledkove?= =?UTF-8?q?k=20=C4=8D=C3=ADsla=20a=20ro=C4=8Dn=C3=ADku=20=20-=20generov?= =?UTF-8?q?=C3=A1n=C3=AD=20TeXu.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../seminar/archiv/cislo_vysledkovka.tex | 4 +- .../seminar/archiv/rocnik_vysledkovka.tex | 7 +- .../templates/seminar/vysledkovka_rocnik.html | 2 +- seminar/urls.py | 16 +- seminar/views/views_all.py | 168 +++++------------- 5 files changed, 57 insertions(+), 140 deletions(-) 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_vysledkovka.tex b/seminar/templates/seminar/archiv/rocnik_vysledkovka.tex index 4218de57..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 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 }} \\ +{% 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/vysledkovka_rocnik.html b/seminar/templates/seminar/vysledkovka_rocnik.html index 424f805b..db9e848a 100644 --- a/seminar/templates/seminar/vysledkovka_rocnik.html +++ b/seminar/templates/seminar/vysledkovka_rocnik.html @@ -18,7 +18,7 @@ {{ rv.titul }}MM {% endif %} {{ rv.resitel.osoba.plne_jmeno }} - {{ rv.resitel.rocnik }} + {{ rv.rocnik_resitele }} {{ rv.body_celkem_odjakziva }} {% for b in rv.body_cisla_sezn %} {{ b }} diff --git a/seminar/urls.py b/seminar/urls.py index adf2cea5..2db6d367 100644 --- a/seminar/urls.py +++ b/seminar/urls.py @@ -75,13 +75,15 @@ 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'), diff --git a/seminar/views/views_all.py b/seminar/views/views_all.py index e72f23aa..bb078614 100644 --- a/seminar/views/views_all.py +++ b/seminar/views/views_all.py @@ -39,6 +39,7 @@ import traceback import sys import csv import logging +import time def verejna_temata(rocnik): @@ -536,26 +537,26 @@ def body_resitelu_odjakziva(rocnik, resitele): # Body za posledních 10 let je dobrá aproximace pro naše potřeby (výsledkovka # s aktivními řešiteli) - body_pred_roky = [] - rok = rocnik.prvni_rok - rocniky = Rocnik.objects.filter(prvni_rok__gt=rok-11) - for roc in rocniky: - 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)] + #body_pred_roky = [] + #rok = rocnik.prvni_rok + #rocniky = Rocnik.objects.filter(prvni_rok__gt=rok-11) + #for roc in rocniky: + # 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é: # (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) + 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 # vrátí slovník řešitel:body obsahující počty bodů zadaných řešitelů za daný ročník @@ -566,24 +567,28 @@ def body_resitelu_za_rocnik(rocnik, aktivni_resitele): body_za_rocnik[str(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 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): + 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) + elf.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 @@ -632,7 +637,8 @@ def vysledkovka_rocniku(rocnik, jen_verejne=True): 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 + 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)) @@ -643,59 +649,6 @@ def vysledkovka_rocniku(rocnik, jen_verejne=True): return radky_vysledkovky - #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 RocnikView(generic.DetailView): model = Rocnik template_name = 'seminar/archiv/rocnik.html' @@ -723,7 +676,8 @@ class RocnikView(generic.DetailView): 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['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) @@ -754,8 +708,9 @@ class RadekVysledkovkyCisla(object): 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 @@ -826,7 +781,6 @@ def secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy=None): pricti_body(cislobody, resitel, body) pricti_body(nadproblem_slovnik, resitel, body) return hlavni_problemy_slovnik, cislobody - def vysledkovka_cisla(cislo, context=None): if context is None: @@ -874,14 +828,14 @@ def vysledkovka_cisla(cislo, context=None): 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 @@ -917,43 +871,6 @@ class CisloView(generic.DetailView): # vrátíme context (aktuálně obsahuje jen věci ohledně výsledkovky 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 @@ -962,19 +879,18 @@ 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 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' From e3d7ee37560f59ba94255537bea7aa7e6cca5e96 Mon Sep 17 00:00:00 2001 From: Anet Date: Wed, 15 Apr 2020 22:33:06 +0200 Subject: [PATCH 20/26] Drobne (myslim) upravy v testutils. --- seminar/testutils.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/seminar/testutils.py b/seminar/testutils.py index 9aec9eba..c8d02316 100644 --- a/seminar/testutils.py +++ b/seminar/testutils.py @@ -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() @@ -407,7 +410,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 = [] From e402f6c386dc761d724d9fb972d23dcb7c5390be Mon Sep 17 00:00:00 2001 From: Anet Date: Wed, 15 Apr 2020 22:53:17 +0200 Subject: [PATCH 21/26] =?UTF-8?q?Zm=C4=9Bna=20kl=C3=AD=C4=8D=C5=AF=20ve=20?= =?UTF-8?q?slovn=C3=ADc=C3=ADch=20ze=20stringu=20obsahuj=C3=ADc=C3=ADho=20?= =?UTF-8?q?id=20na=20id.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seminar/views/views_all.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/seminar/views/views_all.py b/seminar/views/views_all.py index 1ac106d5..71eeb1b1 100644 --- a/seminar/views/views_all.py +++ b/seminar/views/views_all.py @@ -410,7 +410,7 @@ def body_resitelu_odjakziva(rocnik, resitele): body_odjakziva = {} for r in resitele: - body_odjakziva[str(r.id)] = 0 + body_odjakziva[r.id] = 0 ######################################################################### # POZOR! Aktuálně počítá jen za posledních 10 let od zadaného ročníku # ######################################################################### @@ -425,7 +425,7 @@ def body_resitelu_odjakziva(rocnik, resitele): # #for r in resitele: # for i in range(0,10): - # body_odjakziva[str(r.id)] += body_pred_roky[i][str(r.id)] + # body_odjakziva[r.id] += body_pred_roky[i][r.id] # Nasledující řešení je sice správné, ale moc pomalé: @@ -444,7 +444,7 @@ 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())) @@ -468,7 +468,7 @@ class RadekVysledkovkyRocniku(object): self.body_rocnik = body_rocnik self.body_celkem_odjakziva = body_odjakziva self.body_cisla_sezn = body_cisla_sezn - elf.titul = resitel.get_titul(body_odjakziva) + 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 @@ -488,7 +488,7 @@ def vysledkovka_rocniku(rocnik, jen_verejne=True): 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 + 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) @@ -509,7 +509,7 @@ def vysledkovka_rocniku(rocnik, jen_verejne=True): # 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)]) + body_cisla_sezn.append(body_cisla_slov[cislo.id][ar_id]) # vytáhneme informace pro daného řešitele radek = RadekVysledkovkyRocniku( @@ -605,10 +605,10 @@ 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(rocnik, aktivni_resitele): # spočítáme všem řešitelům jejich body za ročník @@ -628,16 +628,16 @@ def secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy=None): 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', @@ -650,7 +650,7 @@ def secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy=None): # ř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()): @@ -700,7 +700,7 @@ def vysledkovka_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 = RadekVysledkovkyCisla( poradi[i], # pořadí From 5958262240c11327da2c4a851ecf25f783831f11 Mon Sep 17 00:00:00 2001 From: Pavel 'LEdoian' Turinsky Date: Wed, 15 Apr 2020 23:18:14 +0200 Subject: [PATCH 22/26] =?UTF-8?q?Body=20odjak=C5=BEiva=20te=C4=8F=20po?= =?UTF-8?q?=C4=8D=C3=ADt=C3=A1=20datab=C3=A1ze?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seminar/views/views_all.py | 46 +++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/seminar/views/views_all.py b/seminar/views/views_all.py index 71eeb1b1..cf85096e 100644 --- a/seminar/views/views_all.py +++ b/seminar/views/views_all.py @@ -409,34 +409,38 @@ def hlavni_problemy_cisla(cislo): def body_resitelu_odjakziva(rocnik, resitele): body_odjakziva = {} - for r in resitele: - body_odjakziva[r.id] = 0 +# 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 = [] - #rok = rocnik.prvni_rok - #rocniky = Rocnik.objects.filter(prvni_rok__gt=rok-11) - #for roc in rocniky: - # body_pred_roky.append(body_resitelu_za_rocnik(roc, resitele)) - # - #for r in resitele: - # for i in range(0,10): - # body_odjakziva[r.id] += body_pred_roky[i][r.id] +## # Body za posledních 10 let je dobrá aproximace pro naše potřeby (výsledkovka +## # s aktivními řešiteli) +## +## body_pred_roky = [] +## rok = rocnik.prvni_rok +## rocniky = Rocnik.objects.filter(prvni_rok__gt=rok-11) +## for roc in rocniky: +## 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é: # (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) +# 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) + +# Zkusíme agregovat: + resitele_s_body = Resitel.objects.annotate(body=Sum('reseni__hodnoceni__body')) + 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 From 7677d77ad26f4f42f50f0e03fb795e718bd9b7b8 Mon Sep 17 00:00:00 2001 From: Pavel 'LEdoian' Turinsky Date: Wed, 15 Apr 2020 23:41:01 +0200 Subject: [PATCH 23/26] =?UTF-8?q?Rozeps=C3=A1n=C3=AD,=20co=20se=20d=C4=9Bj?= =?UTF-8?q?e=20v=20anotaci=20a=20dict=20comprehension?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seminar/views/views_all.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/seminar/views/views_all.py b/seminar/views/views_all.py index cf85096e..05c00cdb 100644 --- a/seminar/views/views_all.py +++ b/seminar/views/views_all.py @@ -439,7 +439,11 @@ def body_resitelu_odjakziva(rocnik, resitele): # pricti_body(body_odjakziva, r, hodn.body) # Zkusíme agregovat: + # 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 From cf5ac6819fdbad47ce7db88b6fa699d91ff48de5 Mon Sep 17 00:00:00 2001 From: Pavel 'LEdoian' Turinsky Date: Thu, 16 Apr 2020 00:02:32 +0200 Subject: [PATCH 24/26] =?UTF-8?q?Smaz=C3=A1ny=20star=C3=A9=20a=20pomal?= =?UTF-8?q?=C3=A9=20verze=20po=C4=8D=C3=ADt=C3=A1n=C3=AD=20bod=C5=AF=20odj?= =?UTF-8?q?ak=C5=BEiva?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seminar/views/views_all.py | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/seminar/views/views_all.py b/seminar/views/views_all.py index 05c00cdb..f1462f5d 100644 --- a/seminar/views/views_all.py +++ b/seminar/views/views_all.py @@ -407,38 +407,6 @@ 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 = [] -## rok = rocnik.prvni_rok -## rocniky = Rocnik.objects.filter(prvni_rok__gt=rok-11) -## for roc in rocniky: -## 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é: -# (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) - -# Zkusíme agregovat: # 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 From 31e037f242fa67fc7f847d6128d81b3d57fdb14b Mon Sep 17 00:00:00 2001 From: Anet Date: Thu, 16 Apr 2020 00:51:38 +0200 Subject: [PATCH 25/26] =?UTF-8?q?Zprovozn=C4=9Bno=20generov=C3=A1n=C3=AD?= =?UTF-8?q?=20titul=C5=AF.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seminar/templates/seminar/archiv/tituly.tex | 3 +- seminar/urls.py | 4 +- seminar/views/views_all.py | 49 +++++++++++---------- 3 files changed, 28 insertions(+), 28 deletions(-) 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/urls.py b/seminar/urls.py index 2db6d367..f0790359 100644 --- a/seminar/urls.py +++ b/seminar/urls.py @@ -87,8 +87,8 @@ urlpatterns = [ 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 71eeb1b1..5a7025d6 100644 --- a/seminar/views/views_all.py +++ b/seminar/views/views_all.py @@ -855,30 +855,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 From f007cac72436fd3983d45945d34ce28ceb26f78c Mon Sep 17 00:00:00 2001 From: Pavel 'LEdoian' Turinsky Date: Wed, 22 Apr 2020 22:21:23 +0200 Subject: [PATCH 26/26] =?UTF-8?q?Drobn=C3=A9=20pro=C4=8Di=C5=A1t=C4=9Bn?= =?UTF-8?q?=C3=AD=20views=20a=20model=C5=AF=20t=C3=BDkaj=C3=ADc=C3=AD=20se?= =?UTF-8?q?=20zobrazov=C3=A1n=C3=AD=20t=C3=A9m=C3=A1tek?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seminar/models.py | 8 ++++---- seminar/views/views_all.py | 24 +++++++++++++++--------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/seminar/models.py b/seminar/models.py index 226fec78..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): diff --git a/seminar/views/views_all.py b/seminar/views/views_all.py index 3d31dfc4..5259c025 100644 --- a/seminar/views/views_all.py +++ b/seminar/views/views_all.py @@ -543,19 +543,25 @@ 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