From 8a896b0e7505c5074183527799195fe27c64aafa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Pracha=C5=99?= <jan.prachar@gmail.com>
Date: Sun, 28 Feb 2021 05:47:14 +0100
Subject: [PATCH] =?UTF-8?q?Formul=C3=A1=C5=99=20na=20p=C5=99id=C3=A1n?=
 =?UTF-8?q?=C3=AD=20=C3=BA=C4=8Dastn=C3=ADka=20do=20sout=C4=9B=C5=BEe?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 mo/web/org_contest.py                         | 81 ++++++++++++++++++-
 mo/web/templates/org_contest.html             | 15 +++-
 mo/web/templates/org_contest_add_user.html    | 26 ++++++
 .../parts/org_participants_table_actions.html |  5 +-
 4 files changed, 123 insertions(+), 4 deletions(-)
 create mode 100644 mo/web/templates/org_contest_add_user.html

diff --git a/mo/web/org_contest.py b/mo/web/org_contest.py
index fdebce5c..bd786672 100644
--- a/mo/web/org_contest.py
+++ b/mo/web/org_contest.py
@@ -17,7 +17,7 @@ import mo
 from mo.csv import FileFormat
 import mo.config as config
 import mo.db as db
-from mo.imports import ImportType, create_import
+from mo.imports import ImportType, ContestImport, ContestImportRow, create_import
 import mo.jobs.submit
 from mo.rights import Right, ContestRights
 import mo.util
@@ -236,6 +236,47 @@ class ParticipantsActionForm(FlaskForm):
         return True
 
 
+class ParticipantAddForm(FlaskForm):
+    email = wtforms.StringField("E-mail", validators=[validators.Required()])
+    first_name = wtforms.StringField("Křestní jméno", validators=[validators.Required()])
+    last_name = wtforms.StringField("Příjmení", validators=[validators.Required()])
+    school_code = wtforms.StringField(
+            "Kód školy",
+            description="Viz katalog škol na tomto webu",
+            validators=[validators.Required()])
+    rocnik = wtforms.StringField(
+            "Ročník",
+            description="Pro základní školy je to číslo od 1 do 9, pro <var>k</var>-tý ročník <var>r</var>-leté střední školy má formát <var>k</var>/<var>r</var>.",
+            validators=[validators.Required()])
+    rok_naroz = wtforms.StringField("Rok narození")
+    participation_place = wtforms.SelectField('Soutěžní místo', coerce=int)
+    save = wtforms.SubmitField("Přidat")
+
+    def set_contest(self, contest_id: int, site_id: int):
+        sess = db.get_session()
+        places = (
+            sess.query(db.Place)
+            .select_from(db.Participation).join(db.Place)
+            .filter(db.Participation.contest_id == contest_id).all()
+        )
+        if len(places) == 0:
+            places = (
+                sess.query(db.Place)
+                .select_from(db.Contest).join(db.Place)
+                .filter(db.Contest.contest_id == contest_id).all()
+            )
+
+        self.participation_place.choices = [(p.place_id, p.name) for p in places]
+        if site_id:
+            self.participation_place.data = site_id
+
+    def validate_email(form, field):
+        try:
+            field.data = mo.users.normalize_email(field.data)
+        except mo.CheckError as e:
+            raise wtforms.ValidationError(str(e))
+
+
 def get_contest(id: int) -> Tuple[db.Contest, db.Contest]:
     """Vrací contest a master_contest pro zadané contest_id.
     Pro nedělená kola platí contest == master_contest.
@@ -440,6 +481,44 @@ def org_contest_import(id: int):
         contest=contest, master_contest=master_contest
     )
 
+@app.route('/org/contest/c/<int:id>/ucastnici/pridat', methods=('GET', 'POST'))
+@app.route('/org/contest/c/<int:id>/site/<int:site_id>/ucastnici/pridat', methods=('GET', 'POST'))
+def org_contest_add_user(id: int, site_id: Optional[int] = None):
+    contest, site, rr = get_contest_site_rr(id, site_id, right_needed=Right.manage_contest)
+
+    errs = None
+    form = ParticipantAddForm()
+    form.set_contest(id, site_id)
+    if form.validate_on_submit():
+        row = ContestImportRow()
+        row.email = form.email.data
+        row.krestni = form.first_name.data
+        row.prijmeni = form.last_name.data
+        row.kod_skoly = form.school_code.data
+        row.rocnik = form.rocnik.data
+        row.rok_naroz = form.rok_naroz.data
+        row.kod_mista = '#'+str(form.participation_place.data)
+
+        imp = ContestImport()
+        imp.user = g.user
+        imp.round = contest.round
+        imp.contest = contest
+        imp.gatekeeper = mo.rights.Gatekeeper(g.user)
+        imp.setup()
+        imp.import_row(row)
+        errs = imp.errors
+        if len(errs) == 0:
+            db.get_session().commit()
+            imp.notify_users()
+            return redirect(url_for('org_contest_list', id=id, site_id=site_id))
+        else:
+            db.get_session().rollback()
+
+    return render_template(
+        'org_contest_add_user.html',
+        contest=contest, site=site,
+        form=form, errs=errs
+    )
 
 @app.route('/org/contest/c/<int:id>/ucastnici', methods=('GET', 'POST'))
 @app.route('/org/contest/c/<int:id>/site/<int:site_id>/ucastnici', methods=('GET', 'POST'))
diff --git a/mo/web/templates/org_contest.html b/mo/web/templates/org_contest.html
index 36eb3b62..948a7cb0 100644
--- a/mo/web/templates/org_contest.html
+++ b/mo/web/templates/org_contest.html
@@ -73,25 +73,36 @@
 {% if places_counts %}
 <table class=data>
 	<thead>
-		<tr><th>Místo<th>Počet účastníků
+		<tr><th>Místo<th>Počet účastníků<th>Akce
 	</thead>
 	{% for (place, count) in places_counts %}
 		<tr>
 			<td><a href="{{ url_for('org_contest', id=contest.contest_id, site_id=place.place_id) }}">{{ place.name }}</a>
 			<td>{{ count }}
+			<td>
+				<a class="btn btn-xs btn-primary" href="{{ url_for('org_contest', id=contest.contest_id, site_id=place.place_id) }}">Detail</a>
+				{% if can_manage %}
+				<a class="btn btn-xs btn-default" href="{{ url_for('org_contest_add_user', id=contest.contest_id, site_id=place.place_id) }}">Přidat účastníka</a>
+				{% endif %}
 		</tr>
 	{% endfor %}
 	<tfoot>
 		<tr>
 			<th>Celkem
 			<th>{{ places_counts|sum(attribute=1) }}
+			<th>
 		</tr>
 	</tfoot>
 </table>
 {% else %}
-<i>Žádní účastníci a žádná soutěžní místa.</i>
+<p><i>Žádní účastníci a žádná soutěžní místa.</i></p>
 {% endif %}
 {% endif %}
+<div class="btn-group">
+	{% if can_manage %}
+	<a class="btn btn-default" href='{{ url_for('org_contest_add_user', id=contest.contest_id) }}'>Přidat účastníka</a>
+	{% endif %}
+</div>
 
 <h3>Úlohy</h3>
 {% if tasks %}
diff --git a/mo/web/templates/org_contest_add_user.html b/mo/web/templates/org_contest_add_user.html
new file mode 100644
index 00000000..c1c03bac
--- /dev/null
+++ b/mo/web/templates/org_contest_add_user.html
@@ -0,0 +1,26 @@
+{% extends "base.html" %}
+{% import "bootstrap/wtf.html" as wtf %}
+{% set round = contest.round %}
+{% set site_id = site.place_id if site else None %}
+
+{% block title %}
+{{ round.round_code() }}: Přidat účastníka {% if site %}do soutěžního místa {{ site.name }}{% else %}do oblasti {{ contest.place.name }}{% endif %}
+{% endblock %}
+{% block breadcrumbs %}
+{{ contest_breadcrumbs(round=round, contest=contest, site=site, action="Přidat účastníka") }}
+{% endblock %}
+
+{% block body %}
+
+{% if errs %}
+<div class="alert alert-danger" role="alert" style="white-space: pre-line">{{ "" -}}
+{% for e in errs %}
+{{ e }}
+{% endfor %}
+</div>
+{% endif %}
+
+{{ wtf.quick_form(form, form_type='simple', button_map={'save': 'primary'}) }}
+
+{% endblock %}
+
diff --git a/mo/web/templates/parts/org_participants_table_actions.html b/mo/web/templates/parts/org_participants_table_actions.html
index 24eb85aa..0eec0d02 100644
--- a/mo/web/templates/parts/org_participants_table_actions.html
+++ b/mo/web/templates/parts/org_participants_table_actions.html
@@ -4,7 +4,10 @@
 
 	{{ table.to_html() }}
 
-	<a class="btn btn-primary pull-right"
+	{% if contest %}
+	<a class="btn btn-primary" href="{{ url_for('org_contest_add_user', id=contest.contest_id, site_id=site.place_id if site else None) }}">Přidat účastníka</a>
+	{% endif %}
+	<a class="btn btn-default"
 	   title="Zobrazí emailové adresy ve snadno zkopírovatelném formátu"
 	   href="{{ url_for('org_contest_list_emails', id=id, site_id=site_id, **request.args) if contest else url_for('org_round_list_emails', id=id, **request.args) }}">
 		Vypsat e-mailové adresy
-- 
GitLab