mamweb/personalni/models/resitel.py

221 lines
6.8 KiB
Python

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()