Jonas Havelka
2 years ago
37 changed files with 1174 additions and 1005 deletions
@ -0,0 +1,11 @@ |
|||||
|
from django.core.management.base import BaseCommand |
||||
|
import seminar.models as m |
||||
|
|
||||
|
|
||||
|
class Command(BaseCommand): |
||||
|
help = "Všem deadlinům se zveřejněnou výsledkovkou vygeneruj výsledkovku" |
||||
|
|
||||
|
def handle(self, *args, **options): |
||||
|
for deadline in m.Deadline.objects.filter(verejna_vysledkovka=True): |
||||
|
deadline.vygeneruj_vysledkovku() |
||||
|
|
@ -0,0 +1,81 @@ |
|||||
|
# Generated by Django 3.2.15 on 2022-10-01 08:44 |
||||
|
|
||||
|
import datetime |
||||
|
from django.db import migrations, models |
||||
|
import django.db.models.deletion |
||||
|
from django.utils import timezone |
||||
|
|
||||
|
import seminar.models as m |
||||
|
|
||||
|
|
||||
|
def vytvor_deadliny(apps, schema_editor): |
||||
|
Cislo = apps.get_model('seminar', 'Cislo') |
||||
|
Deadline = apps.get_model('seminar', 'Deadline') |
||||
|
|
||||
|
for cislo in Cislo.objects.all(): |
||||
|
if cislo.rocnik.rocnik < 26: |
||||
|
Deadline.objects.create( |
||||
|
cislo=cislo, |
||||
|
typ=m.Deadline.TYP_CISLA, |
||||
|
deadline=timezone.make_aware(datetime.datetime.combine(datetime.date(1994 + cislo.rocnik.rocnik, 6, int(cislo.poradi[0])), datetime.time.min)), |
||||
|
verejna_vysledkovka=cislo.verejna_vysledkovka, |
||||
|
) |
||||
|
continue |
||||
|
|
||||
|
def vytvor_deadline(date: datetime.date, typ): |
||||
|
Deadline.objects.create( |
||||
|
cislo=cislo, |
||||
|
typ=typ, |
||||
|
deadline=timezone.make_aware(datetime.datetime.combine(date, datetime.time.min)) + datetime.timedelta(days=1), |
||||
|
verejna_vysledkovka=True |
||||
|
) |
||||
|
|
||||
|
if cislo.datum_deadline_soustredeni and cislo.datum_deadline_soustredeni == cislo.datum_preddeadline: |
||||
|
vytvor_deadline( |
||||
|
date=cislo.datum_deadline_soustredeni, |
||||
|
typ=m.Deadline.TYP_PRVNI_A_SOUS |
||||
|
) |
||||
|
else: |
||||
|
if cislo.datum_deadline_soustredeni: |
||||
|
vytvor_deadline( |
||||
|
date=cislo.datum_deadline_soustredeni, |
||||
|
typ=m.Deadline.TYP_SOUS |
||||
|
) |
||||
|
if cislo.datum_preddeadline: |
||||
|
vytvor_deadline( |
||||
|
date=cislo.datum_preddeadline, |
||||
|
typ=m.Deadline.TYP_PRVNI |
||||
|
) |
||||
|
|
||||
|
if cislo.datum_deadline: |
||||
|
vytvor_deadline( |
||||
|
date=cislo.datum_deadline, |
||||
|
typ=m.Deadline.TYP_CISLA |
||||
|
) |
||||
|
|
||||
|
|
||||
|
class Migration(migrations.Migration): |
||||
|
|
||||
|
dependencies = [ |
||||
|
('seminar', '0102_osoba_jak_se_dozvedeli'), |
||||
|
] |
||||
|
|
||||
|
operations = [ |
||||
|
migrations.CreateModel( |
||||
|
name='Deadline', |
||||
|
fields=[ |
||||
|
('id', models.AutoField(primary_key=True, serialize=False)), |
||||
|
('deadline', models.DateTimeField(default=timezone.make_aware(datetime.datetime.combine(timezone.now(), datetime.time.max)))), |
||||
|
('typ', models.CharField(choices=[('cisla', 'Deadline celého čísla'), ('prvni', 'První deadline'), ('prvniasous', 'Sousový a první deadline'), ('sous', 'Sousový deadline')], max_length=32, verbose_name='typ deadlinu')), |
||||
|
('verejna_vysledkovka', models.BooleanField(db_column='verejna_vysledkovka', default=False, verbose_name='veřejná výsledkovka')), |
||||
|
('cislo', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='deadline_v_cisle', to='seminar.cislo', verbose_name='deadline v čísle')), |
||||
|
], |
||||
|
options={ |
||||
|
'verbose_name': 'Deadline', |
||||
|
'verbose_name_plural': 'Deadliny', |
||||
|
'db_table': 'seminar_deadliny', |
||||
|
'ordering': ['deadline'], |
||||
|
}, |
||||
|
), |
||||
|
migrations.RunPython(vytvor_deadliny, migrations.RunPython.noop), |
||||
|
] |
@ -0,0 +1,86 @@ |
|||||
|
# Generated by Django 3.2.15 on 2022-10-01 09:28 |
||||
|
|
||||
|
from django.db import migrations, models |
||||
|
import django.db.models.deletion |
||||
|
from logging import getLogger |
||||
|
|
||||
|
log = getLogger(__name__) |
||||
|
|
||||
|
def prirad_deadliny(apps, schema_editor): |
||||
|
Hodnoceni = apps.get_model('seminar', 'Hodnoceni') |
||||
|
Deadline = apps.get_model('seminar', 'Deadline') |
||||
|
|
||||
|
for h in Hodnoceni.objects.all(): |
||||
|
if h.cislo_body is not None and h.cislo_body.rocnik.rocnik < 26: |
||||
|
# Deadline připravený v minulé migraci |
||||
|
h.deadline_body = h.cislo_body.deadline_v_cisle.get() |
||||
|
h.save() |
||||
|
continue |
||||
|
|
||||
|
p = h.problem |
||||
|
|
||||
|
if p.polymorphic_ctype.model == 'tema': |
||||
|
t = p.tema |
||||
|
d = Deadline.objects.filter(cislo__rocnik=t.rocnik, deadline__gte=h.reseni.cas_doruceni).first() |
||||
|
|
||||
|
if d is None: |
||||
|
d = Deadline.objects.filter(cislo__rocnik=t.rocnik).last() |
||||
|
|
||||
|
if d is not None: |
||||
|
h.deadline_body = d |
||||
|
h.save() |
||||
|
continue |
||||
|
|
||||
|
cislo = None |
||||
|
|
||||
|
if p.polymorphic_ctype.model == 'uloha': |
||||
|
u = p.uloha |
||||
|
cislo = u.cislo_zadani |
||||
|
|
||||
|
if p.polymorphic_ctype.model == 'clanek': |
||||
|
c = p.clanek |
||||
|
if c.cislo is not None: |
||||
|
cislo = c.cislo |
||||
|
|
||||
|
if cislo is None: |
||||
|
log.warning(f"Číslo hodnocení {h.id} se nepodařilo určit exaktním způsobem. Dané hodnocení házím do jeho cislo_body: {h.cislo_body}.") |
||||
|
cislo = h.cislo_body |
||||
|
|
||||
|
if cislo is not None: |
||||
|
d = Deadline.objects.filter(cislo=cislo, deadline__gte=h.reseni.cas_doruceni).first() |
||||
|
if d is None: |
||||
|
d = Deadline.objects.filter(cislo=cislo).last() |
||||
|
if d is not None: |
||||
|
h.deadline_body = d |
||||
|
h.save() |
||||
|
continue |
||||
|
|
||||
|
d = Deadline.objects.filter(deadline__gte=h.reseni.cas_doruceni).first() |
||||
|
h.deadline_body = d |
||||
|
h.save() |
||||
|
|
||||
|
log.warning(f"Deadline hodnocení {h.id} se nepodařil určit exaktnějším způsobem. Zkouším další. Přiřazen {h.deadline_body}. Původní cislo_body: {h.cislo_body}.") |
||||
|
|
||||
|
# Zběžná kontrola. Předpokládá, že M&M má méně než 10 čísel v ročníku |
||||
|
# a že první znak pořadí je int určující dané pořadí (schroustání 7-8). |
||||
|
if h.cislo_body and h.deadline_body and ( |
||||
|
int(h.deadline_body.cislo.poradi[0]) + 2 < int(h.cislo_body.poradi[0]) |
||||
|
or int(h.deadline_body.cislo.poradi[0]) > int(h.cislo_body.poradi[0]) |
||||
|
): |
||||
|
log.error(f"Hodnocení {h.id} se špatně změnilo číslo z {h.cislo_body} na {h.deadline_body.cislo}") |
||||
|
|
||||
|
|
||||
|
class Migration(migrations.Migration): |
||||
|
|
||||
|
dependencies = [ |
||||
|
('seminar', '0103_deadline'), |
||||
|
] |
||||
|
|
||||
|
operations = [ |
||||
|
migrations.AddField( |
||||
|
model_name='hodnoceni', |
||||
|
name='deadline_body', |
||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='hodnoceni', to='seminar.deadline', verbose_name='deadline pro body'), |
||||
|
), |
||||
|
migrations.RunPython(prirad_deadliny, migrations.RunPython.noop), |
||||
|
] |
@ -0,0 +1,50 @@ |
|||||
|
# Generated by Django 3.2.15 on 2022-10-09 10:14 |
||||
|
|
||||
|
from django.db import migrations |
||||
|
from seminar.models import Deadline |
||||
|
|
||||
|
|
||||
|
def vrat_deadliny(apps, schema_editor): |
||||
|
Cislo = apps.get_model('seminar', 'Cislo') |
||||
|
|
||||
|
for cislo in Cislo.objects.all(): |
||||
|
prvni_deadline = cislo.deadline_v_cisle.filter(typ=Deadline.TYP_PRVNI).last() |
||||
|
sous_deadline = cislo.deadline_v_cisle.filter(typ=Deadline.TYP_SOUS).last() |
||||
|
prvni_a_sous_deadline = cislo.deadline_v_cisle.filter(typ=Deadline.TYP_PRVNI_A_SOUS).last() |
||||
|
posledni_deadline = cislo.deadline_v_cisle.filter(typ=Deadline.TYP_CISLA).last() |
||||
|
|
||||
|
if prvni_a_sous_deadline is not None: |
||||
|
cislo.datum_deadline_soustredeni = prvni_a_sous_deadline.deadline.date() |
||||
|
cislo.datum_preddeadline = prvni_a_sous_deadline.deadline.date() |
||||
|
else: |
||||
|
if sous_deadline is not None: |
||||
|
cislo.datum_deadline_soustredeni = sous_deadline.deadline.date() |
||||
|
if prvni_deadline is not None: |
||||
|
cislo.datum_preddeadline = prvni_deadline.deadline.date() |
||||
|
|
||||
|
if posledni_deadline: |
||||
|
cislo.datum_deadline = posledni_deadline.deadline.date() |
||||
|
|
||||
|
cislo.save() |
||||
|
|
||||
|
class Migration(migrations.Migration): |
||||
|
|
||||
|
dependencies = [ |
||||
|
('seminar', '0104_hodnoceni_deadline_body'), |
||||
|
] |
||||
|
|
||||
|
operations = [ |
||||
|
migrations.RunPython(migrations.RunPython.noop, vrat_deadliny), |
||||
|
migrations.RemoveField( |
||||
|
model_name='cislo', |
||||
|
name='datum_deadline', |
||||
|
), |
||||
|
migrations.RemoveField( |
||||
|
model_name='cislo', |
||||
|
name='datum_deadline_soustredeni', |
||||
|
), |
||||
|
migrations.RemoveField( |
||||
|
model_name='cislo', |
||||
|
name='datum_preddeadline', |
||||
|
), |
||||
|
] |
@ -0,0 +1,27 @@ |
|||||
|
# Generated by Django 3.2.15 on 2022-10-09 11:04 |
||||
|
|
||||
|
from django.db import migrations |
||||
|
from seminar.models import Deadline |
||||
|
|
||||
|
|
||||
|
def vrat_verejnost(apps, schema_editor): |
||||
|
Cislo = apps.get_model('seminar', 'Cislo') |
||||
|
|
||||
|
for cislo in Cislo.objects.all(): |
||||
|
cislo.verejna_vysledkovka = cislo.deadline_v_cisle.filter(typ=Deadline.TYP_CISLA, verejna_vysledkovka=True).exists() |
||||
|
cislo.save() |
||||
|
|
||||
|
|
||||
|
class Migration(migrations.Migration): |
||||
|
|
||||
|
dependencies = [ |
||||
|
('seminar', '0105_odstraneni_deadlinu_cisla'), |
||||
|
] |
||||
|
|
||||
|
operations = [ |
||||
|
migrations.RunPython(migrations.RunPython.noop, vrat_verejnost), |
||||
|
migrations.RemoveField( |
||||
|
model_name='cislo', |
||||
|
name='verejna_vysledkovka', |
||||
|
), |
||||
|
] |
@ -0,0 +1,26 @@ |
|||||
|
# Generated by Django 3.2.15 on 2022-10-10 07:23 |
||||
|
|
||||
|
from django.db import migrations, models |
||||
|
import django.db.models.deletion |
||||
|
|
||||
|
|
||||
|
class Migration(migrations.Migration): |
||||
|
|
||||
|
dependencies = [ |
||||
|
('seminar', '0106_remove_cislo_verejna_vysledkovka'), |
||||
|
] |
||||
|
|
||||
|
operations = [ |
||||
|
migrations.CreateModel( |
||||
|
name='ZmrazenaVysledkovka', |
||||
|
fields=[ |
||||
|
('deadline', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, related_name='vysledkovka_v_deadlinu', serialize=False, to='seminar.deadline')), |
||||
|
('html', models.TextField()), |
||||
|
], |
||||
|
options={ |
||||
|
'verbose_name': 'Zmražená výsledkovka', |
||||
|
'verbose_name_plural': 'Zmražené výsledkovky', |
||||
|
'db_table': 'seminar_vysledkovky', |
||||
|
}, |
||||
|
), |
||||
|
] |
@ -1,9 +1,9 @@ |
|||||
\setlength{\tabcolsep}{3pt} |
\setlength{\tabcolsep}{3pt} |
||||
\begin{longtable}{|r|l|c|r|{% for p in problemy %}c@{\hskip.5em}{% endfor %}|r|r|}\hline |
\begin{longtable}{|r|l|c|r|{% for p in vysledkovka.temata_a_spol %}c@{\hskip.5em}{% endfor %}{% if vysledkovka.je_nejake_ostatni %}|c@{\hskip.5em}{% endif %}|r|r|}\hline |
||||
& & & & \multicolumn{ {{ problemy|length}} }{c|}{\textbf{Témata}} & & \\\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 |
& & & & \multicolumn{ {{ vysledkovka.temata_a_spol|length}} }{c|}{\textbf{Témata}} & & {% if vysledkovka.je_nejake_ostatni %}&{\hskip.5em}{% endif %} \\\textbf{Poř.}& \textbf{Jméno}& \textbf{R.}& \raisebox{0.7mm}{$\sum_{-1}$}& {% for p in vysledkovka.temata_a_spol %}\textbf{ {{ p.kod_v_rocniku }} }&{% endfor %}{% if vysledkovka.je_nejake_ostatni %}\textbf{Ostatní}&{% endif %}\raisebox{0.7mm}{$\sum_0$}&\raisebox{0.7mm}{$\sum_1$}\\\hline |
||||
\endhead |
\endhead |
||||
\hline |
\hline |
||||
\endfoot |
\endfoot |
||||
{% 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}}\\ |
{% for rv in vysledkovka.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_za_temata_seznam %}{{b}}&{% endfor %}{{rv.body_cislo}}&{{rv.body_rocnik|default:0}}\\ |
||||
{% endfor %} |
{% endfor %} |
||||
\end{longtable} |
\end{longtable} |
||||
|
@ -1,149 +0,0 @@ |
|||||
from django.test import TestCase |
|
||||
from datetime import date |
|
||||
|
|
||||
import seminar.models as m |
|
||||
from seminar.utils import deadline, TypDeadline |
|
||||
|
|
||||
class DeadlineTestCase(TestCase): |
|
||||
def setUp(self): |
|
||||
# Chceme pár ročníků a v nich pár čísel |
|
||||
r1 = m.Rocnik.objects.create(rocnik=1, prvni_rok=2000, exportovat=False) |
|
||||
r2 = m.Rocnik.objects.create(rocnik=2, prvni_rok=2001, exportovat=False) |
|
||||
r3 = m.Rocnik.objects.create(rocnik=3, prvni_rok=2002, exportovat=False) |
|
||||
|
|
||||
# První číslo mívá soustřeďkový deadline… |
|
||||
c1_1 = m.Cislo.objects.create(rocnik=r1, poradi='1', |
|
||||
datum_vydani=date.fromisoformat('2000-05-22'), |
|
||||
datum_preddeadline=date.fromisoformat('2000-09-11'), |
|
||||
datum_deadline_soustredeni=date.fromisoformat('2000-09-11'), |
|
||||
datum_deadline=date.fromisoformat('2000-10-01'), |
|
||||
) |
|
||||
c1_2 = m.Cislo.objects.create(rocnik=r1, poradi='2', |
|
||||
datum_vydani=date.fromisoformat('2000-10-19'), |
|
||||
datum_preddeadline=date.fromisoformat('2000-12-05'), |
|
||||
datum_deadline=date.fromisoformat('2001-01-02'), |
|
||||
) |
|
||||
# Některá čísla nemají předdeadline… |
|
||||
c1_3 = m.Cislo.objects.create(rocnik=r1, poradi='3', |
|
||||
datum_vydani=date.fromisoformat('2001-01-28'), |
|
||||
datum_deadline=date.fromisoformat('2001-03-12'), |
|
||||
) |
|
||||
# Poslední číslo nemá ani normální deadline… |
|
||||
c1_4 = m.Cislo.objects.create(rocnik=r1, poradi='4-5', |
|
||||
datum_vydani=date.fromisoformat('2001-04-24'), |
|
||||
) |
|
||||
# První číslo dalšího ročníku se někdy vydá dřív, než poslední minulého… |
|
||||
c2_1 = m.Cislo.objects.create(rocnik=r2, poradi='1', |
|
||||
datum_vydani=date.fromisoformat('2001-04-19'), |
|
||||
datum_deadline_soustredeni=date.fromisoformat('2001-09-26'), |
|
||||
datum_deadline=date.fromisoformat('2001-10-07'), |
|
||||
) |
|
||||
# Tohle číslo má finální deadline až po vydání prvního čísla dalšího ročníku |
|
||||
# To samé se skoro stalo na přelomu (reálných) ročníků 27 a 28. |
|
||||
c2_2 = m.Cislo.objects.create(rocnik=r2, poradi='2', |
|
||||
datum_vydani=date.fromisoformat('2002-03-14'), |
|
||||
datum_preddeadline=date.fromisoformat('2002-05-26'), |
|
||||
datum_deadline=date.fromisoformat('2002-06-30'), |
|
||||
) |
|
||||
# Závěrečné číslo druhého ročníku až na podzim |
|
||||
c2_3 = m.Cislo.objects.create(rocnik=r2, poradi='3', |
|
||||
datum_vydani=date.fromisoformat('2002-09-05'), |
|
||||
) |
|
||||
# Divný případ: sous deadline stejný jako finální |
|
||||
c3_1 = m.Cislo.objects.create(rocnik=r3, poradi='1', |
|
||||
datum_vydani=date.fromisoformat('2002-06-02'), |
|
||||
datum_preddeadline=date.fromisoformat('2002-08-31'), |
|
||||
datum_deadline=date.fromisoformat('2002-09-30'), |
|
||||
datum_deadline_soustredeni=date.fromisoformat('2002-09-30'), |
|
||||
) |
|
||||
|
|
||||
# Celkový harmonogram: |
|
||||
# 2000-05-22 začátek 1. ročníku, číslo 1.1 |
|
||||
|
|
||||
# 2000-09-11 sous a 1. deadline 1.1 |
|
||||
# 2000-10-01 finální deadline 1.1 |
|
||||
# 2000-10-19 Vydání 1.2 |
|
||||
# 2000-12-05 předdeadline 1.2 |
|
||||
# 2001-01-02 finální deadline 1.2 |
|
||||
# 2001-01-28 vyd 1.3 |
|
||||
# 2001-03-12 deadline 1.3 |
|
||||
# 2001-04-19 Začátek 2. ročníku, číslo 2.1 |
|
||||
# 2001-04-24 Vydání 1.4-5 -- závěrečné číslo 1. roč. |
|
||||
|
|
||||
# 2001-09-26 Sous-deadline 2.1 |
|
||||
# 2001-10-07 Deadline 2.1 |
|
||||
# 2002-03-14 Pí den, vydání 2.2 |
|
||||
# 2002-05-26 Předdeadline 2.2 |
|
||||
# 2002-06-02 Třetí ročník, vydání 3.1 |
|
||||
# 2002-06-30 Deadline 2.2 |
|
||||
|
|
||||
# 2002-08-31 Předdeadline 3.1 |
|
||||
# 2002-09-04 Vydání 2.3, konec 2. roč. |
|
||||
# 2002-09-30 Sous a finální deadline 3.1 |
|
||||
|
|
||||
def test_deadline_spravne_vysledky(self): |
|
||||
"""V každém intervalu mezi deadliny dostáváme ten správný deadline""" |
|
||||
# První ročník |
|
||||
self.assertEqual(deadline(date.fromisoformat('2000-05-30')), (TypDeadline.PredDeadline, m.Cislo.objects.get(rocnik__rocnik=1, poradi='1'), date.fromisoformat('2000-09-11'))) |
|
||||
self.assertEqual(deadline(date.fromisoformat('2000-09-15')), (TypDeadline.FinalDeadline, m.Cislo.objects.get(rocnik__rocnik=1, poradi='1'), date.fromisoformat('2000-10-01'))) |
|
||||
|
|
||||
# Trochu divný případ, kdy někdo něco pošle před vydáním čísla. Ale článkům se to asi stát může… |
|
||||
self.assertEqual(deadline(date.fromisoformat('2000-10-10')), (TypDeadline.PredDeadline, m.Cislo.objects.get(rocnik__rocnik=1, poradi='2'), date.fromisoformat('2000-12-05'))) |
|
||||
self.assertEqual(deadline(date.fromisoformat('2000-10-22')), (TypDeadline.PredDeadline, m.Cislo.objects.get(rocnik__rocnik=1, poradi='2'), date.fromisoformat('2000-12-05'))) |
|
||||
self.assertEqual(deadline(date.fromisoformat('2000-12-15')), (TypDeadline.FinalDeadline, m.Cislo.objects.get(rocnik__rocnik=1, poradi='2'), date.fromisoformat('2001-01-02'))) |
|
||||
self.assertEqual(deadline(date.fromisoformat('2001-01-08')), (TypDeadline.FinalDeadline, m.Cislo.objects.get(rocnik__rocnik=1, poradi='3'), date.fromisoformat('2001-03-12'))) |
|
||||
self.assertEqual(deadline(date.fromisoformat('2001-01-30')), (TypDeadline.FinalDeadline, m.Cislo.objects.get(rocnik__rocnik=1, poradi='3'), date.fromisoformat('2001-03-12'))) |
|
||||
self.assertEqual(deadline(date.fromisoformat('2001-03-15')), (TypDeadline.SousDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='1'), date.fromisoformat('2001-09-26'))) |
|
||||
self.assertEqual(deadline(date.fromisoformat('2001-04-22')), (TypDeadline.SousDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='1'), date.fromisoformat('2001-09-26'))) |
|
||||
self.assertEqual(deadline(date.fromisoformat('2001-04-30')), (TypDeadline.SousDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='1'), date.fromisoformat('2001-09-26'))) |
|
||||
|
|
||||
# Druhý ročník |
|
||||
# Pro jistotu ještě prázdniny |
|
||||
self.assertEqual(deadline(date.fromisoformat('2001-07-30')), (TypDeadline.SousDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='1'), date.fromisoformat('2001-09-26'))) |
|
||||
self.assertEqual(deadline(date.fromisoformat('2001-09-27')), (TypDeadline.FinalDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='1'), date.fromisoformat('2001-10-07'))) |
|
||||
self.assertEqual(deadline(date.fromisoformat('2001-12-27')), (TypDeadline.PredDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='2'), date.fromisoformat('2002-05-26'))) |
|
||||
self.assertEqual(deadline(date.fromisoformat('2002-03-15')), (TypDeadline.PredDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='2'), date.fromisoformat('2002-05-26'))) |
|
||||
self.assertEqual(deadline(date.fromisoformat('2002-03-15')), (TypDeadline.PredDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='2'), date.fromisoformat('2002-05-26'))) |
|
||||
|
|
||||
self.assertEqual(deadline(date.fromisoformat('2002-05-27')), (TypDeadline.FinalDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='2'), date.fromisoformat('2002-06-30'))) |
|
||||
# Tohle je trochu podezřelý případ, protože relevantní deadliny existují dvě… Ale ten pro minulý ročník je těsnější a realističtější |
|
||||
self.assertEqual(deadline(date.fromisoformat('2002-06-03')), (TypDeadline.FinalDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='2'), date.fromisoformat('2002-06-30'))) |
|
||||
self.assertEqual(deadline(date.fromisoformat('2002-07-01')), (TypDeadline.PredDeadline, m.Cislo.objects.get(rocnik__rocnik=3, poradi='1'), date.fromisoformat('2002-08-31'))) |
|
||||
|
|
||||
# Třetí ročník |
|
||||
self.assertEqual(deadline(date.fromisoformat('2002-09-01')), (TypDeadline.SousDeadline, m.Cislo.objects.get(rocnik__rocnik=3, poradi='1'), date.fromisoformat('2002-09-30'))) |
|
||||
self.assertEqual(deadline(date.fromisoformat('2002-09-05')), (TypDeadline.SousDeadline, m.Cislo.objects.get(rocnik__rocnik=3, poradi='1'), date.fromisoformat('2002-09-30'))) |
|
||||
|
|
||||
def test_deadline_ve_zlomove_dny(self): |
|
||||
"""Pro dny, kdy je deadline nebo vydání čísla, pořád dostáváme správné deadliny. |
|
||||
|
|
||||
Testuje hlavně přítomnost někde nějakých off-by-one""" |
|
||||
self.assertEqual(deadline(date.fromisoformat('2000-05-22')), (TypDeadline.PredDeadline, m.Cislo.objects.get(rocnik__rocnik=1, poradi='1'), date.fromisoformat('2000-09-11'))) |
|
||||
|
|
||||
self.assertEqual(deadline(date.fromisoformat('2000-09-11')), (TypDeadline.PredDeadline, m.Cislo.objects.get(rocnik__rocnik=1, poradi='1'), date.fromisoformat('2000-09-11'))) |
|
||||
self.assertEqual(deadline(date.fromisoformat('2000-10-01')), (TypDeadline.FinalDeadline, m.Cislo.objects.get(rocnik__rocnik=1, poradi='1'), date.fromisoformat('2000-10-01'))) |
|
||||
self.assertEqual(deadline(date.fromisoformat('2000-10-19')), (TypDeadline.PredDeadline, m.Cislo.objects.get(rocnik__rocnik=1, poradi='2'), date.fromisoformat('2000-12-05'))) |
|
||||
self.assertEqual(deadline(date.fromisoformat('2000-12-05')), (TypDeadline.PredDeadline, m.Cislo.objects.get(rocnik__rocnik=1, poradi='2'), date.fromisoformat('2000-12-05'))) |
|
||||
self.assertEqual(deadline(date.fromisoformat('2001-01-02')), (TypDeadline.FinalDeadline, m.Cislo.objects.get(rocnik__rocnik=1, poradi='2'), date.fromisoformat('2001-01-02'))) |
|
||||
self.assertEqual(deadline(date.fromisoformat('2001-01-28')), (TypDeadline.FinalDeadline, m.Cislo.objects.get(rocnik__rocnik=1, poradi='3'), date.fromisoformat('2001-03-12'))) |
|
||||
self.assertEqual(deadline(date.fromisoformat('2001-03-12')), (TypDeadline.FinalDeadline, m.Cislo.objects.get(rocnik__rocnik=1, poradi='3'), date.fromisoformat('2001-03-12'))) |
|
||||
self.assertEqual(deadline(date.fromisoformat('2001-04-19')), (TypDeadline.SousDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='1'), date.fromisoformat('2001-09-26'))) |
|
||||
self.assertEqual(deadline(date.fromisoformat('2001-04-24')), (TypDeadline.SousDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='1'), date.fromisoformat('2001-09-26'))) |
|
||||
|
|
||||
self.assertEqual(deadline(date.fromisoformat('2001-09-26')), (TypDeadline.SousDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='1'), date.fromisoformat('2001-09-26'))) |
|
||||
self.assertEqual(deadline(date.fromisoformat('2001-10-07')), (TypDeadline.FinalDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='1'), date.fromisoformat('2001-10-07'))) |
|
||||
self.assertEqual(deadline(date.fromisoformat('2002-03-14')), (TypDeadline.PredDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='2'), date.fromisoformat('2002-05-26'))) |
|
||||
self.assertEqual(deadline(date.fromisoformat('2002-05-26')), (TypDeadline.PredDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='2'), date.fromisoformat('2002-05-26'))) |
|
||||
self.assertEqual(deadline(date.fromisoformat('2002-06-02')), (TypDeadline.FinalDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='2'), date.fromisoformat('2002-06-30'))) |
|
||||
self.assertEqual(deadline(date.fromisoformat('2002-06-30')), (TypDeadline.FinalDeadline, m.Cislo.objects.get(rocnik__rocnik=2, poradi='2'), date.fromisoformat('2002-06-30'))) |
|
||||
|
|
||||
self.assertEqual(deadline(date.fromisoformat('2002-08-31')), (TypDeadline.PredDeadline, m.Cislo.objects.get(rocnik__rocnik=3, poradi='1'), date.fromisoformat('2002-08-31'))) |
|
||||
self.assertEqual(deadline(date.fromisoformat('2002-09-04')), (TypDeadline.SousDeadline, m.Cislo.objects.get(rocnik__rocnik=3, poradi='1'), date.fromisoformat('2002-09-30'))) |
|
||||
self.assertEqual(deadline(date.fromisoformat('2002-09-30')), (TypDeadline.SousDeadline, m.Cislo.objects.get(rocnik__rocnik=3, poradi='1'), date.fromisoformat('2002-09-30'))) |
|
||||
|
|
||||
def test_deadline_pro_datetime(self): |
|
||||
"""Testuje, že i pro datetime dostáváme správné deadliny""" |
|
||||
self.skipTest('Chybí implementace testu') |
|
||||
|
|
||||
def test_moc_pozdni_deadline(self): |
|
||||
self.assertIsNone(deadline(date.max)) |
|
@ -1 +0,0 @@ |
|||||
{% include "vysledkovky/vysledkovka_rocnik.html" with radky_vysledkovky=radky_vysledkovky_s_neverejnymi cisla=cisla_s_neverejnymi %} |
|
@ -1,462 +1,492 @@ |
|||||
|
import abc |
||||
|
from functools import cached_property |
||||
|
from typing import Union # TODO: s pythonem 3.10 přepsat na '|' |
||||
|
|
||||
import seminar.models as m |
import seminar.models as m |
||||
from django.db.models import Q, Sum, Count |
from django.db.models import Q, Sum |
||||
from seminar.utils import aktivniResitele, resi_v_rocniku, cisla_rocniku, hlavni_problem, hlavni_problemy_f, problemy_cisla, podproblemy_v_cislu |
from seminar.utils import resi_v_rocniku |
||||
import time |
|
||||
|
|
||||
ROCNIK_ZRUSENI_TEMAT = 25 |
ROCNIK_ZRUSENI_TEMAT = 25 |
||||
|
|
||||
def sloupec_s_poradim(setrizene_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. |
|
||||
Parametr: |
|
||||
setrizene_body (seznam integerů): sestupně setřízená čísla |
|
||||
|
|
||||
Výstup: |
|
||||
sloupec_s_poradim (seznam stringů) |
|
||||
""" |
|
||||
|
|
||||
# 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(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: |
|
||||
sloupec_s_poradim.append("") |
|
||||
continue |
|
||||
velikost_skupiny = 0 |
|
||||
# zjistíme počet po sobě jdoucích stejných hodnot |
|
||||
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(setrizene_body) - 1: |
|
||||
break |
|
||||
# pokud je velikost skupiny 1, vypíšu pořadí |
|
||||
if velikost_skupiny == 1: |
|
||||
sloupec_s_poradim.append("{}.".format(aktualni_poradi)) |
|
||||
# pokud je skupina větší, vypíšu rozsah |
|
||||
else: |
|
||||
sloupec_s_poradim.append("{}.–{}.".format(aktualni_poradi, |
|
||||
aktualni_poradi+velikost_skupiny-1)) |
|
||||
# zvětšíme aktuální pořadí o tolik, kolik pozic bylo přeskočeno |
|
||||
aktualni_poradi = aktualni_poradi + velikost_skupiny |
|
||||
return sloupec_s_poradim |
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
def body_resitelu(resitele, za, odjakziva=True, jen_verejne=False): |
|
||||
""" Funkce počítající počty bodů pro zadané řešitele, |
|
||||
buď odjakživa do daného ročníku/čísla anebo za daný ročník/číslo. |
|
||||
Parametry: |
|
||||
resitele (seznam obsahující položky typu Resitel): aktivní řešitelé |
|
||||
za (Rocnik/Cislo): za co se mají počítat body |
|
||||
(generování starších výsledkovek) |
|
||||
odjakziva (bool): zda se mají počítat body odjakživa, nebo jen za číslo/ročník |
|
||||
zadané v "za" |
|
||||
Výstup: |
|
||||
slovník (Resitel.id):body |
|
||||
""" |
|
||||
resitele_id = [r.id for r in resitele] |
|
||||
# Zjistíme, typ objektu v parametru "za" |
|
||||
if isinstance(za, m.Rocnik): |
|
||||
cislo = None |
|
||||
rocnik = za |
|
||||
rok = rocnik.prvni_rok |
|
||||
elif isinstance(za, m.Cislo): |
|
||||
cislo = za |
|
||||
rocnik = None |
|
||||
rok = cislo.rocnik.prvni_rok |
|
||||
else: |
|
||||
assert True, "body_resitelu: za není ani číslo ani ročník." |
|
||||
|
|
||||
|
|
||||
# Kvůli rychlosti používáme sčítáme body už v databázi, viz |
|
||||
# https://docs.djangoproject.com/en/3.0/topics/db/aggregation/, |
|
||||
# sekce Filtering on annotations (protože potřebujeme filtrovat výsledky |
|
||||
# jen do nějakého data, abychom uměli správně nagenerovat výsledkovky i |
|
||||
# za historická čísla. |
|
||||
|
|
||||
# Níže se vytváří dotaz na součet bodů za správně vyfiltrovaná hodnocení, |
|
||||
# který se použije ve výsledném dotazu. |
|
||||
if cislo and odjakziva: # Body se sčítají odjakživa do zadaného čísla. |
|
||||
# Vyfiltrujeme všechna hodnocení, která jsou buď ze starších ročníků, |
|
||||
# anebo ze stejného ročníku, jak je zadané číslo, tam ale sčítáme jen |
|
||||
# pro čísla s pořadím nejvýše stejným, jako má zadané číslo. |
|
||||
body_k_zapocteni = Sum('reseni__hodnoceni__body', |
|
||||
filter=( Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok__lt=rok) | |
|
||||
Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok=rok, |
|
||||
reseni__hodnoceni__cislo_body__poradi__lte=cislo.poradi) )) |
|
||||
elif cislo and not odjakziva: # Body se sčítají za dané číslo. |
|
||||
body_k_zapocteni = Sum('reseni__hodnoceni__body', |
|
||||
filter=( Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok=rok, |
|
||||
reseni__hodnoceni__cislo_body__poradi__lte=cislo.poradi) )) |
|
||||
elif rocnik and odjakziva: # Spočítáme body za starší ročníky až do zadaného včetně. |
|
||||
if jen_verejne: |
|
||||
body_k_zapocteni = Sum('reseni__hodnoceni__body', |
|
||||
filter= Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok__lte=rok, |
|
||||
reseni__hodnoceni__cislo_body__verejna_vysledkovka=True)) |
|
||||
else: |
|
||||
body_k_zapocteni = Sum('reseni__hodnoceni__body', |
|
||||
filter= Q(reseni__hodnoceni__cislo_body__rocnik__prvni_rok__lte=rok)) |
|
||||
elif rocnik and not odjakziva: # Spočítáme body za daný ročník. |
|
||||
if jen_verejne: |
|
||||
body_k_zapocteni = Sum('reseni__hodnoceni__body', |
|
||||
filter=Q(reseni__hodnoceni__cislo_body__rocnik=rocnik, |
|
||||
reseni__hodnoceni__cislo_body__verejna_vysledkovka=True)) |
|
||||
else: |
|
||||
body_k_zapocteni = Sum('reseni__hodnoceni__body', |
|
||||
filter=Q(reseni__hodnoceni__cislo_body__rocnik=rocnik)) |
|
||||
else: |
|
||||
assert True, "body_resitelu: Neplatná kombinace za a odjakživa." |
|
||||
|
|
||||
# Následující řádek přidá ke každému řešiteli údaj ".body" se součtem jejich bodů |
|
||||
resitele_s_body = m.Resitel.objects.filter(id__in=resitele_id).annotate( |
|
||||
body=body_k_zapocteni) |
|
||||
# Teď jen z QuerySetu řešitelů anotovaných body vygenerujeme slovník |
|
||||
# indexovaný řešitelským id obsahující body. |
|
||||
# Pokud jsou body None, nahradíme za 0. |
|
||||
slovnik = {int(res.id) : (res.body if res.body else 0) for res in resitele_s_body} |
|
||||
return slovnik |
|
||||
|
|
||||
class RadekVysledkovkyRocniku(object): |
|
||||
""" Obsahuje věci, které se hodí vědět při konstruování výsledkovky. |
|
||||
Umožňuje snazší práci v templatu (lepší, než seznam).""" |
|
||||
|
|
||||
def __init__(self, poradi, resitel, body_cisla_sezn, body_rocnik, body_odjakziva, rok): |
|
||||
self.poradi = poradi |
|
||||
self.resitel = resitel |
|
||||
self.rocnik_resitele = resitel.rocnik(rok) |
|
||||
self.body_rocnik = body_rocnik |
|
||||
self.body_celkem_odjakziva = body_odjakziva |
|
||||
self.body_cisla_sezn = body_cisla_sezn |
|
||||
self.titul = resitel.get_titul(body_odjakziva) |
|
||||
|
|
||||
def setrid_resitele_a_body(slov_resitel_body): |
|
||||
setrizeni_resitele_id = [dvojice[0] for dvojice in slov_resitel_body] |
|
||||
setrizene_body = [dvojice[1] for dvojice in slov_resitel_body] |
|
||||
return setrizeni_resitele_id, setrizene_body |
|
||||
|
|
||||
def data_vysledkovky_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" |
|
||||
""" |
|
||||
|
|
||||
start = time.time() |
|
||||
|
|
||||
## 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(resi_v_rocniku(rocnik)) |
|
||||
cisla = cisla_rocniku(rocnik, jen_verejne) |
|
||||
body_cisla_slov = {} |
|
||||
for cislo in cisla: |
|
||||
# získáme body za číslo |
|
||||
_, cislobody = secti_body_za_cislo(cislo, aktivni_resitele) |
|
||||
body_cisla_slov[cislo.id] = cislobody |
|
||||
|
|
||||
# získáme body za ročník, seznam obsahuje dvojice (řešitel_id, body) setřízené sestupně |
|
||||
resitel_rocnikbody_sezn = secti_body_za_rocnik(rocnik, aktivni_resitele, jen_verejne=jen_verejne) |
|
||||
|
|
||||
# setřídíme řešitele podle počtu bodů a získáme seznam s body od nejvyšších po nenižší |
|
||||
setrizeni_resitele_id, setrizene_body = setrid_resitele_a_body(resitel_rocnikbody_sezn) |
|
||||
poradi = sloupec_s_poradim(setrizene_body) |
|
||||
|
|
||||
# získáme body odjakživa |
|
||||
resitel_odjakzivabody_slov = body_resitelu(aktivni_resitele, rocnik, jen_verejne=jen_verejne) |
|
||||
|
|
||||
# vytvoříme jednotlivé sloupce výsledkovky |
|
||||
radky_vysledkovky = [] |
|
||||
i = 0 |
|
||||
setrizeni_resitele_dict = {} # Tento slovnik se vyrab |
|
||||
for r in m.Resitel.objects.filter(id__in=setrizeni_resitele_id).select_related('osoba'): |
|
||||
setrizeni_resitele_dict[r.id] = r |
|
||||
|
|
||||
for ar_id in setrizeni_resitele_id: |
|
||||
# seznam počtu bodů daného řešitele pro jednotlivá čísla |
|
||||
body_cisla_sezn = [] |
|
||||
for cislo in cisla: |
|
||||
body_cisla_sezn.append(body_cisla_slov[cislo.id][ar_id]) |
|
||||
|
|
||||
# vytáhneme informace pro daného řešitele |
|
||||
radek = RadekVysledkovkyRocniku( |
|
||||
poradi[i], # pořadí |
|
||||
setrizeni_resitele_dict[ar_id], # řešitel (z id) |
|
||||
body_cisla_sezn, # seznam bodů za čísla |
|
||||
setrizene_body[i], # body za ročník (spočítané výše s pořadím) |
|
||||
resitel_odjakzivabody_slov[ar_id], # body odjakživa |
|
||||
rocnik) # ročník semináře pro získání ročníku řešitele |
|
||||
radky_vysledkovky.append(radek) |
|
||||
i += 1 |
|
||||
|
|
||||
end = time.time() |
|
||||
print("Vysledkovka rocniku",end-start) |
|
||||
|
|
||||
radky_vysledkovky = [radek for radek in radky_vysledkovky if radek.body_rocnik > 0] |
|
||||
return radky_vysledkovky, cisla |
|
||||
|
|
||||
class RadekVysledkovkyCisla(object): |
|
||||
"""Obsahuje věci, které se hodí vědět při konstruování výsledkovky. |
|
||||
Umožňuje snazší práci v templatu (lepší, než seznam).""" |
|
||||
|
|
||||
def __init__(self, poradi, resitel, body_problemy_sezn, |
|
||||
body_cislo, body_rocnik, body_odjakziva, rok, body_podproblemy, body_podproblemy_iter): |
|
||||
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 |
|
||||
self.poradi = poradi |
|
||||
self.body_problemy_sezn = body_problemy_sezn |
|
||||
self.titul = resitel.get_titul(body_odjakziva) |
|
||||
self.body_podproblemy = body_podproblemy |
|
||||
self.body_podproblemy_iter = body_podproblemy_iter # TODELETE |
|
||||
|
|
||||
|
|
||||
def pricti_body(slovnik, resitel, body): |
|
||||
""" Přiřazuje danému řešiteli body do slovníku. """ |
|
||||
# testujeme na None (""), pokud je to první řešení |
|
||||
# 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 |
|
||||
|
|
||||
# Speciálně pokud jsou body None (hodnocení není obodované), vraťse |
|
||||
# TODO nejde to udělat lépe? |
|
||||
if body is None: |
|
||||
return |
|
||||
|
|
||||
if slovnik[resitel.id] == "": |
|
||||
slovnik[resitel.id] = 0 |
|
||||
|
|
||||
slovnik[resitel.id] += body |
|
||||
|
|
||||
def secti_body_za_rocnik(za, aktivni_resitele, jen_verejne): |
|
||||
""" Spočítá body za ročník (celý nebo do daného čísla), |
|
||||
setřídí je sestupně a vrátí jako seznam. |
|
||||
Parametry: |
|
||||
za (typu Rocnik nebo Cislo) spočítá za ročník, nebo za ročník až do |
|
||||
daného čísla |
|
||||
""" |
|
||||
# spočítáme všem řešitelům jejich body za ročník (False => ne odjakživa) |
|
||||
resitel_rocnikbody_slov = body_resitelu(aktivni_resitele, za, False, jen_verejne=jen_verejne) |
|
||||
# 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 |
|
||||
|
|
||||
def secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy=None): |
|
||||
""" Spočítá u řešitelů body za číslo a za jednotlivé hlavní problémy (témata).""" |
|
||||
# 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ů) |
|
||||
|
|
||||
print("Scitam cislo",cislo) |
|
||||
|
|
||||
if hlavni_problemy is None: |
|
||||
hlavni_problemy = hlavni_problemy_f(problemy_cisla(cislo)) |
|
||||
|
|
||||
def ne_clanek_ne_konfera(problem): |
|
||||
inst = problem.get_real_instance() |
|
||||
return not(isinstance(inst, m.Clanek) or isinstance(inst, m.Konfera)) |
|
||||
|
|
||||
if cislo.rocnik.rocnik < ROCNIK_ZRUSENI_TEMAT: |
|
||||
temata_a_spol = hlavni_problemy |
|
||||
else: |
|
||||
temata_a_spol = list(filter(ne_clanek_ne_konfera, hlavni_problemy)) |
|
||||
|
|
||||
hlavni_problemy_slovnik = {} |
|
||||
for hp in temata_a_spol: |
|
||||
hlavni_problemy_slovnik[hp.id] = {} |
|
||||
|
|
||||
hlavni_problemy_slovnik[-1] = {} |
|
||||
|
|
||||
# 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[ar.id] = "" |
|
||||
for hp in temata_a_spol: |
|
||||
slovnik = hlavni_problemy_slovnik[hp.id] |
|
||||
slovnik[ar.id] = "" |
|
||||
|
|
||||
hlavni_problemy_slovnik[-1][ar.id] = "" |
|
||||
|
|
||||
hodnoceni_do_cisla = m.Hodnoceni.objects.prefetch_related('problem', 'reseni', 'reseni__resitele').filter(cislo_body=cislo) |
|
||||
|
|
||||
start = time.time() |
|
||||
|
|
||||
for hodnoceni in hodnoceni_do_cisla: |
|
||||
prob = hodnoceni.problem |
|
||||
nadproblem = hlavni_problem(prob) |
|
||||
if ne_clanek_ne_konfera(nadproblem): |
|
||||
nadproblem_slovnik = hlavni_problemy_slovnik[nadproblem.id] |
|
||||
else: |
|
||||
nadproblem_slovnik = hlavni_problemy_slovnik[-1] |
|
||||
|
|
||||
body = hodnoceni.body |
|
||||
|
|
||||
# a mít více řešitelů |
|
||||
for resitel in hodnoceni.reseni.resitele.all(): |
|
||||
if resitel not in aktivni_resitele: |
|
||||
print("Skipping {}".format(resitel.id)) |
|
||||
continue |
|
||||
pricti_body(cislobody, resitel, body) |
|
||||
pricti_body(nadproblem_slovnik, resitel, body) |
|
||||
end = time.time() |
|
||||
print("for cykly:", end-start) |
|
||||
return hlavni_problemy_slovnik, cislobody |
|
||||
|
|
||||
|
|
||||
def secti_body_za_cislo_podle_temat(cislo, aktivni_resitele, podproblemy=None, temata=None): |
|
||||
""" Spočítá u řešitelů body za číslo za úlohy v jednotlivých hlavních problémech (témata).""" |
|
||||
if temata is None: |
|
||||
temata = hlavni_problemy_f(problemy_cisla(cislo)) |
|
||||
|
|
||||
if podproblemy is None: |
|
||||
podproblemy_v_cislu(cislo, hlavni_problemy=temata) |
|
||||
|
|
||||
body_slovnik = {} |
|
||||
for tema in temata: |
|
||||
body_slovnik[tema.id] = {} |
|
||||
for problem in podproblemy[tema.id]: |
|
||||
body_slovnik[tema.id][problem.id] = {} |
|
||||
body_slovnik[-1] = {} |
|
||||
for problem in podproblemy[-1]: |
|
||||
body_slovnik[-1][problem.id] = {} |
|
||||
|
|
||||
# zakládání prázdných záznamů pro řešitele |
|
||||
for ar in aktivni_resitele: |
|
||||
for tema in temata: |
|
||||
for problem in podproblemy[tema.id]: |
|
||||
body_slovnik[tema.id][problem.id][ar.id] = "" |
|
||||
|
|
||||
for problem in podproblemy[-1]: |
|
||||
body_slovnik[-1][problem.id][ar.id] = "" |
|
||||
|
|
||||
temata = set(t.id for t in temata) |
|
||||
|
|
||||
hodnoceni_do_cisla = m.Hodnoceni.objects.prefetch_related('problem', 'reseni', 'reseni__resitele').filter(cislo_body=cislo) |
|
||||
|
|
||||
for hodnoceni in hodnoceni_do_cisla: |
|
||||
prob = hodnoceni.problem |
|
||||
nadproblem = hlavni_problem(prob) |
|
||||
if nadproblem.id in temata: |
|
||||
nadproblem_slovnik = body_slovnik[nadproblem.id] |
|
||||
else: |
|
||||
nadproblem_slovnik = body_slovnik[-1] |
|
||||
|
|
||||
problem_slovnik = nadproblem_slovnik[prob.id] |
|
||||
|
|
||||
body = hodnoceni.body |
|
||||
|
|
||||
# a mít více řešitelů |
|
||||
for resitel in hodnoceni.reseni.resitele.all(): |
|
||||
if resitel not in aktivni_resitele: |
|
||||
print("Skipping {}".format(resitel.id)) |
|
||||
continue |
|
||||
pricti_body(problem_slovnik, resitel, body) |
|
||||
return body_slovnik |
|
||||
|
|
||||
|
|
||||
# TODELETE |
|
||||
class FixedIterator: |
class FixedIterator: |
||||
def next(self): |
def next(self): |
||||
return self.niter.__next__() |
return self.niter.__next__() |
||||
|
|
||||
def __init__(self, niter): |
def __init__(self, niter): |
||||
self.niter = niter |
self.niter = niter |
||||
# TODELETE |
|
||||
|
|
||||
|
|
||||
def data_vysledkovky_cisla(cislo): |
def body_resitelu( |
||||
problemy = problemy_cisla(cislo) |
za: Union[m.Cislo, m.Rocnik, None] = None, |
||||
hlavni_problemy = hlavni_problemy_f(problemy) |
do: m.Deadline = None, |
||||
## TODO možná chytřeji vybírat aktivní řešitele |
od: m.Deadline = None, |
||||
# aktivní řešitelé - chceme letos něco poslal, TODO později vyfiltrujeme ty, kdo mají |
jen_verejne: bool = True, |
||||
# u alespoň jedné hodnoty něco jiného než NULL |
resitele=None, |
||||
aktivni_resitele = list(resi_v_rocniku(cislo.rocnik)) |
null=0 # Výchozí hodnota, pokud pro daného řešitele nejsou body |
||||
|
) -> dict[int, int]: |
||||
|
filtr = Q() |
||||
|
|
||||
|
if jen_verejne: |
||||
|
filtr &= Q(reseni__hodnoceni__deadline_body__verejna_vysledkovka=True) |
||||
|
|
||||
|
# Zjistíme, typ objektu v parametru "za" |
||||
|
if isinstance(za, m.Rocnik): |
||||
|
filtr &= Q(reseni__hodnoceni__deadline_body__cislo__rocnik=za) |
||||
|
elif isinstance(za, m.Cislo): |
||||
|
filtr &= Q(reseni__hodnoceni__deadline_body__cislo=za) |
||||
|
|
||||
|
if do: |
||||
|
filtr &= Q(reseni__hodnoceni__deadline_body__deadline__lte=do.deadline) |
||||
|
|
||||
# získáme body za číslo |
if od: |
||||
hlavni_problemy_slovnik, cislobody = secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy) |
filtr &= Q(reseni__hodnoceni__deadline_body__deadline__gte=od.deadline) |
||||
|
|
||||
# získáme body za ročník, seznam obsahuje dvojice (řešitel_id, body) setřízené sestupně |
resiteleQuery = m.Resitel.objects.all() |
||||
resitel_rocnikbody_sezn = secti_body_za_rocnik(cislo, aktivni_resitele, jen_verejne=True) |
|
||||
|
|
||||
# získáme body odjakživa |
if resitele is not None: |
||||
resitel_odjakzivabody_slov = body_resitelu(aktivni_resitele, cislo, jen_verejne=True) |
resitele_id = [r.id for r in resitele] |
||||
|
resiteleQuery = resiteleQuery.filter(id__in=resitele_id) |
||||
|
|
||||
# řešitelé setřídění podle bodů za číslo sestupně |
# Přidáme ke každému řešiteli údaj ".body" se součtem jejich bodů |
||||
setrizeni_resitele_id = [dvojice[0] for dvojice in resitel_rocnikbody_sezn] |
resitele_s_body = resiteleQuery.annotate( |
||||
|
body=Sum('reseni__hodnoceni__body', filter=filtr)) |
||||
|
|
||||
# spočítáme pořadí řešitelů |
# Teď jen z QuerySetu řešitelů anotovaných body vygenerujeme slovník |
||||
setrizeni_resitele_body = [dvojice[1] for dvojice in resitel_rocnikbody_sezn] |
# indexovaný řešitelským id obsahující body. |
||||
poradi = sloupec_s_poradim(setrizeni_resitele_body) |
# Pokud jsou body None, nahradíme za 0. |
||||
|
slovnik = { |
||||
|
int(res.id): (res.body if res.body else null) for res in resitele_s_body |
||||
|
} |
||||
|
return slovnik |
||||
|
|
||||
# vytvoříme jednotlivé sloupce výsledkovky |
|
||||
radky_vysledkovky = [] |
|
||||
i = 0 |
|
||||
|
|
||||
|
class Vysledkovka(abc.ABC): |
||||
|
jen_verejne: bool |
||||
|
rocnik: m.Rocnik |
||||
|
do_deadlinu: m.Deadline |
||||
|
|
||||
|
@property |
||||
|
@abc.abstractmethod |
||||
|
def aktivni_resitele(self) -> list[m.Resitel]: |
||||
|
... |
||||
|
|
||||
|
@cached_property |
||||
|
def resitele_s_body_za_rocnik_setrizeny_seznam(self) -> list[tuple[int, int]]: |
||||
|
# spočítáme všem řešitelům jejich body za ročník |
||||
|
resitel_body_za_rocnik_slovnik = body_resitelu( |
||||
|
resitele=self.aktivni_resitele, |
||||
|
za=self.rocnik, |
||||
|
jen_verejne=self.jen_verejne, |
||||
|
do=self.do_deadlinu |
||||
|
) |
||||
|
|
||||
|
# zeptáme se na dvojice (řešitel, body) za ročník a setřídíme sestupně |
||||
|
resitele_s_body_za_rocnik_setrizeny_seznam = sorted( |
||||
|
resitel_body_za_rocnik_slovnik.items(), |
||||
|
key=lambda x: x[1], reverse=True |
||||
|
) |
||||
|
|
||||
|
return resitele_s_body_za_rocnik_setrizeny_seznam |
||||
|
|
||||
|
@cached_property |
||||
|
def body_za_rocnik_seznamy(self) -> tuple[list[int], list[int]]: |
||||
|
if len(self.resitele_s_body_za_rocnik_setrizeny_seznam) == 0: |
||||
|
return [], [] |
||||
|
return tuple(zip(*self.resitele_s_body_za_rocnik_setrizeny_seznam)) |
||||
|
|
||||
|
@property |
||||
|
def setrizeni_resitele_id(self) -> list[int]: |
||||
|
return self.body_za_rocnik_seznamy[0] |
||||
|
|
||||
|
@property |
||||
|
def setrizene_body(self) -> list[int]: |
||||
|
return self.body_za_rocnik_seznamy[1] |
||||
|
|
||||
|
@cached_property |
||||
|
def resitel_body_odjakziva_slovnik(self) -> dict[int, int]: |
||||
|
return body_resitelu(jen_verejne=self.jen_verejne, do=self.do_deadlinu) |
||||
|
|
||||
|
@cached_property |
||||
|
def poradi(self): |
||||
|
# 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(self.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: |
||||
|
sloupec_s_poradim.append("") |
||||
|
continue |
||||
|
velikost_skupiny = 0 |
||||
|
# zjistíme počet po sobě jdoucích stejných hodnot |
||||
|
while self.setrizene_body[index] == self.setrizene_body[ |
||||
|
index + velikost_skupiny]: |
||||
|
velikost_skupiny += 1 |
||||
|
# na konci musíme ošetřit přetečení seznamu |
||||
|
if (index + velikost_skupiny) > len(self.setrizene_body) - 1: |
||||
|
break |
||||
|
# pokud je velikost skupiny 1, vypíšu pořadí |
||||
|
if velikost_skupiny == 1: |
||||
|
sloupec_s_poradim.append(f"{aktualni_poradi}.") |
||||
|
# pokud je skupina větší, vypíšu rozsah |
||||
|
else: |
||||
|
sloupec_s_poradim.append( |
||||
|
f"{aktualni_poradi}.–{aktualni_poradi + velikost_skupiny - 1}." |
||||
|
) |
||||
|
# zvětšíme aktuální pořadí o tolik, kolik pozic bylo přeskočeno |
||||
|
aktualni_poradi += velikost_skupiny |
||||
|
return sloupec_s_poradim |
||||
|
|
||||
|
|
||||
|
class VysledkovkaRocniku(Vysledkovka): |
||||
|
|
||||
|
def __init__(self, rocnik: m.Rocnik, jen_verejne: bool = True): |
||||
|
self.rocnik = rocnik |
||||
|
self.jen_verejne = jen_verejne |
||||
|
self.do_deadlinu = m.Deadline.objects.filter(cislo__rocnik=rocnik).last() |
||||
|
|
||||
|
@cached_property |
||||
|
def aktivni_resitele(self) -> list[m.Resitel]: |
||||
|
return list(resi_v_rocniku(self.rocnik)) |
||||
|
|
||||
|
@cached_property |
||||
|
def cisla_rocniku(self) -> list[m.Cislo]: |
||||
|
""" Vrátí všechna čísla daného ročníku. """ |
||||
|
if self.jen_verejne: |
||||
|
return self.rocnik.verejne_vysledkovky_cisla() |
||||
|
else: |
||||
|
return self.rocnik.cisla.all().order_by('poradi') |
||||
|
|
||||
|
@cached_property |
||||
|
def body_za_cisla_slovnik(self) -> dict[int, dict[int, int]]: # Výstup: m.Cislo.id → ( m.Resitel.id → body ) |
||||
|
# TODO: Body jsou decimal! |
||||
|
body_cisla_slovnik = dict() |
||||
|
for cislo in self.cisla_rocniku: |
||||
|
# získáme body za číslo |
||||
|
body_za_cislo = body_resitelu( |
||||
|
za=cislo, |
||||
|
resitele=self.aktivni_resitele, |
||||
|
jen_verejne=self.jen_verejne, |
||||
|
null="" |
||||
|
) |
||||
|
body_cisla_slovnik[cislo.id] = body_za_cislo |
||||
|
return body_cisla_slovnik |
||||
|
|
||||
|
class RadekVysledkovkyRocniku: |
||||
|
# TODO: přepsat na dataclass |
||||
|
""" 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_seznam, 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_seznam = body_cisla_seznam |
||||
|
self.titul = resitel.get_titul(body_odjakziva) |
||||
|
|
||||
|
@cached_property |
||||
|
def radky_vysledkovky(self) -> list[RadekVysledkovkyRocniku]: |
||||
|
radky_vysledkovky = [] |
||||
|
|
||||
|
setrizeni_resitele_dict = dict() |
||||
|
for r in m.Resitel.objects.filter( |
||||
|
id__in=self.setrizeni_resitele_id |
||||
|
).select_related('osoba'): |
||||
|
setrizeni_resitele_dict[r.id] = r |
||||
|
|
||||
|
for i, ar_id in enumerate(self.setrizeni_resitele_id): |
||||
|
if self.setrizene_body[i] > 0: |
||||
|
# seznam počtu bodů daného řešitele pro jednotlivá čísla |
||||
|
body_cisla_seznam = [] |
||||
|
for cislo in self.cisla_rocniku: |
||||
|
body_cisla_seznam.append(self.body_za_cisla_slovnik[cislo.id][ar_id]) |
||||
|
|
||||
|
# Pokud řešitel dostal nějaké body |
||||
|
if self.resitele_s_body_za_rocnik_setrizeny_seznam[i] != 0: |
||||
|
# vytáhneme informace pro daného řešitele |
||||
|
radek = self.RadekVysledkovkyRocniku( |
||||
|
poradi=self.poradi[i], |
||||
|
resitel=setrizeni_resitele_dict[ar_id], |
||||
|
body_cisla_seznam=body_cisla_seznam, |
||||
|
body_rocnik=self.setrizene_body[i], |
||||
|
body_odjakziva=self.resitel_body_odjakziva_slovnik[ar_id], |
||||
|
rok=self.rocnik) # ročník semináře pro získání ročníku řešitele |
||||
|
radky_vysledkovky.append(radek) |
||||
|
|
||||
|
return radky_vysledkovky |
||||
|
|
||||
|
|
||||
|
class VysledkovkaCisla(Vysledkovka): |
||||
|
def __init__( |
||||
|
self, |
||||
|
cislo: m.Cislo, |
||||
|
jen_verejne: bool = True, |
||||
|
do_deadlinu: m.Deadline = None |
||||
|
): |
||||
|
self.cislo = cislo |
||||
|
self.rocnik = cislo.rocnik |
||||
|
self.jen_verejne = jen_verejne |
||||
|
if do_deadlinu is None: |
||||
|
do_deadlinu = m.Deadline.objects.filter(cislo=cislo).last() |
||||
|
self.do_deadlinu = do_deadlinu |
||||
|
|
||||
|
@cached_property |
||||
|
def aktivni_resitele(self) -> list[m.Resitel]: |
||||
|
# TODO možná chytřeji vybírat aktivní řešitele |
||||
|
return list(resi_v_rocniku(self.rocnik)) |
||||
|
|
||||
|
@cached_property |
||||
|
def problemy(self) -> list[m.Problem]: |
||||
|
""" Vrátí seznam všech problémů s body v daném čísle. """ |
||||
|
return m.Problem.objects.filter( |
||||
|
hodnoceni__in=m.Hodnoceni.objects.filter(deadline_body__cislo=self.cislo) |
||||
|
).distinct().non_polymorphic().select_related('nadproblem').select_related('nadproblem__nadproblem') |
||||
|
|
||||
|
@cached_property |
||||
|
def hlavni_problemy(self) -> list[m.Problem]: |
||||
|
""" Vrátí seznam všech problémů, které již nemají nadproblém. """ |
||||
|
# hlavní problémy čísla |
||||
|
# (mají vlastní sloupeček ve výsledkovce, nemají nadproblém) |
||||
|
hlavni_problemy = set() |
||||
|
for p in self.problemy: |
||||
|
hlavni_problemy.add(p.hlavni_problem) |
||||
|
|
||||
|
# zunikátnění |
||||
|
hlavni_problemy = list(hlavni_problemy) |
||||
|
hlavni_problemy.sort( |
||||
|
key=lambda k: k.kod_v_rocniku) # setřídit podle t1, t2, c3, ... |
||||
|
|
||||
|
return hlavni_problemy |
||||
|
|
||||
|
# Není cached, protože si myslím, že queryset lze použít ve for jen jednou. |
||||
|
@property |
||||
|
def hodnoceni_do_cisla(self): |
||||
|
hodnoceni = m.Hodnoceni.objects.prefetch_related('reseni__resitele').select_related('problem', 'reseni') |
||||
|
if self.jen_verejne: |
||||
|
hodnoceni = hodnoceni.filter(deadline_body__verejna_vysledkovka=True) |
||||
|
return hodnoceni.filter( |
||||
|
deadline_body__cislo=self.cislo, |
||||
|
deadline_body__deadline__lte=self.do_deadlinu.deadline, |
||||
|
body__isnull=False, |
||||
|
) |
||||
|
|
||||
|
@cached_property |
||||
|
def sectene_body(self): |
||||
|
""" |
||||
|
Sečte body za číslo, hlavní problémy a podproblémy. |
||||
|
|
||||
|
Problém s ID '-1' znamená problémy bez nadproblémů, jež nejsou témata, tj. články, úlohy, konfery, … |
||||
|
""" |
||||
|
|
||||
|
# Body za číslo |
||||
|
body_za_cislo = {ar.id: "" for ar in self.aktivni_resitele} |
||||
|
|
||||
|
# Body za hlavní problémy |
||||
|
body_za_temata = { |
||||
|
hp.id: {ar.id: "" for ar in self.aktivni_resitele} |
||||
|
for hp in self.temata_a_spol |
||||
|
} |
||||
|
# Ostatní body |
||||
|
body_za_temata[-1] = {ar.id: "" for ar in self.aktivni_resitele} |
||||
|
|
||||
|
# Body za podproblémy |
||||
|
body_za_problemy = { |
||||
|
tema.id: { |
||||
|
problem.id: {ar.id: "" for ar in self.aktivni_resitele} |
||||
|
for problem in self.podproblemy[tema.id] |
||||
|
} |
||||
|
for tema in self.temata_a_spol |
||||
|
} |
||||
|
# Ostatní body |
||||
|
body_za_problemy[-1] = { |
||||
|
problem.id: {ar.id: "" for ar in self.aktivni_resitele} |
||||
|
for problem in self.podproblemy[-1] |
||||
|
} |
||||
|
|
||||
|
# Sečteme hodnocení |
||||
|
for hodnoceni in self.hodnoceni_do_cisla: |
||||
|
prob = hodnoceni.problem |
||||
|
nadproblem = prob.hlavni_problem.id |
||||
|
|
||||
|
# Když nadproblém není "téma", pak je "Ostatní" |
||||
|
if nadproblem not in body_za_temata: |
||||
|
nadproblem = -1 |
||||
|
|
||||
|
problem_slovnik = body_za_problemy[nadproblem][prob.id] |
||||
|
nadproblem_slovnik = body_za_temata[nadproblem] |
||||
|
|
||||
|
body = hodnoceni.body |
||||
|
|
||||
|
# Může mít více řešitelů |
||||
|
for resitel in hodnoceni.reseni.resitele.all(): |
||||
|
if resitel not in self.aktivni_resitele: |
||||
|
continue |
||||
|
self.pricti_body(body_za_cislo, resitel, body) |
||||
|
self.pricti_body(nadproblem_slovnik, resitel, body) |
||||
|
self.pricti_body(problem_slovnik, resitel, body) |
||||
|
return body_za_cislo, body_za_temata, body_za_problemy |
||||
|
|
||||
|
@cached_property |
||||
|
def body_za_temata(self) -> dict[int, dict[int, str]]: |
||||
|
return self.sectene_body[1] |
||||
|
|
||||
|
@cached_property |
||||
|
def body_za_cislo(self) -> dict[int, str]: |
||||
|
return self.sectene_body[0] |
||||
|
|
||||
|
@cached_property |
||||
|
def problemy_slovnik(self): |
||||
|
return self.sectene_body[2] |
||||
|
|
||||
|
@cached_property |
||||
|
def temata_a_spol(self) -> list[m.Problem]: |
||||
|
if self.rocnik.rocnik < ROCNIK_ZRUSENI_TEMAT: |
||||
|
return self.hlavni_problemy |
||||
|
else: |
||||
|
return list(filter(self.ne_clanek_ne_konfera, self.hlavni_problemy)) |
||||
|
|
||||
|
@cached_property |
||||
|
def je_nejake_ostatni(self): |
||||
|
return len(self.hlavni_problemy) - len(self.temata_a_spol) > 0 |
||||
|
|
||||
|
@cached_property |
||||
|
def podproblemy(self) -> dict[int, list[m.Problem]]: |
||||
|
podproblemy = {hp.id: [] for hp in self.temata_a_spol} |
||||
|
temata_a_spol = set(self.temata_a_spol) |
||||
|
podproblemy[-1] = [] |
||||
|
|
||||
|
for problem in self.problemy: |
||||
|
h_problem = problem.hlavni_problem |
||||
|
if h_problem in temata_a_spol: |
||||
|
podproblemy[h_problem.id].append(problem) |
||||
|
else: |
||||
|
podproblemy[-1].append(problem) |
||||
|
|
||||
|
for podproblem in podproblemy.keys(): |
||||
|
def int_or_zero(p): |
||||
|
try: |
||||
|
return int(p.kod) |
||||
|
except ValueError: |
||||
|
return 0 |
||||
|
|
||||
|
podproblemy[podproblem] = sorted(podproblemy[podproblem], key=int_or_zero) |
||||
|
return podproblemy |
||||
|
|
||||
|
@cached_property |
||||
|
def podproblemy_seznam(self) -> list[list[m.Problem]]: |
||||
|
return [self.podproblemy[it.id] for it in self.temata_a_spol] + [self.podproblemy[-1]] |
||||
|
|
||||
|
@cached_property |
||||
|
def podproblemy_iter(self) -> FixedIterator: |
||||
|
return FixedIterator(self.podproblemy_seznam.__iter__()) |
||||
|
|
||||
|
class RadekVysledkovkyCisla(object): |
||||
|
# TODO: Přepsat na dataclass |
||||
|
"""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, temata_seznamk, body_cislo, body_rocnik, body_odjakziva, rok, body_podproblemy, body_podproblemy_iter): |
||||
|
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 |
||||
|
self.poradi = poradi |
||||
|
self.body_za_temata_seznam = temata_seznamk |
||||
|
self.titul = resitel.get_titul(body_odjakziva) |
||||
|
self.body_podproblemy = body_podproblemy |
||||
|
self.body_podproblemy_iter = body_podproblemy_iter |
||||
|
|
||||
|
@cached_property |
||||
|
def radky_vysledkovky(self) -> list[RadekVysledkovkyCisla]: |
||||
|
# vytvoříme jednotlivé sloupce výsledkovky |
||||
|
radky_vysledkovky = [] |
||||
|
|
||||
|
setrizeni_resitele_slovnik = {} |
||||
|
setrizeni_resitele = m.Resitel.objects.filter(id__in=self.setrizeni_resitele_id).select_related('osoba') |
||||
|
|
||||
|
for r in setrizeni_resitele: |
||||
|
setrizeni_resitele_slovnik[r.id] = r |
||||
|
|
||||
|
for i, ar_id in enumerate(self.setrizeni_resitele_id): |
||||
|
if self.setrizene_body[i] > 0: |
||||
|
# získáme seznam bodů za problémy pro daného řešitele |
||||
|
body_problemy = [] |
||||
|
body_podproblemy = [] |
||||
|
for hp in self.temata_a_spol: |
||||
|
body_problemy.append(self.body_za_temata[hp.id][ar_id]) |
||||
|
body_podproblemy.append([ |
||||
|
self.problemy_slovnik[hp.id][it.id][ar_id] |
||||
|
for it in self.podproblemy[hp.id] |
||||
|
]) |
||||
|
if self.je_nejake_ostatni: |
||||
|
body_problemy.append(self.body_za_temata[-1][ar_id]) |
||||
|
body_podproblemy.append( |
||||
|
[self.problemy_slovnik[-1][it.id][ar_id] for it in self.podproblemy[-1]]) |
||||
|
# vytáhneme informace pro daného řešitele |
||||
|
radek = self.RadekVysledkovkyCisla( |
||||
|
poradi=self.poradi[i], |
||||
|
resitel=setrizeni_resitele_slovnik[ar_id], |
||||
|
temata_seznamk=body_problemy, |
||||
|
body_cislo=self.body_za_cislo[ar_id], |
||||
|
body_rocnik=self.setrizene_body[i], |
||||
|
body_odjakziva=self.resitel_body_odjakziva_slovnik[ar_id], |
||||
|
rok=self.rocnik, |
||||
|
body_podproblemy=body_podproblemy, # body všech podproblémů |
||||
|
body_podproblemy_iter=FixedIterator(body_podproblemy.__iter__()) |
||||
|
) # ročník semináře pro zjištění ročníku řešitele |
||||
|
radky_vysledkovky.append(radek) |
||||
|
return radky_vysledkovky |
||||
|
|
||||
|
@staticmethod |
||||
|
def pricti_body(slovnik, resitel, body): |
||||
|
""" Přiřazuje danému řešiteli body do slovníku. """ |
||||
|
# testujeme na None (""), pokud je to první řešení |
||||
|
# 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[resitel.id] == "": |
||||
|
slovnik[resitel.id] = 0 |
||||
|
|
||||
|
slovnik[resitel.id] += body |
||||
|
|
||||
|
@staticmethod |
||||
def ne_clanek_ne_konfera(problem): |
def ne_clanek_ne_konfera(problem): |
||||
|
inst = problem.get_real_instance() |
||||
return not(isinstance(problem.get_real_instance(), m.Clanek) or isinstance(problem.get_real_instance(), m.Konfera)) |
return not (isinstance(inst, m.Clanek) or isinstance(inst, m.Konfera)) |
||||
|
|
||||
if cislo.rocnik.rocnik < ROCNIK_ZRUSENI_TEMAT: |
|
||||
temata_a_spol = hlavni_problemy |
class VysledkovkaDoTeXu(VysledkovkaCisla): |
||||
else: |
def __init__( |
||||
temata_a_spol = list(filter(ne_clanek_ne_konfera, hlavni_problemy)) |
self, |
||||
|
nejake_cislo: m.Cislo, |
||||
# získáme body u jednotlivých témat |
od_vyjma: m.Deadline, |
||||
podproblemy = podproblemy_v_cislu(cislo, problemy, temata_a_spol) |
do_vcetne: m.Deadline |
||||
problemy_slovnik = secti_body_za_cislo_podle_temat(cislo, aktivni_resitele, podproblemy, temata_a_spol) |
): |
||||
|
super().__init__(nejake_cislo, False, do_vcetne) |
||||
# def not_empty(value): |
self.od_deadlinu = od_vyjma |
||||
# return value != '' |
|
||||
# |
@cached_property |
||||
# je_nejake_ostatni = any(filter(not_empty, hlavni_problemy_slovnik[-1].values())) > 0 |
def problemy(self) -> list[m.Problem]: |
||||
|
return m.Problem.objects.filter(hodnoceni__in=m.Hodnoceni.objects.filter( |
||||
je_nejake_ostatni = len(hlavni_problemy) - len(temata_a_spol) > 0 |
deadline_body__deadline__gt=self.od_deadlinu.deadline, |
||||
|
deadline_body__deadline__lte=self.do_deadlinu.deadline, |
||||
setrizeni_resitele_slovnik = {} |
)).distinct().non_polymorphic().select_related('nadproblem').select_related('nadproblem__nadproblem') |
||||
setrizeni_resitele = m.Resitel.objects.filter(id__in=setrizeni_resitele_id).select_related('osoba') |
|
||||
for r in setrizeni_resitele: |
@property |
||||
setrizeni_resitele_slovnik[r.id] = r |
def hodnoceni_do_cisla(self): |
||||
|
hodnoceni = m.Hodnoceni.objects.prefetch_related( |
||||
for ar_id in setrizeni_resitele_id: |
'problem', 'reseni', 'reseni__resitele') |
||||
# získáme seznam bodů za problémy pro daného řešitele |
if self.jen_verejne: |
||||
body_problemy = [] |
hodnoceni = hodnoceni.filter(deadline_body__verejna_vysledkovka=True) |
||||
body_podproblemy = [] |
return hodnoceni.filter( |
||||
for hp in temata_a_spol: |
deadline_body__deadline__gt=self.od_deadlinu.deadline, |
||||
body_problemy.append(hlavni_problemy_slovnik[hp.id][ar_id]) |
deadline_body__deadline__lte=self.do_deadlinu.deadline, |
||||
body_podproblemy.append([problemy_slovnik[hp.id][it.id][ar_id] for it in podproblemy[hp.id]]) |
body__isnull=False, |
||||
if je_nejake_ostatni: |
) |
||||
body_problemy.append(hlavni_problemy_slovnik[-1][ar_id]) |
|
||||
body_podproblemy.append([problemy_slovnik[-1][it.id][ar_id] for it in podproblemy[-1]]) |
|
||||
# vytáhneme informace pro daného řešitele |
|
||||
radek = RadekVysledkovkyCisla( |
|
||||
poradi[i], # pořadí |
|
||||
setrizeni_resitele_slovnik[ar_id], # řešitel (z id) |
|
||||
body_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 |
|
||||
cislo.rocnik, |
|
||||
body_podproblemy, # body všech podproblémů |
|
||||
FixedIterator(body_podproblemy.__iter__()) # TODELETE |
|
||||
) # ročník semináře pro zjištění ročníku řešitele |
|
||||
radky_vysledkovky.append(radek) |
|
||||
i += 1 |
|
||||
|
|
||||
# vytahané informace předáváme do kontextu |
|
||||
pt = [podproblemy[it.id] for it in temata_a_spol]+[podproblemy[-1]] |
|
||||
radky_vysledkovky = [radek for radek in radky_vysledkovky if radek.body_rocnik > 0] |
|
||||
return ( |
|
||||
radky_vysledkovky, |
|
||||
temata_a_spol, |
|
||||
je_nejake_ostatni, |
|
||||
pt, |
|
||||
FixedIterator(pt.__iter__()) |
|
||||
) |
|
||||
|
@ -1,37 +0,0 @@ |
|||||
from .utils import data_vysledkovky_cisla, \ |
|
||||
data_vysledkovky_rocniku |
|
||||
|
|
||||
|
|
||||
def vysledkovka_cisla(cislo, context=None): |
|
||||
if context is None: |
|
||||
context = {} |
|
||||
context['cislo'] = cislo |
|
||||
|
|
||||
( |
|
||||
context['radky_vysledkovky'], |
|
||||
context['problemy'], |
|
||||
context['ostatni'], |
|
||||
context['podproblemy'], |
|
||||
context['podproblemy_iter'] |
|
||||
) = data_vysledkovky_cisla(cislo) |
|
||||
return context |
|
||||
|
|
||||
|
|
||||
def vysledkovka_rocniku(rocnik, context=None, request=None, sneverejnou=False): |
|
||||
if context is None: |
|
||||
context = {} |
|
||||
|
|
||||
( |
|
||||
context['radky_vysledkovky'], |
|
||||
context['cisla'] |
|
||||
) = data_vysledkovky_rocniku(rocnik) |
|
||||
|
|
||||
context['vysledkovka'] = len(context['cisla']) != 0 |
|
||||
|
|
||||
if sneverejnou and request and request.user.je_org: |
|
||||
( |
|
||||
context['radky_vysledkovky_s_neverejnymi'], |
|
||||
context['cisla_s_neverejnymi'] |
|
||||
) = data_vysledkovky_rocniku(rocnik, jen_verejne=False) |
|
||||
|
|
||||
return context |
|
Loading…
Reference in new issue