From f61533df0a076f0c458c98299101542bbb62e811 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Fri, 24 Jan 2025 15:13:37 +0100 Subject: [PATCH 01/18] =?UTF-8?q?P=C5=99id=C3=A1n=C3=AD=20Znalosti=20do=20?= =?UTF-8?q?modelu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prednasky/admin.py | 11 +++++- .../0019_znalost_hlasovanioznalostech.py | 39 +++++++++++++++++++ prednasky/models.py | 29 +++++++++++++- 3 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 prednasky/migrations/0019_znalost_hlasovanioznalostech.py diff --git a/prednasky/admin.py b/prednasky/admin.py index 19eace7f..d4bb925c 100644 --- a/prednasky/admin.py +++ b/prednasky/admin.py @@ -4,7 +4,7 @@ from reversion.admin import VersionAdmin from django.utils.safestring import mark_safe from django.utils.html import escape -from .models import Prednaska, Seznam, STAV_NAVRH +from .models import Prednaska, Seznam, STAV_NAVRH, Znalost from soustredeni.models import Soustredeni @@ -64,7 +64,7 @@ admin.site.register(Seznam, SeznamAdmin) class PrednaskaAdmin(VersionAdmin): list_display = ['nazev', 'org', 'obor'] list_filter = ['org', 'obor'] - search_fields = [] + search_fields = ['nazev'] filter_horizontal = ('seznamy', ) actions = ['move_to_soustredeni'] @@ -97,3 +97,10 @@ class PrednaskaAdmin(VersionAdmin): admin.site.register(Prednaska, PrednaskaAdmin) + + +class ZnalostAdmin(PrednaskaAdmin): # Trochu hack, ať nemusím vypisovat všechno znovu + list_display = [] + list_filter = [] + +admin.site.register(Znalost, ZnalostAdmin) diff --git a/prednasky/migrations/0019_znalost_hlasovanioznalostech.py b/prednasky/migrations/0019_znalost_hlasovanioznalostech.py new file mode 100644 index 00000000..916302c8 --- /dev/null +++ b/prednasky/migrations/0019_znalost_hlasovanioznalostech.py @@ -0,0 +1,39 @@ +# Generated by Django 4.2.16 on 2025-01-24 13:41 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('personalni', '0019_rename_upozorneni_resitel_upozornovat_na_opravy_reseni'), + ('prednasky', '0018_post_split_soustredeni'), + ] + + operations = [ + migrations.CreateModel( + name='Znalost', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('nazev', models.CharField(help_text='Např. Neuronové sítě', max_length=200, verbose_name='Nadpis')), + ('text', models.TextField(blank=True, help_text='Např. Perceptron, vrstevnatá síť, forward a backward propagation', null=True, verbose_name='Detailní popis')), + ('seznamy', models.ManyToManyField(to='prednasky.seznam')), + ], + options={ + 'verbose_name': 'Znalost k přednáškám', + 'verbose_name_plural': 'Znalosti k přednáškám', + 'db_table': 'prednasky_znalost', + }, + ), + migrations.CreateModel( + name='HlasovaniOZnalostech', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('odpoved', models.CharField(choices=[(-1, 'Tohle celkem umím'), (0, 'Už jsem o tom slyšel, ale neřekl bychm, že to úplně umím'), (1, 'Tohle vůbec neznám')], max_length=16, verbose_name='odpověď')), + ('seznam', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='prednasky.seznam')), + ('ucastnik', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='personalni.osoba')), + ('znalost', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='prednasky.znalost')), + ], + ), + ] diff --git a/prednasky/models.py b/prednasky/models.py index 74b37403..da21b48d 100644 --- a/prednasky/models.py +++ b/prednasky/models.py @@ -1,7 +1,7 @@ from django.db import models from soustredeni.models import Soustredeni -from personalni.models import Organizator +from personalni.models import Organizator, Osoba STAV_NAVRH = 1 STAV_BUDE = 2 @@ -63,6 +63,33 @@ class Prednaska(models.Model): return "{} ({})".format(self.nazev, self.org) +class Znalost(models.Model): + class Meta: + db_table = 'prednasky_znalost' + verbose_name = 'Znalost k přednáškám' + verbose_name_plural = 'Znalosti k přednáškám' + + nazev = models.CharField('Nadpis', max_length=200, blank=False, null=False, help_text = 'Např. Neuronové sítě') + text = models.TextField('Detailní popis', blank=True, null=True, help_text="Např. Perceptron, vrstevnatá síť, forward a backward propagation") + seznamy = models.ManyToManyField(Seznam) + + def __str__(self): + return self.nazev + + +class HlasovaniOZnalostech(models.Model): + class ODPOVED(models.IntegerChoices): + UMIM = -1, 'Tohle celkem umím' + CIRCA = 0, 'Už jsem o tom slyšel, ale neřekl bychm, že to úplně umím' + NEUMIM = 1, 'Tohle vůbec neznám' + + odpoved = models.CharField(u'odpověď', max_length=16, choices=ODPOVED.choices, blank=False, null=False) + znalost = models.ForeignKey(Znalost, on_delete=models.CASCADE, blank=False, null=False) + ucastnik = models.ForeignKey(Osoba, on_delete=models.CASCADE, blank=False, null=False) + seznam = models.ForeignKey(Seznam, on_delete=models.SET_NULL, blank=True, null=True) + + + class Hlasovani(models.Model): class Meta: db_table = 'prednasky_hlasovani' From 2f814956a7d89a8403f440db9a5b9bfd3cce24d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Fri, 24 Jan 2025 15:20:00 +0100 Subject: [PATCH 02/18] =?UTF-8?q?Nepou=C5=BE=C3=ADvan=C3=BD=20kus=20k?= =?UTF-8?q?=C3=B3du?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prednasky/forms.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/prednasky/forms.py b/prednasky/forms.py index f095a64e..e79048bf 100644 --- a/prednasky/forms.py +++ b/prednasky/forms.py @@ -1,7 +1 @@ from django import forms - -class NewPrednaskyForm(forms.Form): - ucastnik = forms.CharField(label = 'Tvoje jméno', max_length = 100) - - - From e933c6978d93b8ebf4e134accbfdb31724a5c0d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Fri, 24 Jan 2025 15:36:33 +0100 Subject: [PATCH 03/18] =?UTF-8?q?Choices=20na=20Enum=20(u=20p=C5=99edn?= =?UTF-8?q?=C3=A1=C5=A1ek)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prednasky/admin.py | 4 ++-- prednasky/models.py | 45 +++++++++++++++++++-------------------------- prednasky/views.py | 6 +++--- 3 files changed, 24 insertions(+), 31 deletions(-) diff --git a/prednasky/admin.py b/prednasky/admin.py index d4bb925c..f79c7b0a 100644 --- a/prednasky/admin.py +++ b/prednasky/admin.py @@ -4,7 +4,7 @@ from reversion.admin import VersionAdmin from django.utils.safestring import mark_safe from django.utils.html import escape -from .models import Prednaska, Seznam, STAV_NAVRH, Znalost +from .models import Prednaska, Seznam, Znalost from soustredeni.models import Soustredeni @@ -71,7 +71,7 @@ class PrednaskaAdmin(VersionAdmin): def move_to_soustredeni(self, request, queryset): sous = Soustredeni.objects.first() - seznam = Seznam.objects.filter(soustredeni=sous, stav=STAV_NAVRH) + seznam = Seznam.objects.filter(soustredeni=sous, stav=Seznam.Stav.NAVRH) if len(seznam) == 0: self.message_user( request, diff --git a/prednasky/models.py b/prednasky/models.py index da21b48d..5f1e1a76 100644 --- a/prednasky/models.py +++ b/prednasky/models.py @@ -3,15 +3,6 @@ from django.db import models from soustredeni.models import Soustredeni from personalni.models import Organizator, Osoba -STAV_NAVRH = 1 -STAV_BUDE = 2 - - -STAV_CHOICES = ( -(STAV_NAVRH, 'Návrh'), -(STAV_BUDE, 'Bude') -) - class Seznam(models.Model): class Meta: @@ -20,27 +11,18 @@ class Seznam(models.Model): verbose_name_plural = 'Seznamy přednášek' ordering = ['soustredeni', 'stav'] + class Stav(models.IntegerChoices): + NAVRH = 1, "Návrh" + BUDE = 2, "Bude" + id = models.AutoField(primary_key = True) soustredeni = models.ForeignKey(Soustredeni,null = True, default = None, on_delete=models.PROTECT) - stav = models.IntegerField('Stav',choices=STAV_CHOICES,default = STAV_NAVRH) + stav = models.IntegerField('Stav', choices=Stav.choices, default=Stav.NAVRH) def __str__(self): return "Seznam {}přednášek na {}".format("návrhů " - if self.stav == STAV_NAVRH else "", self.soustredeni) - - -CHOICES_OBTIZNOST = ( - (1, 'Lehká'), - (2, 'Střední'), - (3, 'Těžká'), - ) - -CHOICES_BODY = ( - (-1, '-1'), - (0, '0'), - (1, '1'), - ) + if self.stav == Seznam.Stav.NAVRH else "", self.soustredeni) class Prednaska(models.Model): class Meta: @@ -49,12 +31,17 @@ class Prednaska(models.Model): verbose_name_plural = 'Přednášky' ordering = ['org', 'nazev'] + class Obtiznost(models.IntegerChoices): + LEHKA = 1, "Lehká" + STREDNI = 2, "Střední" + TEZKA = 3, "Těžká" + id = models.AutoField(primary_key = True) nazev = models.CharField('Název', max_length = 300) org = models.ForeignKey(Organizator, on_delete=models.PROTECT) popis = models.TextField('Popis pro orgy',null = True, blank = True,help_text = 'Neveřejný popis pro ostatní orgy') anotace = models.TextField('Anotace',null = True, blank = True, help_text = 'Veřejná anotace v hlasování') - obtiznost = models.IntegerField('Obtížnost', choices=CHOICES_OBTIZNOST) + obtiznost = models.IntegerField('Obtížnost', choices=Obtiznost.choices) obor = models.CharField('Obor', max_length = 5, help_text = 'Podmnožina MFIOB') klicova = models.CharField('Klíčová slova', max_length = 200, null = True, blank = True) seznamy = models.ManyToManyField(Seznam) @@ -96,9 +83,15 @@ class Hlasovani(models.Model): verbose_name = 'Hlasování' verbose_name_plural = 'Hlasování' ordering = ['ucastnik', 'prednaska'] + + class Body(models.IntegerChoices): + NECHCI = -1, "rozhodně nechci" + JEDNO = 0, "je mi to jedno" + CHCI = 1, "rozhodně chci" + id = models.AutoField(primary_key = True) prednaska = models.ForeignKey(Prednaska, on_delete=models.CASCADE) - body = models.IntegerField('Body', default = 0, choices = CHOICES_BODY) + body = models.IntegerField('Body', default = Body.JEDNO, choices = Body.choices) ucastnik = models.CharField('Účastník', max_length = 100) seznam = models.ForeignKey(Seznam,null=True,on_delete=models.SET_NULL) diff --git a/prednasky/views.py b/prednasky/views.py index a2a2594c..dd64047f 100644 --- a/prednasky/views.py +++ b/prednasky/views.py @@ -10,14 +10,14 @@ from django.forms import Form from various.views.pomocne import formularOKView from various.models import Nastaveni -from prednasky.models import Prednaska, Hlasovani, Seznam, STAV_NAVRH +from prednasky.models import Prednaska, Hlasovani, Seznam from soustredeni.models import Soustredeni from personalni.models import Osoba def newPrednaska(request): # hlasovani se vztahuje k nejnovejsimu soustredeni sous = Nastaveni.get_solo().aktualni_sous - seznam = Seznam.objects.filter(soustredeni = sous, stav = STAV_NAVRH).first() + seznam = Seznam.objects.filter(soustredeni = sous, stav=Seznam.Stav.NAVRH).first() if sous is None or seznam is None: return render(request, 'universal.html', { 'title': "Nelze hlasovat", @@ -87,7 +87,7 @@ class SeznamListView(generic.ListView): # hlasovani se vztahuje k nejnovejsimu soustredeni sous = Soustredeni.objects.first() - seznam = Seznam.objects.filter(soustredeni = sous, stav = STAV_NAVRH).first() + seznam = Seznam.objects.filter(soustredeni = sous, stav=Seznam.Stav.NAVRH).first() for obj in self.object_list: hlasovani_set = obj.hlasovani_set.filter(seznam=seznam).only('body') From 6c35a5b6f3a7da3f75e6f7662fa692b82fa97dba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Fri, 24 Jan 2025 15:39:37 +0100 Subject: [PATCH 04/18] =?UTF-8?q?Uhlazen=C3=AD=20prednasky.models?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prednasky/models.py | 109 +++++++++++++++++++++----------------------- 1 file changed, 53 insertions(+), 56 deletions(-) diff --git a/prednasky/models.py b/prednasky/models.py index 5f1e1a76..f003878f 100644 --- a/prednasky/models.py +++ b/prednasky/models.py @@ -6,58 +6,79 @@ from personalni.models import Organizator, Osoba class Seznam(models.Model): class Meta: - db_table = 'prednasky_seznam' - verbose_name = 'Seznam přednášek' - verbose_name_plural = 'Seznamy přednášek' - ordering = ['soustredeni', 'stav'] + db_table = "prednasky_seznam" + verbose_name = "Seznam přednášek" + verbose_name_plural = "Seznamy přednášek" + ordering = ["soustredeni", "stav"] class Stav(models.IntegerChoices): NAVRH = 1, "Návrh" BUDE = 2, "Bude" - id = models.AutoField(primary_key = True) - soustredeni = models.ForeignKey(Soustredeni,null = True, default = None, - on_delete=models.PROTECT) - stav = models.IntegerField('Stav', choices=Stav.choices, default=Stav.NAVRH) + id = models.AutoField(primary_key=True) + soustredeni = models.ForeignKey(Soustredeni, null=True, default=None, on_delete=models.PROTECT) + stav = models.IntegerField("Stav", choices=Stav.choices, default=Stav.NAVRH) def __str__(self): - return "Seznam {}přednášek na {}".format("návrhů " - if self.stav == Seznam.Stav.NAVRH else "", self.soustredeni) + return f"Seznam {"návrhů " if self.stav == Seznam.Stav.NAVRH else ""}přednášek na {self.soustredeni}" + class Prednaska(models.Model): class Meta: - db_table = 'prednasky_prednaska' - verbose_name = 'Přednáška' - verbose_name_plural = 'Přednášky' - ordering = ['org', 'nazev'] + db_table = "prednasky_prednaska" + verbose_name = "Přednáška" + verbose_name_plural = "Přednášky" + ordering = ["org", "nazev"] class Obtiznost(models.IntegerChoices): LEHKA = 1, "Lehká" STREDNI = 2, "Střední" TEZKA = 3, "Těžká" - id = models.AutoField(primary_key = True) - nazev = models.CharField('Název', max_length = 300) - org = models.ForeignKey(Organizator, on_delete=models.PROTECT) - popis = models.TextField('Popis pro orgy',null = True, blank = True,help_text = 'Neveřejný popis pro ostatní orgy') - anotace = models.TextField('Anotace',null = True, blank = True, help_text = 'Veřejná anotace v hlasování') - obtiznost = models.IntegerField('Obtížnost', choices=Obtiznost.choices) - obor = models.CharField('Obor', max_length = 5, help_text = 'Podmnožina MFIOB') - klicova = models.CharField('Klíčová slova', max_length = 200, null = True, blank = True) + id = models.AutoField(primary_key=True) + nazev = models.CharField("Název", max_length=300) + org = models.ForeignKey(Organizator, on_delete=models.PROTECT) + popis = models.TextField("Popis pro orgy", null=True, blank=True, help_text="Neveřejný popis pro ostatní orgy") + anotace = models.TextField("Anotace", null=True, blank=True, help_text="Veřejná anotace v hlasování") + obtiznost = models.IntegerField("Obtížnost", choices=Obtiznost.choices) + obor = models.CharField("Obor", max_length=5, help_text="Podmnožina MFIOB") + klicova = models.CharField("Klíčová slova", max_length=200, null=True, blank=True) seznamy = models.ManyToManyField(Seznam) def __str__(self): - return "{} ({})".format(self.nazev, self.org) + return f"{self.nazev} ({self.org})" + + +class Hlasovani(models.Model): + class Meta: + db_table = "prednasky_hlasovani" + verbose_name = "Hlasování" + verbose_name_plural = "Hlasování" + ordering = ["ucastnik", "prednaska"] + + class Body(models.IntegerChoices): + NECHCI = -1, "rozhodně nechci" + JEDNO = 0, "je mi to jedno" + CHCI = 1, "rozhodně chci" + + id = models.AutoField(primary_key=True) + prednaska = models.ForeignKey(Prednaska, on_delete=models.CASCADE) + body = models.IntegerField("Body", default=Body.JEDNO, choices=Body.choices) + ucastnik = models.CharField("Účastník", max_length=100) + seznam = models.ForeignKey(Seznam, null=True, on_delete=models.SET_NULL) + + def __str__(self): + return f"{self.ucastnik} dal {self.body} bodů {self.prednaska} v seznamu {self.seznam}" class Znalost(models.Model): class Meta: - db_table = 'prednasky_znalost' - verbose_name = 'Znalost k přednáškám' - verbose_name_plural = 'Znalosti k přednáškám' + db_table = "prednasky_znalost" + verbose_name = "Znalost k přednáškám" + verbose_name_plural = "Znalosti k přednáškám" - nazev = models.CharField('Nadpis', max_length=200, blank=False, null=False, help_text = 'Např. Neuronové sítě') - text = models.TextField('Detailní popis', blank=True, null=True, help_text="Např. Perceptron, vrstevnatá síť, forward a backward propagation") + nazev = models.CharField("Nadpis", max_length=200, blank=False, null=False, help_text="Např. Neuronové sítě") + text = models.TextField("Detailní popis", blank=True, null=True, help_text="Např. Perceptron, vrstevnatá síť, forward a backward propagation") seznamy = models.ManyToManyField(Seznam) def __str__(self): @@ -66,36 +87,12 @@ class Znalost(models.Model): class HlasovaniOZnalostech(models.Model): class ODPOVED(models.IntegerChoices): - UMIM = -1, 'Tohle celkem umím' - CIRCA = 0, 'Už jsem o tom slyšel, ale neřekl bychm, že to úplně umím' - NEUMIM = 1, 'Tohle vůbec neznám' + UMIM = -1, "Tohle celkem umím" + CIRCA = 0, "Už jsem o tom slyšel, ale neřekl bychm, že to úplně umím" + NEUMIM = 1, "Tohle vůbec neznám" - odpoved = models.CharField(u'odpověď', max_length=16, choices=ODPOVED.choices, blank=False, null=False) + odpoved = models.CharField(u"odpověď", max_length=16, choices=ODPOVED.choices, blank=False, null=False) znalost = models.ForeignKey(Znalost, on_delete=models.CASCADE, blank=False, null=False) ucastnik = models.ForeignKey(Osoba, on_delete=models.CASCADE, blank=False, null=False) seznam = models.ForeignKey(Seznam, on_delete=models.SET_NULL, blank=True, null=True) - - -class Hlasovani(models.Model): - class Meta: - db_table = 'prednasky_hlasovani' - verbose_name = 'Hlasování' - verbose_name_plural = 'Hlasování' - ordering = ['ucastnik', 'prednaska'] - - class Body(models.IntegerChoices): - NECHCI = -1, "rozhodně nechci" - JEDNO = 0, "je mi to jedno" - CHCI = 1, "rozhodně chci" - - id = models.AutoField(primary_key = True) - prednaska = models.ForeignKey(Prednaska, on_delete=models.CASCADE) - body = models.IntegerField('Body', default = Body.JEDNO, choices = Body.choices) - ucastnik = models.CharField('Účastník', max_length = 100) - seznam = models.ForeignKey(Seznam,null=True,on_delete=models.SET_NULL) - - def __str__(self): - return "{} dal {} bodů {} v seznamu {}".format(self.ucastnik, - self.body, self.prednaska, self.seznam) - From bcda95f0b3cd52902ec35010e5577e824a5e7ff3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Fri, 24 Jan 2025 15:51:06 +0100 Subject: [PATCH 05/18] =?UTF-8?q?Stringifikace=20hlasov=C3=A1n=C3=AD=20o?= =?UTF-8?q?=20znalostech?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prednasky/models.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/prednasky/models.py b/prednasky/models.py index f003878f..f9e1e08f 100644 --- a/prednasky/models.py +++ b/prednasky/models.py @@ -96,3 +96,6 @@ class HlasovaniOZnalostech(models.Model): ucastnik = models.ForeignKey(Osoba, on_delete=models.CASCADE, blank=False, null=False) seznam = models.ForeignKey(Seznam, on_delete=models.SET_NULL, blank=True, null=True) + def __str__(self): + return f"{self.ucastnik} dal {self.znalost} bodů {self.znalost} v seznamu {self.seznam}" + From e12b614e1c23ad054cb300a5a10b53722222303f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Fri, 24 Jan 2025 16:01:39 +0100 Subject: [PATCH 06/18] ODPOVED -> Odpoved --- prednasky/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prednasky/models.py b/prednasky/models.py index f9e1e08f..2b980042 100644 --- a/prednasky/models.py +++ b/prednasky/models.py @@ -86,12 +86,12 @@ class Znalost(models.Model): class HlasovaniOZnalostech(models.Model): - class ODPOVED(models.IntegerChoices): + class Odpoved(models.IntegerChoices): UMIM = -1, "Tohle celkem umím" CIRCA = 0, "Už jsem o tom slyšel, ale neřekl bychm, že to úplně umím" NEUMIM = 1, "Tohle vůbec neznám" - odpoved = models.CharField(u"odpověď", max_length=16, choices=ODPOVED.choices, blank=False, null=False) + odpoved = models.CharField(u"odpověď", max_length=16, choices=Odpoved.choices, blank=False, null=False) znalost = models.ForeignKey(Znalost, on_delete=models.CASCADE, blank=False, null=False) ucastnik = models.ForeignKey(Osoba, on_delete=models.CASCADE, blank=False, null=False) seznam = models.ForeignKey(Seznam, on_delete=models.SET_NULL, blank=True, null=True) From fbd75d2f7232b3cb6af0a88b9f2c82d43cb53d98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Fri, 24 Jan 2025 19:40:54 +0100 Subject: [PATCH 07/18] =?UTF-8?q?Hlasov=C3=A1n=C3=AD=20o=20p=C5=99edn?= =?UTF-8?q?=C3=A1=C5=A1k=C3=A1ch=20pomoc=C3=AD=20formset=C5=AF=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mamweb/settings_common.py | 1 + prednasky/admin.py | 4 +- prednasky/forms.py | 14 ++++ prednasky/models.py | 4 + prednasky/templates/prednasky/base.html | 54 ++++++------- prednasky/views.py | 103 +++++++++++++++++------- 6 files changed, 122 insertions(+), 58 deletions(-) diff --git a/mamweb/settings_common.py b/mamweb/settings_common.py index 3bfbcfc4..f8cac53f 100644 --- a/mamweb/settings_common.py +++ b/mamweb/settings_common.py @@ -57,6 +57,7 @@ DOBA_ODHLASENI_PRI_ZASKRTNUTI_NEODHLASOVAT = 365 * 24 * 3600 # rok CSRF_FAILURE_VIEW = 'various.views.csrf.csrf_error' # Modules configuration +FORM_RENDERER = "django.forms.renderers.DjangoDivFormRenderer" AUTHENTICATION_BACKENDS = ( 'django.contrib.auth.backends.ModelBackend', diff --git a/prednasky/admin.py b/prednasky/admin.py index f79c7b0a..a6204d35 100644 --- a/prednasky/admin.py +++ b/prednasky/admin.py @@ -100,7 +100,7 @@ admin.site.register(Prednaska, PrednaskaAdmin) class ZnalostAdmin(PrednaskaAdmin): # Trochu hack, ať nemusím vypisovat všechno znovu - list_display = [] - list_filter = [] + list_display = ("__str__",) + list_filter = () admin.site.register(Znalost, ZnalostAdmin) diff --git a/prednasky/forms.py b/prednasky/forms.py index e79048bf..cee90f7f 100644 --- a/prednasky/forms.py +++ b/prednasky/forms.py @@ -1 +1,15 @@ from django import forms + +from .models import Hlasovani, HlasovaniOZnalostech + +class HlasovaniPrednaskaForm(forms.Form): + prednaska_id = forms.IntegerField(widget=forms.HiddenInput) + body = forms.ChoiceField(label=False, widget=forms.RadioSelect, choices=Hlasovani.Body.choices, initial=Hlasovani.Body.JEDNO) + +HlasovaniPrednaskaFormSet = forms.formset_factory(HlasovaniPrednaskaForm, extra=0) + +class HlasovaniZnalostiForm(forms.Form): + znalost_id = forms.IntegerField(widget=forms.HiddenInput) + odpoved = forms.ChoiceField(label=False, widget=forms.RadioSelect, choices=HlasovaniOZnalostech.Odpoved.choices) + +HlasovaniZnalostiFormSet = forms.formset_factory(HlasovaniZnalostiForm, extra=0) diff --git a/prednasky/models.py b/prednasky/models.py index 2b980042..32d8924e 100644 --- a/prednasky/models.py +++ b/prednasky/models.py @@ -64,6 +64,10 @@ class Hlasovani(models.Model): id = models.AutoField(primary_key=True) prednaska = models.ForeignKey(Prednaska, on_delete=models.CASCADE) body = models.IntegerField("Body", default=Body.JEDNO, choices=Body.choices) + + # (přechod z jména na objekt Osoby nějak kape na tom, + # že všechna předchozí hlasování zde mají náhodný string…) + # TODO Změnit to na Osobu ucastnik = models.CharField("Účastník", max_length=100) seznam = models.ForeignKey(Seznam, null=True, on_delete=models.SET_NULL) diff --git a/prednasky/templates/prednasky/base.html b/prednasky/templates/prednasky/base.html index ce201cc6..37ea178c 100644 --- a/prednasky/templates/prednasky/base.html +++ b/prednasky/templates/prednasky/base.html @@ -5,36 +5,36 @@ {% block content %} -

-{% block nadpis1a %}Hlasování o přednáškách{% endblock %} -

- -

-Jak moc by ses chtěl(a) zúčastnit následujících přednášek? -
-Obtížnost 1 je nejlehčí, 3 nejtěžší. -

+

{% block nadpis1a %}Hlasování o přednáškách{% endblock %}

{% csrf_token %} - - {% for p, h in prednasky %} - - - - - {% if p.klicova %}{% endif%} - - - {% empty %} - Nejsou žádné přednášky o kterých by šlo hlasovat. - {% endfor %} - -

{{p.anotace}}

{{p.obor}}
{{p.obtiznost}}
{{p.klicova}}
Hodnocení: - rozhodně nechci - je mi to jedno - rozhodně chci -
 
+ +

Jak moc by ses chtěl(a) zúčastnit následujících přednášek?

+

Obtížnost 1 je nejlehčí, 3 nejtěžší.

+ {{ form_set_prednasky.management_form }} + {% for f, p in formy_a_prednasky %} +

{{p.nazev}} ({{p.org}})

+

{{p.anotace}}

+ {{p.obor}}
+ {{p.obtiznost}}
+ {% if p.klicova %} {{p.klicova}}
{% endif%} +
+ {{ f }} +
+ {% empty %} + Nejsou žádné přednášky o kterých by šlo hlasovat. + {% endfor %} + + {{ form_set_znalosti.management_form }} + {% for f, z in formy_a_znalosti %} + {% if forloop.first %}

Jak moc znáš následující?

{% endif %} +

{{z.nazev}}

+

{{z.text}}

+ {{ f }} +
+ {% endfor %} +
{% endblock %} diff --git a/prednasky/views.py b/prednasky/views.py index dd64047f..c0951cec 100644 --- a/prednasky/views.py +++ b/prednasky/views.py @@ -1,19 +1,25 @@ import http +import logging from django.shortcuts import render, get_object_or_404 from django.views import generic from django.shortcuts import HttpResponseRedirect from django.core.exceptions import ObjectDoesNotExist -from django.db.models import Sum -from django.forms import Form +from django.db import transaction from various.views.pomocne import formularOKView +from .forms import HlasovaniPrednaskaFormSet, HlasovaniZnalostiFormSet from various.models import Nastaveni -from prednasky.models import Prednaska, Hlasovani, Seznam +from prednasky.models import Prednaska, Hlasovani, Znalost, HlasovaniOZnalostech, Seznam from soustredeni.models import Soustredeni from personalni.models import Osoba +PREDNASKY_PREFIX = "prednasky" +ZNALOSTI_PREFIX = "znalosti" + +logger = logging.getLogger(__name__) + def newPrednaska(request): # hlasovani se vztahuje k nejnovejsimu soustredeni sous = Nastaveni.get_solo().aktualni_sous @@ -23,43 +29,82 @@ def newPrednaska(request): 'title': "Nelze hlasovat", 'text': "Není žádný seznam přednášek, o kterém by se dalo hlasovat.", }, status=http.HTTPStatus.NOT_FOUND) + osoba = Osoba.objects.filter(user=request.user).first() ucastnik = osoba.plne_jmeno() + ' ' + str(osoba.id) - # obsluha formulare + if request.method == 'POST': - form = Form(request.POST, request.FILES) - if form.is_valid(): - # id z důvodu duplicitních jmen (přechod z jména na objekt Osoby nějak kape na tom, - # že všechna předchozí hlasování zde mají náhodný string…) - # TODO Změnit to na Osobu + form_set_prednasky = HlasovaniPrednaskaFormSet(request.POST, prefix=PREDNASKY_PREFIX) + form_set_znalosti = HlasovaniZnalostiFormSet(request.POST, prefix=ZNALOSTI_PREFIX) - # TODO v následujících řádcích je zbytečně mnoho dotazů na QuerySet (pokud účastník hlasoval, hlasoval u všech) - for i in request.POST: - if i[0] == 'q': - prednaska = Prednaska.objects.filter(pk=int(i[1:]))[0] - hlasovani = Hlasovani.objects.filter(ucastnik=ucastnik, prednaska=prednaska).first() - if not hlasovani: - hlasovani = Hlasovani() - hlasovani.prednaska = prednaska - hlasovani.ucastnik = ucastnik - hlasovani.seznam = seznam - hlasovani.body = int(request.POST[i]) - hlasovani.save() + if form_set_prednasky.is_valid() and form_set_znalosti.is_valid(): + with transaction.atomic(): + seznam.hlasovani_set.filter(ucastnik=ucastnik).delete() + seznam.hlasovanioznalostech_set.filter(ucastnik=osoba).delete() + + for form in form_set_prednasky: + prednaska_id = form.cleaned_data['prednaska_id'] + prednaska = Prednaska.objects.filter(id=prednaska_id).first() + if prednaska is None: + logger.error(f"Účastník {ucastnik} hodnotil neexistující přednášku {prednaska_id} číslem {form.cleaned_data['body']}") + continue + + Hlasovani.objects.create( + prednaska=prednaska, + body=form.cleaned_data['body'], + ucastnik=ucastnik, + seznam=seznam, + ) + + for form in form_set_znalosti: + znalost_id = form.cleaned_data['znalost_id'] + znalost = Znalost.objects.filter(id=znalost_id).first() + if znalost is None: + logger.error(f"Účastník {ucastnik} hodnotil neexistující znalost {znalost_id} číslem {form.cleaned_data['odpoved']}") + continue + + HlasovaniOZnalostech.objects.create( + odpoved=form.cleaned_data['odpoved'], + znalost=znalost, + ucastnik=osoba, + seznam=seznam, + ) - # presmerovani na prave vzniklou galerii return HttpResponseRedirect('./hotovo') - - def prednaska_hodnoceni(prednaska): - h = Hlasovani.objects.filter(ucastnik=ucastnik, prednaska=prednaska).first() - if h: - return prednaska, h.body else: - return prednaska, 0 + prednasky = seznam.prednaska_set.all() + znalosti = seznam.znalost_set.all() + # Spadnout, pokud nesedí přednáška/znalost s formulářem. (Nějak se mi to nepovedlo.) + + else: + def odpoved_prednasky(p): + hlasovani = p.hlasovani_set.filter(ucastnik=ucastnik).first() + return hlasovani.body if hlasovani else Hlasovani.Body.JEDNO + + def odpoved_znalosti(z): + hlasovani = z.hlasovanioznalostech_set.filter(ucastnik=osoba).first() + return hlasovani.odpoved if hlasovani else Hlasovani.Body.JEDNO + + prednasky = seznam.prednaska_set.all() + znalosti = seznam.znalost_set.all() + + form_set_prednasky = HlasovaniPrednaskaFormSet(initial=[ + {"prednaska_id": p.id, "body": odpoved_prednasky(p)} for p in prednasky + ], prefix=PREDNASKY_PREFIX) + + form_set_znalosti = HlasovaniZnalostiFormSet(initial=[ + {"znalost_id": z.id, "odpoved": odpoved_znalosti(z)} for z in znalosti + ], prefix=ZNALOSTI_PREFIX) + return render( request, 'prednasky/base.html', - {'prednasky': map(prednaska_hodnoceni, seznam.prednaska_set.all())} + { + 'form_set_prednasky': form_set_prednasky, 'form_set_znalosti': form_set_znalosti, + 'formy_a_prednasky': zip(form_set_prednasky, prednasky), + 'formy_a_znalosti': zip(form_set_znalosti, znalosti), + } ) From 7ca709337177d8b7e1f7552fb4b416528297ebdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Fri, 24 Jan 2025 20:22:38 +0100 Subject: [PATCH 08/18] =?UTF-8?q?Export=20hlasov=C3=A1n=C3=AD=20do=20CSV?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prednasky/urls.py | 5 +++++ prednasky/views.py | 48 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/prednasky/urls.py b/prednasky/urls.py index eecc45ad..c22ea9f9 100644 --- a/prednasky/urls.py +++ b/prednasky/urls.py @@ -17,6 +17,11 @@ urlpatterns = [ org_required(views.SeznamExportView), name='seznam-export' ), + path( + 'prednasky/seznam_prednasek//hlasovani.csv', + org_required(views.PrednaskyExportView), + name='seznam-export-csv' + ), path( 'prednasky/seznam_prednasek//', org_required(views.SeznamListView.as_view()), diff --git a/prednasky/views.py b/prednasky/views.py index c0951cec..e61dfdef 100644 --- a/prednasky/views.py +++ b/prednasky/views.py @@ -1,6 +1,8 @@ +import csv import http import logging +from django.http import HttpResponse from django.shortcuts import render, get_object_or_404 from django.views import generic from django.shortcuts import HttpResponseRedirect @@ -31,7 +33,7 @@ def newPrednaska(request): }, status=http.HTTPStatus.NOT_FOUND) osoba = Osoba.objects.filter(user=request.user).first() - ucastnik = osoba.plne_jmeno() + ' ' + str(osoba.id) + ucastnik = osoba.plne_jmeno() + ' ' + str(osoba.id) # id, kvůli kolizi jmen if request.method == 'POST': form_set_prednasky = HlasovaniPrednaskaFormSet(request.POST, prefix=PREDNASKY_PREFIX) @@ -170,3 +172,47 @@ def SeznamExportView(request, seznam): ) +def PrednaskyExportView(request, seznam: int, **kwargs): + hlasovani = Hlasovani.objects.filter(seznam=seznam).select_related("prednaska") + hlasovani_o_znalostech = HlasovaniOZnalostech.objects.filter(seznam=seznam).select_related('ucastnik', 'znalost') + + prednasky = list(Prednaska.objects.filter(seznamy=seznam)) + znalosti = list(Znalost.objects.filter(seznamy=seznam)) + + prednasky_map: dict[int, int] = {p.id: i for i, p in enumerate(prednasky, 1)} + offset = len(prednasky_map) + znalosti_map: dict[int, int] = {z.id: i for i, z in enumerate(znalosti, offset + 1)} + width = offset + len(znalosti_map) + + table: [str, list[str|Prednaska|Znalost,]] = {} + + for h in hlasovani: + if h.ucastnik not in table: + table[h.ucastnik] = [h.ucastnik] + ([""] * width) + + if h.prednaska.id in prednasky_map: + table[h.ucastnik][prednasky_map[h.prednaska.id]] = h.body + else: + pass # Padat hlasitě? + + for h in hlasovani_o_znalostech: + ucastnik = str(h.ucastnik) + ' ' + str(h.ucastnik.id) # id, kvůli kolizi jmen + if ucastnik not in table: + table[ucastnik] = [ucastnik] + ([""] * width) + + if h.znalost.id in znalosti_map: + table[ucastnik][znalosti_map[h.znalost.id]] = h.odpoved + else: + pass # Padat hlasitě? + + + response = HttpResponse(content_type="text/csv", charset="utf-8") + response["Content-Disposition"] = 'attachment; filename="hlasovani.csv"' + + writer = csv.writer(response) + writer.writerow(["jména \\ přednáška|znalost"] + list(map(str, prednasky + znalosti))) + for row in table.values(): + writer.writerow(list(map(str, row))) + return response + + From 4001822842ba05525f0636a542b15b26062bc137 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Fri, 24 Jan 2025 20:32:28 +0100 Subject: [PATCH 09/18] =?UTF-8?q?Oprava=20pr=C3=A1v=20pro=20aplikaci=20p?= =?UTF-8?q?=C5=99edn=C3=A1=C5=A1ky?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/groups.json | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/data/groups.json b/data/groups.json index df9516a0..93a6c8b2 100644 --- a/data/groups.json +++ b/data/groups.json @@ -248,26 +248,16 @@ "personalni", "skola" ], - [ - "add_hlasovani", - "prednasky", - "hlasovani" - ], - [ - "delete_hlasovani", - "prednasky", - "hlasovani" - ], - [ - "change_hlasovani", - "prednasky", - "hlasovani" - ], [ "view_hlasovani", "prednasky", "hlasovani" ], + [ + "view_hlasovanioznalostech", + "prednasky", + "hlasovanioznalostech" + ], [ "add_prednaska", "prednasky", @@ -308,6 +298,26 @@ "prednasky", "seznam" ], + [ + "add_znalost", + "prednasky", + "znalost" + ], + [ + "change_znalost", + "prednasky", + "znalost" + ], + [ + "delete_znalost", + "prednasky", + "znalost" + ], + [ + "view_znalost", + "prednasky", + "znalost" + ], [ "add_konfera", "soustredeni", From 90e7b97b85402910c51bc1de99509b99b17e1257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Fri, 24 Jan 2025 20:35:24 +0100 Subject: [PATCH 10/18] =?UTF-8?q?Zakomentov=C3=A1n=20star=C3=BD=20export?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/groups.json | 104 +++++++++--------- .../prednasky/metaseznam_prednasek.html | 2 +- prednasky/urls.py | 10 +- prednasky/views.py | 54 ++++----- 4 files changed, 85 insertions(+), 85 deletions(-) diff --git a/data/groups.json b/data/groups.json index 93a6c8b2..cf10eb83 100644 --- a/data/groups.json +++ b/data/groups.json @@ -14,12 +14,12 @@ "flatpage" ], [ - "delete_flatpage", + "change_flatpage", "flatpages", "flatpage" ], [ - "change_flatpage", + "delete_flatpage", "flatpages", "flatpage" ], @@ -34,12 +34,12 @@ "galerie" ], [ - "delete_galerie", + "change_galerie", "galerie", "galerie" ], [ - "change_galerie", + "delete_galerie", "galerie", "galerie" ], @@ -54,12 +54,12 @@ "obrazek" ], [ - "delete_obrazek", + "change_obrazek", "galerie", "obrazek" ], [ - "change_obrazek", + "delete_obrazek", "galerie", "obrazek" ], @@ -104,12 +104,12 @@ "komentar" ], [ - "delete_komentar", + "change_komentar", "korektury", "komentar" ], [ - "change_komentar", + "delete_komentar", "korektury", "komentar" ], @@ -124,12 +124,12 @@ "korekturovanepdf" ], [ - "delete_korekturovanepdf", + "change_korekturovanepdf", "korektury", "korekturovanepdf" ], [ - "change_korekturovanepdf", + "delete_korekturovanepdf", "korektury", "korekturovanepdf" ], @@ -144,12 +144,12 @@ "oprava" ], [ - "delete_oprava", + "change_oprava", "korektury", "oprava" ], [ - "change_oprava", + "delete_oprava", "korektury", "oprava" ], @@ -164,12 +164,12 @@ "novinky" ], [ - "delete_novinky", + "change_novinky", "novinky", "novinky" ], [ - "change_novinky", + "delete_novinky", "novinky", "novinky" ], @@ -204,12 +204,12 @@ "prijemce" ], [ - "delete_prijemce", + "change_prijemce", "personalni", "prijemce" ], [ - "change_prijemce", + "delete_prijemce", "personalni", "prijemce" ], @@ -234,12 +234,12 @@ "skola" ], [ - "delete_skola", + "change_skola", "personalni", "skola" ], [ - "change_skola", + "delete_skola", "personalni", "skola" ], @@ -264,12 +264,12 @@ "prednaska" ], [ - "delete_prednaska", + "change_prednaska", "prednasky", "prednaska" ], [ - "change_prednaska", + "delete_prednaska", "prednasky", "prednaska" ], @@ -284,12 +284,12 @@ "seznam" ], [ - "delete_seznam", + "change_seznam", "prednasky", "seznam" ], [ - "change_seznam", + "delete_seznam", "prednasky", "seznam" ], @@ -324,12 +324,12 @@ "konfera" ], [ - "delete_konfera", + "change_konfera", "soustredeni", "konfera" ], [ - "change_konfera", + "delete_konfera", "soustredeni", "konfera" ], @@ -344,12 +344,12 @@ "konfery_ucastnici" ], [ - "delete_konfery_ucastnici", + "change_konfery_ucastnici", "soustredeni", "konfery_ucastnici" ], [ - "change_konfery_ucastnici", + "delete_konfery_ucastnici", "soustredeni", "konfery_ucastnici" ], @@ -364,12 +364,12 @@ "soustredeni" ], [ - "delete_soustredeni", + "change_soustredeni", "soustredeni", "soustredeni" ], [ - "change_soustredeni", + "delete_soustredeni", "soustredeni", "soustredeni" ], @@ -384,12 +384,12 @@ "soustredeni_organizatori" ], [ - "delete_soustredeni_organizatori", + "change_soustredeni_organizatori", "soustredeni", "soustredeni_organizatori" ], [ - "change_soustredeni_organizatori", + "delete_soustredeni_organizatori", "soustredeni", "soustredeni_organizatori" ], @@ -404,12 +404,12 @@ "soustredeni_ucastnici" ], [ - "delete_soustredeni_ucastnici", + "change_soustredeni_ucastnici", "soustredeni", "soustredeni_ucastnici" ], [ - "change_soustredeni_ucastnici", + "delete_soustredeni_ucastnici", "soustredeni", "soustredeni_ucastnici" ], @@ -424,12 +424,12 @@ "tag" ], [ - "delete_tag", + "change_tag", "taggit", "tag" ], [ - "change_tag", + "delete_tag", "taggit", "tag" ], @@ -444,12 +444,12 @@ "taggeditem" ], [ - "delete_taggeditem", + "change_taggeditem", "taggit", "taggeditem" ], [ - "change_taggeditem", + "delete_taggeditem", "taggit", "taggeditem" ], @@ -464,12 +464,12 @@ "cislo" ], [ - "delete_cislo", + "change_cislo", "tvorba", "cislo" ], [ - "change_cislo", + "delete_cislo", "tvorba", "cislo" ], @@ -484,12 +484,12 @@ "clanek" ], [ - "delete_clanek", + "change_clanek", "tvorba", "clanek" ], [ - "change_clanek", + "delete_clanek", "tvorba", "clanek" ], @@ -519,12 +519,12 @@ "pohadka" ], [ - "delete_pohadka", + "change_pohadka", "tvorba", "pohadka" ], [ - "change_pohadka", + "delete_pohadka", "tvorba", "pohadka" ], @@ -539,12 +539,12 @@ "problem" ], [ - "delete_problem", + "change_problem", "tvorba", "problem" ], [ - "change_problem", + "delete_problem", "tvorba", "problem" ], @@ -559,12 +559,12 @@ "rocnik" ], [ - "delete_rocnik", + "change_rocnik", "tvorba", "rocnik" ], [ - "change_rocnik", + "delete_rocnik", "tvorba", "rocnik" ], @@ -579,12 +579,12 @@ "tema" ], [ - "delete_tema", + "change_tema", "tvorba", "tema" ], [ - "change_tema", + "delete_tema", "tvorba", "tema" ], @@ -599,12 +599,12 @@ "uloha" ], [ - "delete_uloha", + "change_uloha", "tvorba", "uloha" ], [ - "change_uloha", + "delete_uloha", "tvorba", "uloha" ], @@ -619,12 +619,12 @@ "nastaveni" ], [ - "delete_nastaveni", + "change_nastaveni", "various", "nastaveni" ], [ - "change_nastaveni", + "delete_nastaveni", "various", "nastaveni" ], diff --git a/prednasky/templates/prednasky/metaseznam_prednasek.html b/prednasky/templates/prednasky/metaseznam_prednasek.html index 9db97b08..dfc13caf 100644 --- a/prednasky/templates/prednasky/metaseznam_prednasek.html +++ b/prednasky/templates/prednasky/metaseznam_prednasek.html @@ -14,7 +14,7 @@ {% else %} Seznam přednášek na soustředění {{seznam.soustredeni.misto}} {% endif %} - Export + Export {% endfor %} diff --git a/prednasky/urls.py b/prednasky/urls.py index c22ea9f9..24d8535a 100644 --- a/prednasky/urls.py +++ b/prednasky/urls.py @@ -12,11 +12,11 @@ urlpatterns = [ 'prednasky/metaseznam_prednasek', org_required(views.MetaSeznamListView.as_view()), name='metaseznam-list'), - path( - 'prednasky/seznam_prednasek//export', - org_required(views.SeznamExportView), - name='seznam-export' - ), + # path( + # 'prednasky/seznam_prednasek//export', + # org_required(views.SeznamExportView), + # name='seznam-export' + # ), path( 'prednasky/seznam_prednasek//hlasovani.csv', org_required(views.PrednaskyExportView), diff --git a/prednasky/views.py b/prednasky/views.py index e61dfdef..c154c455 100644 --- a/prednasky/views.py +++ b/prednasky/views.py @@ -143,33 +143,33 @@ class SeznamListView(generic.ListView): return context -def SeznamExportView(request, seznam): - """Vypíše výsledky hlasování ve formátu pro prologovský optimalizátor""" - # TODO zřejmě se nepoužívá, časem vyřadit? nahradit tabulkou vhodnější pro - # lidi? - hlasovani = Hlasovani.objects.filter(seznam=seznam) - prednasky = Prednaska.objects.filter(seznamy=seznam) - orgove = set(p.org for p in prednasky) - ucastnici = set(h.ucastnik for h in hlasovani) - - for p in prednasky: - p.body = [] - for u in ucastnici: - try: - p.body.append(hlasovani.get(ucastnik=u, prednaska=p).body) - except ObjectDoesNotExist: - # účastník nehlasoval - p.body.append("?") - - for h in hlasovani: - h.ucastnik = hash(h.ucastnik) - - return render( - request, - 'prednasky/seznam_prednasek_export.txt', - {"hlasovani": hlasovani, "prednasky": prednasky, "orgove": orgove}, - content_type="text/plain" - ) +# def SeznamExportView(request, seznam): +# """Vypíše výsledky hlasování ve formátu pro prologovský optimalizátor""" +# # TODO zřejmě se nepoužívá, časem vyřadit? nahradit tabulkou vhodnější pro +# # lidi? +# hlasovani = Hlasovani.objects.filter(seznam=seznam) +# prednasky = Prednaska.objects.filter(seznamy=seznam) +# orgove = set(p.org for p in prednasky) +# ucastnici = set(h.ucastnik for h in hlasovani) +# +# for p in prednasky: +# p.body = [] +# for u in ucastnici: +# try: +# p.body.append(hlasovani.get(ucastnik=u, prednaska=p).body) +# except ObjectDoesNotExist: +# # účastník nehlasoval +# p.body.append("?") +# +# for h in hlasovani: +# h.ucastnik = hash(h.ucastnik) +# +# return render( +# request, +# 'prednasky/seznam_prednasek_export.txt', +# {"hlasovani": hlasovani, "prednasky": prednasky, "orgove": orgove}, +# content_type="text/plain" +# ) def PrednaskyExportView(request, seznam: int, **kwargs): From 0634cdad8714ab41d7d37163ef0c9b53ab2f0d03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Fri, 24 Jan 2025 20:42:44 +0100 Subject: [PATCH 11/18] =?UTF-8?q?O=C4=8Dividn=C4=9B=20ka=C5=BEd=C3=BD=20sy?= =?UTF-8?q?st=C3=A9m=20=C5=BEere=20uvozovky=20v=20f-stringu=20jinak?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prednasky/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prednasky/models.py b/prednasky/models.py index 32d8924e..fbeb6b21 100644 --- a/prednasky/models.py +++ b/prednasky/models.py @@ -20,7 +20,7 @@ class Seznam(models.Model): stav = models.IntegerField("Stav", choices=Stav.choices, default=Stav.NAVRH) def __str__(self): - return f"Seznam {"návrhů " if self.stav == Seznam.Stav.NAVRH else ""}přednášek na {self.soustredeni}" + return f"Seznam {'návrhů ' if self.stav == Seznam.Stav.NAVRH else ''}přednášek na {self.soustredeni}" class Prednaska(models.Model): From 1719e8be9a1e7a9efffd1c6b1558487b36195ae6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Fri, 24 Jan 2025 21:03:44 +0100 Subject: [PATCH 12/18] =?UTF-8?q?Zapomn=C4=9Bl=20jsem=20p=C5=99idat=20CSS?= =?UTF-8?q?=20m=C3=ADsto=20smazan=C3=A9ho=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mamweb/static/css/modules.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mamweb/static/css/modules.css b/mamweb/static/css/modules.css index 02a9b2bf..e698e2fb 100644 --- a/mamweb/static/css/modules.css +++ b/mamweb/static/css/modules.css @@ -503,5 +503,10 @@ label[for=id_skola] { font-weight: bold; } +/* Přednášky */ +.textznalosti, .textprednasky { + font-style: italic; +} + /*******************/ From cb14e4a91ecb445cb4c0807fa8280459753c6c18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Fri, 24 Jan 2025 21:07:06 +0100 Subject: [PATCH 13/18] =?UTF-8?q?A=20o=C4=8Dividn=C4=9B=20nevygeneroval=20?= =?UTF-8?q?migraci=20k=20p=C5=99eps=C3=A1n=C3=AD=20string=C5=AF=20u=20Hlas?= =?UTF-8?q?ovani.Body=20v=20commitu=20e933c697?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migrations/0020_alter_hlasovani_body.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 prednasky/migrations/0020_alter_hlasovani_body.py diff --git a/prednasky/migrations/0020_alter_hlasovani_body.py b/prednasky/migrations/0020_alter_hlasovani_body.py new file mode 100644 index 00000000..c3c348c2 --- /dev/null +++ b/prednasky/migrations/0020_alter_hlasovani_body.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.16 on 2025-01-24 20:04 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('prednasky', '0019_znalost_hlasovanioznalostech'), + ] + + operations = [ + migrations.AlterField( + model_name='hlasovani', + name='body', + field=models.IntegerField(choices=[(-1, 'rozhodně nechci'), (0, 'je mi to jedno'), (1, 'rozhodně chci')], default=0, verbose_name='Body'), + ), + ] From 9e513bba9ac02f0cbedc6ffe9efe86d6c3112860 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Wed, 29 Jan 2025 00:30:55 +0100 Subject: [PATCH 14/18] =?UTF-8?q?Kop=C3=ADrov=C3=A1n=C3=AD=20je=20=C4=8Das?= =?UTF-8?q?t=C3=BDm=20zdrojem=20chyb?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prednasky/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prednasky/views.py b/prednasky/views.py index c154c455..4f6f28ae 100644 --- a/prednasky/views.py +++ b/prednasky/views.py @@ -85,7 +85,7 @@ def newPrednaska(request): def odpoved_znalosti(z): hlasovani = z.hlasovanioznalostech_set.filter(ucastnik=osoba).first() - return hlasovani.odpoved if hlasovani else Hlasovani.Body.JEDNO + return hlasovani.odpoved if hlasovani else Znalost.Odpoved.CIRCA prednasky = seznam.prednaska_set.all() znalosti = seznam.znalost_set.all() From 51255252386b4389571812adbd169d2f92abb9de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Wed, 29 Jan 2025 01:05:08 +0100 Subject: [PATCH 15/18] Dokumentace aplikace `prednasky` --- prednasky/__init__.py | 3 +++ prednasky/admin.py | 11 +++++++++ prednasky/forms.py | 16 +++++++++++++ prednasky/models.py | 40 ++++++++++++++++++++++++++----- prednasky/views.py | 56 ++++++++++++++++++++++++++++++++----------- 5 files changed, 106 insertions(+), 20 deletions(-) diff --git a/prednasky/__init__.py b/prednasky/__init__.py index e69de29b..b34d6384 100644 --- a/prednasky/__init__.py +++ b/prednasky/__init__.py @@ -0,0 +1,3 @@ +""" +Aplikace umožňující orgům vypisovat si přednášky a účastníkům o nich hlasovat. +""" diff --git a/prednasky/admin.py b/prednasky/admin.py index a6204d35..7ab77d24 100644 --- a/prednasky/admin.py +++ b/prednasky/admin.py @@ -9,6 +9,10 @@ from soustredeni.models import Soustredeni class Seznam_PrednaskaInline(admin.TabularInline): + """ + Pomůcka pro :py:class:`prednasky.admin.SeznamAdmin` zobrazující hezky :py:class:`Přednášky ` + v adminu :py:class:`Seznamu `. + """ model = Prednaska.seznamy.through extra = 0 @@ -55,6 +59,7 @@ class Seznam_PrednaskaInline(admin.TabularInline): class SeznamAdmin(VersionAdmin): + """ Admin pro :py:class:`Seznam ` """ list_display = ['soustredeni', 'stav'] inlines = [Seznam_PrednaskaInline] @@ -62,6 +67,7 @@ admin.site.register(Seznam, SeznamAdmin) class PrednaskaAdmin(VersionAdmin): + """ Admin pro :py:class:`Přednášku """ list_display = ['nazev', 'org', 'obor'] list_filter = ['org', 'obor'] search_fields = ['nazev'] @@ -70,6 +76,7 @@ class PrednaskaAdmin(VersionAdmin): actions = ['move_to_soustredeni'] def move_to_soustredeni(self, request, queryset): + """ Přidá dané přednášky do seznamu, o kterém se právě hlasuje """ sous = Soustredeni.objects.first() seznam = Seznam.objects.filter(soustredeni=sous, stav=Seznam.Stav.NAVRH) if len(seznam) == 0: @@ -100,6 +107,10 @@ admin.site.register(Prednaska, PrednaskaAdmin) class ZnalostAdmin(PrednaskaAdmin): # Trochu hack, ať nemusím vypisovat všechno znovu + """ + Admin pro :py:class:`Znalost + TODO předělat, aby nedědila z :py:class:`prednasky.admin.PrednaskaAdmin`, ale společné věci byly zvlášť + """ list_display = ("__str__",) list_filter = () diff --git a/prednasky/forms.py b/prednasky/forms.py index cee90f7f..7b0e9739 100644 --- a/prednasky/forms.py +++ b/prednasky/forms.py @@ -3,13 +3,29 @@ from django import forms from .models import Hlasovani, HlasovaniOZnalostech class HlasovaniPrednaskaForm(forms.Form): + """ :py:class:`Formulář ` pro pro :py:class:`Hlasování ` o jedné :py:class:`Přednášce ` + (neobsahuje téměř nic, většina se musí doplnit jiným způsobem) + """ + + #: ID :py:class:`Přednášky `, o které se hlasuje prednaska_id = forms.IntegerField(widget=forms.HiddenInput) + #: :py:class:`Hodnocení (Body) ` této přednášky body = forms.ChoiceField(label=False, widget=forms.RadioSelect, choices=Hlasovani.Body.choices, initial=Hlasovani.Body.JEDNO) +#: Množina formulářů (:py:class:`formset ` :py:class:`HlasovaniPrednaskaFormů `) +#: pro :py:class:`Hlasování ` o množině :py:class:`Přednášek ` HlasovaniPrednaskaFormSet = forms.formset_factory(HlasovaniPrednaskaForm, extra=0) class HlasovaniZnalostiForm(forms.Form): + """ :py:class:`Formulář ` pro pro :py:class:`HlasováníOZnalostech ` o jedné :py:class:`Znalosti ` + (neobsahuje téměř nic, většina se musí doplnit jiným způsobem) + """ + + #: ID :py:class:`Znalosti `, o které hlasujeme znalost_id = forms.IntegerField(widget=forms.HiddenInput) + #: :py:class:`Odpověď ` na tuto znalost odpoved = forms.ChoiceField(label=False, widget=forms.RadioSelect, choices=HlasovaniOZnalostech.Odpoved.choices) +#: Množina formulářů (:py:class:`formset ` :py:class:`HlasovaniZnalostiFormů `) +#: pro :py:class:`HlasováníOZnalostech ` o množině :py:class:`Znalostí ` HlasovaniZnalostiFormSet = forms.formset_factory(HlasovaniZnalostiForm, extra=0) diff --git a/prednasky/models.py b/prednasky/models.py index fbeb6b21..f6168fdd 100644 --- a/prednasky/models.py +++ b/prednasky/models.py @@ -5,6 +5,12 @@ from personalni.models import Organizator, Osoba class Seznam(models.Model): + """ + Spojuje :py:class:`Přednášky ` + se :py:class:`Soustředěními `, + kde by mohly zaznít, nebo zazní/zazněly. + """ + class Meta: db_table = "prednasky_seznam" verbose_name = "Seznam přednášek" @@ -12,18 +18,23 @@ class Seznam(models.Model): ordering = ["soustredeni", "stav"] class Stav(models.IntegerChoices): + """ Stav seznamu přednášek (NAVRH se používá k hlasování viz :py:func:`daný view `). """ NAVRH = 1, "Návrh" BUDE = 2, "Bude" id = models.AutoField(primary_key=True) soustredeni = models.ForeignKey(Soustredeni, null=True, default=None, on_delete=models.PROTECT) - stav = models.IntegerField("Stav", choices=Stav.choices, default=Stav.NAVRH) + stav = models.IntegerField("Stav", choices=Stav.choices, default=Stav.NAVRH) #: :py:class:`Stav ` Seznamu def __str__(self): return f"Seznam {'návrhů ' if self.stav == Seznam.Stav.NAVRH else ''}přednášek na {self.soustredeni}" class Prednaska(models.Model): + """ + Reprezentuje přednášku, kterou si org může vypsat a účastník o ní hlasovat. + (Viz :py:class:`Hlasování `.) + """ class Meta: db_table = "prednasky_prednaska" verbose_name = "Přednáška" @@ -40,7 +51,7 @@ class Prednaska(models.Model): org = models.ForeignKey(Organizator, on_delete=models.PROTECT) popis = models.TextField("Popis pro orgy", null=True, blank=True, help_text="Neveřejný popis pro ostatní orgy") anotace = models.TextField("Anotace", null=True, blank=True, help_text="Veřejná anotace v hlasování") - obtiznost = models.IntegerField("Obtížnost", choices=Obtiznost.choices) + obtiznost = models.IntegerField("Obtížnost", choices=Obtiznost.choices) #: :py:class:`Obtížnost ` Přednášky obor = models.CharField("Obor", max_length=5, help_text="Podmnožina MFIOB") klicova = models.CharField("Klíčová slova", max_length=200, null=True, blank=True) seznamy = models.ManyToManyField(Seznam) @@ -50,6 +61,11 @@ class Prednaska(models.Model): class Hlasovani(models.Model): + """ + Reprezentuje hlasování jednoho účastníka + o jedné :py:class:`Přednášce ` + v jednom :py:class:`Seznamu ` (účastníkův pohled se totiž mezi sousy změnit) + """ class Meta: db_table = "prednasky_hlasovani" verbose_name = "Hlasování" @@ -57,17 +73,20 @@ class Hlasovani(models.Model): ordering = ["ucastnik", "prednaska"] class Body(models.IntegerChoices): + """ Ohodnocení přednášky v daném Hlasování (větší číslo = víc chci) """ NECHCI = -1, "rozhodně nechci" JEDNO = 0, "je mi to jedno" CHCI = 1, "rozhodně chci" id = models.AutoField(primary_key=True) prednaska = models.ForeignKey(Prednaska, on_delete=models.CASCADE) + #: Příslušné hlasování: :py:class:`Body ` body = models.IntegerField("Body", default=Body.JEDNO, choices=Body.choices) - # (přechod z jména na objekt Osoby nějak kape na tom, - # že všechna předchozí hlasování zde mají náhodný string…) - # TODO Změnit to na Osobu + #: Účastník, který hlasoval. Pouze string: + #: *(přechod z jména na objekt Osoby nějak kape na tom, + #: že všechna předchozí hlasování zde mají náhodný string…) + #: TODO Změnit to na Osobu* ucastnik = models.CharField("Účastník", max_length=100) seznam = models.ForeignKey(Seznam, null=True, on_delete=models.SET_NULL) @@ -76,6 +95,10 @@ class Hlasovani(models.Model): class Znalost(models.Model): + """ + Reprezentuje znalost, na kterou se můžeme účastníka ptát (nechat je hlasovat). + (Viz :py:class:`HlasováníOZnalostech `.) + """ class Meta: db_table = "prednasky_znalost" verbose_name = "Znalost k přednáškám" @@ -90,12 +113,17 @@ class Znalost(models.Model): class HlasovaniOZnalostech(models.Model): + """ + Reprezentuje hlasování jednoho účastníka + o jedné :py:class:`Znalosti ` + v jednom :py:class:`Seznamu ` (účastníkův pohled se totiž mezi sousy změnit) + """ class Odpoved(models.IntegerChoices): UMIM = -1, "Tohle celkem umím" CIRCA = 0, "Už jsem o tom slyšel, ale neřekl bychm, že to úplně umím" NEUMIM = 1, "Tohle vůbec neznám" - odpoved = models.CharField(u"odpověď", max_length=16, choices=Odpoved.choices, blank=False, null=False) + odpoved = models.CharField(u"odpověď", max_length=16, choices=Odpoved.choices, blank=False, null=False) #: :py:class:`Odpověď ` na HlasováníOZnalostech znalost = models.ForeignKey(Znalost, on_delete=models.CASCADE, blank=False, null=False) ucastnik = models.ForeignKey(Osoba, on_delete=models.CASCADE, blank=False, null=False) seznam = models.ForeignKey(Seznam, on_delete=models.SET_NULL, blank=True, null=True) diff --git a/prednasky/views.py b/prednasky/views.py index 4f6f28ae..68d563cc 100644 --- a/prednasky/views.py +++ b/prednasky/views.py @@ -2,7 +2,7 @@ import csv import http import logging -from django.http import HttpResponse +from django.http import HttpResponse, HttpRequest from django.shortcuts import render, get_object_or_404 from django.views import generic from django.shortcuts import HttpResponseRedirect @@ -22,7 +22,14 @@ ZNALOSTI_PREFIX = "znalosti" logger = logging.getLogger(__name__) -def newPrednaska(request): +def newPrednaska(request: HttpRequest) -> HttpResponse: + """ + View zobrazující a ukládající účastnické hlasování + (:py:class:`Hlasování ` + a :py:class:`HlasováníOZnalostech `) + o :py:class:`Přednáškách ` + a :py:class:`Znalostech ` + """ # hlasovani se vztahuje k nejnovejsimu soustredeni sous = Nastaveni.get_solo().aktualni_sous seznam = Seznam.objects.filter(soustredeni = sous, stav=Seznam.Stav.NAVRH).first() @@ -35,12 +42,14 @@ def newPrednaska(request): osoba = Osoba.objects.filter(user=request.user).first() ucastnik = osoba.plne_jmeno() + ' ' + str(osoba.id) # id, kvůli kolizi jmen - if request.method == 'POST': + if request.method == 'POST': # Když to byl POST, tak ukládáme. + # Načteme data do formsetů form_set_prednasky = HlasovaniPrednaskaFormSet(request.POST, prefix=PREDNASKY_PREFIX) form_set_znalosti = HlasovaniZnalostiFormSet(request.POST, prefix=ZNALOSTI_PREFIX) if form_set_prednasky.is_valid() and form_set_znalosti.is_valid(): with transaction.atomic(): + # Místo updatování data prostě smažeme a vytvoříme nová seznam.hlasovani_set.filter(ucastnik=ucastnik).delete() seznam.hlasovanioznalostech_set.filter(ucastnik=osoba).delete() @@ -73,17 +82,19 @@ def newPrednaska(request): ) return HttpResponseRedirect('./hotovo') - else: + + else: # Pokud je nějaký formset nevalidní, vracíme je k přepracování prednasky = seznam.prednaska_set.all() znalosti = seznam.znalost_set.all() - # Spadnout, pokud nesedí přednáška/znalost s formulářem. (Nějak se mi to nepovedlo.) + # FIXME Spadnout, pokud nesedí přednáška/znalost s formulářem. (Nějak se mi to nepovedlo.) + # Může se totiž stát, že se mezitím změnily přednášky (nějaká byla přidána/odebrána) - else: - def odpoved_prednasky(p): + else: # Když to nebyl POST, tak inicializujeme (pokud už o přednášce/znalosti účastník hlasoval, předvyplníme mu to). + def odpoved_prednasky(p: Prednaska) -> Hlasovani.Body: hlasovani = p.hlasovani_set.filter(ucastnik=ucastnik).first() return hlasovani.body if hlasovani else Hlasovani.Body.JEDNO - def odpoved_znalosti(z): + def odpoved_znalosti(z: Znalost) -> Znalost.Odpoved: hlasovani = z.hlasovanioznalostech_set.filter(ucastnik=osoba).first() return hlasovani.odpoved if hlasovani else Znalost.Odpoved.CIRCA @@ -99,6 +110,7 @@ def newPrednaska(request): ], prefix=ZNALOSTI_PREFIX) + # V případě nePOSTu nebo chyby při ukládání vracíme hlasování return render( request, 'prednasky/base.html', @@ -110,15 +122,21 @@ def newPrednaska(request): ) -def Prednaska_hotovo(request): +def Prednaska_hotovo(request: HttpRequest) -> HttpResponse: + """ View po vyplnění :py:func:`hlasování ` """ return formularOKView(request, "Děkujeme za vyplnění hlasování o přednáškách a těšíme se na soustředění.") class MetaSeznamListView(generic.ListView): + """ Seznam všech :py:class:`Seznamů ` s odkazy na exporty """ model = Seznam template_name = 'prednasky/metaseznam_prednasek.html' class SeznamListView(generic.ListView): + """ + Náhled na to, kolik má která přednáška v :py:class:`Seznamu ` :py:class:`hlasů `. + (Je otázka, zda tento View vůbec chceme. Pokud ano, hodilo by se do něj přidat i znalosti.) + """ template_name = 'prednasky/seznam_prednasek.html' def get_queryset(self): @@ -172,10 +190,19 @@ class SeznamListView(generic.ListView): # ) -def PrednaskyExportView(request, seznam: int, **kwargs): +def PrednaskyExportView(request: HttpRequest, seznam: int, **kwargs) -> HttpResponse: + """ + Vrátí všechna :py:class:`Hlasování ` + i :py:class:`HlasováníOZnalostech ` + v daném :py:class:`Seznamu ` + jako csv soubor (řádky = účastníci, sloupce = přednášky&znalosti). + + :param seznam: ID daného :py:class:`Seznamu ` + """ hlasovani = Hlasovani.objects.filter(seznam=seznam).select_related("prednaska") hlasovani_o_znalostech = HlasovaniOZnalostech.objects.filter(seznam=seznam).select_related('ucastnik', 'znalost') + # Inicializujeme sloupce prednasky = list(Prednaska.objects.filter(seznamy=seznam)) znalosti = list(Znalost.objects.filter(seznamy=seznam)) @@ -184,26 +211,27 @@ def PrednaskyExportView(request, seznam: int, **kwargs): znalosti_map: dict[int, int] = {z.id: i for i, z in enumerate(znalosti, offset + 1)} width = offset + len(znalosti_map) + # A po inicializaci sloupců vyplníme tabulku table: [str, list[str|Prednaska|Znalost,]] = {} for h in hlasovani: - if h.ucastnik not in table: + if h.ucastnik not in table: # Pokud jsme účastníka ještě neviděli, předgenerujeme si jeho řádek table[h.ucastnik] = [h.ucastnik] + ([""] * width) if h.prednaska.id in prednasky_map: table[h.ucastnik][prednasky_map[h.prednaska.id]] = h.body else: - pass # Padat hlasitě? + pass # TODO Padat hlasitě? for h in hlasovani_o_znalostech: ucastnik = str(h.ucastnik) + ' ' + str(h.ucastnik.id) # id, kvůli kolizi jmen - if ucastnik not in table: + if ucastnik not in table: # Pokud jsme účastníka ještě neviděli, předgenerujeme si jeho řádek table[ucastnik] = [ucastnik] + ([""] * width) if h.znalost.id in znalosti_map: table[ucastnik][znalosti_map[h.znalost.id]] = h.odpoved else: - pass # Padat hlasitě? + pass # TODO Padat hlasitě? response = HttpResponse(content_type="text/csv", charset="utf-8") From c1da67dbb4832181262496fbd92d86a79bb966b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Tue, 4 Feb 2025 20:33:03 +0100 Subject: [PATCH 16/18] =?UTF-8?q?Dob=C5=99e,=20p=C5=99=C3=AD=C5=A1t=C4=9B?= =?UTF-8?q?=20u=C5=BE=20p=C5=99i=20dokumentaci=20nebudu=20hrabat=20na=20ty?= =?UTF-8?q?pov=C3=A9=20anotace.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prednasky/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prednasky/views.py b/prednasky/views.py index 68d563cc..43ac983b 100644 --- a/prednasky/views.py +++ b/prednasky/views.py @@ -94,9 +94,9 @@ def newPrednaska(request: HttpRequest) -> HttpResponse: hlasovani = p.hlasovani_set.filter(ucastnik=ucastnik).first() return hlasovani.body if hlasovani else Hlasovani.Body.JEDNO - def odpoved_znalosti(z: Znalost) -> Znalost.Odpoved: + def odpoved_znalosti(z: Znalost) -> HlasovaniOZnalostech.Odpoved: hlasovani = z.hlasovanioznalostech_set.filter(ucastnik=osoba).first() - return hlasovani.odpoved if hlasovani else Znalost.Odpoved.CIRCA + return hlasovani.odpoved if hlasovani else HlasovaniOZnalostech.Odpoved.CIRCA prednasky = seznam.prednaska_set.all() znalosti = seznam.znalost_set.all() From 5d4b600b00c79864f39a4f03f5f0afdd71e5973a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Tue, 4 Feb 2025 21:21:15 +0100 Subject: [PATCH 17/18] =?UTF-8?q?Oto=C4=8Den=C3=AD=20v=C3=BDznamu=20odpov?= =?UTF-8?q?=C4=9Bd=C3=AD=20na=20hlasov=C3=A1n=C3=AD=20o=20znalostech=20+?= =?UTF-8?q?=20WTF=20pro=C4=8D=20to=20byl=20string?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0021_alter_hlasovanioznalostech_odpoved.py | 24 +++++++++++++++++++ prednasky/models.py | 7 +++--- 2 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 prednasky/migrations/0021_alter_hlasovanioznalostech_odpoved.py diff --git a/prednasky/migrations/0021_alter_hlasovanioznalostech_odpoved.py b/prednasky/migrations/0021_alter_hlasovanioznalostech_odpoved.py new file mode 100644 index 00000000..ff0c63b9 --- /dev/null +++ b/prednasky/migrations/0021_alter_hlasovanioznalostech_odpoved.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.16 on 2025-02-04 20:09 + +from django.db import migrations, models + +def zmena_bodu(apps, _schema_editor): + HlasovaniOZnalostech = apps.get_model('prednasky','HlasovaniOZnalostech') + for h in HlasovaniOZnalostech.objects.all(): + h.odpoved = -int(h.odpoved) + h.save() + +class Migration(migrations.Migration): + + dependencies = [ + ('prednasky', '0020_alter_hlasovani_body'), + ] + + operations = [ + migrations.AlterField( + model_name='hlasovanioznalostech', + name='odpoved', + field=models.IntegerField(choices=[(1, 'Tohle celkem umím'), (0, 'Už jsem o tom slyšel, ale neřekl bychm, že to úplně umím'), (-1, 'Tohle vůbec neznám')], verbose_name='odpověď'), + ), + migrations.RunPython(zmena_bodu, reverse_code=zmena_bodu), + ] diff --git a/prednasky/models.py b/prednasky/models.py index f6168fdd..a0597be2 100644 --- a/prednasky/models.py +++ b/prednasky/models.py @@ -119,11 +119,12 @@ class HlasovaniOZnalostech(models.Model): v jednom :py:class:`Seznamu ` (účastníkův pohled se totiž mezi sousy změnit) """ class Odpoved(models.IntegerChoices): - UMIM = -1, "Tohle celkem umím" + """ Na kolik danou znalost účastník ovládá v daném Hlasování (větší číslo = víc zná) """ + UMIM = 1, "Tohle celkem umím" CIRCA = 0, "Už jsem o tom slyšel, ale neřekl bychm, že to úplně umím" - NEUMIM = 1, "Tohle vůbec neznám" + NEUMIM = -1, "Tohle vůbec neznám" - odpoved = models.CharField(u"odpověď", max_length=16, choices=Odpoved.choices, blank=False, null=False) #: :py:class:`Odpověď ` na HlasováníOZnalostech + odpoved = models.IntegerField(u"odpověď", choices=Odpoved.choices, blank=False, null=False) #: :py:class:`Odpověď ` na HlasováníOZnalostech znalost = models.ForeignKey(Znalost, on_delete=models.CASCADE, blank=False, null=False) ucastnik = models.ForeignKey(Osoba, on_delete=models.CASCADE, blank=False, null=False) seznam = models.ForeignKey(Seznam, on_delete=models.SET_NULL, blank=True, null=True) From 9460c484f72c98648e4a0f623c34a5fa74965c8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Tue, 4 Feb 2025 23:09:47 +0100 Subject: [PATCH 18/18] =?UTF-8?q?Zobrazen=C3=AD=20Znalost=C3=AD=20(stejn?= =?UTF-8?q?=C4=9B=20jako=20P=C5=99edn=C3=A1=C5=A1ek)=20u=20dan=C3=A9ho=20s?= =?UTF-8?q?eznamu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prednasky/admin.py | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/prednasky/admin.py b/prednasky/admin.py index 7ab77d24..07615f43 100644 --- a/prednasky/admin.py +++ b/prednasky/admin.py @@ -58,10 +58,40 @@ class Seznam_PrednaskaInline(admin.TabularInline): def has_add_permission(self, req, obj): return False +class Seznam_ZnalostInline(admin.TabularInline): + """ + Pomůcka pro :py:class:`prednasky.admin.SeznamAdmin` zobrazující hezky :py:class:`Znalosti ` + v adminu :py:class:`Seznamu `. + """ + model = Znalost.seznamy.through + extra = 0 + + def znalost__nazev(self, obj): + return mark_safe( + f"{obj.znalost.nazev}" + ) + + def znalost__text(self, obj): + return mark_safe( + f"
{escape(obj.znalost.text)}
" + ) + + znalost__nazev.short_description = u'Přednáška' + znalost__text.short_description = u'Popis pro orgy' + + readonly_fields = [ + 'znalost__nazev', + 'znalost__text', + ] + exclude = ['znalost'] + + def has_add_permission(self, req, obj): return False + + class SeznamAdmin(VersionAdmin): """ Admin pro :py:class:`Seznam ` """ list_display = ['soustredeni', 'stav'] - inlines = [Seznam_PrednaskaInline] + inlines = [Seznam_PrednaskaInline, Seznam_ZnalostInline] admin.site.register(Seznam, SeznamAdmin)