@ -1,222 +1,449 @@
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ů )
"""
class FixedIterator :
def next ( self ) :
return self . niter . __next__ ( )
def __init__ ( self , niter ) :
self . niter = niter
def body_resitelu (
za : Union [ m . Cislo , m . Rocnik , None ] = None ,
do : m . Deadline = None ,
od : m . Deadline = None ,
jen_verejne : bool = True ,
resitele = None ,
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 )
if od :
filtr & = Q ( reseni__hodnoceni__deadline_body__deadline__gte = od . deadline )
resiteleQuery = m . Resitel . objects . all ( )
if resitele is not None :
resitele_id = [ r . id for r in resitele ]
resiteleQuery = resiteleQuery . filter ( id__in = resitele_id )
# Přidáme ke každému řešiteli údaj ".body" se součtem jejich bodů
resitele_s_body = resiteleQuery . annotate (
body = Sum ( ' reseni__hodnoceni__body ' , filter = filtr ) )
# 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 null ) for res in resitele_s_body
}
return slovnik
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
# ze seznamu obsahujícího setřízené body spočítáme sloupec s pořadím
aktualni_poradi = 1
aktualni_poradi = 1
sloupec_s_poradim = [ ]
sloupec_s_poradim = [ ]
# seskupíme seznam všech bodů podle hodnot
# seskupíme seznam všech bodů podle hodnot
for index in range ( 0 , len ( setrizene_body ) ) :
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
# pokud je pořadí větší než číslo řádku, tak jsme vypsali větší rozsah
# vypsat už jen prázdné místo, než dojdeme na správný řádek
# a chceme vypsat už jen prázdné místo, než dojdeme na správný řádek
if ( index + 1 ) < aktualni_poradi :
if ( index + 1 ) < aktualni_poradi :
sloupec_s_poradim . append ( " " )
sloupec_s_poradim . append ( " " )
continue
continue
velikost_skupiny = 0
velikost_skupiny = 0
# zjistíme počet po sobě jdoucích stejných hodnot
# zjistíme počet po sobě jdoucích stejných hodnot
while setrizene_body [ index ] == setrizene_body [ index + velikost_skupiny ] :
while self . setrizene_body [ index ] == self . setrizene_body [
velikost_skupiny = velikost_skupiny + 1
index + velikost_skupiny ] :
velikost_skupiny + = 1
# na konci musíme ošetřit přetečení seznamu
# na konci musíme ošetřit přetečení seznamu
if ( index + velikost_skupiny ) > len ( setrizene_body ) - 1 :
if ( index + velikost_skupiny ) > len ( self . setrizene_body ) - 1 :
break
break
# pokud je velikost skupiny 1, vypíšu pořadí
# pokud je velikost skupiny 1, vypíšu pořadí
if velikost_skupiny == 1 :
if velikost_skupiny == 1 :
sloupec_s_poradim . append ( " {} . " . format ( aktualni_poradi ) )
sloupec_s_poradim . append ( f " { aktualni_poradi } . " )
# pokud je skupina větší, vypíšu rozsah
# pokud je skupina větší, vypíšu rozsah
else :
else :
sloupec_s_poradim . append ( " {} .– {} . " . format ( aktualni_poradi ,
sloupec_s_poradim . append (
aktualni_poradi + velikost_skupiny - 1 ) )
f " { aktualni_poradi } .– { aktualni_poradi + velikost_skupiny - 1 } . "
)
# zvětšíme aktuální pořadí o tolik, kolik pozic bylo přeskočeno
# zvětšíme aktuální pořadí o tolik, kolik pozic bylo přeskočeno
aktualni_poradi = aktualni_poradi + velikost_skupiny
aktualni_poradi + = velikost_skupiny
return sloupec_s_poradim
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 ( )
def body_resitelu ( resitele , za , odjakziva = True , jen_verejne = False ) :
@cached_property
""" Funkce počítající počty bodů pro zadané řešitele,
def aktivni_resitele ( self ) - > list [ m . Resitel ] :
buď odjakživa do daného ročníku / čísla anebo za daný ročník / číslo .
return list ( resi_v_rocniku ( self . rocnik ) )
Parametry :
resitele ( seznam obsahující položky typu Resitel ) : aktivní řešitelé
@cached_property
za ( Rocnik / Cislo ) : za co se mají počítat body
def cisla_rocniku ( self ) - > list [ m . Cislo ] :
( generování starších výsledkovek )
""" Vrátí všechna čísla daného ročníku. """
odjakziva ( bool ) : zda se mají počítat body odjakživa , nebo jen za číslo / ročník
if self . jen_verejne :
zadané v " za "
return self . rocnik . verejne_vysledkovky_cisla ( )
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 :
else :
assert True , " body_resitelu: Neplatná kombinace za a odjakživa. "
return self . rocnik . cisla . all ( ) . order_by ( ' poradi ' )
# Následující řádek přidá ke každému řešiteli údaj ".body" se součtem jejich bodů
@cached_property
resitele_s_body = m . Resitel . objects . filter ( id__in = resitele_id ) . annotate (
def body_za_cisla_slovnik ( self ) - > dict [ int , dict [ int , int ] ] : # Výstup: m.Cislo.id → ( m.Resitel.id → body )
body = body_k_zapocteni )
# TODO: Body jsou decimal!
# Teď jen z QuerySetu řešitelů anotovaných body vygenerujeme slovník
body_cisla_slovnik = dict ( )
# indexovaný řešitelským id obsahující body.
for cislo in self . cisla_rocniku :
# Pokud jsou body None, nahradíme za 0.
# získáme body za číslo
slovnik = { int ( res . id ) : ( res . body if res . body else 0 ) for res in resitele_s_body }
body_za_cislo = body_resitelu (
return slovnik
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 ( object ) :
class RadekVysledkovkyRocniku :
# TODO: přepsat na dataclass
""" Obsahuje věci, které se hodí vědět při konstruování výsledkovky.
""" Obsahuje věci, které se hodí vědět při konstruování výsledkovky.
Umožňuje snazší práci v templatu ( lepší , než seznam ) . """
Umožňuje snazší práci v templatu ( lepší , než seznam ) . """
def __init__ ( self , poradi , resitel , body_cisla_sezn , body_rocnik , body_odjakziva , rok ) :
def __init__ ( self , poradi , resitel , body_cisla_seznam , body_rocnik , body_odjakziva , rok ) :
self . poradi = poradi
self . poradi = poradi
self . resitel = resitel
self . resitel = resitel
self . rocnik_resitele = resitel . rocnik ( rok )
self . rocnik_resitele = resitel . rocnik ( rok )
self . body_rocnik = body_rocnik
self . body_rocnik = body_rocnik
self . body_celkem_odjakziva = body_odjakziva
self . body_celkem_odjakziva = body_odjakziva
self . body_cisla_sezn = body_cisla_sezn
self . body_cisla_seznam = body_cisla_seznam
self . titul = resitel . get_titul ( body_odjakziva )
self . titul = resitel . get_titul ( body_odjakziva )
def setrid_resitele_a_body ( slov_resitel_body ) :
@cached_property
setrizeni_resitele_id = [ dvojice [ 0 ] for dvojice in slov_resitel_body ]
def radky_vysledkovky ( self ) - > list [ RadekVysledkovkyRocniku ] :
setrizene_body = [ dvojice [ 1 ] for dvojice in slov_resitel_body ]
radky_vysledkovky = [ ]
return setrizeni_resitele_id , setrizene_body
def data_vysledkovky_rocniku ( rocnik , jen_verejne = True ) :
setrizeni_resitele_dict = dict ( )
""" Přebírá ročník (např. context[ " rocnik " ]) a vrací výsledkovou listinu ve
for r in m . Resitel . objects . filter (
formě vhodné pro šablonu " seminar/vysledkovka_rocniku.html "
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 .
start = time . time ( )
Problém s ID ' -1 ' znamená problémy bez nadproblémů , jež nejsou témata , tj . články , úlohy , konfery , …
"""
## TODO možná chytřeji vybírat aktivní řešitele
# Body za číslo
# aktivní řešitelé - chceme letos něco poslal, TODO později vyfiltrujeme ty, kdo mají
body_za_cislo = { ar . id : " " for ar in self . aktivni_resitele }
# u alespoň jedné hodnoty něco jiného než NULL
aktivni_resitele = list ( resi_v_rocniku ( rocnik ) )
# Body za hlavní problémy
cisla = cisla_rocniku ( rocnik , jen_verejne )
body_za_temata = {
body_cisla_slov = { }
hp . id : { ar . id : " " for ar in self . aktivni_resitele }
for cislo in cisla :
for hp in self . temata_a_spol
# získáme body za číslo
}
_ , cislobody = secti_body_za_cislo ( cislo , aktivni_resitele )
# Ostatní body
body_cisla_slov [ cislo . id ] = cislobody
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
# získáme body za ročník, seznam obsahuje dvojice (řešitel_id, body) setřízené sestupně
# Když nadproblém není "téma", pak je "Ostatní"
resitel_rocnikbody_sezn = secti_body_za_rocnik ( rocnik , aktivni_resitele , jen_verejne = jen_verejne )
if nadproblem not in body_za_temata :
nadproblem = - 1
# setřídíme řešitele podle počtu bodů a získáme seznam s body od nejvyšších po nenižší
problem_slovnik = body_za_problemy [ nadproblem ] [ prob . id ]
setrizeni_resitele_id , setrizene_body = setrid_resitele_a_body ( resitel_rocnikbody_sezn )
nadproblem_slovnik = body_za_temata [ nadproblem ]
poradi = sloupec_s_poradim ( setrizene_body )
# získáme body odjakživa
body = hodnoceni . body
resitel_odjakzivabody_slov = body_resitelu ( aktivni_resitele , rocnik , jen_verejne = jen_verejne )
# vytvoříme jednotlivé sloupce výsledkovky
# Může mít více řešitelů
radky_vysledkovky = [ ]
for resitel in hodnoceni . reseni . resitele . all ( ) :
i = 0
if resitel not in self . aktivni_resitele :
setrizeni_resitele_dict = { } # Tento slovnik se vyrab
continue
for r in m . Resitel . objects . filter ( id__in = setrizeni_resitele_id ) . select_related ( ' osoba ' ) :
self . pricti_body ( body_za_cislo , resitel , body )
setrizeni_resitele_dict [ r . id ] = r
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 ar_id in setrizeni_resitele_id :
for podproblem in podproblemy . keys ( ) :
# seznam počtu bodů daného řešitele pro jednotlivá čísla
def int_or_zero ( p ) :
body_cisla_sezn = [ ]
try :
for cislo in cisla :
return int ( p . kod )
body_cisla_sezn . append ( body_cisla_slov [ cislo . id ] [ ar_id ] )
except ValueError :
return 0
# vytáhneme informace pro daného řešitele
podproblemy [ podproblem ] = sorted ( podproblemy [ podproblem ] , key = int_or_zero )
radek = RadekVysledkovkyRocniku (
return podproblemy
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 ( )
@cached_property
print ( " Vysledkovka rocniku " , end - start )
def podproblemy_seznam ( self ) - > list [ list [ m . Problem ] ] :
return [ self . podproblemy [ it . id ] for it in self . temata_a_spol ] + [ self . podproblemy [ - 1 ] ]
radky_vysledkovky = [ radek for radek in radky_vysledkovky if radek . body_rocnik > 0 ]
@cached_property
return radky_vysledkovky , cisla
def podproblemy_iter ( self ) - > FixedIterator :
return FixedIterator ( self . podproblemy_seznam . __iter__ ( ) )
class RadekVysledkovkyCisla ( object ) :
class RadekVysledkovkyCisla ( object ) :
# TODO: Přepsat na dataclass
""" Obsahuje věci, které se hodí vědět při konstruování výsledkovky.
""" Obsahuje věci, které se hodí vědět při konstruování výsledkovky.
Umožňuje snazší práci v templatu ( lepší , než seznam ) . """
Umožňuje snazší práci v templatu ( lepší , než seznam ) . """
def __init__ ( self , poradi , resitel , body_problemy_sezn ,
def __init__ ( self , poradi , resitel , temata_seznamk , body_cislo , body_rocnik , body_odjakziva , rok , body_podproblemy , body_podproblemy_iter ) :
body_cislo , body_rocnik , body_odjakziva , rok , body_podproblemy , body_podproblemy_iter ) :
self . resitel = resitel
self . resitel = resitel
self . rocnik_resitele = resitel . rocnik ( rok )
self . rocnik_resitele = resitel . rocnik ( rok )
self . body_cislo = body_cislo
self . body_cislo = body_cislo
self . body_rocnik = body_rocnik
self . body_rocnik = body_rocnik
self . body_celkem_odjakziva = body_odjakziva
self . body_celkem_odjakziva = body_odjakziva
self . poradi = poradi
self . poradi = poradi
self . body_problemy_sezn = body_problemy_sezn
self . body_za_temata_seznam = temata_seznamk
self . titul = resitel . get_titul ( body_odjakziva )
self . titul = resitel . get_titul ( body_odjakziva )
self . body_podproblemy = body_podproblemy
self . body_podproblemy = body_podproblemy
self . body_podproblemy_iter = body_podproblemy_iter # TODELETE
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 ) :
def pricti_body ( slovnik , resitel , body ) :
""" Přiřazuje danému řešiteli body do slovníku. """
""" Přiřazuje danému řešiteli body do slovníku. """
# testujeme na None (""), pokud je to první řešení
# testujeme na None (""), pokud je to první řešení
@ -224,239 +451,42 @@ def pricti_body(slovnik, resitel, body):
# (v dalším kroku přičteme reálný počet bodů),
# (v dalším kroku přičteme reálný počet bodů),
# rozlišujeme tím mezi 0 a neodevzdaným řešením
# 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 ] == " " :
if slovnik [ resitel . id ] == " " :
slovnik [ resitel . id ] = 0
slovnik [ resitel . id ] = 0
slovnik [ resitel . id ] + = body
slovnik [ resitel . id ] + = body
def secti_body_za_rocnik ( za , aktivni_resitele , jen_verejne ) :
@staticmethod
""" 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 ) :
def ne_clanek_ne_konfera ( problem ) :
inst = problem . get_real_instance ( )
inst = problem . get_real_instance ( )
return not ( isinstance ( inst , m . Clanek ) or isinstance ( inst , 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
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 :
def next ( self ) :
return self . niter . __next__ ( )
def __init__ ( self , niter ) :
self . niter = niter
# TODELETE
def data_vysledkovky_cisla ( cislo ) :
problemy = problemy_cisla ( cislo )
hlavni_problemy = hlavni_problemy_f ( problemy )
## 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 ( cislo . rocnik ) )
# získáme body za číslo
hlavni_problemy_slovnik , cislobody = secti_body_za_cislo ( cislo , aktivni_resitele , hlavni_problemy )
# získáme body za ročník, seznam obsahuje dvojice (řešitel_id, body) setřízené sestupně
resitel_rocnikbody_sezn = secti_body_za_rocnik ( cislo , aktivni_resitele , jen_verejne = True )
# získáme body odjakživa
resitel_odjakzivabody_slov = body_resitelu ( aktivni_resitele , cislo , jen_verejne = True )
# řešitelé setřídění podle bodů za číslo sestupně
setrizeni_resitele_id = [ dvojice [ 0 ] for dvojice in resitel_rocnikbody_sezn ]
# spočítáme pořadí řešitelů
setrizeni_resitele_body = [ dvojice [ 1 ] for dvojice in resitel_rocnikbody_sezn ]
poradi = sloupec_s_poradim ( setrizeni_resitele_body )
# vytvoříme jednotlivé sloupce výsledkovky
class VysledkovkaDoTeXu ( VysledkovkaCisla ) :
radky_vysledkovky = [ ]
def __init__ (
i = 0
self ,
nejake_cislo : m . Cislo ,
def ne_clanek_ne_konfera ( problem ) :
od_vyjma : m . Deadline ,
do_vcetne : m . Deadline
return not ( isinstance ( problem . get_real_instance ( ) , m . Clanek ) or isinstance ( problem . get_real_instance ( ) , m . Konfera ) )
) :
super ( ) . __init__ ( nejake_cislo , False , do_vcetne )
if cislo . rocnik . rocnik < ROCNIK_ZRUSENI_TEMAT :
self . od_deadlinu = od_vyjma
temata_a_spol = hlavni_problemy
else :
@cached_property
temata_a_spol = list ( filter ( ne_clanek_ne_konfera , hlavni_problemy ) )
def problemy ( self ) - > list [ m . Problem ] :
return m . Problem . objects . filter ( hodnoceni__in = m . Hodnoceni . objects . filter (
# získáme body u jednotlivých témat
deadline_body__deadline__gt = self . od_deadlinu . deadline ,
podproblemy = podproblemy_v_cislu ( cislo , problemy , temata_a_spol )
deadline_body__deadline__lte = self . do_deadlinu . deadline ,
problemy_slovnik = secti_body_za_cislo_podle_temat ( cislo , aktivni_resitele , podproblemy , temata_a_spol )
) ) . distinct ( ) . non_polymorphic ( ) . select_related ( ' nadproblem ' ) . select_related ( ' nadproblem__nadproblem ' )
# def not_empty(value):
@property
# return value != ''
def hodnoceni_do_cisla ( self ) :
#
hodnoceni = m . Hodnoceni . objects . prefetch_related (
# je_nejake_ostatni = any(filter(not_empty, hlavni_problemy_slovnik[-1].values())) > 0
' problem ' , ' reseni ' , ' reseni__resitele ' )
if self . jen_verejne :
je_nejake_ostatni = len ( hlavni_problemy ) - len ( temata_a_spol ) > 0
hodnoceni = hodnoceni . filter ( deadline_body__verejna_vysledkovka = True )
return hodnoceni . filter (
setrizeni_resitele_slovnik = { }
deadline_body__deadline__gt = self . od_deadlinu . deadline ,
setrizeni_resitele = m . Resitel . objects . filter ( id__in = setrizeni_resitele_id ) . select_related ( ' osoba ' )
deadline_body__deadline__lte = self . do_deadlinu . deadline ,
for r in setrizeni_resitele :
body__isnull = False ,
setrizeni_resitele_slovnik [ r . id ] = r
for ar_id in setrizeni_resitele_id :
# získáme seznam bodů za problémy pro daného řešitele
body_problemy = [ ]
body_podproblemy = [ ]
for hp in temata_a_spol :
body_problemy . append ( hlavni_problemy_slovnik [ hp . id ] [ ar_id ] )
body_podproblemy . append ( [ problemy_slovnik [ hp . id ] [ it . id ] [ ar_id ] for it in podproblemy [ hp . id ] ] )
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__ ( ) )
)
)