From 858b5ce054ffc8f9a9e7fd9193eeaac18aeff3d3 Mon Sep 17 00:00:00 2001 From: Pavel 'LEdoian' Turinsky Date: Tue, 19 Mar 2024 22:58:15 +0100 Subject: [PATCH] =?UTF-8?q?Smaz=C3=A1n=C3=AD,=20makemigrations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...oba_user_remove_prijemce_osoba_and_more.py | 48 ++ seminar/models/personalni.py | 453 ------------------ 2 files changed, 48 insertions(+), 453 deletions(-) create mode 100644 seminar/migrations/0120_remove_osoba_user_remove_prijemce_osoba_and_more.py delete mode 100644 seminar/models/personalni.py diff --git a/seminar/migrations/0120_remove_osoba_user_remove_prijemce_osoba_and_more.py b/seminar/migrations/0120_remove_osoba_user_remove_prijemce_osoba_and_more.py new file mode 100644 index 00000000..6e4f4612 --- /dev/null +++ b/seminar/migrations/0120_remove_osoba_user_remove_prijemce_osoba_and_more.py @@ -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', + ), + ] diff --git a/seminar/models/personalni.py b/seminar/models/personalni.py deleted file mode 100644 index 5286e1be..00000000 --- a/seminar/models/personalni.py +++ /dev/null @@ -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