# -*- 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 ) :
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_duplicates = True )
@python_2_unicode_compatible
class Osoba ( SeminarModelBase ) :
class Meta :
db_table = ' seminar_osoby '
verbose_name = ' Osoba '
verbose_name_plural = ' Osoby '
ordering = [ ' prijmeni ' , ' jmeno ' ]
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 )
# User, pokud má na webu účet
user = models . OneToOneField ( settings . AUTH_USER_MODEL , blank = True , null = True , verbose_name = ' uživatel ' )
# 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, ...) ' )
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 } )
def plne_jmeno ( self ) :
return force_unicode ( ' %s %s ' % ( self . jmeno , self . prijmeni ) )
def inicial_krestni ( self ) :
return force_unicode ( ' %s . ' % ( self . jmeno [ 0 ] ) )
def __str__ ( self ) :
return force_unicode ( " Osoba( {} ) " . format ( self . plne_jmeno ( ) ) )
@reversion . register ( ignore_duplicates = True )
@python_2_unicode_compatible
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 force_unicode ( ' %s , %s , %s ' % ( 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í
@reversion . register ( ignore_duplicates = True )
@python_2_unicode_compatible
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 . ForeignKey ( Osoba , blank = False , null = False , verbose_name = ' osoba ' )
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) ' )
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 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_duplicates = True )
@python_2_unicode_compatible
class Rocnik ( SeminarModelBase ) :
class Meta :
db_table = ' seminar_rocniky '
verbose_name = ' Ročník '
verbose_name_plural = ' Ročníky '
ordering = [ ' -rocnik ' ]
# 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 ' )
def __str__ ( self ) :
return force_unicode ( ' %s ( %d / %d ) ' % ( self . rocnik , self . prvni_rok , self . prvni_rok + 1 ) )
# Ročník v římských číslech
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 = ' 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_duplicates = True )
@python_2_unicode_compatible
class Cislo ( SeminarModelBase ) :
class Meta :
db_table = ' seminar_cisla '
verbose_name = ' Číslo '
verbose_name_plural = ' Čísla '
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 )
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! ' )
datum_vydani = models . DateField ( ' datum vydání ' , blank = True , null = True ,
help_text = ' Datum vydání finální verze ' )
datum_deadline = models . DateField ( ' datum deadline ' , blank = True , null = True ,
help_text = ' Datum pro příjem řešení úloh zadaných v tomto čísle ' )
datum_deadline_soustredeni = models . DateField (
' datum deadline soustředění ' ,
blank = True , null = True ,
help_text = ' Datum pro příjem řešení pro účast na soustředění ' )
verejne_db = models . BooleanField ( ' číslo zveřejněno ' ,
db_column = ' verejne ' , default = False )
verejna_vysledkovka = models . BooleanField (
' zveřejněna výsledkovka ' ,
default = False ,
help_text = ' Je-li false u veřejného čísla, \
není výsledkovka zatím veřejná . ' )
poznamka = models . TextField ( ' neveřejná poznámka ' , blank = True ,
help_text = ' Neveřejná poznámka k číslu (plain text) ' )
pdf = models . FileField ( ' pdf ' , upload_to = cislo_pdf_filename , null = True , blank = True ,
help_text = ' Pdf čísla, které si mohou řešitelé stáhnout ' )
def kod ( self ) :
return ' %s . %s ' % ( self . rocnik . rocnik , self . cislo )
kod . short_description = ' 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 ( ' %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_duplicates = True )
@python_2_unicode_compatible
class Organizator ( SeminarModelBase ) :
# zmena dedicnosti z models.Model na SeminarModelBase, potencialni vznik bugu
osoba = models . ForeignKey ( Osoba , verbose_name = ' osoba ' , related_name = ' org ' ,
help_text = ' osobní údaje organizátora ' , null = False , blank = False )
vytvoreno = models . DateTimeField (
' Vytvořeno ' ,
default = timezone . now ,
blank = True ,
editable = False
)
organizuje_od = models . DateTimeField ( ' Organizuje od ' , blank = False , null = False )
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 u " %s ' %s ' %s " . format ( self . osoba . jmeno ,
self . osoba . prezdivka ,
self . osoba . prijmeni )
else :
return u " %s %s " . format ( self . osoba . jmeno , self . osoba . prijmeni )
class Meta :
verbose_name = ' Organizátor '
verbose_name_plural = ' Organizátoři '
@reversion . register ( ignore_duplicates = True )
@python_2_unicode_compatible
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 force_unicode ( ' %s ( %s ) ' . 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 )
@python_2_unicode_compatible
class Problem ( SeminarModelBase ) :
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
verbose_name = ' Problém '
verbose_name_plural = ' Problémy '
ordering = [ ' nazev ' ]
# Interní ID
id = models . AutoField ( primary_key = True )
# Název
nazev = models . CharField ( ' název ' , max_length = 256 )
# Problém má podproblémy
nadproblem = models . ForeignKey ( ' self ' , verbose_name = ' nadřazený problém ' ,
related_name = ' nadproblem_ %(class)s ' , null = True , blank = True )
STAV_NAVRH = ' navrh '
STAV_ZADANY = ' zadany '
STAV_VYRESENY = ' vyreseny '
STAV_SMAZANY = ' smazany '
STAV_CHOICES = [
( STAV_NAVRH , ' Návrh ' ) ,
( STAV_ZADANY , ' Zadaný ' ) ,
( STAV_VYRESENY , ' Vyřešený ' ) ,
( STAV_SMAZANY , ' Smazaný ' ) ,
]
stav = models . CharField ( ' stav problému ' , max_length = 32 , choices = STAV_CHOICES , blank = False , default = STAV_NAVRH )
zamereni = TaggableManager ( verbose_name = ' zaměření ' , help_text = ' Zaměření M/F/I/O problému, příp. další tagy ' , blank = True )
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 ... ' )
autor = models . ForeignKey ( Organizator , verbose_name = ' autor problému ' ,
related_name = ' autor_problemu_ %(class)s ' , null = True , blank = True )
garant = models . ForeignKey ( Organizator , verbose_name = ' garant zadaného problému ' ,
related_name = ' garant_problemu_ %(class)s ' , null = True , blank = True )
opravovatele = models . ManyToManyField ( Organizator , verbose_name = ' opravovatelé ' ,
blank = True , related_name = ' opravovatele_ %(class)s ' )
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 ' )
vytvoreno = models . DateTimeField ( ' vytvořeno ' , default = timezone . now , blank = True , editable = False )
def __str__ ( self ) :
return force_unicode ( ' %s ' % ( self . nazev , ) )
# Implicitini implementace, jednotlivé dědící třídy si přepíšou
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ý> '
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
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 " ( {} \u2009 b) " . 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 ' ) ,
]
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 force_unicode ( self . nadproblem . kod_v_rocniku ( ) + " .t {} " . format ( self . kod ) )
return force_unicode ( " 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 force_unicode ( " 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 . ForeignKey ( Text , verbose_name = ' veřejné zadání ' , related_name = " uloha_zadani_set " , blank = True , null = True )
vzorak = models . ForeignKey ( 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 " {} .u {} " . format ( self . cislo_zadani . cislo , self . kod )
if self . nadproblem :
return force_unicode ( self . nadproblem . kod_v_rocniku ( ) + name )
return force_unicode ( name )
return ' <Není zadaný> '
@reversion . register ( ignore_duplicates = True )
@python_2_unicode_compatible
class Reseni ( SeminarModelBase ) :
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 ' ]
# 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 )
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 ' ) ,
]
forma = models . CharField ( ' forma řešení ' , max_length = 16 , choices = FORMA_CHOICES , blank = False ,
default = FORMA_EMAIL )
text_cely = models . ForeignKey ( 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 ' )
def __str__ ( self ) :
return force_unicode ( u " %s : %s " . format ( self . resitel . osoba . plne_jmeno ( ) ,
self . problem . nazev ) )
# 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 ' )
## 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 )
@python_2_unicode_compatible
class PrilohaReseni ( SeminarModelBase ) :
class Meta :
db_table = ' seminar_priloha_reseni '
verbose_name = ' Příloha řešení '
verbose_name_plural = ' Přílohy řešení '
ordering = [ ' reseni ' , ' vytvoreno ' ]
# 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 ' )
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 = ' Pohádka '
verbose_name_plural = ' Pohádky '
ordering = [ ' uloha__cislo_zadani ' , ' uloha__kod ' , ' -pred ' ]
# Interní ID
id = models . AutoField ( primary_key = True )
text = models . TextField ( ' Text pohádky ' )
uloha = models . ForeignKey (
Uloha ,
verbose_name = ' Ú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 ( ' Před úlohou ' , default = True )
autor = models . ForeignKey (
Organizator ,
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
)
vytvoreno = models . DateTimeField (
' Vytvořeno ' ,
default = timezone . now ,
blank = True ,
editable = False
)
def __str__ ( self ) :
uryvek = self . text if len ( self . text ) < 50 else self . text [ : ( 50 - 3 ) ] + " ... "
return force_unicode ( uryvek )
@reversion . register ( ignore_duplicates = True )
@python_2_unicode_compatible
class Soustredeni_Ucastnici ( SeminarModelBase ) :
# zmena dedicnosti z models.Model na SeminarModelBase, potencialni vznik bugu
class Meta :
db_table = ' seminar_soustredeni_ucastnici '
verbose_name = ' Účast na soustředění '
verbose_name_plural = ' Účasti na soustředění '
ordering = [ ' soustredeni ' , ' resitel ' ]
# 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) ' )
def __str__ ( self ) :
return force_unicode ( ' %s na %s ' . format ( self . resitel , self . soustredeni ) )
# NOTE: Poteciální DB HOG bez select_related
@reversion . register ( ignore_duplicates = True )
@python_2_unicode_compatible
class Soustredeni_Organizatori ( SeminarModelBase ) :
# zmena dedicnosti z models.Model na SeminarModelBase, potencialni vznik bugu
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í '
ordering = [ ' soustredeni ' , ' organizator ' ]
# 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) ' )
def __str__ ( self ) :
return force_unicode ( ' %s na %s ' . format ( self . organizator , self . soustredeni ) )
# NOTE: Poteciální DB HOG bez select_related
@reversion . register ( ignore_duplicates = True )
@python_2_unicode_compatible
class Konfera ( models . Model ) :
class Meta :
db_table = ' seminar_konfera '
verbose_name = ' Konfera '
verbose_name_plural = ' Konfery '
# 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 ' ,
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) ' )
reseni = models . ForeignKey ( 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 )
TYP_VELETRH = ' veletrh '
TYP_PREZENTACE = ' prezentace '
TYP_CHOICES = [
( TYP_VELETRH , ' Veletrh (postery) ' ) ,
( TYP_PREZENTACE , ' Prezentace (přednáška) ' ) ,
]
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 ' ,
upload_to = generate_filename_konfera , blank = True )
materialy = models . FileField ( ' materialy ' ,
help_text = ' 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 ) " . format ( self . nazev , self . soustredeni ) )
# Vazebna tabulka. Mozna se generuje automaticky.
@reversion . register ( ignore_duplicates = True )
@python_2_unicode_compatible
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 force_unicode ( ' %s od %s ' . format ( self . reseni , self . resitel ) )
# NOTE: Poteciální DB HOG bez select_related
@reversion . register ( ignore_duplicates = True )
@python_2_unicode_compatible
class Konfery_Ucastnici ( models . Model ) :
class Meta :
db_table = ' seminar_konfery_ucastnici '
verbose_name = ' Účast na konfeře '
verbose_name_plural = ' Účasti na konfeře '
ordering = [ ' konfera ' , ' resitel ' ]
# 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) ' )
def __str__ ( self ) :
return force_unicode ( ' %s na %s ' . format ( self . resitel , self . konfera , ) )
# NOTE: Poteciální DB HOG bez select_related
class Obrazek ( SeminarModelBase ) :
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 )
## 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)
#
# 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 ) :
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 )
def __str__ ( self ) :
return ' Nastavení semináře '
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 ) :
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 ( Organizator , 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 '
ordering = [ ' -datum ' ]