import reversion from django.db import models from mamweb.models.base import SeminarModelBase from .osoba import Osoba from .skola import Skola @reversion.register(ignore_duplicates=True) class Resitel(SeminarModelBase): class Meta: db_table = 'seminar_resitele' verbose_name = 'Řešitel' verbose_name_plural = 'Řešitelé' ordering = ['osoba'] # Interní ID id = models.AutoField(primary_key=True) prezdivka_resitele = models.CharField( 'přezdívka řešitele', blank=True, null=True, max_length=256, unique=True, ) osoba = models.OneToOneField( Osoba, blank=False, null=False, verbose_name='osoba', on_delete=models.PROTECT, ) skola = models.ForeignKey( Skola, blank=True, null=True, verbose_name='škola', on_delete=models.SET_NULL, ) # Očekávaný rok maturity a vyřazení z aktivních řešitelů rok_maturity = models.IntegerField('rok maturity', blank=True, null=True) ZASILAT_DOMU = 'domu' ZASILAT_DO_SKOLY = 'do_skoly' ZASILAT_NIKAM = 'nikam' ZASILAT_CHOICES = [ (ZASILAT_DOMU, 'Domů'), (ZASILAT_DO_SKOLY, 'Do školy'), (ZASILAT_NIKAM, 'Nezasílat papírově'), ] zasilat = models.CharField( 'kam zasílat', max_length=32, choices=ZASILAT_CHOICES, blank=False, default=ZASILAT_DOMU, ) zasilat_cislo_emailem = models.BooleanField( 'zasílat číslo emailem', help_text='True pokud chce řešitel dostávat číslo emailem', default=False, ) zasilat_cislo_papirove = models.BooleanField( 'zasílat číslo papírově', help_text='True pokud chce řešitel dostávat číslo papírově', default=True, ) poznamka = models.TextField( 'neveřejná poznámka', blank=True, help_text='Neveřejná poznámka k řešiteli (plain text)', ) def export_row(self): """Slovnik pro pouziti v AESOP exportu""" return { 'id': self.id, 'name': self.osoba.jmeno, 'surname': self.osoba.prijmeni, 'gender': 'M' if self.osoba.pohlavi_muz else 'F', 'born': self.osoba.datum_narozeni.isoformat() if self.osoba.datum_narozeni else '', 'email': self.osoba.email, 'end-year': self.rok_maturity or '', 'street': self.osoba.ulice, 'town': self.osoba.mesto, 'postcode': self.osoba.psc, 'country': self.osoba.stat, 'spam-flag': 'Y' if self.osoba.datum_souhlasu_zasilani else '', 'spam-date': self.osoba.datum_souhlasu_zasilani.isoformat() if self.osoba.datum_souhlasu_zasilani else '', 'school': self.skola.aesop_id if self.skola else '', 'school-name': str(self.skola) if self.skola else 'Skola neni znama', } def rocnik(self, rocnik): """Vrati skolni rocnik resitele pro zadany Rocnik. Vraci '' pro neznamy rok maturity resitele, Z* pro ekvivalent ZŠ.""" if self.rok_maturity is None: return '' rozdil = 5 - (self.rok_maturity - rocnik.prvni_rok) if rozdil >= 1: return str(rozdil) else: return 'Z' + str(rozdil + 9) def vsechny_body(self): """Spočítá body odjakživa.""" vsechna_reseni = self.reseni_set.all() from odevzdavatko.models import Hodnoceni vsechna_hodnoceni = Hodnoceni.objects.filter( reseni__in=vsechna_reseni) return sum(h.body for h in list(vsechna_hodnoceni) if h.body is not None) def get_titul(self, body=None): """Vrati titul jako řetězec.""" # Nejprve si zadefinujeme titul from enum import Enum from functools import total_ordering @total_ordering class Titul(Enum): """Třída reprezentující možné tituly. Hodnoty jsou dvojice (dolní hranice, stringifikace).""" nic = (0, '') bc = (20, 'Bc.') mgr = (50, 'Mgr.') dr = (100, 'Dr.') doc = (200, 'Doc.') prof = (500, 'Prof.') akad = (1000, 'Akad.') def __lt__(self, other): return True if self.value[0] < other.value[0] else False def __eq__(self, other): # Měla by být implicitní, ale klidně explicitně. return True if self.value[0] == other.value[0] else False def __str__(self): return self.value[1] @classmethod def z_bodu(cls, body): aktualni = cls.nic # TODO: ověřit, že to funguje for titul in cls: # Kdyžtak použít __members__.items() if titul.value[0] <= body: aktualni = titul else: break return aktualni # Hledáme body v databázi # V listopadu 2020 jsme se na filosofické schůzce shodli o změně hranic titulů: # - body z 25. ročníku a dříve byly shledány dvakrát hodnotnějšími # - proto se započítávají dvojnásobně a byly posunuté hranice titulů # - staré tituly se ale nemají odebrat, pokud řešitel v t.č. minulém (26.) ročníku měl titul, má ho mít pořád. from odevzdavatko.models import Hodnoceni hodnoceni_do_25_rocniku = Hodnoceni.objects.filter( deadline_body__cislo__rocnik__rocnik__lte=25, reseni__in=self.reseni_set.all() ) novejsi_hodnoceni = Hodnoceni.objects.filter(reseni__in=self.reseni_set.all()).difference(hodnoceni_do_25_rocniku) def body_z_hodnoceni(hh: list): return sum(h.body for h in hh if h.body is not None) stare_body = body_z_hodnoceni(hodnoceni_do_25_rocniku) if body is None: nove_body = body_z_hodnoceni(novejsi_hodnoceni) else: # Zjistíme, kolik bodů jsou staré, tedy hodnotnější nove_body = max(0, body - stare_body) # Všechny body nad počet původních hodnotnějších stare_body = min(stare_body, body) # Skutečný počet hodnotnějších bodů logicke_body = 2*stare_body + nove_body # Titul se určí následovně: # - Pokud se řeší body, které jsou starší, než do 26 ročníku (včetně), dáváme tituly postaru. # - Jinak dáváme tituly po novu... # - ... ale titul se nesmí odebrat, pokud se zmenšil. def titul_do_26_rocniku(body): """Původní hranice bodů za tituly""" if body < 10: return Titul.nic elif body < 20: return Titul.bc elif body < 50: return Titul.mgr elif body < 100: return Titul.dr elif body < 200: return Titul.doc elif body < 500: return Titul.prof else: return Titul.akad from odevzdavatko.models import Hodnoceni hodnoceni_do_26_rocniku = Hodnoceni.objects.filter( deadline_body__cislo__rocnik__rocnik__lte=26, reseni__in=self.reseni_set.all() ) novejsi_body = body_z_hodnoceni( Hodnoceni.objects.filter(reseni__in=self.reseni_set.all()) .difference(hodnoceni_do_26_rocniku) ) starsi_body = body_z_hodnoceni(hodnoceni_do_26_rocniku) if body is not None: # Ještě z toho vybereme ty správně staré body novejsi_body = max(0, body - starsi_body) starsi_body = min(starsi_body, body) # Titul pro 26. ročník stary_titul = titul_do_26_rocniku(starsi_body) # Titul podle aktuálních pravidel novy_titul = Titul.z_bodu(logicke_body) if novejsi_body == 0: # Žádné nové body -- titul podle starých pravidel return str(stary_titul) return str(max(novy_titul, stary_titul)) def __str__(self): return self.osoba.plne_jmeno()