You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1307 lines
40 KiB

# -*- 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_text
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):
6 years ago
class Meta:
abstract = True
6 years ago
def verejne(self):
return False
6 years ago
def get_absolute_url(self):
return self.verejne_url() # TODO "absolute"
6 years ago
def admin_url(self):
model_name = self.__class__.__name__.lower()
return reverse('admin:seminar_{}_change'.format(model_name), args=(self.id, ))
6 years ago
def verejne_url(self):
return None
@reversion.register(ignore_duplicates=True)
class Osoba(SeminarModelBase):
6 years ago
class Meta:
db_table = 'seminar_osoby'
verbose_name = 'Osoba'
verbose_name_plural = 'Osoby'
ordering = ['prijmeni','jmeno']
6 years ago
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', max_length=256)
6 years ago
# User, pokud má na webu účet
user = models.OneToOneField(settings.AUTH_USER_MODEL, blank=True, null=True,
verbose_name='uživatel')
6 years ago
# 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='')
6 years ago
telefon = models.CharField('telefon', max_length=256, blank=True, default='')
6 years ago
datum_narozeni = models.DateField('datum narození', blank=True, null=True)
6 years ago
# 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ů')
6 years ago
# 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ů')
6 years ago
# Alespoň odhad (rok či i měsíc)
datum_registrace = models.DateField('datum registrace do semináře', default=timezone.now)
6 years ago
# 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='')
6 years ago
# 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, ...)')
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})
6 years ago
def plne_jmeno(self):
return '{} {}'.format(self.jmeno, self.prijmeni)
6 years ago
def inicial_krestni(self):
jmena = self.jmeno.split()
return " ".join(['{}.'.format(jmeno[0]) for jmeno in jmena])
6 years ago
def __str__(self):
return self.plne_jmeno()
#
# 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']
# 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)
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'
# 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.ForeignKey(Osoba, verbose_name='komu', blank=False, null=False,
help_text='Které osobě či na jakou adresu se mají zasílat čísla')
# FIXME: možná chceme něco jako vazbu na osobu XOR školu a počet kusů k zaslání
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']
# Interní ID
id = models.AutoField(primary_key = True)
osoba = models.OneToOneField(Osoba, blank=False, null=True, verbose_name='osoba') # FIXME opravit po prvni migraci
skola = models.ForeignKey(Skola, blank=True, null=True, verbose_name='škola')
# 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, 'Nikam'),
]
zasilat = models.CharField('kam zasílat', max_length=32, choices=ZASILAT_CHOICES, blank=False, default=ZASILAT_DOMU)
poznamka = models.TextField('neveřejná poznámka', blank=True,
help_text='Neveřejná poznámka k řešiteli (plain text)')
6 years ago
def export_row(self):
"Slovnik pro pouziti v AESOP exportu"
6 years ago
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',
6 years ago
}
6 years ago
def rocnik(self, rocnik):
"""Vrati skolni rocnik resitele pro zadany Rocnik.
Vraci '' pro neznamy rok maturity resitele, Z* pro ekvivalent ."""
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.'
def __str__():
return self.osoba.plne_jmeno()
@reversion.register(ignore_duplicates=True)
class Rocnik(SeminarModelBase):
6 years ago
class Meta:
db_table = 'seminar_rocniky'
verbose_name = 'Ročník'
verbose_name_plural = 'Ročníky'
6 years ago
ordering = ['-rocnik']
6 years ago
# Interní ID
id = models.AutoField(primary_key = True)
prvni_rok = models.IntegerField('první rok', db_index=True, unique=True)
rocnik = models.IntegerField('číslo ročníku', db_index=True, unique=True)
exportovat = models.BooleanField('export do AESOPa', db_column='exportovat', default=False,
help_text='Exportuje se jen podle tohoto flagu (ne veřejnosti), a to jen čísla s veřejnou výsledkovkou')
6 years ago
def __str__(self):
return '{} ({}/{})'.format(self.rocnik, self.prvni_rok, self.prvni_rok+1)
# Ročník v římských číslech
6 years ago
def roman(self):
return roman(int(self.rocnik))
6 years ago
def verejne(self):
return len(self.verejna_cisla()) > 0
verejne.boolean = True
verejne.short_description = 'Veřejný (jen dle čísel)'
6 years ago
def verejna_cisla(self):
vc = [c for c in self.cisla.all() if c.verejne()]
vc.sort(key=lambda c: c.cislo)
return vc
6 years ago
def posledni_verejne_cislo(self):
vc = self.verejna_cisla()
return vc[-1] if vc else None
6 years ago
def verejne_vysledkovky_cisla(self):
vc = list(self.cisla.filter(verejna_vysledkovka=True))
vc.sort(key=lambda c: c.cislo)
return vc
6 years ago
def posledni_zverejnena_vysledkovka_cislo(self):
vc = self.verejne_vysledkovky_cisla()
return vc[-1] if vc else None
6 years ago
def druhy_rok(self):
return self.prvni_rok + 1
6 years ago
def verejne_url(self):
return reverse('seminar_rocnik', kwargs={'rocnik': self.rocnik})
6 years ago
@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):
6 years ago
rocnik = str(self.rocnik.rocnik)
return os.path.join('cislo', 'pdf', rocnik, '{}-{}.pdf'.format(rocnik, self.cislo))
@reversion.register(ignore_duplicates=True)
class Cislo(SeminarModelBase):
6 years ago
class Meta:
db_table = 'seminar_cisla'
verbose_name = 'Číslo'
verbose_name_plural = 'Čísla'
6 years ago
ordering = ['-rocnik__rocnik', '-cislo']
# Interní ID
id = models.AutoField(primary_key = True)
rocnik = models.ForeignKey(Rocnik, verbose_name='ročník', related_name='cisla', db_index=True)
6 years ago
cislo = models.CharField('název čísla', max_length=32, db_index=True,
help_text='Většinou jen "1", vyjímečně "7-8", lexikograficky určuje pořadí v ročníku!')
6 years ago
datum_vydani = models.DateField('datum vydání', blank=True, null=True,
help_text='Datum vydání finální verze')
6 years ago
datum_deadline = models.DateField('datum deadline', blank=True, null=True,
help_text='Datum pro příjem řešení úloh zadaných v tomto čísle')
6 years ago
datum_deadline_soustredeni = models.DateField(
'datum deadline soustředění',
6 years ago
blank=True, null=True,
help_text='Datum pro příjem řešení pro účast na soustředění')
6 years ago
verejne_db = models.BooleanField('číslo zveřejněno',
db_column='verejne', default=False)
6 years ago
verejna_vysledkovka = models.BooleanField(
'zveřejněna výsledkovka',
6 years ago
default=False,
help_text='Je-li false u veřejného čísla,\
není výsledkovka zatím veřejná.')
6 years ago
poznamka = models.TextField('neveřejná poznámka', blank=True,
help_text='Neveřejná poznámka k číslu (plain text)')
6 years ago
pdf = models.FileField('pdf', upload_to=cislo_pdf_filename, null=True, blank=True,
help_text='Pdf čísla, které si mohou řešitelé stáhnout')
6 years ago
def kod(self):
return '%s.%s' % (self.rocnik.rocnik, self.cislo)
kod.short_description = 'Kód čísla'
6 years ago
def __str__(self):
# Potenciální DB HOG, pokud by se ročník necachoval
r = Rocnik.cached_rocnik(self.rocnik_id)
return '{}.{}'.format(r.rocnik, self.cislo)
6 years ago
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):
"Vrací None, pokud je toto poslední"
6 years ago
return self.relativni_v_rocniku(1)
def predchozi(self):
"Vrací None, pokud je toto první"
6 years ago
return self.relativni_v_rocniku(-1)
def relativni_v_rocniku(self, rel_index):
"Číslo o `index` dále v ročníku. None pokud neexistuje."
6 years ago
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_duplicates=True)
class Organizator(SeminarModelBase):
# zmena dedicnosti z models.Model na SeminarModelBase, potencialni vznik bugu
osoba = models.OneToOneField(Osoba, verbose_name='osoba', related_name='org',
help_text='osobní údaje organizátora', null=True, blank=False) #FIXME opravit po migraci
vytvoreno = models.DateTimeField(
'Vytvořeno',
default=timezone.now,
blank=True,
editable=False
)
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=u"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=u"Škola, např. MFF, VŠCHT, VUT, ... prostě aby se nemuselo psát do studuje"
"školu, ale jen obor, možnost zobrazit zvlášť")
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'
@reversion.register(ignore_duplicates=True)
class Soustredeni(SeminarModelBase):
class Meta:
db_table = 'seminar_soustredeni'
verbose_name = 'Soustředění'
verbose_name_plural = 'Soustředění'
ordering = ['-rocnik__rocnik', '-datum_zacatku']
# Interní ID
id = models.AutoField(primary_key = True)
rocnik = models.ForeignKey(Rocnik, verbose_name='ročník', related_name='soustredeni')
datum_zacatku = models.DateField('datum začátku', blank=True, null=True,
help_text='První den soustředění')
datum_konce = models.DateField('datum konce', blank=True, null=True,
help_text='Poslední den soustředění')
verejne_db = models.BooleanField('soustředění zveřejněno', db_column='verejne', default=False)
misto = models.CharField('místo soustředění', max_length=256, blank=True, default='',
help_text='Místo (název obce, volitelně též objektu')
ucastnici = models.ManyToManyField(Resitel, verbose_name='účastníci soustředění',
help_text='Seznam účastníků soustředění', through='Soustredeni_Ucastnici')
organizatori = models.ManyToManyField(Organizator,
verbose_name='Organizátoři soustředění',
help_text='Seznam organizátorů soustředění',
through='Soustredeni_Organizatori')
text = models.TextField('text k soustředění (HTML)', blank=True, default='')
TYP_JARNI = 'jarni'
TYP_PODZIMNI = 'podzimni'
TYP_VIKEND = 'vikend'
TYP_CHOICES = [
(TYP_JARNI, 'Jarní soustředění'),
(TYP_PODZIMNI, 'Podzimní soustředění'),
(TYP_VIKEND, 'Víkendový sraz'),
]
typ = models.CharField('typ akce', max_length=16, choices=TYP_CHOICES, blank=False, default=TYP_PODZIMNI)
exportovat = models.BooleanField('export do AESOPa', db_column='exportovat', default=False,
help_text='Exportuje se jen podle tohoto flagu (ne veřejnosti)')
def __str__(self):
return '{} ({})'.format(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_duplicates=True)
class Problem(SeminarModelBase):
6 years ago
class Meta:
# Není abstraktní, protože se na něj jinak nedají dělat ForeignKeys.
# TODO: Udělat to polymorfní (pomocí django-polymorphic), abychom dostali po těch vazbách přímo tu úlohu/témátko vč. fieldů, které nejsou součástí modelu Problem?
#abstract = True
db_table = 'seminar_problemy'
verbose_name = 'Problém'
verbose_name_plural = 'Problémy'
6 years ago
ordering = ['nazev']
# Interní ID
id = models.AutoField(primary_key = True)
# Název
nazev = models.CharField('název', max_length=256)
6 years ago
# Problém má podproblémy
nadproblem = models.ForeignKey('self', verbose_name='nadřazený problém',
related_name='nadproblem_%(class)s', null=True, blank=True)
6 years ago
STAV_NAVRH = 'navrh'
STAV_ZADANY = 'zadany'
STAV_VYRESENY = 'vyreseny'
6 years ago
STAV_SMAZANY = 'smazany'
STAV_CHOICES = [
(STAV_NAVRH, 'Návrh'),
(STAV_ZADANY, 'Zadaný'),
(STAV_VYRESENY, 'Vyřešený'),
(STAV_SMAZANY, 'Smazaný'),
6 years ago
]
stav = models.CharField('stav problému', max_length=32, choices=STAV_CHOICES, blank=False, default=STAV_NAVRH)
6 years ago
zamereni = TaggableManager(verbose_name='zaměření',
help_text='Zaměření M/F/I/O problému, příp. další tagy', blank=True)
6 years ago
poznamka = models.TextField('org poznámky (HTML)', blank=True,
help_text='Neveřejný návrh úlohy, návrh řešení, text zadání, poznámky ...')
6 years ago
autor = models.ForeignKey(Organizator, verbose_name='autor problému',
related_name='autor_problemu_%(class)s', null=True, blank=True)
6 years ago
garant = models.ForeignKey(Organizator, verbose_name='garant zadaného problému',
related_name='garant_problemu_%(class)s', null=True, blank=True)
6 years ago
opravovatele = models.ManyToManyField(Organizator, verbose_name='opravovatelé',
blank=True, related_name='opravovatele_%(class)s')
6 years ago
kod = models.CharField('lokální kód', max_length=32, blank=True, default='',
help_text='Číslo/kód úlohy v čísle nebo kód tématu/článku/seriálu v ročníku')
6 years ago
vytvoreno = models.DateTimeField('vytvořeno', default=timezone.now, blank=True, editable=False)
6 years ago
def __str__(self):
return self.nazev
6 years ago
# Implicitini implementace, jednotlivé dědící třídy si přepíšou
6 years ago
def kod_v_rocniku(self):
if self.stav == 'zadany':
if self.nadproblem:
return force_unicode(self.nadproblem.kod_v_rocniku()+".{}".format(self.kod))
return force_unicode(str(self.kod))
return '<Není zadaný>'
6 years ago
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, ))
# FIXME - k úloze
6 years ago
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 "({}\u2009b)".format(pocet_bodu) if self.body else ""
class Tema(Problem):
class Meta:
db_table = 'seminar_temata'
verbose_name = 'Téma'
verbose_name_plural = 'Témata'
TEMA_TEMA = 'tema'
TEMA_SERIAL = 'serial'
TEMA_CHOICES = [
(TEMA_TEMA, 'Téma'),
(TEMA_SERIAL, 'Seriál'),
]
tema_typ = models.CharField('Typ tématu', max_length=16, choices=TEMA_CHOICES,
blank=False, default=TEMA_TEMA)
rocnik = models.ForeignKey(Rocnik, verbose_name='ročník',blank=True, null=True)
def kod_v_rocniku(self):
if self.stav == 'zadany':
if self.nadproblem:
return self.nadproblem.kod_v_rocniku()+".t{}".format(self.kod)
return "t{}".format(self.kod)
return '<Není zadaný>'
class Clanek(Problem):
class Meta:
db_table = 'seminar_clanky'
verbose_name = 'Článek'
verbose_name_plural = 'Články'
cislo = models.ForeignKey(Cislo, verbose_name='číslo', blank=True, null=True)
def kod_v_rocniku(self):
if self.stav == 'zadany':
# Nemělo by být potřeba
# if self.nadproblem:
# return force_unicode(self.nadproblem.kod_v_rocniku()+".c{}".format(self.kod))
return "c{}".format(self.kod)
return '<Není zadaný>'
class Text(SeminarModelBase):
class Meta:
db_table = 'seminar_texty'
verbose_name = 'text'
verbose_name_plural = 'texty'
na_web = models.TextField('text na web', blank=True,
help_text='Text ke zveřejnění na webu')
do_cisla = models.TextField('text do čísla', blank=True,
help_text='Text ke zveřejnění v čísle')
# obrázky mají návaznost opačným směrem (vazba z druhé strany)
class Uloha(Problem):
class Meta:
db_table = 'seminar_ulohy'
verbose_name = 'Úloha'
verbose_name_plural = 'Úlohy'
zadani = models.OneToOneField(Text, verbose_name='veřejné zadání',
related_name="uloha_zadani_set", blank=True, null=True)
vzorak = models.OneToOneField(Text, verbose_name='vzorové řešení',
related_name="uloha_vzorak_set", blank=True, null=True)
cislo_zadani = models.ForeignKey(Cislo, verbose_name='číslo zadání', blank=True,
null=True, related_name='zadane_ulohy')
cislo_deadline = models.ForeignKey(Cislo, verbose_name='číslo deadlinu', blank=True,
null=True, related_name='deadlinove_ulohy')
cislo_reseni = models.ForeignKey(Cislo, verbose_name='číslo řešení', blank=True,
null=True, related_name='resene_ulohy',
help_text='Číslo s řešením úlohy, jen pro úlohy')
max_body = models.DecimalField(max_digits=8, decimal_places=1, verbose_name='maximum bodů',
blank=True, null=True)
def kod_v_rocniku(self):
if self.stav == 'zadany':
name="{}.u{}".format(self.cislo_zadani.cislo,self.kod)
if self.nadproblem:
return self.nadproblem.kod_v_rocniku()+name
return name
return '<Není zadaný>'
@reversion.register(ignore_duplicates=True)
class Reseni(SeminarModelBase):
6 years ago
class Meta:
db_table = 'seminar_reseni'
verbose_name = 'Řešení'
verbose_name_plural = 'Řešení'
#ordering = ['-problem', 'resitele'] # FIXME: Takhle to chceme, ale nefunguje to.
ordering = ['-cas_doruceni']
6 years ago
# Interní ID
id = models.AutoField(primary_key = True)
# Ke každé dvojici řešní a problém existuje nanejvýš jedno hodnocení, doplnění vazby.
problem = models.ManyToManyField(Problem, verbose_name='problém', help_text='Problém',
through='Hodnoceni')
resitele = models.ManyToManyField(Resitel, verbose_name='autoři řešení',
help_text='Seznam autorů řešení', through='Reseni_Resitele')
cas_doruceni = models.DateTimeField('čas_doručení', default=timezone.now, blank=True)
6 years ago
FORMA_PAPIR = 'papir'
FORMA_EMAIL = 'email'
FORMA_UPLOAD = 'upload'
FORMA_CHOICES = [
(FORMA_PAPIR, 'Papírové řešení'),
(FORMA_EMAIL, 'Emailem'),
(FORMA_UPLOAD, 'Upload přes web'),
6 years ago
]
forma = models.CharField('forma řešení', max_length=16, choices=FORMA_CHOICES, blank=False,
default=FORMA_EMAIL)
text_cely = models.OneToOneField(Text, verbose_name='Plná verze textu řešení',
blank=True, null=True, related_name="reseni_cely_set")
text_zkraceny = models.ManyToManyField(Text, verbose_name='zkrácené verze řešení',
help_text='Seznam úryvků z řešení',related_name="reseni_zkraceny_set")
poznamka = models.TextField('neveřejná poznámka', blank=True,
help_text='Neveřejná poznámka k řešení (plain text)')
zverejneno = models.BooleanField('řešení zveřejněno', default=False,
help_text='Udává, zda je řešení zveřejněno')
6 years ago
def __str__(self):
return "{}: {}".format(self.resitel.osoba.plne_jmeno(), self.problem.nazev)
6 years ago
# NOTE: Potenciální DB HOG (bez select_related)
## Pravdepodobne uz nebude potreba:
# 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)
class Hodnoceni(SeminarModelBase):
class Meta:
db_table = 'seminar_hodnoceni'
verbose_name = 'Hodnocení'
verbose_name_plural = 'Hodnocení'
# Interní ID
id = models.AutoField(primary_key = True)
body = models.DecimalField(max_digits=8, decimal_places=1, verbose_name='body',
blank=False, null=False)
cislo_body = models.ForeignKey(Cislo, verbose_name='číslo pro body',
related_name='hodnoceni', blank=False, null=False)
reseni = models.ForeignKey(Reseni, verbose_name='řešení')
problem = models.ForeignKey(Problem, verbose_name='problém')
def __str__(self):
return "{}, {}, {}".format(self.problem, self.reseni, self.body)
## FIXME: Budeme řešit později, pokud to bude potřeba.
#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_duplicates=True)
class PrilohaReseni(SeminarModelBase):
6 years ago
class Meta:
db_table = 'seminar_priloha_reseni'
verbose_name = 'Příloha řešení'
verbose_name_plural = 'Přílohy řešení'
ordering = ['reseni', 'vytvoreno']
6 years ago
# Interní ID
id = models.AutoField(primary_key = True)
reseni = models.ForeignKey(Reseni, verbose_name='řešení', related_name='prilohy')
vytvoreno = models.DateTimeField('vytvořeno', default=timezone.now, blank=True, editable=False)
soubor = models.FileField('soubor', upload_to = generate_filename)
poznamka = models.TextField('neveřejná poznámka', blank=True,
help_text='Neveřejná poznámka k příloze řešení (plain text), např. o původu')
6 years ago
def __str__(self):
return self.soubor
class Pohadka(SeminarModelBase):
6 years ago
u"""Kus pohádky před/za úlohou v čísle"""
6 years ago
class Meta:
db_table = 'seminar_pohadky'
verbose_name = 'Pohádka'
verbose_name_plural = 'Pohádky'
6 years ago
ordering = ['uloha__cislo_zadani', 'uloha__kod', '-pred']
6 years ago
# Interní ID
id = models.AutoField(primary_key=True)
text = models.TextField('Text pohádky')
6 years ago
uloha = models.ForeignKey(
Uloha,
verbose_name='Úloha',
related_name='pohadky',
null=True
6 years ago
)
6 years ago
# 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('Před úlohou', default=True)
6 years ago
autor = models.ForeignKey(
Organizator,
6 years ago
verbose_name="Autor pohádky",
6 years ago
# Při nahrávání z TeXu není vyplnění vyžadováno, v adminu je
null=True,
blank=False
)
vytvoreno = models.DateTimeField(
'Vytvořeno',
6 years ago
default=timezone.now,
blank=True,
editable=False
)
6 years ago
def __str__(self):
uryvek = self.text if len(self.text) < 50 else self.text[:(50-3)]+"..."
return uryvek
@reversion.register(ignore_duplicates=True)
class Soustredeni_Ucastnici(SeminarModelBase):
# zmena dedicnosti z models.Model na SeminarModelBase, potencialni vznik bugu
6 years ago
class Meta:
db_table = 'seminar_soustredeni_ucastnici'
verbose_name = 'Účast na soustředění'
verbose_name_plural = 'Účasti na soustředění'
6 years ago
ordering = ['soustredeni', 'resitel']
6 years ago
# Interní ID
id = models.AutoField(primary_key = True)
resitel = models.ForeignKey(Resitel, verbose_name='řešitel')
soustredeni = models.ForeignKey(Soustredeni, verbose_name='soustředění')
poznamka = models.TextField('neveřejná poznámka', blank=True,
help_text='Neveřejná poznámka k účasti (plain text)')
6 years ago
def __str__(self):
return '{} na {}'.format(self.resitel, self.soustredeni)
6 years ago
# NOTE: Poteciální DB HOG bez select_related
@reversion.register(ignore_duplicates=True)
class Soustredeni_Organizatori(SeminarModelBase):
# zmena dedicnosti z models.Model na SeminarModelBase, potencialni vznik bugu
6 years ago
class Meta:
db_table = 'seminar_soustredeni_organizatori'
verbose_name = 'Účast organizátorů na soustředění'
verbose_name_plural = 'Účasti organizátorů na soustředění'
6 years ago
ordering = ['soustredeni', 'organizator']
6 years ago
# Interní ID
id = models.AutoField(primary_key = True)
organizator = models.ForeignKey(Organizator, verbose_name='organizátor')
soustredeni = models.ForeignKey(Soustredeni, verbose_name='soustředění')
poznamka = models.TextField('neveřejná poznámka', blank=True,
help_text='Neveřejná poznámka k účasti organizátora (plain text)')
6 years ago
def __str__(self):
return '{} na {}'.format(self.organizator, self.soustredeni)
6 years ago
# NOTE: Poteciální DB HOG bez select_related
@reversion.register(ignore_duplicates=True)
class Konfera(models.Model):
6 years ago
class Meta:
db_table = 'seminar_konfera'
verbose_name = 'Konfera'
verbose_name_plural = 'Konfery'
6 years ago
# Interní ID
id = models.AutoField(primary_key = True)
nazev = models.CharField('název konfery', max_length=40, help_text = 'Název konfery')
anotace = models.TextField('anotace', blank=True,
help_text='Popis, o čem bude konfera.')
abstrakt = models.TextField('abstrakt', blank=True,
help_text='Abstrakt konfery tak, jak byl uveden ve sborníku')
organizator = models.ForeignKey(Organizator, verbose_name='organizátor', related_name='konfery',
6 years ago
on_delete = models.SET_NULL, null=True)
ucastnici = models.ManyToManyField(Resitel, verbose_name='účastníci konfery',
help_text='Seznam účastníků konfery', through='Konfery_Ucastnici')
soustredeni = models.ForeignKey(Soustredeni, verbose_name='soustředění',
related_name='konfery', on_delete = models.SET_NULL, null=True)
poznamka = models.TextField('neveřejná poznámka', blank=True,
help_text='Neveřejná poznámka ke konfeře(plain text)')
# Jedno reseni se vztahuje nejvyse k jedne konfere
reseni = models.OneToOneField(Reseni, verbose_name='článek ke konfeře', related_name='konfery',
help_text='Účastnický přípěvek o konfeře', on_delete = models.SET_NULL,
null=True, blank=True)
6 years ago
TYP_VELETRH = 'veletrh'
TYP_PREZENTACE = 'prezentace'
TYP_CHOICES = [
(TYP_VELETRH, 'Veletrh (postery)'),
(TYP_PREZENTACE, 'Prezentace (přednáška)'),
6 years ago
]
typ_prezentace = models.CharField('typ prezentace', max_length=16, choices=TYP_CHOICES,
blank=False, default=TYP_VELETRH)
prezentace = models.FileField('prezentace',help_text = 'Prezentace nebo fotka posteru',
6 years ago
upload_to = generate_filename_konfera, blank=True)
materialy = models.FileField('materialy',
help_text = 'Další materiály ke konfeře zabalené do jednoho souboru',
6 years ago
upload_to = generate_filename_konfera, blank=True)
def __str__(self):
return "{}: ({})".format(self.nazev, self.soustredeni)
# Vazebna tabulka. Mozna se generuje automaticky.
@reversion.register(ignore_duplicates=True)
class Reseni_Resitele(models.Model):
class Meta:
db_table = 'seminar_reseni_resitele'
verbose_name = 'Řešení řešitelů'
verbose_name_plural = 'Řešení řešitelů'
ordering = ['reseni', 'resitele']
# Interní ID
id = models.AutoField(primary_key = True)
resitele = models.ForeignKey(Resitel, verbose_name='řešitel')
reseni = models.ForeignKey(Reseni, verbose_name='řešení')
def __str__(self):
return '{} od {}'.format(self.reseni, self.resitel)
# NOTE: Poteciální DB HOG bez select_related
@reversion.register(ignore_duplicates=True)
class Konfery_Ucastnici(models.Model):
6 years ago
class Meta:
db_table = 'seminar_konfery_ucastnici'
verbose_name = 'Účast na konfeře'
verbose_name_plural = 'Účasti na konfeře'
6 years ago
ordering = ['konfera', 'resitel']
6 years ago
# Interní ID
id = models.AutoField(primary_key = True)
resitel = models.ForeignKey(Resitel, verbose_name='řešitel')
konfera = models.ForeignKey(Konfera, verbose_name='konfera')
poznamka = models.TextField('neveřejná poznámka', blank=True,
help_text='Neveřejná poznámka k účasti (plain text)')
6 years ago
def __str__(self):
return '{} na {}'.format(self.resitel, self.konfera)
6 years ago
# NOTE: Poteciální DB HOG bez select_related
class Obrazek(SeminarModelBase):
6 years ago
class Meta:
db_table = 'seminar_obrazky'
verbose_name = 'obrázek'
verbose_name_plural = 'obrázky'
# Interní ID
id = models.AutoField(primary_key = True)
na_web = models.ImageField('obrázek na web', upload_to='obrazky/%Y/%m/%d/',
null=True, blank=True)
text = models.ForeignKey(Text, verbose_name='text',
help_text='text, ve kterém se obrázek vyskytuje', null=False, blank=False)
do_cisla_barevny = models.FileField('barevný obrázek do čísla',
help_text = 'Barevná verze obrázku do čísla',
upload_to = 'obrazky/%Y/%m/%d/', blank=True, null=True)
do_cisla_cernobily = models.FileField('černobílý obrázek do čísla',
help_text = 'Černobílá verze obrázku do čísla',
upload_to = 'obrazky/%Y/%m/%d/', blank=True, null=True)
class TreeNode(models.Model):
class Meta:
abstract = True
root = models.ForeignKey('self',
related_name="potomci_set",
null = True,
blank = False,
on_delete = models.SET_NULL, # Vrcholy s null kořenem jsou sirotci bez ročníku
verbose_name="kořen stromu")
first_child = models.ForeignKey('self',
null = True,
blank = True,
on_delete=models.SET_NULL,
verbose_name="první potomek")
succ = models.OneToOneField('self',
related_name="prev",
null = True,
blank = True,
on_delete=models.SET_NULL,
verbose_name="další element na stejné úrovni")
class RocnikNode(TreeNode):
class Meta:
db_table = 'seminar_nodes_rocnik'
verbose_name = 'Ročník (Node)'
verbose_name_plural = 'Ročníky (Node)'
rocnik = models.OneToOneField(Rocnik,
on_delete = models.PROTECT, # Pokud chci mazat ročník, musím si Node pořešit ručně
verbose_name = "ročník")
class CisloNode(TreeNode):
class Meta:
db_table = 'seminar_nodes_cislo'
verbose_name = 'Číslo (Node)'
verbose_name_plural = 'Čísla (Node)'
cislo = models.OneToOneField(Cislo,
on_delete = models.PROTECT, # Pokud chci mazat číslo, musím si Node pořešit ručně
verbose_name = "číslo")
class MezicisloNode(TreeNode):
class Meta:
db_table = 'seminar_nodes_mezicislo'
verbose_name = 'Mezičíslo (Node)'
verbose_name_plural = 'Mezičísla (Node)'
class TemaVCisleNode(TreeNode):
""" Obsahuje příspěvky k tématu v daném čísle """
class Meta:
db_table = 'seminar_nodes_temavcisle'
verbose_name = 'Téma v čísle (Node)'
verbose_name_plural = 'Témata v čísle (Node)'
tema = models.ForeignKey(Tema,
on_delete=models.PROTECT, # Pokud chci mazat téma, musím si Node pořešit ručně
verbose_name = "téma v čísle")
class KonferaNode(TreeNode):
class Meta:
db_table = 'seminar_nodes_konfera'
verbose_name = 'Konfera (Node)'
verbose_name_plural = 'Konfery (Node)'
class ClanekNode(TreeNode):
class Meta:
db_table = 'seminar_nodes_clanek'
verbose_name = 'Článek (Node)'
verbose_name_plural = 'Články (Node)'
class UlohaNode(TreeNode):
class Meta:
db_table = 'seminar_nodes_uloha'
verbose_name = 'Úloha (Node)'
verbose_name_plural = 'Úlohy (Node)'
class TextNode(TreeNode):
class Meta:
db_table = 'seminar_nodes_obsah'
verbose_name = 'Text (Node)'
verbose_name_plural = 'Text (Node)'
text = models.ForeignKey(Text,
on_delete=models.PROTECT,
verbose_name = 'text')
## FIXME: Logiku přesunout do views.
#@python_2_unicode_compatible
#class VysledkyBase(SeminarModelBase):
#
# class Meta:
# verbose_name = 'Řádek výsledkovky'
# verbose_name_plural = 'Řádky výsledkovky'
# ordering = ['body']
# abstract = True
# managed = False
#
# dummy_id = models.CharField('dummy ID pro view', max_length=32, primary_key=True,
# db_column='id')
#
# cislo = models.ForeignKey(Cislo, verbose_name='číslo pro body', db_column='cislo_id',
# on_delete=models.DO_NOTHING)
#
# resitel = models.ForeignKey(Resitel, verbose_name='řešitel', db_column='resitel_id',
# on_delete=models.DO_NOTHING)
#
6 years ago
# body = models.DecimalField(max_digits=8, decimal_places=1, db_column='body',
# verbose_name='body za číslo')
#
# def __str__(self):
# return force_unicode(u"%s: %sb (%s)".format(self.resitel.plne_jmeno(), self.body,
# str(self.cislo)))
# # NOTE: DB zatez pri vypisu (ale nepouzivany)
## FIXME: Logiku přesunout do views.
#class VysledkyZaCislo(VysledkyBase):
#
# class Meta:
# db_table = 'seminar_body_za_cislo'
# abstract = False
# managed = False
#
#
## FIXME: Logiku přesunout do views.
#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='body do čísla (za ročník)')
#
#
## FIXME: Logiku přesunout do views.
#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='body do čísla (i minulé ročníky)')
#
#
## FIXME: Logiku přesunout do views.
#@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='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_duplicates=True)
@python_2_unicode_compatible
class Nastaveni(SingletonModel):
6 years ago
class Meta:
db_table = 'seminar_nastaveni'
verbose_name = 'Nastavení semináře'
aktualni_rocnik = models.ForeignKey(Rocnik, verbose_name='aktuální ročník', null=False)
aktualni_cislo = models.ForeignKey(Cislo, verbose_name='poslední vydané číslo',
null=False)
6 years ago
def __str__(self):
return 'Nastavení semináře'
6 years ago
def admin_url(self):
return reverse('admin:seminar_nastaveni_change', args=(self.id, ))
def verejne(self):
return False
@reversion.register(ignore_duplicates=True)
@python_2_unicode_compatible
class Novinky(models.Model):
class Meta:
verbose_name = 'Novinka'
verbose_name_plural = 'Novinky'
ordering = ['-datum']
6 years ago
datum = models.DateField(auto_now_add=True)
6 years ago
text = models.TextField('Text novinky', blank=True, null=True)
6 years ago
obrazek = models.ImageField('Obrázek', upload_to='image_novinky/%Y/%m/%d/',
null=True, blank=True)
6 years ago
obrazek_maly = ImageSpecField(source='obrazek',
processors=[
ResizeToFit(350, 200, upscale=False)
],
options={'quality': 95})
autor = models.ForeignKey(Organizator, verbose_name='Autor novinky', null=True)
6 years ago
zverejneno = models.BooleanField('Zveřejněno', default="False")
def __str__(self):
return '[' + str(self.datum) + '] ' + self.text[0:50]