diff --git a/mo/web/__init__.py b/mo/web/__init__.py index a219a25de57df98b9a44b46a46e7378160592623..26a26e7fd08743fd24987dae5bd16cb8a273576a 100644 --- a/mo/web/__init__.py +++ b/mo/web/__init__.py @@ -63,4 +63,5 @@ app.before_request(init_request) # Většina webu je v samostatných modulech import mo.web.main import mo.web.org +import mo.web.org_users import mo.web.menu diff --git a/mo/web/menu.py b/mo/web/menu.py index 077e4b0d90feac5ba00db6333b1f8c924c1672bb..e46ee11504755285564b78964026e72da224aca0 100644 --- a/mo/web/menu.py +++ b/mo/web/menu.py @@ -27,7 +27,8 @@ def get_menu(): MenuItem(url_for('org_index'), "Org"), MenuItem(url_for('org_place_root'), "Místa"), MenuItem(url_for('org_contest_root'), "Soutěž"), - MenuItem(url_for('org_users'), "Uživatelé"), + MenuItem(url_for('org_users'), "Soutěžící"), + MenuItem(url_for('org_users_orgs'), "Organizátoři"), ] # Login / user settings diff --git a/mo/web/org.py b/mo/web/org.py index a5bfa3cbcf707469197e0a640cc3a4d36356bd52..58f2ca2711e9b7966f3660d92fdbbb891b35f54c 100644 --- a/mo/web/org.py +++ b/mo/web/org.py @@ -378,11 +378,6 @@ def org_place_rights(id: int): ) -@app.route('/org/users/') -def org_users(): - return render_template('not_implemented.html') - - @app.route('/org/contest/') def org_contest_root(): sess = db.get_session() diff --git a/mo/web/org_users.py b/mo/web/org_users.py new file mode 100644 index 0000000000000000000000000000000000000000..29b2cc20ebf948973bbc4117ab9ec5c7d6c92433 --- /dev/null +++ b/mo/web/org_users.py @@ -0,0 +1,166 @@ +from operator import or_ +from flask import render_template, redirect, url_for, flash, request +from flask_wtf import FlaskForm +import werkzeug.exceptions +import wtforms + +from typing import Optional, List + +import mo +import mo.db as db +import mo.rights +import mo.util +from mo.web import app + + +class PagerForm(FlaskForm): + limit = wtforms.IntegerField() + offset = wtforms.IntegerField() + next = wtforms.SubmitField("Další") + previous = wtforms.SubmitField("Předchozí") + + +class UsersFilterForm(PagerForm): + # participants + year = wtforms.IntegerField("Ročník") + school_code = wtforms.StringField("Škola") + + # rounds->participations + round_year = wtforms.IntegerField("Ročník") + round_category = wtforms.SelectField("Kategorie", choices=['*'] + sorted(db.get_categories())) + round_seq = wtforms.SelectField("Kolo", choices=['*'] + sorted(db.get_seqs())) + contest_site_code = wtforms.StringField("Soutěžní místo") + participation_state = wtforms.SelectField('Účast', choices=[('*', '*')] + list(db.PartState.choices())) + + submit = wtforms.SubmitField("Filtrovat") + + +@app.route('/org/users/') +def org_users(): + sess = db.get_session() + + q = sess.query(db.User).filter_by(is_admin=False, is_org=False) + filter = UsersFilterForm(request.args) + + filter_errors = [] + + participant_filter = sess.query(db.Participant.user_id) + participant_filter_apply = False + if filter.year.data: + participant_filter = participant_filter.filter_by(year=filter.year.data) + participant_filter_apply = True + print(filter.school_code.data) + if filter.school_code.data: + place = db.place_by_code(filter.school_code.data) + if place: + participant_filter = participant_filter.filter_by(school=place.place_id) + participant_filter_apply = True + else: + filter_errors.append("Neexistující kód školy") + + if participant_filter_apply: + q = q.filter(db.User.user_id.in_(participant_filter)) + + round_filter = sess.query(db.Round.round_id) + round_filter_apply = False + if filter.round_year.data: + round_filter = round_filter.filter_by(year=filter.round_year.data) + round_filter_apply = True + if filter.round_category.data and filter.round_category.data != "*": + round_filter = round_filter.filter_by(category=filter.round_category.data) + round_filter_apply = True + if filter.round_seq.data and filter.round_seq.data != "*": + round_filter = round_filter.filter_by(seq=filter.round_seq.data) + round_filter_apply = True + + contest_filter = sess.query(db.Contest.contest_id) + contest_filter_apply = False + if round_filter_apply: + contest_filter = contest_filter.filter(db.Contest.round_id.in_(round_filter)) + contest_filter_apply = True + if filter.contest_site_code.data: + place = db.place_by_code(filter.contest_site_code.data) + if place: + contest_filter = contest_filter.filter_by(place_id=place.place_id) + contest_filter_apply = True + else: + filter_errors.append("Neexistující kód soutěžního místa") + + participation_filter = sess.query(db.Participation.user_id) + participation_filter_apply = False + if contest_filter_apply: + participation_filter = participation_filter.filter(db.Participation.contest_id.in_(contest_filter)) + participation_filter_apply = True + if filter.participation_state.data and filter.participation_state.data != '*': + participation_filter = participation_filter.filter_by(state=filter.participation_state.data) + participation_filter_apply = True + + if participation_filter_apply: + q = q.filter(db.User.user_id.in_(participation_filter)) + + # print(str(q)) + count = q.count() + + if not filter.offset.data: + filter.offset.data = 0 + if not filter.limit.data: + filter.limit.data = 50 + + if filter.previous.data: + filter.offset.data = max(0, filter.offset.data - 50) + if filter.next.data: + filter.offset.data = min(count // 50 * 50, filter.offset.data + 50) + + q = q.offset(filter.offset.data) + q = q.limit(filter.limit.data) + users = q.all() + + return render_template('org_users.html', users=users, count=count, filter=filter, filter_errors=filter_errors) + + +class OrgsFilterForm(PagerForm): + # TODO: filtering by roles? + submit = wtforms.SubmitField("Filtrovat") + + +@app.route('/org/users/orgs/') +def org_users_orgs(): + sess = db.get_session() + + q = sess.query(db.User).filter(or_(db.User.is_admin, db.User.is_org)) + filter = OrgsFilterForm(request.args) + # TODO: filtering by roles? + + count = q.count() + + if not filter.offset.data: + filter.offset.data = 0 + if not filter.limit.data: + filter.limit.data = 50 + + if filter.previous.data: + filter.offset.data = max(0, filter.offset.data - 50) + if filter.next.data: + filter.offset.data = min(count // 50 * 50, filter.offset.data + 50) + + q = q.offset(filter.offset.data) + q = q.limit(filter.limit.data) + users = q.all() + + return render_template('org_users_orgs.html', users=users, count=count, filter=filter, filter_errors=None) + + +@app.route('/org/user/<int:id>/') +def org_user(id: int): + return render_template('not_implemented.html') + + +@app.route('/org/user/<int:id>/edit') +def org_user_edit(id: int): + return render_template('not_implemented.html') + + +@app.route('/org/user/new/', defaults={'type': None}) +@app.route('/org/user/new/<type>/') +def org_user_new(type: Optional[str]): + return render_template('not_implemented.html') diff --git a/templates/org_users.html b/templates/org_users.html new file mode 100644 index 0000000000000000000000000000000000000000..5710cb4af4218283e447345727b9977fb3c45ccb --- /dev/null +++ b/templates/org_users.html @@ -0,0 +1,86 @@ +{% extends "base.html" %} +{% import "bootstrap/wtf.html" as wtf %} +{% block body %} +<a class="pull-right btn btn-primary" href="{{ url_for('org_user_new') }}">Nový soutěžící</a> + +<h2>Soutěžící</h2> + +{% if filter_errors %} +<div class="alert alert-danger" role="alert"> + {{ filter_errors|join("<br>") }} +</div> +{% endif %} + +<form action="" method="GET" class="form" role="form"> + <div class="row"> + <div class='col-sm-2'><strong>Účast v kole:</strong></div> + <div class="form-group col-sm-2"> + {{ wtf.form_field(filter.round_year) }} + </div> + <div class="form-group col-sm-2"> + {{ wtf.form_field(filter.round_category) }} + </div> + <div class="form-group col-sm-2"> + {{ wtf.form_field(filter.round_seq) }} + </div> + <div class="form-group col-sm-2"> + {{ wtf.form_field(filter.contest_site_code, placeholder='Kód / #ID') }} + </div> + <div class="form-group col-sm-2"> + {{ wtf.form_field(filter.participation_state) }} + </div> + </div> + <div class="row"> + <div class='col-sm-2'><strong>Registrace:</strong></div> + <div class="form-group col-sm-2"> + {{ wtf.form_field(filter.year) }} + </div> + <div class="form-group col-sm-2"> + {{ wtf.form_field(filter.school_code, placeholder='Kód / #ID') }} + </div> + <div class="form-group col-sm-6 btn-group"> + {{ wtf.form_field(filter.submit, class='btn btn-primary') }} + {% if filter.offset.data > 0 %} + {{ wtf.form_field(filter.previous) }} + {% else %} + <button class="btn" disabled>Předchozí</button> + {% endif %} + {% if count > filter.offset.data + filter.limit.data %} + {{ wtf.form_field(filter.next) }} + {% else %} + <button class="btn" disabled>Další</button> + {% endif %} + + {% set max = filter.offset.data + filter.limit.data if filter.offset.data + filter.limit.data < count else count %} + + <br><br>Zobrazuji záznamy <b>{{filter.offset.data + 1}}</b> až <b>{{ max }}</b> z <b>{{count}} nalezených soutěžících</b>. + </div> + </div> + <input type="hidden" name="offset" value="{{filter.offset.data}}"> + <input type="hidden" name="limit" value="{{filter.limit.data}}"> +</form> + +{% if users %} +<table class="table"> + <thead> + <tr> + <th>Jméno</th><th>Příjmení</th><th>E-mail</th><th>Akce</th> + </tr> + </thead> +{% for user in users %} + <tr> + <td>{{ user.first_name }}</td><td>{{ user.last_name }}</td> + <td><a href="mailto:{{ user.email }}">{{ user.email }}</a></td> + <td class='btn-group'> + <a class="btn btn-xs btn-default" href="{{ url_for('org_user', id=user.user_id) }}">Detail</a> + <a class="btn btn-xs btn-default" href="{{ url_for('org_user_edit', id=user.user_id) }}">Edit</a> + </td> + </tr> +{% endfor %} +</table> +{% else %} +Zadanému filtru nevyhovují žádní soutěžící. +{% endif %} + + +{% endblock %} diff --git a/templates/org_users_orgs.html b/templates/org_users_orgs.html new file mode 100644 index 0000000000000000000000000000000000000000..a7e54c460c0fc1cf96721af3079e641328847209 --- /dev/null +++ b/templates/org_users_orgs.html @@ -0,0 +1,60 @@ +{% extends "base.html" %} +{% import "bootstrap/wtf.html" as wtf %} +{% block body %} +<a class="pull-right btn btn-primary" href="{{ url_for('org_user_new', type='org') }}">Nový organizátor</a> + +<h2>Organizátoři</h2> + +{% if filter_errors %} +<div class="alert alert-danger" role="alert"> + {{ filter_errors|join("<br>") }} +</div> +{% endif %} + +<form action="" method="GET" class="form" role="form"> + <div class="row"> + <div class="form-group col-sm-6 btn-group"> + {% if filter.offset.data > 0 %} + {{ wtf.form_field(filter.previous) }} + {% else %} + <button class="btn" disabled>Předchozí</button> + {% endif %} + {% if count > filter.offset.data + filter.limit.data %} + {{ wtf.form_field(filter.next) }} + {% else %} + <button class="btn" disabled>Další</button> + {% endif %} + + {% set max = filter.offset.data + filter.limit.data if filter.offset.data + filter.limit.data < count else count %} + + <br><br>Zobrazuji záznamy <b>{{filter.offset.data + 1}}</b> až <b>{{ max }}</b> z <b>{{count}} nalezených organizátorů</b>. + </div> + </div> + <input type="hidden" name="offset" value="{{filter.offset.data}}"> + <input type="hidden" name="limit" value="{{filter.limit.data}}"> +</form> + +{% if users %} +<table class="table"> + <thead> + <tr> + <th>Jméno</th><th>Příjmení</th><th>E-mail</th><th>Akce</th> + </tr> + </thead> +{% for user in users %} + <tr> + <td>{{ user.first_name }}</td><td>{{ user.last_name }}</td> + <td><a href="mailto:{{ user.email }}">{{ user.email }}</a></td> + <td class='btn-group'> + <a class="btn btn-xs btn-default" href="{{ url_for('org_user', id=user.user_id) }}">Detail</a> + <a class="btn btn-xs btn-default" href="{{ url_for('org_user_edit', id=user.user_id) }}">Edit</a> + </td> + </tr> +{% endfor %} +</table> +{% else %} +Zadanému filtru nevyhovují žádní organizátoři. +{% endif %} + + +{% endblock %}