# -*- coding: utf-8 -*- import os import random from django.db import models from django.contrib import auth from django.utils import timezone from django.conf import settings from django.utils.encoding import python_2_unicode_compatible from django.utils.encoding import force_unicode from django.utils.text import slugify from django.core.urlresolvers import reverse from django.core.cache import cache from django.core.exceptions import ObjectDoesNotExist from django.utils.text import get_valid_filename from imagekit.models import ImageSpecField, ProcessedImageField from imagekit.processors import ResizeToFit, Transpose from django_countries.fields import CountryField from solo.models import SingletonModel from taggit.managers import TaggableManager from reversion import revisions as reversion from seminar.utils import roman from unidecode import unidecode class SeminarModelBase(models.Model): class Meta: abstract = True def verejne(self): return False def get_absolute_url(self): return self.verejne_url() # TODO "absolute" def admin_url(self): model_name = self.__class__.__name__.lower() return reverse('admin:seminar_%s_change'%(model_name, ), args=(self.id, )) def verejne_url(self): return None # # Mělo by být částečně vytaženo z Aesopa # viz https://ovvp.mff.cuni.cz/wiki/aesop/export-skol. # @reversion.register(ignore_duplicate_revisions=True) @python_2_unicode_compatible class Skola(SeminarModelBase): class Meta: db_table = 'seminar_skoly' verbose_name = u'Škola' verbose_name_plural = u'Školy' ordering = ['mesto', 'nazev'] # Interní ID id = models.AutoField(primary_key = True) # Aesopi ID "izo:..." nebo "aesop:..." # NULL znamená v exportu do aesopa "ufo" aesop_id = models.CharField(u'Aesop ID', max_length=32, blank=True, default='', help_text=u'Aesopi ID typu "izo:..." nebo "aesop:..."') # Staré (do 2015) MAMOPER.MM_RIESITELIA.ID z DAKOSu -- jen u importovaných záznamů import_dakos_id = models.CharField(u'importované DKSROOT.V_SKOLA.ID', max_length=32, blank=True, default='', help_text=u'DKSROOT.V_SKOLA.ID z DAKOS importu, jen historický význam') # IZO školy (jen české školy) izo = models.CharField(u'IZO', max_length=32, blank=True, help_text=u'IZO školy (jen české školy)') # Celý název školy nazev = models.CharField(u'název', max_length=256, help_text=u'Celý název školy') # Zkraceny nazev pro zobrazení ve výsledkovce, volitelné. # Není v Aesopovi, musíme vytvářet sami. kratky_nazev = models.CharField(u'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(u'ulice', max_length=256) mesto = models.CharField(u'město', max_length=256) psc = models.CharField(u'PSČ', max_length=32) # ISO 3166-1 dvojznakovy kod zeme velkym pismem (CZ, SK) # Ekvivalentní s CharField(max_length=2, default='CZ', ...) stat = CountryField(u'stát', default='CZ', help_text=u'ISO 3166-1 kód země velkými písmeny (CZ, SK, ...)') # Jaké vzdělání škpla poskytuje? je_zs = models.BooleanField(u'základní stupeň', default=True) je_ss = models.BooleanField(u'střední stupeň', default=True) poznamka = models.TextField(u'neveřejná poznámka', blank=True, help_text=u'Neveřejná poznámka ke škole (plain text)') def __str__(self): return force_unicode(u'%s, %s, %s' % (self.nazev, self.ulice, self.mesto)) @reversion.register(ignore_duplicate_revisions=True) @python_2_unicode_compatible class Resitel(SeminarModelBase): class Meta: db_table = 'seminar_resitele' verbose_name = u'Řešitel' verbose_name_plural = u'Řešitelé' ordering = ['prijmeni', 'jmeno'] # Interní ID id = models.AutoField(primary_key = True) jmeno = models.CharField(u'jméno', max_length=256) prijmeni = models.CharField(u'příjmení', max_length=256) # User, pokud má na webu účet user = models.OneToOneField(settings.AUTH_USER_MODEL, blank=True, null=True, verbose_name=u'uživatel') # Pohlaví. Že ho neznáme se snad nestane (a ušetří to práci při programování) pohlavi_muz = models.BooleanField(u'pohlaví (muž)', default=False) skola = models.ForeignKey(Skola, blank=True, null=True, verbose_name=u'škola') # Očekávaný rok maturity a vyřazení z aktivních řešitelů rok_maturity = models.IntegerField(u'rok maturity', blank=True, null=True) email = models.EmailField(u'e-mail', max_length=256, blank=True, default='') telefon = models.CharField(u'telefon', max_length=256, blank=True, default='') datum_narozeni = models.DateField(u'datum narození', blank=True, null=True) # NULL dokud nedali souhlas datum_souhlasu_udaje = models.DateField(u'datum souhlasu (údaje)', blank=True, null=True, help_text=u'Datum souhlasu se zpracováním osobních údajů') # NULL dokud nedali souhlas datum_souhlasu_zasilani = models.DateField(u'datum souhlasu (spam)', blank=True, null=True, help_text=u'Datum souhlasu se zasíláním MFF materiálů') # Alespoň odhad (rok či i měsíc) datum_prihlaseni = models.DateField(u'datum přihlášení', default=timezone.now) ZASILAT_DOMU = 'domu' ZASILAT_DO_SKOLY = 'do_skoly' ZASILAT_NIKAM = 'nikam' ZASILAT_CHOICES = [ (ZASILAT_DOMU, u'Domů'), (ZASILAT_DO_SKOLY, u'Do školy'), (ZASILAT_NIKAM, u'Nikam'), ] zasilat = models.CharField(u'kam zasílat', max_length=32, choices=ZASILAT_CHOICES, blank=False, default=ZASILAT_DOMU) # Ulice může být i jen číslo ulice = models.CharField(u'ulice', max_length=256, blank=True, default='') mesto = models.CharField(u'město', max_length=256, blank=True, default='') psc = models.CharField(u'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(u'stát', default='CZ', help_text=u'ISO 3166-1 kód země velkými písmeny (CZ, SK, ...)') poznamka = models.TextField(u'neveřejná poznámka', blank=True, help_text=u'Neveřejná poznámka k řešiteli (plain text)') # Staré (do 2015) MAMOPER.MM_RIESITELIA.ID z DAKOS -- jen u importovaných záznamů import_mamoper_id = models.CharField(u'importované MM_RIESITELIA.ID', max_length=32, blank=True, default='', help_text=u'MAMOPER.MM_RIESITELIA.ID z DAKOS importu, jen historický význam') def plne_jmeno(self): return force_unicode(u'%s %s' % (self.jmeno, self.prijmeni)) def inicial_krestni(self): return force_unicode(u'%s.' % (self.jmeno[0])) def __str__(self): return force_unicode(self.plne_jmeno()) def export_row(self): "Slovnik pro pouziti v OVVP exportu" return { 'id': self.id, 'name': self.jmeno, 'surname': self.prijmeni, 'gender': 'M' if self.pohlavi_muz else 'F', 'born': self.datum_narozeni.isoformat() if self.datum_narozeni else '', 'email': self.email, 'end-year': self.rok_maturity or '', # TODO(gavento): Adresa skoly, kdyz preferuje zasilani tam? 'street': self.ulice, 'town': self.mesto, 'postcode': self.psc, 'country': self.stat, 'spam-flag': 'Y' if self.datum_souhlasu_zasilani else '', 'spam-date': self.datum_souhlasu_zasilani.isoformat() if self.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 get_titul(self, celkove_body): "Vrati titul podle zadaneho poctu bodu." if celkove_body < 10: return '' elif celkove_body < 20: return 'Bc.' elif celkove_body < 50: return 'Mgr.' elif celkove_body < 100: return 'Dr.' elif celkove_body < 200: return 'Doc.' elif celkove_body < 500: return 'Prof.' else: return 'Akad.' @reversion.register(ignore_duplicate_revisions=True) @python_2_unicode_compatible class Rocnik(SeminarModelBase): class Meta: db_table = 'seminar_rocniky' verbose_name = u'Ročník' verbose_name_plural = u'Ročníky' ordering = ['-rocnik'] # Interní ID id = models.AutoField(primary_key = True) prvni_rok = models.IntegerField(u'první rok', db_index=True, unique=True) rocnik = models.IntegerField(u'číslo ročníku', db_index=True, unique=True) exportovat = models.BooleanField(u'export do AESOPa', db_column='exportovat', default=False, help_text=u'Exportuje se jen podle tohoto flagu (ne veřejnosti), a to jen čísla s veřejnou výsledkovkou') def __str__(self): return force_unicode(u'%s (%d/%d)' % (self.rocnik, self.prvni_rok, self.prvni_rok+1)) def roman(self): return force_unicode(roman(int(self.rocnik))) def verejne(self): return len(self.verejna_cisla()) > 0 verejne.boolean = True verejne.short_description = u'Veřejný (jen dle čísel)' def verejna_cisla(self): vc = [c for c in self.cisla.all() if c.verejne()] vc.sort(key=lambda c: c.cislo) return vc def posledni_verejne_cislo(self): vc = self.verejna_cisla() return vc[-1] if vc else None def verejne_vysledkovky_cisla(self): vc = list(self.cisla.filter(verejna_vysledkovka=True)) vc.sort(key=lambda c: c.cislo) return vc def posledni_zverejnena_vysledkovka_cislo(self): vc = self.verejne_vysledkovky_cisla() return vc[-1] if vc else None def druhy_rok(self): return self.prvni_rok + 1 def verejne_url(self): return reverse('seminar_rocnik', kwargs={'rocnik': self.rocnik}) @classmethod def cached_rocnik(cls, r_id): name = 'rocnik_%s' % (r_id, ) c = cache.get(name) if c is None: c = cls.objects.get(id=r_id) cache.set(name, c, 300) return c def cislo_pdf_filename(self, filename): rocnik = str(self.rocnik.rocnik) return os.path.join('cislo', 'pdf', rocnik, '{}-{}.pdf'.format(rocnik, self.cislo)) @reversion.register(ignore_duplicate_revisions=True) @python_2_unicode_compatible class Cislo(SeminarModelBase): class Meta: db_table = 'seminar_cisla' verbose_name = u'Číslo' verbose_name_plural = u'Čísla' ordering = ['-rocnik__rocnik', '-cislo'] # Interní ID id = models.AutoField(primary_key = True) rocnik = models.ForeignKey(Rocnik, verbose_name=u'ročník', related_name='cisla', db_index=True) cislo = models.CharField(u'název čísla', max_length=32, db_index=True, help_text=u'Většinou jen "1", vyjímečně "7-8", lexikograficky určuje pořadí v ročníku!') datum_vydani = models.DateField(u'datum vydání', blank=True, null=True, help_text=u'Datum vydání finální verze') datum_deadline = models.DateField(u'datum deadline', blank=True, null=True, help_text=u'Datum pro příjem řešení úloh zadaných v tomto čísle') datum_deadline_soustredeni = models.DateField( u'datum deadline soustředění', blank=True, null=True, help_text=u'Datum pro příjem řešení pro účast na soustředění') verejne_db = models.BooleanField(u'číslo zveřejněno', db_column='verejne', default=False) verejna_vysledkovka = models.BooleanField(u'zveřejněna výsledkovka', default=False, help_text=u'Je-li false u veřejného čísla, není výsledkovka zatím veřejná.') poznamka = models.TextField(u'neveřejná poznámka', blank=True, help_text=u'Neveřejná poznámka k číslu (plain text)') pdf = models.FileField(u'pdf', upload_to=cislo_pdf_filename, null=True, blank=True, help_text=u'Pdf čísla, které si mohou řešitelé stáhnout') FAZE_ADMIN = u'admin' FAZE_TEX = u'tex' FAZE_NAHRANO = u'nahrano' FAZE_CHOICES = [ (FAZE_ADMIN, u'Úpravy na webu'), (FAZE_TEX, u'Úpravy v TeXu'), (FAZE_NAHRANO, u'Nahráno na web'), ] faze = models.CharField( u'Fáze vytváření obsahu', max_length=32, default=FAZE_ADMIN, choices=FAZE_CHOICES, help_text=( u'Během fáze "{}" se obsah čísla vytváří (a případně ' u'komentuje) ve webovém rozhraní. Během fáze "{}" už obsah ve ' u'webovém rozhraní editovat nelze a návrhy na úpravy se píší ' u'do korekturovátka a zanášejí do gitu. Z něj se pak vygeneruje ' u'verze pro web a číslo se přepne do fáze "{}", což jen znamená, ' u'že už nejde automaticky stáhnout obsah pro založení čísla v ' u'TeXu.' ).format(FAZE_CHOICES[0][1], FAZE_CHOICES[1][1], FAZE_CHOICES[2][1]) ) def kod(self): return u'%s.%s' % (self.rocnik.rocnik, self.cislo) kod.short_description = u'Kód čísla' def __str__(self): # Potenciální DB HOG, pokud by se ročník necachoval r = Rocnik.cached_rocnik(self.rocnik_id) return force_unicode(u'%s.%s' % (r.rocnik, self.cislo, )) def verejne(self): return self.verejne_db verejne.boolean = True def verejne_url(self): return reverse('seminar_cislo', kwargs={'rocnik': self.rocnik.rocnik, 'cislo': self.cislo}) def nasledujici(self): u"Vrací None, pokud je toto poslední" return self.relativni_v_rocniku(1) def predchozi(self): u"Vrací None, pokud je toto první" return self.relativni_v_rocniku(-1) def relativni_v_rocniku(self, rel_index): u"Číslo o `index` dále v ročníku. None pokud neexistuje." cs = self.rocnik.cisla.order_by('cislo').all() i = list(cs).index(self) + rel_index if (i < 0) or (i >= len(cs)): return None return cs[i] @classmethod def get(cls, rocnik, cislo): try: r = Rocnik.objects.get(rocnik=rocnik) c = r.cisla.get(cislo=cislo) except ObjectDoesNotExist: return None return c @reversion.register(ignore_duplicate_revisions=True) @python_2_unicode_compatible class Problem(SeminarModelBase): class Meta: db_table = 'seminar_problemy' verbose_name = u'Problém' verbose_name_plural = u'Problémy' ordering = ['nazev'] # Interní ID id = models.AutoField(primary_key = True) # Název nazev = models.CharField(u'název', max_length=256) TYP_ULOHA = 'uloha' TYP_TEMA = 'tema' TYP_SERIAL = 'serial' TYP_KONFERA = 'konfera' TYP_ORG_CLANEK = 'org-clanek' TYP_RES_CLANEK = 'res-clanek' TYP_CHOICES = [ (TYP_ULOHA, u'Úloha'), (TYP_TEMA, u'Téma'), (TYP_SERIAL, u'Seriál'), (TYP_KONFERA, u'Konfera'), (TYP_ORG_CLANEK, u'Organizátorský článek'), (TYP_RES_CLANEK, u'Řešitelský článek'), ] typ = models.CharField(u'typ problému', max_length=32, choices=TYP_CHOICES, blank=False, default=TYP_ULOHA) STAV_NAVRH = 'navrh' STAV_ZADANY = 'zadany' STAV_SMAZANY = 'smazany' STAV_CHOICES = [ (STAV_NAVRH, u'Návrh'), (STAV_ZADANY, u'Zadaný'), (STAV_SMAZANY, u'Smazaný'), ] stav = models.CharField(u'stav problému', max_length=32, choices=STAV_CHOICES, blank=False, default=STAV_NAVRH) zamereni = TaggableManager(verbose_name=u'zaměření', help_text='Zaměření M/F/I/O problému, příp. další tagy', blank=True) text_org = models.TextField(u'org poznámky (HTML)', blank=True, help_text=u'Neveřejný návrh úlohy, návrh řešení, text zadání, poznámky ...') text_zadani = models.TextField(u'veřejné zadání (HTML)', blank=True, help_text=u'Veřejný text zadání (HTML)') text_reseni = models.TextField(u'veřejné řešení (HTML)', blank=True, help_text=u'Veřejný text řešení (HTML, u témat i příspěvky a komentáře)') autor = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=u'autor problému', related_name='autor_uloh', null=True, blank=True) opravovatel = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=u'opravovatel', null=True, blank=True, related_name='opravovatel_uloh') kod = models.CharField(u'lokální kód', max_length=32, blank=True, default='', help_text=u'Číslo/kód úlohy v čísle nebo kód tématu/článku/seriálu v ročníku') cislo_zadani = models.ForeignKey(Cislo, verbose_name=u'číslo zadání', blank=True, null=True, related_name=u'zadane_problemy') cislo_reseni = models.ForeignKey(Cislo, verbose_name=u'číslo řešení', blank=True, null=True, related_name=u'resene_problemy', help_text=u'Číslo s řešením úlohy, jen pro úlohy') body = models.DecimalField(max_digits=8, decimal_places=1, verbose_name=u'maximum bodů', blank=True, null=True) timestamp = models.DateTimeField(u'vytvořeno', default=timezone.now, blank=True, editable=False) # Staré (do 2014) ID problému z DAKOSU -- jen u importovaných záznamů import_dakos_id = models.CharField(u'importované ID s typem', max_length=32, blank=True, default='', help_text=(u'ID z importu z DAKOSU s prefixem podle původu: "AZAD:xxx (MAMOPER.MM_AZAD), "' + u'"DOZ:xxx" (MAMOPER.MM_DOZ), "ZAD:rocnik.cislo.uloha.typ" (MAMOPER.MM_ZADANIA), "ULOHA:xxx" (MAMOPER.MM_ULOHY)')) def __str__(self): return force_unicode(u'%s' % (self.nazev, )) def kod_v_rocniku(self): if self.stav == 'zadany': if self.typ == self.TYP_ULOHA: return force_unicode(u"%s.u%s" % (self.cislo_zadani.cislo, self.kod,)) if self.typ == self.TYP_TEMA: return force_unicode(u"t%s" % (self.kod,)) else: return force_unicode(self.kod) return u'' def nazev_typu(self): return dict(self.TYP_CHOICES)[self.typ] def verejne(self): return (self.cislo_zadani and self.cislo_zadani.verejne()) verejne.boolean = True def verejne_url(self): return reverse('seminar_problem', kwargs={'pk': self.id}) def admin_url(self): if self.stav == Problem.STAV_ZADANY: return reverse('admin:seminar_problemzadany_change', args=(self.id, )) else: return reverse('admin:seminar_problemnavrh_change', args=(self.id, )) def body_v_zavorce(self): """Vrať string s body v závorce jsou-li u problému vyplněné, jinak '' Je-li desetinná část nulová, nezobrazuj ji. """ pocet_bodu = None if self.body: b = self.body pocet_bodu = int(b) if int(b) == b else b return u"({}\u2009b)".format(pocet_bodu) if self.body else "" @reversion.register(ignore_duplicate_revisions=True) @python_2_unicode_compatible class Reseni(SeminarModelBase): class Meta: db_table = 'seminar_reseni' verbose_name = u'Řešení' verbose_name_plural = u'Řešení' ordering = ['problem_id', 'resitel__prijmeni', 'resitel__jmeno',] # Interní ID id = models.AutoField(primary_key = True) problem = models.ForeignKey(Problem, verbose_name=u'problém', related_name='reseni') resitel = models.ForeignKey(Resitel, verbose_name=u'řešitel', related_name='reseni') body = models.DecimalField(max_digits=8, decimal_places=1, verbose_name=u'body', blank=True, null=True) cislo_body = models.ForeignKey(Cislo, verbose_name=u'číslo pro body', related_name='bodovana_reseni', blank=True, null=True) timestamp = models.DateTimeField(u'vytvořeno', default=timezone.now, blank=True, editable=False) FORMA_PAPIR = 'papir' FORMA_EMAIL = 'email' FORMA_UPLOAD = 'upload' FORMA_CHOICES = [ (FORMA_PAPIR, u'Papírové řešení'), (FORMA_EMAIL, u'Emailem'), (FORMA_UPLOAD, u'Upload přes web'), ] forma = models.CharField(u'forma řešení', max_length=16, choices=FORMA_CHOICES, blank=False, default=FORMA_EMAIL) poznamka = models.TextField(u'neveřejná poznámka', blank=True, help_text=u'Neveřejná poznámka k řešení (plain text)') def __str__(self): return force_unicode(u"%s: %s (%sb)" % (self.resitel.plne_jmeno(), self.problem.nazev, self.body)) # NOTE: Potenciální DB HOG (bez select_related) def save(self, *args, **kwargs): if ((self.cislo_body is None) and (self.problem.cislo_reseni) and (self.problem.typ == Problem.TYP_ULOHA)): self.cislo_body = self.problem.cislo_reseni super(Reseni, self).save(*args, **kwargs) def aux_generate_filename(self, filename): """Pomocná funkce generující ošetřený název souboru v adresáři s datem""" clean = get_valid_filename( unidecode(filename.replace('/', '-').replace('\0', '')) ) datedir = timezone.now().strftime('%Y-%m') fname = "%s_%s" % ( timezone.now().strftime('%Y-%m-%d-%H:%M'), clean) return os.path.join(datedir, fname) # Django neumí jednoduše serializovat partial nebo třídu s __call__ # (https://docs.djangoproject.com/en/1.8/topics/migrations/), # neprojdou pak migrace. Takže rozlišení funkcí generujících názvy souboru # podle adresáře řešíme takto. def generate_filename_konfera(self, filename): return os.path.join( settings.SEMINAR_KONFERY_DIR, aux_generate_filename(self, filename) ) def generate_filename(self, filename): return os.path.join( settings.SEMINAR_RESENI_DIR, aux_generate_filename(self, filename) ) @reversion.register(ignore_duplicate_revisions=True) @python_2_unicode_compatible class PrilohaReseni(SeminarModelBase): class Meta: db_table = 'seminar_priloha_reseni' verbose_name = u'Příloha řešení' verbose_name_plural = u'Přílohy řešení' ordering = ['reseni', 'timestamp'] # Interní ID id = models.AutoField(primary_key = True) reseni = models.ForeignKey(Reseni, verbose_name=u'řešení', related_name='prilohy') timestamp = models.DateTimeField(u'vytvořeno', default=timezone.now, blank=True, editable=False) soubor = models.FileField(u'soubor', upload_to = generate_filename) poznamka = models.TextField(u'neveřejná poznámka', blank=True, help_text=u'Neveřejná poznámka k příloze řešení (plain text), např. o původu') def __str__(self): return force_unicode(self.soubor) @python_2_unicode_compatible class Pohadka(SeminarModelBase): u"""Kus pohádky před/za úlohou v čísle""" class Meta: db_table = 'seminar_pohadky' verbose_name = u'Pohádka' verbose_name_plural = u'Pohádky' ordering = ['uloha__cislo_zadani', 'uloha__kod', '-pred'] # Interní ID id = models.AutoField(primary_key=True) text = models.TextField(u'Text pohádky') uloha = models.ForeignKey( Problem, verbose_name=u'Úloha', related_name='pohadky' ) # Kusů pohádky je v čísle obvykle o 1 více, než úloh. Jeden bude za úlohou # místo před ní. pred = models.BooleanField(u'Před úlohou', default=True) autor = models.ForeignKey( settings.AUTH_USER_MODEL, verbose_name="Autor pohádky", # Při nahrávání z TeXu není vyplnění vyžadováno, v adminu je null=True, blank=False ) timestamp = models.DateTimeField( u'Vytvořeno', default=timezone.now, blank=True, editable=False ) def __str__(self): uryvek = self.text if len(self.text) < (50-3) else self.text[:50]+"..." return force_unicode(uryvek) @reversion.register(ignore_duplicate_revisions=True) class Prispevek(SeminarModelBase): problem = models.ForeignKey(Problem, verbose_name='Problém') # TODO autokompleet nazev = models.CharField('Název', max_length=200) reseni = models.OneToOneField(Reseni, verbose_name='Řešení', blank = True, null = True) text_org = models.TextField('Orgovský text', blank = True, null = True) text_resitel = models.TextField('Řešitelský text', blank = True, null = True) zverejnit = models.BooleanField('Zveřejnit?') class Meta: verbose_name = 'Příspěvek k problému' verbose_name_plural = 'Příspěvky k problémům' def __unicode__(self): if self.reseni: return force_unicode(self.nazev) + ' (' + \ force_unicode(self.reseni.resitel) + ') ' else: return force_unicode(self.nazev) + ' ' @reversion.register(ignore_duplicate_revisions=True) @python_2_unicode_compatible class Organizator(models.Model): user = models.OneToOneField(settings.AUTH_USER_MODEL, verbose_name='Osoba', help_text = 'Vyber účet spřažený s organizátorem.') prezdivka = models.CharField('Přezdívka', max_length = 32, null = True, blank = True) organizuje_od_roku = models.IntegerField('Organizuje od roku', null = True, blank = True) organizuje_do_roku = models.IntegerField('Organizuje do roku', null = True, blank = 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) foto = ProcessedImageField(verbose_name='Fotografie organizátora', upload_to='image_organizatori/velke/%Y/', null = True, blank = True, help_text = 'Vlož fotografii organizátora 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}) def __str__(self): if self.prezdivka: return u"%s '%s' %s" % (self.user.first_name, self.prezdivka, self.user.last_name) else: return u"%s %s" % (self.user.first_name, self.user.last_name) class Meta: verbose_name = 'Organizátor' verbose_name_plural = 'Organizátoři' @reversion.register(ignore_duplicate_revisions=True) @python_2_unicode_compatible class Soustredeni(SeminarModelBase): class Meta: db_table = 'seminar_soustredeni' verbose_name = u'Soustředění' verbose_name_plural = u'Soustředění' ordering = ['-rocnik__rocnik', '-datum_zacatku'] # Interní ID id = models.AutoField(primary_key = True) rocnik = models.ForeignKey(Rocnik, verbose_name=u'ročník', related_name='soustredeni') datum_zacatku = models.DateField(u'datum začátku', blank=True, null=True, help_text=u'První den soustředění') datum_konce = models.DateField(u'datum konce', blank=True, null=True, help_text=u'Poslední den soustředění') verejne_db = models.BooleanField(u'soustředění zveřejněno', db_column='verejne', default=False) misto = models.CharField(u'místo soustředění', max_length=256, blank=True, default='', help_text=u'Místo (název obce, volitelně též objektu') ucastnici = models.ManyToManyField(Resitel, verbose_name=u'účastníci soustředění', help_text=u'Seznam účastníků soustředění', through='Soustredeni_Ucastnici') organizatori = models.ManyToManyField(Organizator, verbose_name=u'Organizátoři soustředění', help_text=u'Seznam organizátorů soustředění', through='Soustredeni_Organizatori') text = models.TextField(u'text k soustředění (HTML)', blank=True, default='') TYP_JARNI = 'jarni' TYP_PODZIMNI = 'podzimni' TYP_VIKEND = 'vikend' TYP_CHOICES = [ (TYP_JARNI, u'Jarní soustředění'), (TYP_PODZIMNI, u'Podzimní soustředění'), (TYP_VIKEND, u'Víkendový sraz'), ] typ = models.CharField(u'typ akce', max_length=16, choices=TYP_CHOICES, blank=False, default=TYP_PODZIMNI) exportovat = models.BooleanField(u'export do AESOPa', db_column='exportovat', default=False, help_text=u'Exportuje se jen podle tohoto flagu (ne veřejnosti)') def __str__(self): return force_unicode(u'%s (%s)' % (self.misto, self.datum_zacatku)) def verejne(self): return self.verejne_db verejne.boolean = True def verejne_url(self): #return reverse('seminar_soustredeni', kwargs={'pk': self.id}) return reverse('seminar_seznam_soustredeni') @reversion.register(ignore_duplicate_revisions=True) @python_2_unicode_compatible class Soustredeni_Ucastnici(models.Model): class Meta: db_table = 'seminar_soustredeni_ucastnici' verbose_name = u'Účast na soustředění' verbose_name_plural = u'Účasti na soustředění' ordering = ['soustredeni', 'resitel'] # Interní ID id = models.AutoField(primary_key = True) resitel = models.ForeignKey(Resitel, verbose_name=u'řešitel') soustredeni = models.ForeignKey(Soustredeni, verbose_name=u'soustředění') poznamka = models.TextField(u'neveřejná poznámka', blank=True, help_text=u'Neveřejná poznámka k účasti (plain text)') def __str__(self): return force_unicode(u'%s na %s' % (self.resitel, self.soustredeni, )) # NOTE: Poteciální DB HOG bez select_related @reversion.register(ignore_duplicate_revisions=True) @python_2_unicode_compatible class Soustredeni_Organizatori(models.Model): class Meta: db_table = 'seminar_soustredeni_organizatori' verbose_name = u'Účast organizátorů na soustředění' verbose_name_plural = u'Účasti organizátorů na soustředění' ordering = ['soustredeni', 'organizator'] # Interní ID id = models.AutoField(primary_key = True) organizator = models.ForeignKey(Organizator, verbose_name=u'organizátor') soustredeni = models.ForeignKey(Soustredeni, verbose_name=u'soustředění') poznamka = models.TextField(u'neveřejná poznámka', blank=True, help_text=u'Neveřejná poznámka k účasti organizátora (plain text)') def __str__(self): return force_unicode(u'%s na %s' % (self.organizator, self.soustredeni, )) # NOTE: Poteciální DB HOG bez select_related @reversion.register(ignore_duplicate_revisions=True) @python_2_unicode_compatible class Konfera(models.Model): class Meta: db_table = 'seminar_konfera' verbose_name = u'Konfera' verbose_name_plural = u'Konfery' # Interní ID id = models.AutoField(primary_key = True) nazev = models.CharField(u'název konfery', max_length=40, help_text = u'Název konfery') popis = models.TextField(u'popis konfery', blank=True, help_text=u'Popis konfery k zobrazení na webu') abstrakt = models.TextField(u'abstrakt', blank=True, help_text=u'Abstrakt konfery tak, jak byl uveden ve sborníku') organizator = models.ForeignKey(Organizator, verbose_name=u'organizátor', related_name='konfery', on_delete = models.SET_NULL, null=True) ucastnici = models.ManyToManyField(Resitel, verbose_name=u'účastníci konfery', help_text=u'Seznam účastníků konfery', through='Konfery_Ucastnici') soustredeni = models.ForeignKey(Soustredeni, verbose_name=u'soustředění', related_name='konfery', on_delete = models.SET_NULL, null=True) org_poznamka = models.TextField(u'neveřejná poznámka', blank=True, help_text=u'Neveřejná poznámka ke konfeře(plain text)') prispevek = models.ForeignKey(Problem, verbose_name=u'příspěvek do čísla', related_name='konfery', help_text=u'Účastnický přípěvek o konfeře',on_delete = models.SET_NULL, null=True, blank=True) TYP_VELETRH = 'veletrh' TYP_PREZENTACE = 'prezentace' TYP_CHOICES = [ (TYP_VELETRH, u'Veletrh (postery)'), (TYP_PREZENTACE, u'Prezentace (přednáška)'), ] typ_prezentace = models.CharField(u'typ prezentace', max_length=16, choices=TYP_CHOICES, blank=False, default=TYP_VELETRH) prezentace = models.FileField(u'prezentace',help_text = u'Prezentace nebo fotka posteru', upload_to = generate_filename_konfera, blank=True) materialy = models.FileField(u'materialy',help_text = u'Další materiály ke konfeře zabalené do jednoho souboru', upload_to = generate_filename_konfera, blank=True) def __str__(self): return force_unicode(u"%s: (%s)" % (self.nazev, self.soustredeni)) @reversion.register(ignore_duplicate_revisions=True) @python_2_unicode_compatible class Konfery_Ucastnici(models.Model): class Meta: db_table = 'seminar_konfery_ucastnici' verbose_name = u'Účast na konfeře' verbose_name_plural = u'Účasti na konfeře' ordering = ['konfera', 'resitel'] # Interní ID id = models.AutoField(primary_key = True) resitel = models.ForeignKey(Resitel, verbose_name=u'řešitel') konfera = models.ForeignKey(Konfera, verbose_name=u'konfera') poznamka = models.TextField(u'neveřejná poznámka', blank=True, help_text=u'Neveřejná poznámka k účasti (plain text)') def __str__(self): return force_unicode(u'%s na %s' % (self.resitel, self.konfera, )) # NOTE: Poteciální DB HOG bez select_related @python_2_unicode_compatible class VysledkyBase(SeminarModelBase): class Meta: verbose_name = u'Řádek výsledkovky' verbose_name_plural = u'Řádky výsledkovky' ordering = ['body'] abstract = True managed = False dummy_id = models.CharField(u'dummy ID pro view', max_length=32, primary_key=True, db_column='id') cislo = models.ForeignKey(Cislo, verbose_name=u'číslo pro body', db_column='cislo_id', on_delete=models.DO_NOTHING) resitel = models.ForeignKey(Resitel, verbose_name=u'řešitel', db_column='resitel_id', on_delete=models.DO_NOTHING) body = models.DecimalField(max_digits=8, decimal_places=1, db_column='body', verbose_name=u'body za číslo') def __str__(self): return force_unicode(u"%s: %sb (%s)" % (self.resitel.plne_jmeno(), self.body, str(self.cislo))) # NOTE: DB zatez pri vypisu (ale nepouzivany) class VysledkyZaCislo(VysledkyBase): class Meta: db_table = 'seminar_body_za_cislo' abstract = False managed = False class VysledkyKCisluZaRocnik(VysledkyBase): class Meta: db_table = 'seminar_body_k_cislu_rocnik' abstract = False managed = False # body = models.DecimalField(max_digits=8, decimal_places=1, db_column='body', # verbose_name=u'body do čísla (za ročník)') class VysledkyKCisluOdjakziva(VysledkyBase): class Meta: db_table = 'seminar_body_k_cislu_odjakziva' abstract = False managed = False # body = models.DecimalField(max_digits=8, decimal_places=1, db_column='body', # verbose_name=u'body do čísla (i minulé ročníky)') @python_2_unicode_compatible class VysledkyCelkemKCislu(VysledkyBase): class Meta: db_table = 'seminar_body_celkem_k_cislu' abstract = False managed = False body_celkem = models.DecimalField(max_digits=8, decimal_places=1, db_column='body_celkem', verbose_name=u'body celkem do čísla včetně minulých ročníků') def __str__(self): # NOTE: DB HOG (ale nepouzivany) return force_unicode(u"%s: %sb / %sb (do %s)" % (self.resitel.plne_jmeno(), self.body, self.body_celkem, str(self.cislo))) #mozna potreba upravit @reversion.register(ignore_duplicate_revisions=True) @python_2_unicode_compatible class Nastaveni(SingletonModel): class Meta: db_table = 'seminar_nastaveni' verbose_name = u'Nastavení semináře' aktualni_rocnik = models.ForeignKey(Rocnik, verbose_name=u'aktuální ročník', null=False) aktualni_cislo = models.ForeignKey(Cislo, verbose_name=u'poslední vydané číslo', null=False) def __str__(self): return u'Nastavení semináře' def admin_url(self): return reverse('admin:seminar_nastaveni_change', args=(self.id, )) def verejne(self): return False @reversion.register(ignore_duplicate_revisions=True) @python_2_unicode_compatible class Novinky(models.Model): datum = models.DateField(auto_now_add=True) text = models.TextField('Text novinky', blank=True, null=True) obrazek = models.ImageField('Obrázek', upload_to='image_novinky/%Y/%m/%d/', null=True, blank=True) obrazek_maly = ImageSpecField(source='obrazek', processors=[ ResizeToFit(350, 200, upscale=False) ], options={'quality': 95}) autor = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='Autor novinky') zverejneno = models.BooleanField('Zveřejněno', default="False") def __str__(self): return '[' + str(self.datum) + '] ' + self.text[0:50] class Meta: verbose_name = 'Novinka' verbose_name_plural = 'Novinky'