Skip to content
Snippets Groups Projects
Select Git revision
  • 2345c9418f7efa2cf0806a841628c42ecd085801
  • devel default
  • master
  • fo
  • jirka/typing
  • fo-base
  • mj/submit-images
  • jk/issue-96
  • jk/issue-196
  • honza/add-contestant
  • honza/mr7
  • honza/mrf
  • honza/mrd
  • honza/mra
  • honza/mr6
  • honza/submit-images
  • honza/kolo-vs-soutez
  • jh-stress-test-wip
  • shorten-schools
19 results

org_round.py

Blame
  • org_round.py 5.96 KiB
    from typing import Optional, Tuple
    from flask import render_template, g, redirect, url_for, flash, request
    import locale
    import os
    import secrets
    from flask_wtf.form import FlaskForm
    from sqlalchemy.orm import joinedload
    import werkzeug.exceptions
    import wtforms
    from wtforms import validators
    from wtforms.fields.html5 import DateTimeLocalField
    
    import mo
    import mo.csv
    import mo.db as db
    import mo.imports
    import mo.rights
    import mo.util
    from mo.web import app
    from mo.web.org_contest import ImportForm, ParticipantsActionForm, ParticipantsFilterForm, get_contestants_query, make_contestant_table
    
    
    def get_round(id: int) -> db.Round:
        round = db.get_session().query(db.Round).get(id)
        if not round:
            raise werkzeug.exceptions.NotFound()
        return round
    
    
    def get_round_rr(id: int, right_needed: Optional[mo.rights.Right]) -> Tuple[db.Round, mo.rights.Rights]:
        round = get_round(id)
    
        rr = mo.rights.Rights(g.user)
        rr.get_for_round(round)
    
        if not (right_needed is None or rr.have_right(right_needed)):
            raise werkzeug.exceptions.Forbidden()
    
        return round, rr
    
    
    @app.route('/org/contest/')
    def org_rounds():
        sess = db.get_session()
    
        rounds = sess.query(db.Round).filter_by(year=mo.current_year).order_by(db.Round.year, db.Round.category, db.Round.seq)
        return render_template('org_rounds.html', rounds=rounds, level_names=mo.db.place_level_names)
    
    
    @app.route('/org/contest/r/<int:id>/')
    def org_round(id: int):
        sess = db.get_session()
        round, rr = get_round_rr(id, None)
    
        contests = (sess.query(db.Contest)
                    .filter_by(round=round)
                    .options(joinedload(db.Contest.place))
                    .all())
    
        contests.sort(key=lambda c: locale.strxfrm(c.place.name))
    
        return render_template(
            'org_round.html',
            round=round,
            contests=contests,
            level_names=mo.db.place_level_names,
            can_manage_round=rr.have_right(mo.rights.Right.manage_round),
            can_manage_contestants=rr.have_right(mo.rights.Right.manage_contest),
        )
    
    
    @app.route('/org/contest/r/<int:id>/list', methods=('GET', 'POST'))
    def org_round_list(id: int):
        round, rr = get_round_rr(id, mo.rights.Right.manage_contest)
        format = request.args.get('format', "")
    
        filter = ParticipantsFilterForm(request.args)
        filter.validate()
        query = get_contestants_query(
            round=round,
            school=db.get_place_by_code(filter.school.data),
            contest_place=db.get_place_by_code(filter.contest_place.data),
            participation_place=db.get_place_by_code(filter.participation_place.data),
            participation_state=None if filter.participation_state.data == '*' else filter.participation_state.data
        )
    
        action_form = ParticipantsActionForm()
        if action_form.do_action(round=round, rights=rr, query=query):
            # Action happened, redirect
            return redirect(request.url)
    
        (count, query) = filter.apply_limits(query, pagesize=50)
        # count = query.count()
    
        if format == "":
            table = make_contestant_table(query, add_contest_column=True, add_checkbox=True)
            return render_template(
                'org_round_list.html',
                round=round,
                table=table,
                filter=filter, count=count, action_form=action_form,
            )
        else:
            table = make_contestant_table(query)
            return table.send_as(format)
    
    
    @app.route('/org/contest/r/<int:id>/import', methods=('GET', 'POST'))
    def org_round_import(id: int):
        round, rr = get_round_rr(id, mo.rights.Right.manage_contest)
    
        form = ImportForm()
        errs = []
        if form.validate_on_submit():
            tmp_name = secrets.token_hex(16) + '.csv'
            tmp_path = os.path.join(app.instance_path, 'imports', tmp_name)
            form.file.data.save(tmp_path)
    
            imp = mo.imports.Import(g.user)
            if imp.import_contest(round, None, tmp_path):
                flash(f'Účastníci importováni (založeno {imp.cnt_new_users} uživatelů, {imp.cnt_new_participations} účastí)', 'success')
                return redirect(url_for('org_round', id=round.round_id))
            else:
                flash('Došlo k chybě při importu (detaily níže)', 'danger')
                errs = imp.errors
    
        return render_template(
            'org_round_import.html',
            round=round,
            form=form,
            errs=errs,
        )
    
    
    class RoundEditForm(FlaskForm):
        state = wtforms.SelectField("Stav kola", choices=db.RoundState.choices())
        # Only the desktop Firefox does not support datetime-local field nowadays,
        # other browsers does provide date and time picker UI :(
        submit_start = DateTimeLocalField(
            "Začátek kola", validators=[validators.Optional()],
            description="Ve formátu 2020-01-01 00:00:00"
        )
        ct_submit_end = DateTimeLocalField(
            "Konec odevzdávání pro účastníky", validators=[validators.Optional()],
            description="Ve formátu 2020-01-01 00:00:00"
        )
        pr_submit_end = DateTimeLocalField(
            "Konec odevzdávání pro dozor", validators=[validators.Optional()],
            description="Ve formátu 2020-01-01 00:00:00"
        )
        submit = wtforms.SubmitField('Uložit')
    
    
    @app.route('/org/contest/r/<int:id>/edit', methods=('GET', 'POST'))
    def org_round_edit(id: int):
        sess = db.get_session()
        round, rr = get_round_rr(id, mo.rights.Right.manage_round)
    
        form = RoundEditForm(obj=round)
        if form.validate_on_submit():
            form.populate_obj(round)
    
            if sess.is_modified(round):
                changes = db.get_object_changes(round)
    
                app.logger.info(f"Round {id} modified, changes: {changes}")
                mo.util.log(
                    type=db.LogType.round,
                    what=id,
                    details={'action': 'edit', 'changes': changes},
                )
                sess.commit()
                flash('Změny kola uloženy', 'success')
            else:
                flash(u'Žádné změny k uložení', 'info')
    
            return redirect(url_for('org_round', id=id))
    
        return render_template(
            'org_round_edit.html',
            round=round,
            form=form,
        )