diff --git a/mo/web/org_contest.py b/mo/web/org_contest.py
index fb69c52daaa1e440b2c48f3301bdd7f8700364ac..37d88c21175eabbbd90debd11b899b93293b5719 100644
--- a/mo/web/org_contest.py
+++ b/mo/web/org_contest.py
@@ -9,11 +9,13 @@ from sqlalchemy.orm import joinedload, aliased
from sqlalchemy.orm.query import Query
from sqlalchemy.dialects.postgresql import insert as pgsql_insert
from typing import Any, List, Tuple, Optional, Sequence, Dict
+import urllib.parse
import werkzeug.exceptions
import wtforms
import mo
from mo.csv import FileFormat
+import mo.config as config
import mo.db as db
from mo.imports import ImportType, create_import
import mo.jobs.submit
@@ -435,9 +437,11 @@ def org_contest_import(id: int):
@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'))
+@app.route('/org/contest/c/<int:id>/ucastnici/emails', endpoint="org_contest_list_emails")
+@app.route('/org/contest/c/<int:id>/site/<int:site_id>/ucastnici/emails', endpoint="org_contest_list_emails")
def org_contest_list(id: int, site_id: Optional[int] = None):
contest, master_contest, site, rr = get_contest_site_rr(id, site_id, Right.view_contestants)
- can_edit = rr.have_right(Right.manage_contest)
+ can_edit = rr.have_right(Right.manage_contest) and request.endpoint != 'org_contest_list_emails'
format = request.args.get('format', "")
filter = ParticipantsFilterForm(request.args)
@@ -458,14 +462,27 @@ def org_contest_list(id: int, site_id: Optional[int] = None):
return redirect(request.url)
if format == "":
- # (count, query) = filter.apply_limits(query, pagesize=50)
- count = db.get_count(query)
+ table = None
+ emails = None
+ mailto_link = None
+ if request.endpoint == 'org_contest_list_emails':
+ users = [pion.user for (pion, _, _) in query.all()]
+ emails = [f'{u.first_name} {u.last_name} <{u.email}>' for u in users]
+ mailto_link = (
+ 'mailto:' + urllib.parse.quote(config.MAIL_CONTACT, safe='@')
+ + '?subject=' + urllib.parse.quote('[OSMO] Zpráva pro účastníky')
+ + '&bcc=' + ','.join([urllib.parse.quote(email, safe='@') for email in emails])
+ )
+ count = len(users)
+ else:
+ # (count, query) = filter.apply_limits(query, pagesize=50)
+ count = db.get_count(query)
+ table = make_contestant_table(query, add_checkbox=can_edit)
- table = make_contestant_table(query, add_checkbox=can_edit)
return render_template(
'org_contest_list.html',
contest=contest, site=site,
- table=table,
+ table=table, emails=emails, mailto_link=mailto_link,
filter=filter, count=count, action_form=action_form,
)
else:
diff --git a/mo/web/org_round.py b/mo/web/org_round.py
index 766193bef61cd865bc5c5ce7413758181e1df9ab..2853dbc82330fdeb78dbbd0b0703fa9e97c3fdef 100644
--- a/mo/web/org_round.py
+++ b/mo/web/org_round.py
@@ -5,12 +5,14 @@ from sqlalchemy import func
from sqlalchemy.orm import joinedload
from sqlalchemy.sql.functions import coalesce
from typing import Optional, Tuple
+import urllib.parse
import werkzeug.exceptions
import wtforms
from wtforms import validators, ValidationError
from wtforms.fields.html5 import IntegerField
import mo
+import mo.config as config
import mo.db as db
import mo.imports
from mo.rights import Right, RoundRights
@@ -338,8 +340,10 @@ def org_round_task_batch_points(round_id: int, task_id: int):
@app.route('/org/contest/r/<int:id>/list', methods=('GET', 'POST'))
+@app.route('/org/contest/r/<int:id>/list/emails', endpoint="org_round_list_emails")
def org_round_list(id: int):
- round, master_round, r = get_round_rr(id, Right.view_contestants, True)
+ round, master_round, rr = get_round_rr(id, Right.view_contestants, True)
+ can_edit = rr.have_right(Right.manage_round) and request.endpoint != 'org_round_list_emails'
format = request.args.get('format', "")
filter = ParticipantsFilterForm(request.args)
@@ -352,20 +356,36 @@ def org_round_list(id: int):
participation_state=filter.f_participation_state,
)
- action_form = ParticipantsActionForm()
- if action_form.do_action(round=master_round, query=query):
- # Action happened, redirect
- return redirect(request.url)
+ action_form = None
+ if can_edit:
+ action_form = ParticipantsActionForm()
+ if action_form.do_action(round=master_round, query=query):
+ # Action happened, redirect
+ return redirect(request.url)
if format == "":
- (count, query) = filter.apply_limits(query, pagesize=50)
- # count = db.get_count(query)
+ table = None
+ emails = None
+ mailto_link = None
+ if request.endpoint == 'org_round_list_emails':
+ users = [pion.user for (pion, _, _) in query.all()]
+ emails = [f'{u.first_name} {u.last_name} <{u.email}>' for u in users]
+ emails.append("můj@supeč---email.cz")
+ mailto_link = (
+ 'mailto:' + urllib.parse.quote(config.MAIL_CONTACT, safe='@')
+ + '?subject=' + urllib.parse.quote('[OSMO] Zpráva pro účastníky')
+ + '&bcc=' + ','.join([urllib.parse.quote(email, safe='@') for email in emails])
+ )
+ count = len(users)
+ else:
+ (count, query) = filter.apply_limits(query, pagesize=50)
+ # count = db.get_count(query)
+ table = make_contestant_table(query, add_contest_column=True, add_checkbox=True)
- table = make_contestant_table(query, add_contest_column=True, add_checkbox=True)
return render_template(
'org_round_list.html',
round=round,
- table=table,
+ table=table, emails=emails, mailto_link=mailto_link,
filter=filter, count=count, action_form=action_form,
)
else:
diff --git a/mo/web/templates/org_contest_list.html b/mo/web/templates/org_contest_list.html
index 3f817038d356c560a25fe8d42ccd1100057b4eff..a2f85591a86dc80a8b3a45a2c5dde6becd4d48a1 100644
--- a/mo/web/templates/org_contest_list.html
+++ b/mo/web/templates/org_contest_list.html
@@ -8,6 +8,8 @@ Seznam účastníků {% if site %}soutěžního místa {{ site.name }}{% else %}
{% block breadcrumbs %}
{{ contest_breadcrumbs(round=round, contest=contest, site=site, action="Seznam účastníků") }}
{% endblock %}
+{% set id = contest.contest_id %}
+{% set site_id = site.place_id if site else None %}
{% block body %}
<div class="form-frame">
@@ -20,8 +22,10 @@ Seznam účastníků {% if site %}soutěžního místa {{ site.name }}{% else %}
{{ wtf.form_field(filter.participation_state) }}
<div class="btn-group">
{{ wtf.form_field(filter.submit, class='btn btn-primary') }}
+ {% if table %}
<button class="btn btn-default" name="format" value="cs_csv" title="Stáhnout celý výsledek v CSV">↓ CSV</button>
<button class="btn btn-default" name="format" value="tsv" title="Stáhnout celý výsledek v TSV">↓ TSV</button>
+ {% endif %}
</div>
{% if not site %}
</div>
diff --git a/mo/web/templates/org_round_list.html b/mo/web/templates/org_round_list.html
index 417006a92f9cc1c543874a4bc3d150c5c0bce603..544a9eb5ee5c2a5999829a1a63ca2de817979817 100644
--- a/mo/web/templates/org_round_list.html
+++ b/mo/web/templates/org_round_list.html
@@ -5,6 +5,7 @@
{% block breadcrumbs %}
{{ contest_breadcrumbs(round=round, action="Seznam účastníků") }}
{% endblock %}
+{% set id = round.round_id %}
{% block body %}
<div class="form-frame">
@@ -18,10 +19,12 @@
<div class="form-row" style="margin-top: 5px;">
<div class="btn-group">
{{ wtf.form_field(filter.submit, class='btn btn-primary') }}
+ {% if table %}
<button class="btn btn-default" name="format" value="cs_csv" title="Stáhnout celý výsledek v CSV">↓ CSV</button>
<button class="btn btn-default" name="format" value="tsv" title="Stáhnout celý výsledek v TSV">↓ TSV</button>
+ {% endif %}
</div>
-
+ {% if table %}
<div style="float: right">
Stránka {{ filter.offset.data // filter.limit.data + 1}} z {{ (count / filter.limit.data)|round(0, 'ceil')|int }}:
<div class="btn-group">
@@ -41,6 +44,9 @@
</div>
{% set max = filter.offset.data + filter.limit.data if filter.offset.data + filter.limit.data < count else count %}
Zobrazuji záznamy <b>{{filter.offset.data + 1}}</b> až <b>{{ max }}</b> z <b>{{count}} nalezených účastníků</b>.
+ {% else %}
+ Celkem {{count}} nalezených účastníků.
+ {% endif %}
</div>
</form>
</div>
diff --git a/mo/web/templates/parts/org_participants_table_actions.html b/mo/web/templates/parts/org_participants_table_actions.html
index dc98b92903e4d0cd2978b840a025a42e22e83d39..d06c8922675b976e5076fb8bec759e1709624d7d 100644
--- a/mo/web/templates/parts/org_participants_table_actions.html
+++ b/mo/web/templates/parts/org_participants_table_actions.html
@@ -1,7 +1,18 @@
{% if action_form %}
<form action="" method="POST" class="form form-horizontal" role="form">
+{% endif %}
+
+{% if table %}
{{ table.to_html() }}
+ <a class="btn btn-primary pull-right"
+ 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 emailové adresy
+ </a>
+{% endif %}
+
+{% if action_form %}
{{ action_form.csrf_token }}
<h3>Provést akci</h3>
<div class="form-frame">
@@ -44,9 +55,28 @@
</div>
</div>
</form>
-{% else %}
-{{ table.to_html() }}
-<p>
-<i>Nemáte právo k editaci účastníků v této oblasti.</i>
-</p>
+{% elif table %}
+<p><i>Nemáte právo k editaci účastníků v této oblasti.</i></p>
+{% elif emails %}
+<a class="btn btn-default pull-right" style="margin-top: 15px;"
+ href="{{ url_for('org_contest_list', id=id, site_id=site_id, **request.args) if contest else url_for('org_round_list', id=id, **request.args) }}">
+ Zpět na tabulku účastníků
+</a>
+
+<h3>Emailové adresy</h3>
+<div class="form-frame">
+<p>Pokud máte emailového klienta, který umí <code>mailto:</code> odkazy, tak vám následující tlačítko předvyplní nový email:
+<a class="btn btn-primary" href="{{ mailto_link }}">Vytvořit email ({{ count|inflected("adresát", "adresáti", "adresátů") }})</a>
+
+<p>Emailové adresy si také můžete zkopírovat z následujícího pole. Prosím posílejte jako <b>skrytou kopii</b>, ať účastníci nevidí navzájem své emaily.</p>
+<code><textarea id="emails-textarea" class="form-control" readonly style="resize: none;" onclick="this.focus(); this.select();">
+{% for email in emails %}
+{{ email|escape }}
+{% endfor %}
+</textarea></code>
+</div>
+<script type="text/javascript">
+document.getElementById('emails-textarea').style.height = (document.getElementById('emails-textarea').scrollHeight + 5) + "px";
+</script>
+
{% endif %}