Browse Source

merge správných novinek

export_seznamu_prednasek
Kateřina Č 4 years ago
parent
commit
6b9d9f35a7
  1. 16
      init_local.sh
  2. 1603
      obalky/lisak.eps
  3. 78
      obalky/obalky-template.tex
  4. 53
      obalky/obalky.py
  5. 26
      obalky/obalky.sql
  6. 3
      seminar/export.py
  7. 23
      seminar/migrations/0084_clanek_cislo.py
  8. 32
      seminar/migrations/0085_nepovinna_prezdivka.py
  9. 9
      seminar/models.py
  10. 73
      seminar/testutils.py
  11. 108
      seminar/utils.py
  12. 93
      seminar/views/views_all.py

16
init_local.sh

@ -0,0 +1,16 @@
#!/bin/sh
set -e # Spadni pokud něco spadne
# Check venvu
# NOTE: zkontroluje i správnou složku -- existencí Makefilu
make venv_check
# Vygenerujeme testdata
./manage.py testdata
# Nahrajeme statický obsah modelů
./manage.py loaddata flat.json sitetree_new.json
# Posbíráme statické soubory
./manage.py collectstatic

1603
obalky/lisak.eps

File diff suppressed because it is too large

78
obalky/obalky-template.tex

@ -1,78 +0,0 @@
\input czech.sty
\input epsf
\special{landscape}
\nopagenumbers
\hoffset=-1in
\voffset=-1in
\advance\voffset by 0.9cm
%nove pridano, aby to fungovalo...
\advance\hoffset by 6.5cm
\hsize=22cm
\vsize=16cm
\font\adrfonta=csssbx10 at 14pt
\font\adrfontb=csssbx10 at 12pt
\font\adrfontc=csss12
\font\tofont=csr12 at 16pt
\newdimen\fromskip
\newdimen\toskip
\fromskip=4.35cm
\toskip=13.2cm
\def\first{\relax}
\catcode`_=13
%\catcode`_=12
\def_{$\_$}
\advance\voffset by 2.5cm
\def\obalka#1#2#3#4#5#6#7{
\def\jmeno{#1}
\def\prijm{#2}
\def\skola{#3}
\def\popis{}
\def\first{}
\def\ulice{#4}
\def\PSC{#5}
\def\mesto{#6}
\def\stat{#7}
\vskip-4mm\vbox to 0pt{\hbox to 0pt{\hskip1.4cm\epsfysize=2.55cm\epsfbox{lisak.eps}\hss}\vss}
\baselineskip=13pt
\parindent=\fromskip
\line{\indent\adrfonta Časopis M\&M,\hfil}
\vskip3pt
\line{\indent\adrfontb OPMK UK MFF\hfil}
\vskip3pt
\line{\indent\adrfontc Ke Karlovu 3, 121 16 Praha 2\hfil}
\line{\indent\adrfontc Tel.: +420 221 911 235\hss}
\line{\indent\adrfontc mam@atrey.karlin.mff.cuni.cz\hfil}
\vskip6.15cm
\vbox to 0pt{\parindent=1.4cm\hsize=\toskip\advance\hsize by -1cm
\vbox to 60pt{\vfil} \popis\vss}
\parindent=\toskip
\baselineskip=18pt
\line{\indent\tofont\first\hfil}
\line{\indent\tofont\jmeno\ \prijm\hfil}
\ifx \skola \empty
{}
\else
\line{\indent\tofont\skola\hfil}
\fi
\line{\indent\tofont\ulice\hfil}
\line{\tofont\item{\PSC} \mesto\hfil}
\vskip5pt
\line{\indent\tofont\stat\hfil}
\vfil\eject
}
%\obalka{Lenka}{Kopfová}{Leknínová 10}{746 01}{Opava}

53
obalky/obalky.py

@ -1,53 +0,0 @@
#!/usr/bin/python
#coding: utf-8
import psycopg2
import sys
import subprocess
import re
dbname="mam-prod"
user="mam"
conn = psycopg2.connect("dbname={0} user={1}".format(dbname,user))
cur = conn.cursor()
names = []
with open("obalky.sql") as qfile, open("obalky-template.tex") as texheader, open("obalky.tex","w") as texout :
texout.write(texheader.read())
cur.execute(qfile.read())
for row in cur.fetchall():
(muz,jmeno,prijmeni,skola,ulice,mesto,psc,stat)=row
if (stat=='CZ'):
stat = ""
elif (stat=='SK'):
stat = "Slovenská republika"
else:
print("Neznamy stat: {}\n".format(stat))
if (skola==None):
skola=""
psc = psc.replace(" ","")
psc = psc[0:3]+" "+psc[3:]
texout.write("\\obalka{{{0}}}{{{1}}}{{{2}}}{{{3}}}{{{4}}}{{{5}}}{{{6}}}\n".format(jmeno,prijmeni,skola,ulice,psc,mesto,stat))
names.append((jmeno,prijmeni))
texout.write("\\bye\n")
cur.close()
conn.close()
print("Spoustim csplain ...")
output = subprocess.check_output(["csplain","obalky.tex"],stderr=subprocess.STDOUT)
page = 0
for line in output.decode("utf-8").splitlines():
pmatch = re.search("\[([0-9]+)\]",line)
if pmatch:
page = int(pmatch.group(1))
errmatch = re.match("Overfull",line)
if errmatch:
print("Preteceni na strane",page,"u osoby",names[page][0],names[page][1])
print("Spoustim dvipdf ...")
subprocess.call(["dvipdf","obalky.dvi"])
print("Hotovo.")

26
obalky/obalky.sql

@ -1,26 +0,0 @@
WITH akt_rocnik AS (
SELECT rocnik FROM seminar_rocniky
WHERE id=(SELECT aktualni_rocnik_id FROM seminar_nastaveni)
), id_rocniku AS (
SELECT id,prvni_rok FROM seminar_rocniky
WHERE rocnik=(SELECT * FROM akt_rocnik) OR rocnik=(SELECT * FROM akt_rocnik)-1
), id_cisel AS (
SELECT seminar_cisla.id FROM seminar_cisla
INNER JOIN id_rocniku ON rocnik_id=id_rocniku.id
), problemy AS (
SELECT seminar_problemy.id FROM seminar_problemy
INNER JOIN id_cisel ON cislo_zadani_id = id_cisel.id
), resitele AS(
SELECT DISTINCT resitel_id FROM seminar_reseni
INNER JOIN problemy ON problem_id=problemy.id
)
SELECT pohlavi_muz,jmeno,prijmeni,NULL AS skola,ulice,mesto,psc,stat FROM seminar_resitele
INNER JOIN resitele ON seminar_resitele.id=resitel_id
WHERE zasilat='domu' AND rok_maturity > (SELECT MAX(prvni_rok) FROM id_rocniku)
UNION
SELECT res.pohlavi_muz,res.jmeno,res.prijmeni,sk.nazev,sk.ulice,sk.mesto,sk.psc,sk.stat
FROM seminar_resitele AS res
INNER JOIN resitele ON res.id=resitel_id
INNER JOIN seminar_skoly AS sk ON sk.id=skola_id
WHERE zasilat='do_skoly' AND rok_maturity > (SELECT MAX(prvni_rok) FROM id_rocniku)
ORDER BY prijmeni ASC, jmeno ASC

3
seminar/export.py

@ -9,6 +9,7 @@ from .models import Problem, Cislo, Reseni, Nastaveni, Rocnik, Soustredeni
#from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva #from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva
from .ovvpfile import OvvpFile from .ovvpfile import OvvpFile
from seminar import views from seminar import views
from seminar.utils import aktivniResitele
class ExportIndexView(generic.View): class ExportIndexView(generic.View):
def get(self, request): def get(self, request):
@ -76,7 +77,7 @@ class ExportRocnikView(generic.View):
rocnik = get_object_or_404(Rocnik, prvni_rok=pr, exportovat=True) rocnik = get_object_or_404(Rocnik, prvni_rok=pr, exportovat=True)
cislo = rocnik.posledni_zverejnena_vysledkovka_cislo() cislo = rocnik.posledni_zverejnena_vysledkovka_cislo()
resitele = views.aktivniResitele(cislo.rocnik.rocnik, cislo.poradi, True) resitele = aktivniResitele(cislo, True)
slovnik_body = views.secti_body_za_rocnik(cislo, resitele) slovnik_body = views.secti_body_za_rocnik(cislo, resitele)
_, setrizeni_resitele, setrizene_body = views.setrid_resitele_a_body(slovnik_body) _, setrizeni_resitele, setrizene_body = views.setrid_resitele_a_body(slovnik_body)

23
seminar/migrations/0084_clanek_cislo.py

@ -5,25 +5,32 @@ import django.db.models.deletion
from seminar.treelib import get_parent from seminar.treelib import get_parent
def najdi_cislo(apps, schema_editor): def najdi_cislo(apps, schema_editor):
#
Clanek = apps.get_model('seminar', 'Clanek') Clanek = apps.get_model('seminar', 'Clanek')
Hodnoceni = apps.get_model('seminar', 'Hodnoceni') Hodnoceni = apps.get_model('seminar', 'Hodnoceni')
Reseni = apps.get_model('seminar', 'Reseni') Reseni = apps.get_model('seminar', 'Reseni')
ReseniNode = apps.get_model('seminar', 'ReseniNode') ReseniNode = apps.get_model('seminar', 'ReseniNode')
CisloNode = apps.get_model('seminar', 'CisloNode') CisloNode = apps.get_model('seminar', 'CisloNode')
ContentType = apps.get_model('contenttypes', 'ContentType')
for c in Clanek.objects.all(): for c in Clanek.objects.all():
reseni = c.reseni_set reseni = c.reseni_set
if (len(reseni) != 1): if (reseni.count() != 1): # Pozor, reseni_set je Manager, takže se na něj musí trošku jinak
raise ValueError("Článek k sobě má nejedno řešení!") raise ValueError("Článek k sobě má nejedno řešení!")
r = reseni[0] r = reseni.first()
aktualniNode = r.reseninode aktualniNode = r.text_cely # Hlavní ReseniNode pro řešení
while aktualniNode is not None: while aktualniNode is not None:
if isinstance(aktualniNode, CisloNode): #if isinstance(aktualniNode, CisloNode): # Nejde, protože aktualniNode se tváří jako obecný TreeNode...
c.cislo = aktualniNode.cislo # Ale v dokumentaci k django-polymorphic se píše, že tam jsou nastavené nějaké fieldy na známé hodnoty :-)
# https://django-polymorphic.readthedocs.io/en/stable/migrating.html
cislonode_ct = ContentType.objects.get_for_model(CisloNode)
akt_ct = aktualniNode.polymorphic_ctype
if akt_ct == cislonode_ct:
# Zneužíváme tu opačnou vazbu k treenode_ptr, protože
# aktualniNode je "jen" TreeNode a ne CisloNode
c.cislo = aktualniNode.cislonode.cislo
c.save() c.save()
break break
aktualniNode = get_parent() aktualniNode = get_parent(aktualniNode)
@ -39,5 +46,5 @@ class Migration(migrations.Migration):
name='cislo', name='cislo',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='vydane_clanky', to='seminar.Cislo', verbose_name='číslo vydání'), field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='vydane_clanky', to='seminar.Cislo', verbose_name='číslo vydání'),
), ),
migrations.RunPython(najdi_cislo), migrations.RunPython(najdi_cislo, migrations.RunPython.noop),
] ]

32
seminar/migrations/0085_nepovinna_prezdivka.py

@ -0,0 +1,32 @@
# Generated by Django 2.2.13 on 2020-06-24 22:57
from django.db import migrations, models
def smaz_prezdivku(apps, schema_editor):
Osoba = apps.get_model('seminar', 'Osoba')
for o in Osoba.objects.filter(prezdivka=''):
o.prezdivka = None
o.save()
def pridej_prezdivku(apps, schema_editor):
Osoba = apps.get_model('seminar', 'Osoba')
for o in Osoba.objects.filter(prezdivka=None):
o.prezdivka = ''
o.save()
class Migration(migrations.Migration):
dependencies = [
('seminar', '0084_clanek_cislo'),
]
operations = [
migrations.AlterField(
model_name='osoba',
name='prezdivka',
field=models.CharField(blank=True, max_length=256, null=True, verbose_name='přezdívka'),
),
migrations.RunPython(smaz_prezdivku, pridej_prezdivku),
]

9
seminar/models.py

@ -13,7 +13,7 @@ from django.utils.encoding import force_text
from django.utils.text import slugify from django.utils.text import slugify
from django.urls import reverse from django.urls import reverse
from django.core.cache import cache from django.core.cache import cache
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.utils.text import get_valid_filename from django.utils.text import get_valid_filename
from imagekit.models import ImageSpecField, ProcessedImageField from imagekit.models import ImageSpecField, ProcessedImageField
@ -66,7 +66,7 @@ class Osoba(SeminarModelBase):
prijmeni = models.CharField('příjmení', max_length=256) prijmeni = models.CharField('příjmení', max_length=256)
prezdivka = models.CharField('přezdívka', max_length=256) prezdivka = models.CharField('přezdívka', blank=True, null=True, max_length=256)
# User, pokud má na webu účet # User, pokud má na webu účet
user = models.OneToOneField(settings.AUTH_USER_MODEL, blank=True, null=True, user = models.OneToOneField(settings.AUTH_USER_MODEL, blank=True, null=True,
@ -581,6 +581,11 @@ class Organizator(SeminarModelBase):
help_text="Škola, např. MFF, VŠCHT, VUT, ... prostě aby se nemuselo psát do studuje" help_text="Škola, např. MFF, VŠCHT, VUT, ... prostě aby se nemuselo psát do studuje"
"školu, ale jen obor, možnost zobrazit zvlášť") "školu, ale jen obor, možnost zobrazit zvlášť")
def clean(self):
if self.organizuje_od > self.organizuje_do:
raise ValidationError("Organizátor nemůže skončit s organizováním dříve než začal!")
super().clean()
def __str__(self): def __str__(self):
if self.osoba.prezdivka: if self.osoba.prezdivka:
return "{} '{}' {}".format(self.osoba.jmeno, return "{} '{}' {}".format(self.osoba.jmeno,

73
seminar/testutils.py

@ -681,6 +681,77 @@ def otec_syn(otec, syn):
syn.save() syn.save()
otec.save() otec.save()
def gen_clanek(rnd):
logger.info("Generuji článek do čísla 22.2")
clanek = m.Clanek.objects.create(
nazev="Článek o Lorem ipsum",
nadproblem=None,
stav='vyreseny',
zamereni=['I'],
garant=rnd.choice(m.Organizator.objects.all()),
kod='cl',
)
clanek.save()
reseni = m.Reseni.objects.create(
zverejneno=True,
)
reseni.resitele.add(rnd.choice(m.Resitel.objects.all()))
reseni.save()
cislo = m.Cislo.objects.get(rocnik__rocnik=22, poradi=2)
cislonode = cislo.cislonode
hodnoceni = m.Hodnoceni.objects.create(
body=15.0,
cislo_body=cislo,
reseni=reseni,
problem=clanek,
)
hodnoceni.save()
reseninode = m.ReseniNode.objects.create(
reseni=reseni
)
reseninode.save()
# Bude to celý text
reseni.text_cely = reseninode
reseni.save()
from seminar.treelib import insert_last_child, create_child
insert_last_child(cislonode, reseninode)
# Vyrobíme nějaký obsah
# FIXME: Ten, kdo vymyslel TreeLib (mj. týž, kdo psal tenhle kód),
# nevyrobil vhodnou funkci, takže to postavíme pozpátku pomocí create_child
# (které vyrábí _prvního_ syna)
create_child(reseninode, m.CastNode, nadpis="Lorem ipsum")
# Taky ten člověk nevyrobil vracení nových věcí...
castnode = reseninode.first_child
# Úvodní odstaveček
obsah = "Tohle je zamyšlení o textu lorem ipsum. Začneme a skončíme ukázkou."
text = m.Text.objects.create(
na_web=obsah,
do_cisla=obsah,
)
text.save()
create_child(reseninode, m.TextNode, text=text)
# Několik odstavců lorem ipsum
for _ in range(rnd.randint(3, 7)):
lipsum = lorem.paragraph()
text = m.Text.objects.create(
na_web=lipsum,
do_cisla=lipsum,
)
text.save()
create_child(castnode, m.TextNode, text=text)
logger.info(f"Článek vygenerován (reseni={reseni.id}, treenode={reseninode.id})")
@transaction.atomic @transaction.atomic
def create_test_data(size = 6, rnd = None): def create_test_data(size = 6, rnd = None):
logger.info('Vyrábím testovací data (size={})...'.format(size)) logger.info('Vyrábím testovací data (size={})...'.format(size))
@ -768,6 +839,8 @@ def create_test_data(size = 6, rnd = None):
# TODO: mezičíslo node # TODO: mezičíslo node
# TODO: přidat ke konferám řešení a dát je do čísel # TODO: přidat ke konferám řešení a dát je do čísel
# Dohackované vytvoření jednoho článku
gen_clanek(rnd)
# obecné nastavení semináře, musí být už přidané ročníky a čísla, jinak se nastaví divně # obecné nastavení semináře, musí být už přidané ročníky a čísla, jinak se nastaví divně

108
seminar/utils.py

@ -3,19 +3,25 @@
import datetime import datetime
from django.contrib.auth.decorators import user_passes_test from django.contrib.auth.decorators import user_passes_test
from html.parser import HTMLParser from html.parser import HTMLParser
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
import seminar.models as m import seminar.models as m
import seminar.treelib as t
staff_member_required = user_passes_test(lambda u: u.is_staff) staff_member_required = user_passes_test(lambda u: u.is_staff)
class FirstTagParser(HTMLParser): class FirstTagParser(HTMLParser):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.firstTag = None self.firstTag = None
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def handle_data(self, data): def handle_data(self, data):
if self.firstTag == None: if self.firstTag == None:
self.firstTag = data self.firstTag = data
def histogram(seznam): def histogram(seznam):
d = {} d = {}
for i in seznam: for i in seznam:
@ -24,9 +30,10 @@ def histogram(seznam):
d[i] += 1 d[i] += 1
return d return d
# Pozor: zarovnáno velmi netradičně pro přehlednost
roman_numerals = zip((1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1), roman_numerals = zip((1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1),
('M', 'CM', 'D', 'CD','C', 'XC','L','XL','X','IX','V','IV','I')) ('M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I'))
def roman(num): def roman(num):
res = "" res = ""
@ -35,6 +42,7 @@ def roman(num):
num %= i num %= i
return res return res
def from_roman(rom): def from_roman(rom):
if not rom: if not rom:
return 0 return 0
@ -58,9 +66,9 @@ def seznam_problemu():
except: except:
url = None url = None
if url: if url:
s += '<a href="%s">%s</a>, ' % (url, o.pk, ) s += '<a href="%s">%s</a>, ' % (url, o.pk,)
else: else:
s += '%s, ' % (o.pk, ) s += '%s, ' % (o.pk,)
s = s[:-2] + ']' s = s[:-2] + ']'
problemy.append(s) problemy.append(s)
@ -73,7 +81,7 @@ def seznam_problemu():
jmena[j].append(r) jmena[j].append(r)
for j in jmena: for j in jmena:
if len(jmena[j]) > 1: if len(jmena[j]) > 1:
prb(m.Resitel, 'Duplicitní jméno "%s"' % (j, ), jmena[j]) prb(m.Resitel, 'Duplicitní jméno "%s"' % (j,), jmena[j])
# Data maturity a narození # Data maturity a narození
for r in m.Resitel.objects.all(): for r in m.Resitel.objects.all():
@ -81,11 +89,99 @@ def seznam_problemu():
prb(m.Resitel, 'Neznámý rok maturity', [r]) prb(m.Resitel, 'Neznámý rok maturity', [r])
if r.rok_maturity and (r.rok_maturity < 1990 or r.rok_maturity > datetime.date.today().year + 10): if r.rok_maturity and (r.rok_maturity < 1990 or r.rok_maturity > datetime.date.today().year + 10):
prb(m.Resitel, 'Podezřelé datum maturity', [r]) prb(m.Resitel, 'Podezřelé datum maturity', [r])
if r.osoba.datum_narozeni and (r.osoba.datum_narozeni.year < 1970 or r.osoba.datum_narozeni.year > datetime.date.today().year - 12): if r.osoba.datum_narozeni and (
r.osoba.datum_narozeni.year < 1970 or r.osoba.datum_narozeni.year > datetime.date.today().year - 12):
prb(m.Resitel, 'Podezřelé datum narození', [r]) prb(m.Resitel, 'Podezřelé datum narození', [r])
# if not r.email: # if not r.email:
# prb(Resitel, u'Neznámý email', [r]) # prb(Resitel, u'Neznámý email', [r])
## Kontroly konzistence databáze a TreeNodů
# Články
for clanek in m.Clanek.objects.all():
# získáme řešení svázané se článkem a z něj node ve stromě
reseni = clanek.reseni_set
if (reseni.count() != 1):
raise ValueError("Článek k sobě má nejedno řešení!")
r = reseni.first()
clanek_node = r.text_cely # vazba na ReseniNode z Reseni
# content type je věc pomáhající rozeznávat různé typy objektů v django-polymorphic
# protože isinstance vrátí vždy jen TreeNode
# https://django-polymorphic.readthedocs.io/en/stable/migrating.html
cislonode_ct = ContentType.objects.get_for_model(m.CisloNode)
node = clanek_node
while node is not None:
node_ct = node.polymorphic_ctype
if node_ct == cislonode_ct: # dostali jsme se k CisloNode
# zkontrolujeme, že stromové číslo odpovídá atributu
# .cislonode je opačná vazba k treenode_ptr, abychom z TreeNode dostali
# CisloNode
if clanek.cislo != node.cislonode.cislo:
prb(m.Clanek, "Číslo otištění uložené u článku nesedí s "
"číslem otištění podle struktury treenodů.", [clanek])
break
node = t.get_parent(node)
return problemy return problemy
### Generovani obalek
def resi_v_rocniku(rocnik, cislo=None):
""" Vrátí seznam řešitelů, co vyřešili nějaký problém v daném ročníku, do daného čísla.
Parametry:
rocnik (typu Rocnik) ročník, ze kterého chci řešitele, co něco odevzdali
cislo (typu Cislo) číslo, do kterého včetně se počítá, že v daném
ročníku řešitel něco poslal.
Pokud není zadané, počítají se všechna řešení z daného ročníku.
Výstup:
QuerySet objektů typu Resitel """
if cislo is None:
# filtrujeme pouze podle ročníku
letosni_reseni = m.Reseni.objects.filter(hodnoceni__cislo_body__rocnik=rocnik)
else: # filtrujeme podle ročníku i čísla
letosni_reseni = m.Reseni.objects.filter(hodnoceni__cislo_body__rocnik=rocnik,
hodnoceni__cislo_body__poradi__lte=cislo.poradi)
# vygenerujeme queryset řešitelů, co letos něco poslali
letosni_resitele = m.Resitel.objects.none()
for reseni in letosni_reseni:
letosni_resitele = letosni_resitele | reseni.resitele.filter(rok_maturity__gte=rocnik.druhy_rok())
return letosni_resitele.distinct()
def aktivniResitele(cislo, pouze_letosni=False):
""" Vrací QuerySet aktivních řešitelů, což jsou ti, co ještě neodmaturovali
a letos něco poslali (anebo loni něco poslali, pokud jde o první tři čísla).
Parametry:
cislo (typu Cislo) číslo, o které se jedná
pouze_letosni jen řešitelé, kteří tento rok něco poslali
"""
letos = cislo.rocnik
# detekujeme, zda jde o první tři čísla či nikoli (tj. zda spamovat řešitele z minulého roku)
zacatek_rocniku = True
try:
if int(cislo.poradi) > 3:
zacatek_rocniku = False
except ValueError:
if cislo.poradi != '7-8':
raise ValueError(f'{cislo} je neplatné číslo čísla (není int a není 7-8)')
zacatek_rocniku = False
# nehledě na číslo chceme jen řešitele, kteří letos něco odevzdali
if pouze_letosni:
zacatek_rocniku = False
try:
loni = m.Rocnik.objects.get(rocnik=letos.rocnik - 1)
except ObjectDoesNotExist:
# Pro první ročník neexistuje ročník předchozí
zacatek_rocniku = False
if not zacatek_rocniku:
return resi_v_rocniku(letos, cislo)
else:
# spojíme querysety s řešiteli loni a letos do daného čísla
return (resi_v_rocniku(loni) | resi_v_rocniku(letos, cislo)).distinct()

93
seminar/views/views_all.py

@ -41,6 +41,8 @@ import csv
import logging import logging
import time import time
from seminar.utils import aktivniResitele, resi_v_rocniku
def verejna_temata(rocnik): def verejna_temata(rocnik):
"""Vrací queryset zveřejněných témat v daném ročníku. """Vrací queryset zveřejněných témat v daném ročníku.
@ -51,7 +53,7 @@ def temata_v_rocniku(rocnik):
return Problem.objects.filter(typ=Problem.TYP_TEMA, rocnik=rocnik) return Problem.objects.filter(typ=Problem.TYP_TEMA, rocnik=rocnik)
def get_problemy_k_tematu(tema): def get_problemy_k_tematu(tema):
return Problemy.objects.filter(nadproblem = tema) return Problem.objects.filter(nadproblem = tema)
class VlozBodyView(generic.ListView): class VlozBodyView(generic.ListView):
@ -227,10 +229,29 @@ class AktualniZadaniView(TreeNodeView):
### Titulni strana ### Titulni strana
def spravne_novinky(request):
"""
Vrátí správný QuerySet novinek, tedy ten, který daný uživatel smí vidět.
Tj. Organizátorům všechny, ostatním jen veřejné
"""
user = request.user
# Využíváme líné vyhodnocování QuerySetů
qs = Novinky.objects.all()
# TODO: Tohle by mělo spíš kontrolovat, že je/není někdo org, než že může do Adminu.
if not user.is_staff:
qs = qs.filter(zverejneno=True)
return qs.order_by('-datum')
class TitulniStranaView(generic.ListView): class TitulniStranaView(generic.ListView):
model = Novinky
template_name='seminar/titulnistrana.html' template_name='seminar/titulnistrana.html'
<<<<<<< HEAD
queryset = Novinky.objects.order_by('-datum')[:2] queryset = Novinky.objects.order_by('-datum')[:2]
=======
def get_queryset(self):
return spravne_novinky(self.request)[:5]
>>>>>>> data_migrations
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(TitulniStranaView, self).get_context_data(**kwargs) context = super(TitulniStranaView, self).get_context_data(**kwargs)
@ -267,9 +288,10 @@ class TitulniStranaView(generic.ListView):
return context return context
class StareNovinkyView(generic.ListView): class StareNovinkyView(generic.ListView):
model = Novinky
template_name = 'seminar/stare_novinky.html' template_name = 'seminar/stare_novinky.html'
queryset = Novinky.objects.filter(zverejneno=True).order_by('-datum')
def get_queryset(self):
return spravne_novinky(self.request)
### Co je M&M ### Co je M&M
@ -707,7 +729,7 @@ def vysledkovka_cisla(cislo, context=None):
## TODO možná chytřeji vybírat aktivní řešitele ## TODO možná chytřeji vybírat aktivní řešitele
# aktivní řešitelé - chceme letos něco poslal, TODO později vyfiltrujeme ty, kdo mají # aktivní řešitelé - chceme letos něco poslal, TODO později vyfiltrujeme ty, kdo mají
# u alespoň jedné hodnoty něco jiného než NULL # u alespoň jedné hodnoty něco jiného než NULL
aktivni_resitele = list(aktivniResitele(cislo.rocnik.rocnik, cislo.poradi)) aktivni_resitele = list(aktivniResitele(cislo))
# získáme body za číslo # získáme body za číslo
hlavni_problemy_slovnik, cislobody = secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy) hlavni_problemy_slovnik, cislobody = secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy)
@ -814,66 +836,9 @@ class RocnikVysledkovkaView(RocnikView):
content_type = 'text/plain; charset=UTF8' content_type = 'text/plain; charset=UTF8'
#vypise na stranku textovy obsah vyTeXane vysledkovky k okopirovani #vypise na stranku textovy obsah vyTeXane vysledkovky k okopirovani
### Generovani obalek
def resi_v_rocniku(rocnik, cislo=None):
""" Vrátí seznam řešitelů, co vyřešili nějaký problém v daném ročníku, do daného čísla.
Parametry:
rocnik (typu Rocnik) ročník, ze kterého chci řešitele, co něco odevzdali
cislo (typu Cislo) číslo, do kterého včetně se počítá, že v daném
ročníku řešitel něco poslal.
Pokud není zadané, počítají se všechna řešení z daného ročníku.
Výstup:
QuerySet objektů typu Resitel """
if cislo is None:
# filtrujeme pouze podle ročníku
letosni_reseni = Reseni.objects.filter(hodnoceni__cislo_body__rocnik = rocnik)
else: # filtrujeme podle ročníku i čísla
letosni_reseni = Reseni.objects.filter(hodnoceni__cislo_body__rocnik = rocnik,
hodnoceni__cislo_body__poradi__lte=cislo.poradi)
# vygenerujeme queryset řešitelů, co letos něco poslali
letosni_resitele = Resitel.objects.none()
for reseni in letosni_reseni:
letosni_resitele = letosni_resitele | reseni.resitele.filter(rok_maturity__gte=rocnik.druhy_rok())
return letosni_resitele.distinct()
def aktivniResitele(rocnik, cislo, pouze_realni=False):
""" Vrací QuerySet aktivních řešitelů, což jsou ti, co ještě neodmaturovali
a letos něco poslali (anebo loni něco poslali, pokud jde o první tři čísla).
Parametry:
rocnik (typu int) číslo ročníku, o který se jedná
cislo (typu int) pořadí čísla, o které se jedná
pouze_realni jen řešitelé, kteří tento rok něco poslali
"""
letos = Rocnik.objects.get(rocnik = rocnik)
#TODO: co se stane, když zadané kombinace neexistují? ošetřit
aktualni_cislo = Cislo.objects.get(rocnik = rocnik, poradi = cislo)
loni = Rocnik.objects.get(rocnik = rocnik - 1)
# detekujeme, zda jde o první tři čísla či nikoli
zacatek_rocniku = True
try:
if int(aktualni_cislo.poradi) > 3:
zacatek_rocniku = False
except ValueError:
# pravděpodobně se jedná o číslo 7-8
zacatek_rocniku = False
# nehledě na číslo chceme jen řešitele, kteří letos něco odevzdali
if pouze_realni:
zacatek_rocniku = False
if not zacatek_rocniku:
return resi_v_rocniku(letos)
else:
# spojíme querysety s řešiteli loni a letos do daného čísla
return (resi_v_rocniku(loni) | resi_v_rocniku(letos, aktualni_cislo)).distinct()
def cisloObalkyView(request, rocnik, cislo): def cisloObalkyView(request, rocnik, cislo):
return obalkyView(request, aktivniResitele(rocnik, cislo)) realne_cislo = Cislo.objects.get(poradi=cislo, rocnik__rocnik=rocnik)
return obalkyView(request, aktivniResitele(realne_cislo))
def obalkyView(request, resitele): def obalkyView(request, resitele):

Loading…
Cancel
Save