From a0b9a79eee6c33588de2259c0ae0c90ecc20de56 Mon Sep 17 00:00:00 2001
From: Jiri Kalvoda <jirikalvoda@kam.mff.cuni.cz>
Date: Fri, 23 Jul 2021 18:50:54 +0200
Subject: [PATCH] =?UTF-8?q?P=C5=99d=C3=A1no=20filtrov=C3=A1n=C3=AD=20org?=
=?UTF-8?q?=C5=AF=20podle=20rol=C3=AD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Closes #183
---
mo/web/org_users.py | 115 +++++++++++++++++++++++++++++----
mo/web/templates/org_orgs.html | 31 ++++++++-
2 files changed, 131 insertions(+), 15 deletions(-)
diff --git a/mo/web/org_users.py b/mo/web/org_users.py
index efa5b789..7442b123 100644
--- a/mo/web/org_users.py
+++ b/mo/web/org_users.py
@@ -1,4 +1,4 @@
-from typing import Optional
+from typing import Optional, List
from flask import render_template, g, redirect, url_for, flash, request
from flask_wtf import FlaskForm
import werkzeug.exceptions
@@ -147,43 +147,130 @@ def org_users():
can_add=rr.have_right(Right.add_users),
)
-
class OrgsFilterForm(PagerForm):
# user
search_name = wtforms.TextField("Jméno/příjmení", render_kw={'autofocus': True})
search_email = wtforms.TextField("E-mail")
- # TODO: filtering by roles?
+ search_role = wtforms.SelectMultipleField('Role', choices=db.RoleType.choices(), coerce=db.RoleType.coerce, validators=[validators.Optional()])
+ search_right_for_place_code = wtforms.StringField('Právo pro oblast', validators=[validators.Optional()])
+ search_in_place_code = wtforms.StringField('V oblasti', validators=[validators.Optional()])
+ search_place_level = wtforms.SelectMultipleField("Úroveň oblasti", choices=[(i.level, i.name) for i in db.place_levels], validators=[validators.Optional()], coerce=int)
+ search_year = wtforms.StringField('Ročník', validators=[validators.Optional()])
+ search_category = wtforms.StringField("Kategorie", validators=[validators.Optional()])
+ search_seq = wtforms.StringField("Kolo", validators=[validators.Optional()])
+
submit = wtforms.SubmitField("Filtrovat")
+ show_role_filter = wtforms.SubmitField("Zobrazit filtrování dle rolí")
+ hide_role_filter = wtforms.SubmitField("Skrýt filtrování dle rolí")
- # Výstupní hodnoty filtru, None při nepoužitém filtru, prázdná db hodnota při
- # nepovedené filtraci (neexistující místo a podobně)
+ is_role_filter = wtforms.HiddenField(default="") # "" -> skrýt. "yes" -> zobrazit
+
+ # Výstupní hodnoty filtru, None při nepoužitém filtru nebo špatné hodnotě (takové filtry ignorujeme)
f_search_name: Optional[str] = None
f_search_email: Optional[str] = None
+ f_search_role: Optional[List[db.RoleType]] = None
+ f_search_right_for_place: Optional[db.Place] = None
+ f_search_in_place: Optional[db.Place] = None
+ f_search_year: Optional[List[int]] = None
+ f_search_category: Optional[List[str]] = None
+ f_search_place_level: Optional[List[int]] = None
+ f_search_seq: Optional[List[int]] = None
+
+ def validate_search_name(self, field):
+ self.f_search_name = f"%{field.data}%"
+
+ def validate_search_email(self, field):
+ self.f_search_email = f"%{field.data}%"
+
+ def validate_search_role(self, field):
+ self.f_search_role = field.data
+
+ def validate_search_right_for_place_code(self, field):
+ self.f_search_right_for_place = db.get_place_by_code(field.data)
+ if self.f_search_right_for_place is None:
+ raise wtforms.ValidationError("Chybné označení oblasti")
+
+ def validate_search_in_place_code(self, field):
+ self.f_search_in_place = db.get_place_by_code(field.data)
+ if self.f_search_in_place is None:
+ raise wtforms.ValidationError("Chybné označení oblasti")
+
+ def validate_search_year(self, field):
+ try:
+ self.f_search_year = mo.util.parse_int_list(field.data)
+ except mo.CheckError as e:
+ raise wtforms.ValidationError(str(e))
- def validate(self):
- self.f_search_name = f"%{self.search_name.data}%" if self.search_name.data else None
- self.f_search_email = f"%{self.search_email.data}%" if self.search_email.data else None
+ def validate_search_category(self, field):
+ self.f_search_category = field.data.split(",")
+ def validate_search_place_level(self, field):
+ self.f_search_place_level = field.data
-@app.route('/org/org/')
+ def validate_search_seq(self, field):
+ try:
+ self.f_search_seq = mo.util.parse_int_list(field.data)
+ except mo.CheckError as e:
+ raise wtforms.ValidationError(str(e))
+
+ def prepare_role_filter(self):
+ if self.show_role_filter.data:
+ self.is_role_filter.data = "yes"
+ if self.hide_role_filter.data:
+ self.is_role_filter.data = ""
+ if self.is_role_filter.data:
+ del self.show_role_filter
+ else:
+ del self.hide_role_filter
+ del self.search_role
+ del self.search_right_for_place_code
+ del self.search_in_place_code
+ del self.search_place_level
+ del self.search_year
+ del self.search_category
+ del self.search_seq
+
+
+@app.route('/org/org/', methods=('GET', 'POST'))
def org_orgs():
sess = db.get_session()
rr = g.gatekeeper.rights_generic()
-
q = sess.query(db.User).filter(or_(db.User.is_admin, db.User.is_org)).options(
subqueryload(db.User.roles).joinedload(db.UserRole.place)
)
- filter = OrgsFilterForm(request.args)
- filter.validate()
+ filter = OrgsFilterForm()
+ filter.validate_on_submit()
+ filter.prepare_role_filter()
if filter.f_search_name:
q = q.filter(or_(
db.User.first_name.ilike(filter.f_search_name),
db.User.last_name.ilike(filter.f_search_name)
))
- if filter.f_search_email:
- q = q.filter(db.User.email.ilike(filter.f_search_email))
+
+ if filter.is_role_filter.data:
+ qr = sess.query(db.UserRole.user_id)
+ if filter.f_search_role is not None:
+ qr = qr.filter(db.UserRole.role.in_(filter.f_search_role))
+ if filter.f_search_category is not None:
+ qr = qr.filter(or_(db.UserRole.category.in_(filter.f_search_category), db.UserRole.category == None))
+ if filter.f_search_seq is not None:
+ qr = qr.filter(or_(db.UserRole.seq.in_(filter.f_search_seq), db.UserRole.seq == None))
+ if filter.f_search_year is not None:
+ qr = qr.filter(or_(db.UserRole.year.in_(filter.f_search_year), db.UserRole.year == None))
+ if filter.f_search_in_place is not None:
+ qr = qr.filter(db.UserRole.place_id.in_(db.place_descendant_cte(filter.f_search_in_place)))
+ if filter.f_search_right_for_place is not None:
+ qr = qr.filter(db.UserRole.place_id.in_([x.place_id for x in db.get_place_parents(filter.f_search_right_for_place)]))
+ # Po n>3 hodinách v mo.db jsem dospěl k závěru, že to hezčeji neumím (neumím vyrobit place_parents_cte)
+ if filter.f_search_place_level is not None:
+ qr = qr.filter(db.UserRole.place_id.in_(
+ sess.query(db.Place.place_id).filter(db.Place.level.in_(filter.f_search_place_level))
+ ))
+ q = q.filter(db.User.user_id.in_(qr))
+
+ q = q.order_by(db.User.user_id)
(count, q) = filter.apply_limits(q, pagesize=50)
users = q.all()
diff --git a/mo/web/templates/org_orgs.html b/mo/web/templates/org_orgs.html
index dbd6cc7e..1de29298 100644
--- a/mo/web/templates/org_orgs.html
+++ b/mo/web/templates/org_orgs.html
@@ -7,7 +7,9 @@
{% endif %}
<div class="form-frame">
-<form action="" method="GET" role="form">
+<form action="" method="POST" role="form">
+ {{ filter.csrf_token }}
+ {{ filter.is_role_filter }}
<div class="row">
<div class='col-sm-2'><strong>Filtr organizátorů</strong></div>
<div class="col-sm-3">
@@ -17,8 +19,35 @@
{{ wtf.form_field(filter.search_email, placeholder='Libovolná část e-mailu') }}
</div>
</div>
+ {% if filter.is_role_filter.data %}
+ <div class="row">
+ <div class='col-sm-2'><strong>Filtr podle rolí</strong><p>Hledá pouze organizátory, kteří mají přidělenu nějakou roli.</p></div>
+ <div class="col-sm-2">
+ {{ wtf.form_field(filter.search_role, size=filter.search_role.choices|length, class="form-control no-scroll") }}
+ </div>
+ <div class="col-sm-2">
+ {{ wtf.form_field(filter.search_year, placeholder='Např. 65-67,70') }}
+ {{ wtf.form_field(filter.search_category, placeholder='A,P,Z9') }}
+ </div>
+ <div class="col-sm-2">
+ {{ wtf.form_field(filter.search_seq, placeholder='1,3-4') }}
+ </div>
+ <div class="col-sm-2">
+ <span title="Omezí role na ty, které jsou přiděleny k dané oblasti a nebo její podoblasti.">{{ wtf.form_field(filter.search_in_place_code, placeholder='Kód oblasti') }}</span>
+ <span title="Omezí role na ty, které mají právo k dané oblasti. Tedy mohou být přiděleny i k nadřazené oblasti.">{{ wtf.form_field(filter.search_right_for_place_code, placeholder='Kód oblasti') }}</span>
+ </div>
+ <div class="col-sm-2">
+ {{ wtf.form_field(filter.search_place_level, size=filter.search_place_level.choices|length, class="form-control no-scroll" ) }}
+ </div>
+ </div>
+ {% endif %}
<div class="btn-group">
{{ wtf.form_field(filter.submit, class='btn btn-primary') }}
+ {% if filter.is_role_filter.data %}
+ {{ wtf.form_field(filter.hide_role_filter) }}
+ {% else %}
+ {{ wtf.form_field(filter.show_role_filter) }}
+ {% endif %}
{% if filter.offset.data > 0 %}
{{ wtf.form_field(filter.previous) }}
{% else %}
--
GitLab