Browse Source

Merge branch 'develop' into unsafe_develop

# Conflicts:
#	seminar/models.py
export_seznamu_prednasek
Jonas Havelka 3 years ago
parent
commit
b6d22d2a3b
  1. 111
      aesop/ovvpfile.py
  2. 10
      checklinks.sh
  3. 4
      galerie/views.py
  4. 1
      korektury/migrations/0011_prevod_autora_z_charField_na_Organizator.py
  5. 40
      korektury/templates/korektury/help.html
  6. 3
      korektury/urls.py
  7. 2
      odevzdavatko/templates/odevzdavatko/detail.html
  8. 3
      odevzdavatko/templates/odevzdavatko/tabulka.html
  9. 20
      seminar/management/commands/auth.py
  10. 9
      seminar/testutils.py
  11. 5
      seminar/utils.py
  12. 3
      setup/README
  13. 50
      setup/nginx/mam-test.ks.matfyz.cz
  14. 65
      setup/nginx/mam.mff.cuni.cz
  15. 10
      setup/systemd/mamweb-prod.service
  16. 10
      setup/systemd/mamweb-test.service
  17. 0
      setup/uwsgi/mamweb_prod.ini
  18. 0
      setup/uwsgi/mamweb_test.ini

111
aesop/ovvpfile.py

@ -1,81 +1,30 @@
# -*- coding: utf-8 -*-
try:
from django.http import HttpResponse
from django.utils.encoding import force_text
except:
force_text = str
class OvvpFile(object):
def __init__(self):
# { header: value, ... }
self.headers = {}
# [ 'column-name', ... ]
self.columns = []
# [ { column: value, ...}, ...]
self.rows = []
def to_lines(self):
# header
for hk in sorted(self.headers.keys()):
yield '%s\t%s\n' % (hk, self.headers[hk])
yield '\n'
# columns
yield '\t'.join([c for c in self.columns]) + '\n'
# rows
for r in self.rows:
yield '\t'.join([force_text(r[c]) for c in self.columns]) + '\n'
def to_string(self):
return ''.join(self.to_lines())
def to_HttpResponse(self):
return HttpResponse(self.to_string(), content_type='text/plain; charset=utf-8')
def parse_from(self, source, with_headers=True):
"Parse data from file, string or line iterator, overwriting self"
if isinstance(source, str) or isinstance(source, unicode):
return self.parse_from(source.split('\n'))
it = iter(source)
# header
self.headers = {}
if with_headers:
for r in it:
if isinstance(r, str):
r = r.decode('utf8')
assert isinstance(r, unicode)
r = r.rstrip('\n')
if r == u"":
break
k, v = r.split(u'\t', 1)
self.headers[k] = v
# columns
r = it.next()
if isinstance(r, str):
r = r.decode('utf8')
self.columns = [cn.strip() for cn in r.split(u'\t') if cn.strip() != ""]
# rows
self.rows = []
for r in it:
if isinstance(r, str):
r = r.decode('utf8')
r = r.rstrip('\n')
if not r:
break
rtup = r.split(u'\t')
rdict = {}
for ci in range(len(self.columns)):
rdict[self.columns[ci]] = rtup[ci]
self.rows.append(rdict)
def parse(source, with_headers=True):
o = OvvpFile()
o.parse_from(source, with_headers=with_headers)
return o
from django.http import HttpResponse
from django.utils.encoding import force_text
class OvvpFile:
def __init__(self):
# { header: value, ... }
self.headers = {}
# [ 'column-name', ... ]
self.columns = []
# [ { column: value, ...}, ...]
self.rows = []
def to_lines(self):
# header
for hk in sorted(self.headers.keys()):
yield f'{hk}\t{self.headers[hk]}\n'
yield '\n'
# columns
yield '\t'.join(self.columns) + '\n'
# rows
for r in self.rows:
yield '\t'.join([force_text(r[c]) for c in self.columns]) + '\n'
def to_string(self):
return ''.join(self.to_lines())
# Pozn: tohle je ta jediná funkce, která se reálně používá…
def to_HttpResponse(self):
return HttpResponse(self.to_string(), content_type='text/plain; charset=utf-8')

10
checklinks.sh

@ -3,8 +3,16 @@
# Props to https://www.commandlinefu.com/commands/view/8234/check-broken-links-using-wget-as-a-spider
set -eu
if test "$#" -lt 1; then
echo >&2 "Usage: $0 <wget parameters, especially URL to test>"
exit 1
fi
logfile="$(pwd)/wget.log.$(date +%FT%T)"
tmp=$(mktemp --directory)
cd "$tmp"
wget --spider -o "$logfile" -r -p -X '/soustredeni/*/fotogalerie/' "$@"
wget --spider -o "$logfile" -r -p -X '/soustredeni/*/fotogalerie/' "$@" || true # wget nejspíš skončí s chybou, že něco nestáhl…
echo "Result: (a last few lines of the file $logfile)"
sed -ne '/^Found [0-9]* broken links/,$ p' "$logfile"

4
galerie/views.py

@ -24,7 +24,7 @@ def zobrazit(galerie, request):
def cesta_od_korene(g):
"""Vrátí seznam galerií od kořene ke g"""
cesta = []
while g != None:
while g is not None:
cesta.append(g)
g = g.galerie_up
return reversed(cesta)
@ -53,7 +53,7 @@ def nahled(request, pk, soustredeni):
for g in sourozenci:
if g.pk == galerie.pk:
predchozi = minuly
if minuly != None and minuly.pk == galerie.pk:
if minuly is not None and minuly.pk == galerie.pk:
nasledujici = g
break
minuly = g

1
korektury/migrations/0011_prevod_autora_z_charField_na_Organizator.py

@ -4,7 +4,6 @@ from __future__ import unicode_literals
from django.db import migrations, models
def transform_autor(apps, schema_editor):
print
Organizator = apps.get_model('seminar', 'Organizator')
# preorgovani oprav

40
korektury/templates/korektury/help.html

@ -2,47 +2,45 @@
{% load staticfiles %}
{% block title %} Nápověda ke korigovátku {% endblock title %}
{% block title %} Nápověda ke korekturovátku {% endblock title %}
{% block content %}
<h1> Nápověda ke korigovátku</h1>
<p> Korigovátko slouží ke korigování PDF souborů. Umožňuje přidávat a komentovat
korektury a označovat je jako zanesené / irelevantní. Rovněž umožňuje o PDF
<h1> Nápověda ke korekturovátku</h1>
<p> Korekturovátko slouží k přidávání korektur do PDF souborů. Umožňuje přidávat a komentovat
korektury a označovat je jako k zanesení, zanesené nebo irelevantní. Rovněž umožňuje o PDF
říci, že jsou právě zanášeny korektury nebo že je zastaralé.
<h2> Použití </h2>
<p>
Kliknu do PDF tam, kam chci zadat korekturu, napíši text a kliknu na Oprav!
(nebo Ctrl-Enter).
Korektura se zobrazí na pravé straně červeně. Korektura nelze smazat ani
upravit.
</p><p>
(nebo Ctrl-Enter). Korektura se zobrazí na pravé straně červeně.
Pokud chci korekturu okomentovat, kliknu na ikonu <img src="{% static "korektury/imgs/comment.png" %}"/>,
napíši komentář a kliknu na Oprav! (nebo Ctrl-Enter). Komentář se zobrazí pod
původní korekturou.
</p>
<h2> Tlačítka u korektury </h2>
<ul>
<li> <img src="{% static "korektury/imgs/delete.png" %}"/> - smazat korekturu
<li> <img src="{% static "korektury/imgs/check.png" %}"/> - označt koreturu jako zanesenou
<li> <img src="{% static "korektury/imgs/cross.png" %}"/> - označit korekturu jako irelevantní
<li> <img src="{% static "korektury/imgs/delete.png" %}"/> &ndash; smazat korekturu
<li> <img src="{% static "korektury/imgs/check.png" %}"/> &ndash; označt koreturu jako zanesenou
<li> <img src="{% static "korektury/imgs/cross.png" %}"/> &ndash; označit korekturu jako irelevantní
(není to chyba, nebude zaneseno)
<li> <img src="{% static "korektury/imgs/edit.png" %}"/> - upravit text korektury
<li> <img src="{% static "korektury/imgs/comment.png" %}"/> - okomentovat korekturu
<li> <img src="{% static "korektury/imgs/tex.png" %}"/> &ndash; označt koreturu jako připravenou k zanesení
<li> <img src="{% static "korektury/imgs/edit.png" %}"/> &ndash; upravit text korektury
<li> <img src="{% static "korektury/imgs/comment.png" %}"/> &ndash; okomentovat korekturu
<li> <img src="{% static "korektury/imgs/hide.png" %}"/> &ndash; srolovat korekturu
</ul>
<h2> Stavy </h2>
<h3> Korektura </h3>
<ul>
<li> K opravě - zadaná, čeká na zanesení / zahození
<li> Zanesená - zanesená v TeXu
<li> Irelevantní - není to chyba, nebude zanesena
<li> K reakci - vyžaduje reakci od autora <i>(zatím není
implementováno)</i>
<li> K vyřešení (červená) &ndash; bug report či návrh úpravy, probíhá diskuze, zatím nerozhodnuto
<li> Zanesená (modrá) &ndash; zanesená v TeXu
<li> Irelevantní (šedá) &ndash; není to chyba, nebude zanesena
<li> K zanesení (zelená) &ndash; rozhodnuto, čeká na zanesení do TeXu
</ul>
<h3> PDF </h3>
<ul>
<li> Přidávání - probíhá přidávání korektur
<li> Zanášení - probíhá zanášení korektur do TeXu
<li> Zastaralé - PDF je zastaralé, nepřidávat nové korektury
<li> Přidávání &ndash; probíhá přidávání korektur
<li> Zanášení (žluté pozadí) &ndash; probíhá zanášení korektur do TeXu
<li> Zastaralé (červené pozadí) &ndash; PDF je zastaralé, nepřidávat nové korektury
</ul>
{% endblock content %}

3
korektury/urls.py

@ -3,8 +3,7 @@ from seminar.utils import org_required
from . import views
urlpatterns = [
path('korektury/', org_required(views.KorekturyAktualniListView.as_view()), name='korektury_list'),
path('korektury/seskupene/', org_required(views.KorekturySeskupeneListView.as_view()), name='korektury_seskupene_list'),
path('korektury/', org_required(views.KorekturySeskupeneListView.as_view()), name='korektury_list'),
path('korektury/zastarale/', org_required(views.KorekturyZastaraleListView.as_view()), name='korektury_stare_list'),
path('korektury/<int:pdf>/', org_required(views.KorekturyView.as_view()), name='korektury'),
path('korektury/help/', org_required(views.KorekturyHelpView.as_view()), name='korektury-help'),

2
odevzdavatko/templates/odevzdavatko/detail.html

@ -126,7 +126,7 @@ $(document).ready(function(){
<script type="text/javascript">
function zkontroluj_hodnoceni() {
const pocet = document.getElementById("form_set").childElementCount;
const pocet = $('.hodnoceni').length;
if (pocet === 1) { {# vydím pouze plusko #}
const vysledek = confirm("Odstranil jsi všechny problémy tohoto řešení. Nepůjde tedy dohledat přes problémy, co řeší, tj. například v došlých řešeních. Přesto odeslat?");
if (!vysledek) {

3
odevzdavatko/templates/odevzdavatko/tabulka.html

@ -17,6 +17,8 @@ Do: {{ filtr.reseni_do }}
<input type=number max="{{ aktualni_rocnik.rocnik }}" min=1 id=jiny_rocnik placeholder="Jiný ročník" value="{{ rocnik }}">
<input type=submit value="Změň ročník">
</form>
<div style="overflow-x: scroll;">
<table class="dosla_reseni">
<tr>
<td></td> {# Prázdná buňka v levém horním rohu #}
@ -45,6 +47,7 @@ Do: {{ filtr.reseni_do }}
</tr>
{% endfor %}
</table>
</div>
{% endblock %}

20
seminar/management/commands/auth.py

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
from django.core.management.base import BaseCommand
from django.contrib.sessions.models import Session
from django.contrib.auth.models import User
class Command(BaseCommand):
u"""Vypiš username přihlášeného orga s daným session_key.
Příkaz pro manage.py, který ze vstupu přečte session_key (tak, jak je
uložen v cookie sessionid) a pokud session existuje a příslušný přihlášený
uživatel právo přihlásit se do admina, vypíše jeho username.
"""
def handle(self, *args, **options):
session_key = raw_input()
s = Session.objects.get(pk=session_key).get_decoded()
user_id = s['_auth_user_id']
user = User.objects.get(pk=user_id)
if user.is_staff:
print(user.username)

9
seminar/testutils.py

@ -77,8 +77,8 @@ def gen_osoby(rnd, size):
prezdivka = rnd.choice(prezdivky)
email = "@".join([unidecode.unidecode(jmeno), rnd.choice(domain)])
telefon = "".join([str(rnd.choice([k for k in range(10)])) for i in range(9)])
narozeni = datetime.date(rnd.randint(1980, 2020), rnd.randint(1, 12),
rnd.randint(1, 28))
narozeni = datetime.date(rnd.randint(1980, datetime.datetime.now().year),
rnd.randint(1, 12), rnd.randint(1, 28))
ulic = rnd.choice(seznam_ulic)
cp = rnd.randint(1, 99)
ulice = " ".join([ulic, str(cp)])
@ -142,7 +142,7 @@ def gen_resitele(rnd, osoby, skoly):
os.save()
os.user.user_permissions.add(resitel_perm)
resitele.append(Resitel.objects.create(osoba=os, skola=rnd.choice(skoly),
rok_maturity=rnd.randint(2019, 2029),
rok_maturity=os.datum_narozeni.year + rnd.randint(18, 21),
zasilat=rnd.choice(Resitel.ZASILAT_CHOICES)[0]))
return resitele
@ -890,6 +890,9 @@ def create_test_data(size = 6, rnd = None):
# Dohackované vytvoření jednoho článku
gen_clanek(rnd, organizatori, resitele)
# TODO: přidat články včetně zařazení do struktury treenodů,
# a následně otestovat konsistency check databáze z utils.py
# pomocí stránky /stav
# obecné nastavení semináře, musí být už přidané ročníky a čísla, jinak se nastaví divně
nastaveni = Nastaveni.objects.create(

5
seminar/utils.py

@ -88,6 +88,11 @@ def from_roman(rom):
def seznam_problemu():
"""Funkce pro hledání nekonzistencí v databázi a dalších nežádoucích stavů webu/databáze.
Nijak nesouvisí s Problémy zadanými řešitelům."""
# FIXME: přejmenovat funkci?
# FIXME: Tak, jak je napsaná, asi spíš patří někam k views a ne do utils (?)
problemy = []
# Pomocna fce k formatovani problemovych hlasek

3
setup/README

@ -0,0 +1,3 @@
Tato složka obsahuje různé konfiguráky potřebné k rozběhnutí webu na serveru.
TODO: Napsat sem i přehled toho, jak to funguje.

50
setup/nginx/mam-test.ks.matfyz.cz

@ -0,0 +1,50 @@
server {
listen 195.113.20.177:80;
listen [2001:718:1e03:801::b1]:80;
server_name mam-test.ks.matfyz.cz;
return 301 https://$server_name$request_uri;
}
server {
# SSL configuration
listen 195.113.20.177:443 ssl;
listen [2001:718:1e03:801::b1]:443 ssl;
# SSL keys
ssl on;
ssl_certificate /etc/letsencrypt/live/mam-test.ks.matfyz.cz/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/mam-test.ks.matfyz.cz/privkey.pem; # managed by Certbot
ssl_dhparam /etc/ssl/dhparams.pem;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 5m;
server_name mam-test.ks.matfyz.cz;
client_max_body_size 50M;
auth_basic "MaMweb test - access restricted";
auth_basic_user_file /akce/mam/www/mamweb-test/.htpasswd;
location /static/ {
root /akce/mam/www/mamweb-test/;
}
location /media/ {
root /akce/mam/www/mamweb-test/;
}
location /aesop-export/ {
auth_basic "AESOP API";
auth_basic_user_file /akce/mam/www/mamweb-test/.htpasswd-aesop;
try_files $uri @mamweb_test;
}
location / { try_files $uri @mamweb_test; }
location @mamweb_test {
include uwsgi_params;
uwsgi_pass unix:/tmp/uwsgi-mamweb_test.sock;
}
}

65
setup/nginx/mam.mff.cuni.cz

@ -0,0 +1,65 @@
server {
listen 195.113.20.177:80;
listen [2001:718:1e03:801::b1]:80;
server_name mam.mff.cuni.cz;
return 301 https://$server_name$request_uri;
}
server {
# SSL configuration
#
listen 195.113.20.177:443 ssl;
listen [2001:718:1e03:801::b1]:443 ssl;
# SSL keys
ssl on;
ssl_certificate /etc/ssl/domains/mam.mff.cuni.cz/bundle.pem;
ssl_certificate_key /etc/ssl/domains/mam.mff.cuni.cz/privkey.pem;
ssl_dhparam /etc/ssl/dhparams.pem;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 5m;
server_name mam.mff.cuni.cz;
# server_name mamweb.bezva.org;
client_max_body_size 50M;
location /aesop-export/ {
auth_basic "AESOP API";
auth_basic_user_file /akce/mam/www/mamweb-prod/.htpasswd-aesop;
try_files $uri @mamweb_prod;
}
location /static/ {
root /akce/mam/www/mamweb-prod/;
}
location /media/ {
root /akce/mam/www/mamweb-prod/;
}
location /wiki/ {
proxy_pass http://127.0.0.1:5001/;
proxy_set_header X-Real_IP $remote_addr;
proxy_redirect / /wiki/;
#rewrite '/' '/wiki';
sub_filter_once off;
sub_filter 'href="/' 'href="/wiki/';
sub_filter 'src="/' 'src="/wiki/';
sub_filter 'action="/' 'action="/wiki/';
# Overkill:
#sub_filter '="/' '="/wiki/';
#sub_filter ':5001/' '/wiki/';
#sub_filter 'Location: /' 'Location: /wiki/';
#sub_filter '_login' '_test';
}
location / { try_files $uri @mamweb_prod; }
location @mamweb_prod {
include uwsgi_params;
uwsgi_pass unix:/tmp/uwsgi-mamweb_prod.sock;
}
}

10
setup/systemd/mamweb-prod.service

@ -0,0 +1,10 @@
[Unit]
Description=uWSGI instance to serve mam.mff.cuni.cz
After=network.target
[Service]
WorkingDirectory=/akce/mam/www/mamweb-prod
ExecStart=/usr/bin/uwsgi --ini mamweb_prod.ini
[Install]
WantedBy=default.target

10
setup/systemd/mamweb-test.service

@ -0,0 +1,10 @@
[Unit]
Description=uWSGI instance to serve mam-test.kam.mff.cuni.cz
After=network.target
[Service]
WorkingDirectory=/akce/mam/www/mamweb-test
ExecStart=/usr/bin/uwsgi --ini mamweb_test.ini
[Install]
WantedBy=default.target

0
mamweb_prod.ini → setup/uwsgi/mamweb_prod.ini

0
mamweb_test.ini → setup/uwsgi/mamweb_test.ini

Loading…
Cancel
Save