Pavel 'LEdoian' Turinsky
8 months ago
3 changed files with 584 additions and 6 deletions
@ -0,0 +1,124 @@ |
|||||
|
# Generated by Django 4.2.8 on 2024-03-12 21:10 |
||||
|
|
||||
|
from django.db import migrations, models |
||||
|
import django.utils.timezone |
||||
|
import django_countries.fields |
||||
|
import imagekit.models.fields |
||||
|
|
||||
|
|
||||
|
class Migration(migrations.Migration): |
||||
|
|
||||
|
initial = True |
||||
|
|
||||
|
dependencies = [ |
||||
|
('personalni', '0002_auto_20240312_2118'), |
||||
|
('seminar', '0118_alter_organizator_options_alter_osoba_options_and_more'), |
||||
|
] |
||||
|
|
||||
|
operations = [ |
||||
|
migrations.CreateModel( |
||||
|
name='Organizator', |
||||
|
fields=[ |
||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
||||
|
('vytvoreno', models.DateTimeField(blank=True, default=django.utils.timezone.now, editable=False, verbose_name='Vytvořeno')), |
||||
|
('organizuje_od', models.DateTimeField(blank=True, null=True, verbose_name='Organizuje od')), |
||||
|
('organizuje_do', models.DateTimeField(blank=True, null=True, verbose_name='Organizuje do')), |
||||
|
('studuje', models.CharField(blank=True, help_text="Např. 'Studuje Obecnou fyziku (Bc.), 3. ročník', 'Vystudovala Diskrétní modely a algoritmy (Mgr.)' nebo 'Přednáší na MFF'", max_length=256, null=True, verbose_name='Studium aj.')), |
||||
|
('strucny_popis_organizatora', models.TextField(blank=True, null=True, verbose_name='Stručný popis organizátora')), |
||||
|
('skola', models.CharField(blank=True, help_text='Škola, např. MFF, VŠCHT, VUT, ... prostě aby se nemuselo psát do studuješkolu, ale jen obor, možnost zobrazit zvlášť', max_length=256, null=True, verbose_name='Škola, kterou studuje')), |
||||
|
], |
||||
|
options={ |
||||
|
'verbose_name': 'Organizátor', |
||||
|
'verbose_name_plural': 'Organizátoři', |
||||
|
'ordering': ['-organizuje_do', 'osoba__jmeno', 'osoba__prijmeni'], |
||||
|
'managed': False, |
||||
|
}, |
||||
|
), |
||||
|
migrations.CreateModel( |
||||
|
name='Osoba', |
||||
|
fields=[ |
||||
|
('id', models.AutoField(primary_key=True, serialize=False)), |
||||
|
('jmeno', models.CharField(max_length=256, verbose_name='jméno')), |
||||
|
('prijmeni', models.CharField(max_length=256, verbose_name='příjmení')), |
||||
|
('prezdivka', models.CharField(blank=True, max_length=256, null=True, verbose_name='přezdívka')), |
||||
|
('pohlavi_muz', models.BooleanField(default=False, verbose_name='pohlaví (muž)')), |
||||
|
('email', models.EmailField(blank=True, default='', max_length=256, verbose_name='e-mail')), |
||||
|
('telefon', models.CharField(blank=True, default='', max_length=256, verbose_name='telefon')), |
||||
|
('datum_narozeni', models.DateField(blank=True, null=True, verbose_name='datum narození')), |
||||
|
('datum_souhlasu_udaje', models.DateField(blank=True, help_text='Datum souhlasu se zpracováním osobních údajů', null=True, verbose_name='datum souhlasu (údaje)')), |
||||
|
('datum_souhlasu_zasilani', models.DateField(blank=True, help_text='Datum souhlasu se zasíláním MFF materiálů', null=True, verbose_name='datum souhlasu (spam)')), |
||||
|
('datum_registrace', models.DateField(default=django.utils.timezone.now, verbose_name='datum registrace do semináře')), |
||||
|
('ulice', models.CharField(blank=True, default='', max_length=256, verbose_name='ulice')), |
||||
|
('mesto', models.CharField(blank=True, default='', max_length=256, verbose_name='město')), |
||||
|
('psc', models.CharField(blank=True, default='', max_length=32, verbose_name='PSČ')), |
||||
|
('stat', django_countries.fields.CountryField(default='CZ', help_text='ISO 3166-1 kód země velkými písmeny (CZ, SK, ...)', max_length=2, verbose_name='stát')), |
||||
|
('jak_se_dozvedeli', models.TextField(blank=True, verbose_name='Jak se dozvěděli')), |
||||
|
('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka k osobě (plain text)', verbose_name='neveřejná poznámka')), |
||||
|
('foto', imagekit.models.fields.ProcessedImageField(blank=True, help_text='Vlož fotografii osoby o libovolné velikosti', null=True, upload_to='image_osoby/velke/%Y/', verbose_name='Fotografie osoby')), |
||||
|
], |
||||
|
options={ |
||||
|
'verbose_name': 'Osoba', |
||||
|
'verbose_name_plural': 'Osoby', |
||||
|
'db_table': 'seminar_osoby', |
||||
|
'ordering': ['prijmeni', 'jmeno'], |
||||
|
'managed': False, |
||||
|
}, |
||||
|
), |
||||
|
migrations.CreateModel( |
||||
|
name='Prijemce', |
||||
|
fields=[ |
||||
|
('id', models.AutoField(primary_key=True, serialize=False)), |
||||
|
('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka k příemci čísel (plain text)', verbose_name='neveřejná poznámka')), |
||||
|
('zasilat_cislo_emailem', models.BooleanField(default=False, help_text='True pokud chce příjemce dostávat číslo emailem', verbose_name='zasílat číslo emailem')), |
||||
|
], |
||||
|
options={ |
||||
|
'verbose_name': 'příjemce', |
||||
|
'verbose_name_plural': 'příjemce', |
||||
|
'db_table': 'seminar_prijemce', |
||||
|
'managed': False, |
||||
|
}, |
||||
|
), |
||||
|
migrations.CreateModel( |
||||
|
name='Resitel', |
||||
|
fields=[ |
||||
|
('id', models.AutoField(primary_key=True, serialize=False)), |
||||
|
('prezdivka_resitele', models.CharField(blank=True, max_length=256, null=True, unique=True, verbose_name='přezdívka řešitele')), |
||||
|
('rok_maturity', models.IntegerField(blank=True, null=True, verbose_name='rok maturity')), |
||||
|
('zasilat', models.CharField(choices=[('domu', 'Domů'), ('do_skoly', 'Do školy'), ('nikam', 'Nezasílat papírově')], default='domu', max_length=32, verbose_name='kam zasílat')), |
||||
|
('zasilat_cislo_emailem', models.BooleanField(default=False, help_text='True pokud chce řešitel dostávat číslo emailem', verbose_name='zasílat číslo emailem')), |
||||
|
('zasilat_cislo_papirove', models.BooleanField(default=True, help_text='True pokud chce řešitel dostávat číslo papírově', verbose_name='zasílat číslo papírově')), |
||||
|
('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka k řešiteli (plain text)', verbose_name='neveřejná poznámka')), |
||||
|
], |
||||
|
options={ |
||||
|
'verbose_name': 'Řešitel', |
||||
|
'verbose_name_plural': 'Řešitelé', |
||||
|
'db_table': 'seminar_resitele', |
||||
|
'ordering': ['osoba'], |
||||
|
'managed': False, |
||||
|
}, |
||||
|
), |
||||
|
migrations.CreateModel( |
||||
|
name='Skola', |
||||
|
fields=[ |
||||
|
('id', models.AutoField(primary_key=True, serialize=False)), |
||||
|
('aesop_id', models.CharField(blank=True, default='', help_text='Aesopi ID typu "izo:..." nebo "aesop:..."', max_length=32, verbose_name='Aesop ID')), |
||||
|
('izo', models.CharField(blank=True, help_text='IZO školy (jen české školy)', max_length=32, verbose_name='IZO')), |
||||
|
('nazev', models.CharField(help_text='Celý název školy', max_length=256, verbose_name='název')), |
||||
|
('kratky_nazev', models.CharField(blank=True, help_text='Zkrácený název pro zobrazení ve výsledkovce', max_length=256, verbose_name='zkrácený název')), |
||||
|
('ulice', models.CharField(max_length=256, verbose_name='ulice')), |
||||
|
('mesto', models.CharField(max_length=256, verbose_name='město')), |
||||
|
('psc', models.CharField(max_length=32, verbose_name='PSČ')), |
||||
|
('stat', django_countries.fields.CountryField(default='CZ', help_text='ISO 3166-1 kód země velkými písmeny (CZ, SK, ...)', max_length=2, verbose_name='stát')), |
||||
|
('je_zs', models.BooleanField(default=True, verbose_name='základní stupeň')), |
||||
|
('je_ss', models.BooleanField(default=True, verbose_name='střední stupeň')), |
||||
|
('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka ke škole (plain text)', verbose_name='neveřejná poznámka')), |
||||
|
], |
||||
|
options={ |
||||
|
'verbose_name': 'Škola', |
||||
|
'verbose_name_plural': 'Školy', |
||||
|
'db_table': 'seminar_skoly', |
||||
|
'ordering': ['mesto', 'nazev'], |
||||
|
'managed': False, |
||||
|
}, |
||||
|
), |
||||
|
] |
@ -0,0 +1,453 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
import logging |
||||
|
|
||||
|
from django.db import models |
||||
|
from django.utils import timezone |
||||
|
from django.conf import settings |
||||
|
from django.core.exceptions import ValidationError |
||||
|
from imagekit.models import ImageSpecField, ProcessedImageField |
||||
|
from imagekit.processors import ResizeToFit, Transpose |
||||
|
|
||||
|
from django_countries.fields import CountryField |
||||
|
|
||||
|
from reversion import revisions as reversion |
||||
|
|
||||
|
from seminar.models.base import SeminarModelBase |
||||
|
|
||||
|
logger = logging.getLogger(__name__) |
||||
|
|
||||
|
|
||||
|
@reversion.register(ignore_duplicates=True) |
||||
|
class Osoba(SeminarModelBase): |
||||
|
|
||||
|
class Meta: |
||||
|
db_table = 'seminar_osoby' |
||||
|
verbose_name = 'Osoba' |
||||
|
verbose_name_plural = 'Osoby' |
||||
|
ordering = ['prijmeni','jmeno'] |
||||
|
managed = False |
||||
|
|
||||
|
id = models.AutoField(primary_key = True) |
||||
|
|
||||
|
jmeno = models.CharField('jméno', max_length=256) |
||||
|
|
||||
|
prijmeni = models.CharField('příjmení', max_length=256) |
||||
|
|
||||
|
prezdivka = models.CharField('přezdívka', blank=True, null=True, max_length=256) |
||||
|
|
||||
|
# User, pokud má na webu účet |
||||
|
user = models.OneToOneField(settings.AUTH_USER_MODEL, blank=True, null=True, |
||||
|
verbose_name='uživatel', on_delete=models.DO_NOTHING) |
||||
|
|
||||
|
# Pohlaví. Že ho neznáme se snad nestane (a ušetří to práci při programování) |
||||
|
pohlavi_muz = models.BooleanField('pohlaví (muž)', default=False) |
||||
|
|
||||
|
email = models.EmailField('e-mail', max_length=256, blank=True, default='') |
||||
|
|
||||
|
telefon = models.CharField('telefon', max_length=256, blank=True, default='') |
||||
|
|
||||
|
datum_narozeni = models.DateField('datum narození', blank=True, null=True) |
||||
|
|
||||
|
# NULL dokud nedali souhlas |
||||
|
datum_souhlasu_udaje = models.DateField('datum souhlasu (údaje)', blank=True, null=True, |
||||
|
help_text='Datum souhlasu se zpracováním osobních údajů') |
||||
|
|
||||
|
# NULL dokud nedali souhlas |
||||
|
datum_souhlasu_zasilani = models.DateField('datum souhlasu (spam)', blank=True, null=True, |
||||
|
help_text='Datum souhlasu se zasíláním MFF materiálů') |
||||
|
|
||||
|
# Alespoň odhad (rok či i měsíc) |
||||
|
datum_registrace = models.DateField('datum registrace do semináře', default=timezone.now) |
||||
|
|
||||
|
# Ulice může být i jen číslo |
||||
|
ulice = models.CharField('ulice', max_length=256, blank=True, default='') |
||||
|
|
||||
|
mesto = models.CharField('město', max_length=256, blank=True, default='') |
||||
|
|
||||
|
psc = models.CharField('PSČ', max_length=32, blank=True, default='') |
||||
|
|
||||
|
# ISO 3166-1 dvojznakovy kod zeme velkym pismem (CZ, SK) |
||||
|
# Ekvivalentní s CharField(max_length=2, default='CZ', ...) |
||||
|
stat = CountryField('stát', default='CZ', |
||||
|
help_text='ISO 3166-1 kód země velkými písmeny (CZ, SK, ...)') |
||||
|
|
||||
|
jak_se_dozvedeli = models.TextField('Jak se dozvěděli', blank=True) |
||||
|
|
||||
|
poznamka = models.TextField('neveřejná poznámka', blank=True, |
||||
|
help_text='Neveřejná poznámka k osobě (plain text)') |
||||
|
|
||||
|
foto = ProcessedImageField(verbose_name='Fotografie osoby', |
||||
|
upload_to='image_osoby/velke/%Y/', null = True, blank = True, |
||||
|
help_text = 'Vlož fotografii osoby o libovolné velikosti', |
||||
|
processors=[ |
||||
|
Transpose(Transpose.AUTO), |
||||
|
ResizeToFit(500, 500, upscale=False) |
||||
|
], |
||||
|
options={'quality': 95}) |
||||
|
foto_male = ImageSpecField(source='foto', |
||||
|
processors=[ |
||||
|
ResizeToFit(200, 200, upscale=False) |
||||
|
], |
||||
|
options={'quality': 95}) |
||||
|
|
||||
|
# má OneToOneField nejvýše s: |
||||
|
# Resitel |
||||
|
# Prijemce |
||||
|
# Organizator |
||||
|
|
||||
|
def plne_jmeno(self): |
||||
|
return '{} {}'.format(self.jmeno, self.prijmeni) |
||||
|
|
||||
|
def inicial_krestni(self): |
||||
|
jmena = self.jmeno.split() |
||||
|
return " ".join(['{}.'.format(jmeno[0]) for jmeno in jmena]) |
||||
|
|
||||
|
def __str__(self): |
||||
|
return self.plne_jmeno() |
||||
|
|
||||
|
# Overridujeme save Osoby, aby když si změní e-mail, aby se projevil i v |
||||
|
# Userovi (a tak se dal poslat mail s resetem hesla) |
||||
|
def save(self, *args, **kwargs): |
||||
|
if self.user is not None: |
||||
|
u = self.user |
||||
|
# U svatého tučňáka, prosím ať tohle funguje. |
||||
|
# (Takhle se kódit asi nemá...) |
||||
|
u.email = self.email |
||||
|
u.save() |
||||
|
super().save() |
||||
|
|
||||
|
# |
||||
|
# Mělo by být částečně vytaženo z Aesopa |
||||
|
# viz https://ovvp.mff.cuni.cz/wiki/aesop/export-skol. |
||||
|
# |
||||
|
|
||||
|
@reversion.register(ignore_duplicates=True) |
||||
|
class Skola(SeminarModelBase): |
||||
|
|
||||
|
class Meta: |
||||
|
db_table = 'seminar_skoly' |
||||
|
verbose_name = 'Škola' |
||||
|
verbose_name_plural = 'Školy' |
||||
|
ordering = ['mesto', 'nazev'] |
||||
|
managed = False |
||||
|
|
||||
|
# Interní ID |
||||
|
id = models.AutoField(primary_key = True) |
||||
|
|
||||
|
# Aesopi ID "izo:..." nebo "aesop:..." |
||||
|
# NULL znamená v exportu do aesopa "ufo" |
||||
|
aesop_id = models.CharField('Aesop ID', max_length=32, blank=True, default='', |
||||
|
help_text='Aesopi ID typu "izo:..." nebo "aesop:..."') |
||||
|
|
||||
|
# IZO školy (jen české školy) |
||||
|
izo = models.CharField('IZO', max_length=32, blank=True, |
||||
|
help_text='IZO školy (jen české školy)') |
||||
|
|
||||
|
# Celý název školy |
||||
|
nazev = models.CharField('název', max_length=256, |
||||
|
help_text='Celý název školy') |
||||
|
|
||||
|
# Zkraceny nazev pro zobrazení ve výsledkovce, volitelné. |
||||
|
# Není v Aesopovi, musíme vytvářet sami. |
||||
|
kratky_nazev = models.CharField('zkrácený název', max_length=256, blank=True, |
||||
|
help_text="Zkrácený název pro zobrazení ve výsledkovce") |
||||
|
|
||||
|
# Ulice může být jen číslo |
||||
|
ulice = models.CharField('ulice', max_length=256) |
||||
|
|
||||
|
mesto = models.CharField('město', max_length=256) |
||||
|
|
||||
|
psc = models.CharField('PSČ', max_length=32) |
||||
|
|
||||
|
# ISO 3166-1 dvojznakovy kod zeme velkym pismem (CZ, SK) |
||||
|
# Ekvivalentní s CharField(max_length=2, default='CZ', ...) |
||||
|
stat = CountryField('stát', default='CZ', |
||||
|
help_text='ISO 3166-1 kód země velkými písmeny (CZ, SK, ...)') |
||||
|
|
||||
|
# Jaké vzdělání škpla poskytuje? |
||||
|
je_zs = models.BooleanField('základní stupeň', default=True) |
||||
|
je_ss = models.BooleanField('střední stupeň', default=True) |
||||
|
|
||||
|
poznamka = models.TextField('neveřejná poznámka', blank=True, |
||||
|
help_text='Neveřejná poznámka ke škole (plain text)') |
||||
|
|
||||
|
kontaktni_osoba = models.ForeignKey(Osoba, verbose_name='Kontaktní osoba', |
||||
|
blank=True, null=True, on_delete=models.SET_NULL) |
||||
|
|
||||
|
def __str__(self): |
||||
|
return '{}, {}, {}'.format(self.nazev, self.ulice, self.mesto) |
||||
|
|
||||
|
class Prijemce(SeminarModelBase): |
||||
|
class Meta: |
||||
|
db_table = 'seminar_prijemce' |
||||
|
verbose_name = 'příjemce' |
||||
|
verbose_name_plural = 'příjemce' |
||||
|
managed = False |
||||
|
|
||||
|
|
||||
|
# Interní ID |
||||
|
id = models.AutoField(primary_key = True) |
||||
|
|
||||
|
poznamka = models.TextField('neveřejná poznámka', blank=True, |
||||
|
help_text='Neveřejná poznámka k příemci čísel (plain text)') |
||||
|
|
||||
|
osoba = models.OneToOneField(Osoba, verbose_name='komu', blank=False, null=False, |
||||
|
help_text='Které osobě či na jakou adresu se mají zasílat čísla', |
||||
|
on_delete=models.CASCADE) |
||||
|
|
||||
|
zasilat_cislo_emailem = models.BooleanField('zasílat číslo emailem', help_text='True pokud chce příjemce dostávat číslo emailem', default=False) |
||||
|
|
||||
|
# FIXME: možná chceme něco jako vazbu na osobu XOR školu a počet kusů k zaslání |
||||
|
# FIXME: a možná taky posílání na mail a možná taky přes něj chceme posílat i řešitelům |
||||
|
|
||||
|
def __str__(self): |
||||
|
return self.osoba.plne_jmeno() |
||||
|
|
||||
|
|
||||
|
@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'] |
||||
|
managed = False |
||||
|
|
||||
|
# 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 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 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 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() |
||||
|
|
||||
|
|
||||
|
@reversion.register(ignore_duplicates=True) |
||||
|
class Organizator(SeminarModelBase): |
||||
|
|
||||
|
class Meta: |
||||
|
verbose_name = 'Organizátor' |
||||
|
verbose_name_plural = 'Organizátoři' |
||||
|
# Řadí aktivní orgy na začátek, pod tím v pořadí od nejstarších neaktivní orgy. |
||||
|
# TODO: Chtěl bych spíš mít nejstarší orgy dole. |
||||
|
# TODO: Zohledňovat přezdívky? |
||||
|
# TODO: Sjednotit s tím, jak se řadí organizátoři v seznau orgů na webu |
||||
|
ordering = ['-organizuje_do', 'osoba__jmeno', 'osoba__prijmeni'] |
||||
|
managed = False |
||||
|
|
||||
|
osoba = models.OneToOneField(Osoba, verbose_name='osoba', related_name='org', |
||||
|
help_text='osobní údaje organizátora', null=False, blank=False, |
||||
|
on_delete=models.PROTECT) |
||||
|
|
||||
|
vytvoreno = models.DateTimeField( |
||||
|
'Vytvořeno', |
||||
|
default=timezone.now, |
||||
|
blank=True, |
||||
|
editable=False |
||||
|
) |
||||
|
|
||||
|
# Ne, date to nebude. SQLite: invalid literal for int() with base 10: b'17 23:00:00' |
||||
|
organizuje_od = models.DateTimeField('Organizuje od', blank=True, null=True) |
||||
|
|
||||
|
organizuje_do = models.DateTimeField('Organizuje do', blank=True, null=True) |
||||
|
|
||||
|
studuje = models.CharField('Studium aj.', max_length = 256, |
||||
|
null = True, blank = True, |
||||
|
help_text="Např. 'Studuje Obecnou fyziku (Bc.), 3. ročník', " |
||||
|
"'Vystudovala Diskrétní modely a algoritmy (Mgr.)' nebo " |
||||
|
"'Přednáší na MFF'") |
||||
|
|
||||
|
strucny_popis_organizatora = models.TextField('Stručný popis organizátora', |
||||
|
null = True, blank = True) |
||||
|
|
||||
|
skola = models.CharField('Škola, kterou studuje', max_length = 256, null=True, blank=True, |
||||
|
help_text="Škola, např. MFF, VŠCHT, VUT, ... prostě aby se nemuselo psát do studuje" |
||||
|
"školu, ale jen obor, možnost zobrazit zvlášť") |
||||
|
|
||||
|
def clean(self): |
||||
|
if self.organizuje_od and self.organizuje_do and (self.organizuje_od > self.organizuje_do): |
||||
|
raise ValidationError("Organizátor nemůže skončit s organizováním dříve než začal!") |
||||
|
super().clean() |
||||
|
|
||||
|
def __str__(self): |
||||
|
if self.osoba.prezdivka: |
||||
|
return "{} '{}' {}".format(self.osoba.jmeno, |
||||
|
self.osoba.prezdivka, |
||||
|
self.osoba.prijmeni) |
||||
|
else: |
||||
|
return "{} {}".format(self.osoba.jmeno, self.osoba.prijmeni) |
Loading…
Reference in new issue