Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • mj/mo-submit
1 result
Select Git revision
Show changes
Commits on Source (16)
__pycache__
.mypy_cache
/db/ruian
/db/skoly/html
/db/skoly/parsed
/data
/extra
/mo/config.py
/osmo.egg-info
/venv
......@@ -46,7 +46,7 @@
setfacl -m u:www-data:x /akce/mo/osmo-test /akce/mo/osmo-test/var
# Inicializovat regiony v DB
# Obstarat si db/ruian/ a db/schools/parsed/ z jiné instance (nebo je znovu stáhnout)
# Obstarat si extra/ruian/ a extra/schools/parsed/ z jiné instance (nebo je znovu stáhnout)
. ../venv/bin/activate
bin/test-init # případně podmnožinu
......
......@@ -3,6 +3,7 @@
import argparse
import mo.db as db
import mo.users
import mo.util
from mo.util import die, init_standalone
......@@ -22,9 +23,9 @@ session = db.get_session()
if args.email and args.uid:
parser.error('--email a --uid nesmí být uvedeny současně')
elif args.email:
user = session.query(db.User).filter_by(email=args.email).first()
user = mo.users.user_by_email(args.email)
elif args.uid:
user = session.query(db.User).filter_by(user_id=args.uid).first()
user = mo.users.user_by_uid(args.uid)
else:
parser.error('Je nutné vybrat uživatele pomocí --email nebo --uid')
......
......@@ -17,17 +17,18 @@ parser.add_argument('--passwd', type=str, help='nastaví počáteční heslo')
parser.add_argument('--mail', default=False, action='store_true', help='pošle uživateli mail o založení účtu')
args = parser.parse_args()
email = mo.users.normalize_email(args.email)
mo.util.init_standalone()
session = db.get_session()
user = session.query(db.User).filter_by(email=args.email).one_or_none()
user = session.query(db.User).filter_by(email=email).one_or_none()
if user is not None:
print(f'Uživatel s e-mailem {args.email} již existuje (user_id {user.user_id}).')
print(f'Uživatel s e-mailem {email} již existuje (user_id {user.user_id}).')
sys.exit(0)
user = db.User(
email=args.email,
email=email,
first_name=args.first_name,
last_name=args.last_name,
is_org=args.org,
......
#!/usr/bin/env python3
# Naplní databázi školami a obcemi, v nichž školy sídlí
# Používá db/skoly/parsed/*.tsv
# Používá extra/skoly/parsed/*.tsv
#
# Pozor, zrada: rejstřík škol je sice rozdělený do okresů dle NUTS/LAU,
# ale školy tam řadí podle úřadu, u nějž je škole registrovaná, což vůbec
......@@ -182,14 +182,14 @@ ruian_obec_to_okres_nuts: DefaultDict[str, List[str]] = defaultdict(list)
def load_ruian():
ocols, okresy = load_ruian_csv('db/ruian/UI_OKRES.csv')
ocols, okresy = load_ruian_csv('extra/ruian/UI_OKRES.csv')
okres_by_id: Dict[int, List[str]] = {}
for o in okresy:
id = int(o[ocols['KOD']])
assert id not in okres_by_id
okres_by_id[id] = o
mcols, mesta = load_ruian_csv('db/ruian/UI_OBEC.csv')
mcols, mesta = load_ruian_csv('extra/ruian/UI_OBEC.csv')
for m in mesta:
jmeno = m[mcols['NAZEV']]
oid = int(m[mcols['OKRES_KOD']])
......@@ -204,7 +204,7 @@ args = parser.parse_args()
load_ruian()
for path in Path('db/skoly/parsed').glob('*.tsv'):
for path in Path('extra/skoly/parsed').glob('*.tsv'):
m = re.fullmatch(r'^[A-Z]-(CZ\w+)\.tsv', path.name)
assert m is not None
nuts = m[1]
......
# Zdroj: https://www.czso.cz/csu/czso/klasifikace-uzemnich-statistickych-jednotek-cz-nuts
# XXX: Vysočina a Jihomoravský kraj mají od roku 2008 jiné kódy, ale Rejstřík škol používá ty staré, tak se jich zatím držme.
CZ Česko
CZ0 Česko
CZ01 Praha
......
......@@ -197,6 +197,7 @@ CREATE TYPE role_type AS ENUM (
'garant', -- celostátní garant (může být omezený na kategorii)
'garant_kraj', -- krajský garant
'garant_okres', -- okresní garant
'garant_skola', -- školní garant
'dozor', -- dozor na soutěži (může odevzdávat řešení za účastníky)
'opravovatel' -- opravovatel
);
......
#!/usr/bin/env python3
from dataclasses import dataclass
import mo.csv
@dataclass
class Garant(mo.csv.Row):
kod_oblasti: str = ""
jmeno: str = ""
email: str = ""
f = open('extra/garanti/garanti.csv')
rows = mo.csv.read(f, mo.csv.FileFormat.en_csv, Garant)
for g in rows:
print(f'bin/create-user --org --mail {g.email} {g.jmeno}')
for g in rows:
if len(g.kod_oblasti) == 1:
role = 'garant_kraj'
else:
role = 'garant_okres'
print(f'bin/add-role --email {g.email} --role {role} --place {g.kod_oblasti} --cat Z')
......@@ -89,7 +89,7 @@ for line in sys.stdin:
role = re.sub(' +', ' ', role)
raw_name = re.sub('^((Mgr|Paed?Dr|Bc|doc|PhDr|RNDr)\. *)+', "", name)
raw_name = re.sub('^((Mgr|Ing|Paed?Dr|Bc|doc|PhDr|RNDr)\. *)+', "", name)
print(f': {name} -> {raw_name}', file=sys.stderr)
curr = Garant(name=name, raw_name=raw_name, role=role, emails=[], phones=[])
curr_g.append(curr)
......
#!/bin/bash
set -e
rm -rf parsed
mkdir parsed
rm -rf extra/parsed
mkdir extra/parsed
for src in html/*.html ; do
dst=parsed/$(basename $src .html).tsv
for src in extra/html/*.html ; do
dst=extra/parsed/$(basename $src .html).tsv
echo -n "$src -> "
./rejskol-parse <$src >$dst
wc -l $dst
......
......@@ -8,7 +8,7 @@ my $mech = WWW::Mechanize->new(autocheck => 1, strict_forms => 1);
$mech->get('https://rejstriky.msmt.cz/rejskol/VREJVerejne/VerejneRozhrani.aspx');
$mech->form_id('form1');
mkdir 'html';
mkdir 'extra/html';
download_type('B'); # Základní školy
download_type('C'); # Střední školy
exit 0;
......@@ -59,7 +59,7 @@ sub download_region {
sleep 1;
my $resp = $mech->click_button(id => 'btnVybrat');
open my $f, '>:utf8', "html/$type-$nuts.html";
open my $f, '>:utf8', "extra/html/$type-$nuts.html";
print $f $resp->decoded_content;
close $f;
......
SET ROLE 'mo_osmo';
ALTER TYPE role_type ADD VALUE 'garant_skola' AFTER 'garant_okres';
......@@ -348,6 +348,7 @@ class RoleType(MOEnum):
garant = auto()
garant_kraj = auto()
garant_okres = auto()
garant_skola = auto()
dozor = auto()
opravovatel = auto()
......@@ -359,6 +360,7 @@ role_type_names = {
RoleType.garant: 'celostátní garant',
RoleType.garant_kraj: 'krajský garant',
RoleType.garant_okres: 'okresní garant',
RoleType.garant_skola: 'školní garant',
RoleType.dozor: 'dozor',
RoleType.opravovatel: 'opravovatel',
}
......
......@@ -82,6 +82,25 @@ roles: List[Role] = [
Right.edit_points,
Right.add_users,
Right.edit_users,
Right.add_orgs,
Right.edit_orgs,
},
),
Role(
role=db.RoleType.garant_skola,
rights={
# FIXME: Až se pořádně rozjedou školní kola, asi chceme školním správcům omezit
# práva na editaci uživatelů. Viz issue #66.
Right.assign_rights,
Right.edit_place,
Right.manage_contest,
Right.view_submits,
Right.upload_submits,
Right.edit_points,
Right.add_users,
Right.edit_users,
Right.add_orgs,
Right.edit_orgs,
},
),
Role(
......
......@@ -188,6 +188,11 @@ class ParticipantsActionForm(FlaskForm):
return True
class DownloadButtonForm(FlaskForm):
download = wtforms.SubmitField('Stáhnout všechna řešení jako ZIP')
download_feedback = wtforms.SubmitField('Stáhnout opravená jako ZIP')
def get_contest(id: int) -> db.Contest:
contest = (db.get_session().query(db.Contest)
.options(joinedload(db.Contest.place),
......@@ -687,6 +692,23 @@ def get_solutions_query(
return query
def download_solutions(form: DownloadButtonForm, solutions: List[db.Solution], subject: str) -> bool:
if not form.validate_on_submit():
return False
if form.download.data:
paper_ids = [sol.final_submit for sol in solutions if sol.final_submit is not None]
mo.jobs.submit.schedule_download_submits(paper_ids, f'Odevzdaná řešení {subject}', g.user)
flash('Příprava odevzdaných řešení ke stažení zahájena.', 'success')
return True
if form.download_feedback.data:
paper_ids = [sol.final_feedback or sol.final_submit for sol in solutions if sol.final_submit is not None]
mo.jobs.submit.schedule_download_submits(paper_ids, f'Opravená řešení {subject}', g.user)
flash('Příprava opravených řešení ke stažení zahájena.', 'success')
return True
@app.route('/org/contest/c/<int:contest_id>/task/<int:task_id>/', methods=('GET', 'POST'))
@app.route('/org/contest/c/<int:contest_id>/site/<int:site_id>/task/<int:task_id>/', methods=('GET', 'POST'))
def org_contest_task_submits(contest_id: int, task_id: int, site_id: Optional[int] = None):
......@@ -697,10 +719,8 @@ def org_contest_task_submits(contest_id: int, task_id: int, site_id: Optional[in
rows: List[Tuple[db.Participation, db.Solution]] = q.all()
rows.sort(key=lambda r: r[0].user.sort_key())
if request.method == 'POST' and 'download' in request.form:
paper_ids = [sol.final_submit for pion, sol in rows if sol is not None and sol.final_submit is not None]
mo.jobs.submit.schedule_download_submits(paper_ids, f'Odevzdaná řešení úlohy {sc.task.code}', g.user)
flash('Příprava řešení ke stažení zahájena.', 'success')
download_form = DownloadButtonForm()
if request.method == 'POST' and download_solutions(download_form, [sol for pion, sol in rows if sol is not None], f'úlohy {sc.task.code}'):
return redirect(url_for('org_jobs'))
# Count papers for each solution
......@@ -725,6 +745,7 @@ def org_contest_task_submits(contest_id: int, task_id: int, site_id: Optional[in
sc=sc, rows=rows, paper_counts=paper_counts,
db=db, # kvůli hodnotám enumů
paper_link=paper_link,
download_form=download_form,
)
......@@ -825,10 +846,8 @@ def org_contest_solutions(id: int, site_id: Optional[int] = None):
):
paper_counts[(user_id, task_id, type.name)] = count
if request.method == 'POST' and 'download' in request.form:
paper_ids = [sol.final_submit for sol in sols if sol is not None and sol.final_submit is not None]
mo.jobs.submit.schedule_download_submits(paper_ids, 'Odevzdaná řešení', g.user)
flash('Příprava řešení ke stažení zahájena.', 'success')
download_form = DownloadButtonForm()
if request.method == 'POST' and download_solutions(download_form, [sol for sol in sols if sol is not None], sc.round.round_code()):
return redirect(url_for('org_jobs'))
task_sols: Dict[int, Dict[int, db.Solution]] = {}
......@@ -851,4 +870,5 @@ def org_contest_solutions(id: int, site_id: Optional[int] = None):
can_edit_points=sc.allow_edit_points and sc.round.state == db.RoundState.grading,
paper_link=paper_link,
db=db, # kvůli hodnotám enumů
download_form=download_form,
)
......@@ -4,7 +4,11 @@
<p>Na provedení této operace nemáte právo.
{% if g.user.is_org %}
<p>Zkontrolujte prosím v <a href='{{ url_for('user_settings') }}'>nastavení svého účtu</a>,
jaké máte přidělené role. Pokud vám nějaká role nebo právo chybí, napište prosim
garantovi své kategorie nebo správci OSMO.
{% else %}
<p>Pokud máte pocit, že byste ho mít měli, napište správci OSMO.
{% endif %}
{% endblock %}
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block body %}
{% set round = contest.round %}
{% set site_id = site.place_id if site else None %}
......@@ -86,8 +87,6 @@ konkrétní úlohu. Symbol <b>+</b> značí, že existuje více verzí dostupný
{% endfor %}
</table>
<form method=POST action="">
<button type=submit name=download value=go>Stáhnout všechna řešení jako ZIP</button>
</form>
{{ wtf.quick_form(download_form, form_type='basic') }}
{% endblock %}
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block body %}
{% set contest = sc.contest %}
{% set ct_id = contest.contest_id %}
......@@ -88,8 +89,6 @@ naleznete v detailu řešení.
{% endfor %}
</table>
<form method=POST action="">
<button type=submit name=download value=go>Stáhnout všechna řešení jako ZIP</button>
</form>
{{ wtf.quick_form(download_form, form_type='basic') }}
{% endblock %}