from flask import Flask, redirect, flash, render_template, session, g, request, get_flashed_messages from wtforms import Form, BooleanField, StringField, PasswordField, validators, SubmitField, IntegerField, DateTimeField from wtforms.validators import ValidationError from flask_wtf import FlaskForm from flask_bootstrap import Bootstrap import time from datetime import datetime from flask_sqlalchemy import SQLAlchemy from sqlalchemy import exc, update import hashlib import bcrypt import os import werkzeug.exceptions import wtforms from wtforms.fields import EmailField from wtforms.widgets import NumberInput import hra.config as config import hra.web.html as html import hra.db as db from hra.web import app import hra.web.jinja_mac as jinja_mac from hra.util import hash_passwd @html.WrapAfterBuilder_decorator def BasePage(b, content): b._current_tag = html.Tag(b, "html", []) b._root_tag = b._current_tag with b.head(): b.title()("KSP hra") b.meta(name="viewport", content="width=device-width, initial-scale=1.0") b.link(rel="stylesheet", href=app.url_for('static', filename='bootstrap.min.css'), type='text/css', media="all") b.link(rel="stylesheet", href=app.url_for('static', filename='ksp-mhd.css'), type='text/css', media="all") b.link(rel="icon", type="image/png", sizes="16x16", href="static/favicon.ico") b.link(rel="shortcut icon", href=app.url_for('static', filename='img/favicon.ico')) with b.body() as body: with b.header(_class=f"flavor-{config.WEB_FLAVOR}"): with b.div(_class="content"): with b.a(href="/", title="Na hlavní stránku"): b.img(src=app.url_for('static', filename='hippo.png'), style="width: 60px;height: auto;", alt="KSP") b.h1()("Hra na soustředění KSP") with b.div(id="nav-wrapper"): with b.nav(id="main-menu", _class="content"): for item in g.menu: b.a(href=item.url)(item.name) if g.user: b.a(_class="right", href="/")(f"Přihlášen: {g.user.username}") b.a(_class="right", href="/logout")(f"Odhlásit") else: b.a(_class="right", href="/login")(f"Přihlásit") b.a(_class="right", href="/registration")(f"Registrovat") with b.main(): messages = get_flashed_messages(with_categories=True) if messages: for category, message in messages: if category == "message": category = warning b.div(_class=f"alert alert-{category}", role="alert")(message) b(*content) class OptionalIntField(wtforms.IntegerField): widget = NumberInput() def process_formdata(self, valuelist): self.data = None if valuelist: if valuelist[0]: try: self.data = int(valuelist[0]) except ValueError: raise wtforms.ValidationError('Nejedná se o číslo.') class RegistrationForm(FlaskForm): username = StringField('Jméno týmu', [validators.Length(min=2, max=25), validators.DataRequired()]) captcha = StringField('Captcha', validators=[validators.DataRequired()]) passwd = PasswordField('Heslo', [ validators.DataRequired(), validators.EqualTo('confirm', message='Passwords must match') ]) confirm = PasswordField('Heslo znovu', validators=[validators.DataRequired()]) submit = SubmitField("Založit") def validate_captcha(form, field): if field.data != config.CAPTCHA: raise ValidationError("Chyba!") class LoginForm(FlaskForm): username = StringField('Jméno týmu', [validators.DataRequired()]) passwd = PasswordField('Heslo', [validators.DataRequired()]) submit = SubmitField("Přihlásit") @app.route("/registration", methods=['GET', 'POST']) def registration(): f = RegistrationForm() if f.validate_on_submit(): u = db.User(org=False, username=f.username.data, passwd=hash_passwd(f.passwd.data)) try: db.get_session().add(u) db.get_session().commit() except exc.IntegrityError: flash("Uživatelské jméno již existuje") return render_template('registration.html', form=f) flash("Přidán nový uživatel.", 'success') return redirect("login") b = BasePage() b(jinja_mac.quick_form(f, form_type='horizontal')) return b._print_file() @app.route("/login", methods=['GET', 'POST']) def login(): f = LoginForm() if f.validate_on_submit(): p_hash=hash_passwd(f.passwd.data) user = db.get_session().query(db.User).filter_by(username=f.username.data).one_or_none() print(user, p_hash) if user and user.passwd == p_hash: session.clear() session['uid'] = user.id flash("Přihlášení hotovo.", 'success') return redirect("/") flash("Chybné jméno nebo heslo.", 'danger') b = BasePage() b(jinja_mac.quick_form(f, form_type='horizontal')) return b._print_file() @app.route("/logout") def logout(): session.clear() return redirect('/') @app.template_filter() def none_as_minus(x): return x if x is not None else '-' @app.template_filter() def round_points(x): if x is None: return None return round(x,3) @app.template_filter() def print_time(t): if t == None: return "-" return ("-" if t<0 else "") + str(abs(t)//1000//60) + ":" + ("00{0:d}".format(abs(t)//1000%60))[-2:] @app.route("/", methods=['GET', 'POST']) def web_index(): b = BasePage() return b._print_file() @app.route("/org/users") def web_users(): users = db.get_session().query(db.User).all() return render_template("org_users.html", users=users) @app.route("/org/user/", methods=['GET', 'POST']) def web_orgf_user(user_id): user = db.get_session().query(db.User).filter_by(id=user_id).one_or_none() f = FindingForm() del f.user if f.validate_on_submit(): f.fill_empty() find = Findings(user=user.id, code=f.f_code, time=f.f_time, round=get_round_id()) db.get_session().add(find) db.get_session().commit() flash(f"Kód {find.code}… přijat", 'success') f.code.data = "" return redirect(f"/org/user/{user_id}") if not user: raise werkzeug.exceptions.NotFound() calc_point(user) findings = db.get_session().query(Findings).filter_by(user=user.id, round=get_round_id()).order_by(Findings.time).all() return render_template("org_user.html", user=user, findings=findings, form=f) @app.route("/org/admin", methods=['GET', 'POST']) def web_admin(): obj_round = get_round() f_round = FormRound(obj=obj_round, prefix="r") if f_round.validate_on_submit(): f_round.populate_obj(obj_round) db.get_session().commit() return render_template("org_admin.html", f_round=f_round) @app.route("/org/act", methods=['GET', 'POST']) def web_act(): obj_act_round = db.get_session().query(ActualRound).one() f_act_round = FormActRound(obj=obj_act_round, prefix="act") if f_act_round.validate_on_submit(): f_act_round.populate_obj(obj_act_round) db.get_session().commit() return render_template("org_act.html", f_act_round=f_act_round) class SuForm(FlaskForm): username = StringField('Uživatel') time_move = OptionalIntField('Časový posun') round = OptionalIntField('Kolo') submit = SubmitField("Změnit") def validate_username(form, field): form.f_user = db.get_session().query(db.User).filter_by(username=field.data).one_or_none() if form.f_user == None and field.data: raise ValidationError("Uživatel neexistuje.") @app.route("/org/su", methods=['GET', 'POST']) def web_su(): f = SuForm() if not f.is_submitted(): f.username.data = g.user.username if f.validate_on_submit(): if not f.f_user or session['uid'] != f.f_user.id: session['uid'] = f.f_user.id if f.f_user else None flash("Uživatel vtělen!") if f.time_move.data is not None: session['time_move'] = f.time_move.data * 1000 flash("Čas vtělen!") if f.round.data is not None: session['round'] = f.round.data flash("Kolo vtěleno!") return redirect('/') return render_template("org_su.html", f=f)