Smazání, makemigrations
This commit is contained in:
parent
ace30c931e
commit
858b5ce054
2 changed files with 48 additions and 453 deletions
|
@ -0,0 +1,48 @@
|
|||
# Generated by Django 4.2.11 on 2024-03-19 21:57
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('seminar', '0119_alter_konfera_ucastnici_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='osoba',
|
||||
name='user',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='prijemce',
|
||||
name='osoba',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='resitel',
|
||||
name='osoba',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='resitel',
|
||||
name='skola',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='skola',
|
||||
name='kontaktni_osoba',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='Organizator',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='Osoba',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='Prijemce',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='Resitel',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='Skola',
|
||||
),
|
||||
]
|
|
@ -1,453 +0,0 @@
|
|||
# -*- 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 .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,
|
||||
related_name='user_old')
|
||||
|
||||
# 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, related_name='kontaktni_osoba_old')
|
||||
|
||||
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, related_name='osobad_old1')
|
||||
|
||||
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, related_name='osoba_old2')
|
||||
|
||||
|
||||
skola = models.ForeignKey(Skola, blank=True, null=True, verbose_name='škola',
|
||||
on_delete=models.SET_NULL, related_name='skola_old3')
|
||||
|
||||
# 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):
|
||||
osoba = models.OneToOneField(Osoba, verbose_name='osoba', related_name='org_old4',
|
||||
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)
|
||||
|
||||
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
|
Loading…
Reference in a new issue