# -*- coding: utf-8 -*- import os import datetime 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_countries.fields import CountryField from solo.models import SingletonModel from taggit.managers import TaggableManager import reversion from seminar.utils import roman # TODO společná báze (admin url, url, veřejné, ...) class SeminarModelBase(models.Model): class Meta: abstract = True def verejne(self): return False def public_url(self): if self.Meta.url_name: return reverse(self.Meta.url_name, kwargs={'id': self.id, 'pk': self.id}) return None 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' # 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é ID z DAKOSU -- jen u importovaných záznamů dakos_id = models.CharField(u'Stare DaKoS ID', max_length=32, blank=True, default='', help_text=u'DaKoS ID z exportu, jen historický význam, podle tabulky dksroot.V_skola') # 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 zeme 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' % (self.nazev, 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') 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é ID z DAKOSU -- jen u importovaných záznamů dakos_id = models.CharField(u'Stare DaKoS ID', max_length=32, blank=True, default='', help_text=u'DaKoS ID z exportu, jen historický význam, podle tabulky mamoper.MM_RIESITELIA') def plne_jmeno(self): return force_unicode(u'%s %s' % (self.jmeno, self.prijmeni)) def __str__(self): return force_unicode(self.plne_jmeno()) @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') rocnik = models.CharField(u'číslo ročníku', max_length=16) def __str__(self): return force_unicode(u'%s (%d/%d)' % (self.rocnik, self.prvni_rok, self.prvni_rok+1)) def roman(self): if self.rocnik.isdigit(): return force_unicode(roman(int(self.rocnik))) else: return force_unicode(self.rocnik) def verejne(self): return len(self.verejna_cisla()) > 0 verejne.boolean = True 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 druhy_rok(self): return self.prvni_rok + 1 def verejne_url(self): return reverse('seminar_rocnik', kwargs={'pk': self.id}) @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') cislo = models.CharField(u'název čísla', max_length=32, help_text=u'Většinou jen "1", vyjímečně "7-8", lexikograficky určije 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') verejne_db = models.BooleanField(u'číslo zveřejněno', db_column='verejne', default=False) def kod(self): return u'%s.%s' % (self.rocnik.rocnik, self.cislo) kod.short_description = u'Kód čísla' def __str__(self): return force_unicode(u'%s' % (self.kod(),)) def verejne(self): return self.verejne_db verejne.boolean = True def verejne_url(self): return reverse('seminar_cislo', kwargs={'pk': self.id}) @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' # 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_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_ORG_CLANEK, u'Organizátorský článek'), (TYP_RES_CLANEK, u'Řesitelský č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_problemu_org = models.TextField(u'organizátorský (neveřejný) text', blank=True) text_problemu = models.TextField(u'veřejný text zadání a řešení', blank=True) 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.IntegerField(u'maximum bodů', blank=True, null=True) timestamp = models.DateTimeField(u'vytvořeno', auto_now=True) # Staré ID z DAKOSU -- jen u importovaných záznamů dakos_id = models.CharField(u'Staré DaKoS ID', max_length=32, blank=True, default='', help_text=(u'DaKoS ID z exportu, s prefixem podle původu: "AZAD:xxx (z MM_AZAD), "' + u'"DOZ:xxx" (z MM_DOZ), "ZAD:rocnik.cislo.uloha.typ" (z MM_ZADANIA)')) def __str__(self): return force_unicode(u'%s (%s)' % (self.nazev, self.stav)) def kod_v_rocniku(self): 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,)) return '' 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}) @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', 'resitel'] # 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.IntegerField(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', auto_now=True) poznamka = models.TextField(u'neveřejná poznámka', blank=True, help_text=u'Neveřejná poznámka k řešení (plain text, editace v detailu řešení)') def __str__(self): return force_unicode(u"%s: %s (%sb)" % (self.resitel.plne_jmeno(), self.problem.nazev, self.body)) # PrilohaReseni method def generate_filename(self, filename): clean = filename.replace('/','-').replace('\0', '') datedir = datetime.datetime.now().strftime('%Y-%m') fname = "%s_%s" % ( datetime.datetime.now().strftime('%Y-%m-%d-%H:%M'), clean) return os.path.join(settings.SEMINAR_RESENI_DIRNAME, datedir, fname) @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', auto_now=True) 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) @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í', db_table='seminar_soustredeni_ucastnici') def __str__(self): return force_unicode(u'%s (%s)' % (self.misto, self.datum_zacatek)) def verejne(self): return self.verejne_db verejne.boolean = True def verejne_url(self): return reverse('seminar_soustredeni', kwargs={'pk': self.id}) @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.IntegerField(u'body za číslo', db_column='body') def __str__(self): return force_unicode(u"%s: %sb (%s)" % (self.resitel.plne_jmeno(), self.body, str(self.cislo))) @python_2_unicode_compatible class VysledkyZaCislo(VysledkyBase): class Meta: db_table = 'seminar_body_za_cislo' abstract = False managed = False def __str__(self): return force_unicode(u"%s: %sb (za %s)" % (self.resitel.plne_jmeno(), self.body, str(self.cislo))) @python_2_unicode_compatible class VysledkyKCislu(VysledkyBase): class Meta: db_table = 'seminar_body_k_cislu' abstract = False managed = False body_celkem = models.IntegerField(u'body celkem do čísla', db_column='body_celkem') def __str__(self): return force_unicode(u"%s: %sb / %sb (do %s)" % (self.resitel.plne_jmeno(), self.body, self.body_celkem, str(self.cislo))) @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