diff --git a/db/db.ddl b/db/db.ddl
index be92253b1738e6992debdced44cf5e55804545fb..7b3dc02526a384c48b54481f366ea20035a5e8e1 100644
--- a/db/db.ddl
+++ b/db/db.ddl
@@ -404,6 +404,7 @@ CREATE TABLE reg_requests (
 -- Statistiky
 
 -- Pro každý region spočítáme všechna podřízená místa
+-- (každý region je svým vlastním potomkem)
 CREATE VIEW region_descendants AS
 	WITH RECURSIVE descendant_regions(place_id, descendant) AS (
 		SELECT place_id, place_id FROM places
diff --git a/mo/rights.py b/mo/rights.py
index b9724ca31669eae40b3e3492b3d092681037b1fc..fc82662101aee292a4070209dbf22517d93d4a0a 100644
--- a/mo/rights.py
+++ b/mo/rights.py
@@ -146,6 +146,7 @@ roles: List[Role] = [
         rights={
             Right.view_contestants,
             Right.view_submits,
+            Right.upload_solutions,
             Right.upload_feedback,
             Right.edit_points,
             Right.view_statement,
diff --git a/mo/web/org.py b/mo/web/org.py
index e27f3587ade8e6c2e6a70b9587400f91585095be..d1b0087277136f9d741d7931e31b144634b16f7e 100644
--- a/mo/web/org.py
+++ b/mo/web/org.py
@@ -1,8 +1,9 @@
+from collections import defaultdict
 from dataclasses import dataclass, field
 from flask import render_template, redirect, url_for, request, flash, g
 from sqlalchemy import and_, or_, tuple_, not_
 from sqlalchemy.orm import aliased, joinedload
-from typing import List, Set, Optional, Dict, Tuple
+from typing import List, Set, Optional, Tuple, DefaultDict
 
 import mo.config as config
 import mo.db as db
@@ -66,19 +67,16 @@ def org_index():
            .outerjoin(db.Contest, and_(db.Contest.round_id == db.Round.round_id, db.Contest.place_id == db.UserRole.place_id))
            .all())
 
-    # Soutěžní místa, ke kterým máme roli dozoru nebo opravovatele
+    # Soutěžní místa, ke kterým máme nějakou roli
     contest_place = aliased(db.Place)
     place_rcu = (rcu_base
                  .filter(not_(db.Round.state.in_([db.RoundState.preparing, db.RoundState.closed])))   # for performance
-                 .filter(db.Place.level == 4)
-                 .filter(db.Place.level > db.Round.level)
-                 .join(db.RegionDescendant, db.RegionDescendant.descendant == db.Place.place_id)
-                 .join(contest_place, db.RegionDescendant.region == contest_place.place_id)
-                 .filter(contest_place.level == db.Round.level)
-                 .join(db.Contest, and_(db.Contest.round_id == db.Round.round_id, db.Contest.place_id == contest_place.place_id))
+                 .filter(db.Place.level >= db.Round.level)
+                 .join(db.Contest, db.Contest.round_id == db.Round.round_id)
                  .filter(sess.query(db.Participation)
                          .filter(and_(db.Participation.contest_id == db.Contest.contest_id,
-                                      db.Participation.place_id == db.Place.place_id))
+                                      db.Participation.place_id == db.Place.place_id,
+                                      db.Participation.place_id != db.Contest.place_id))
                          .exists())
                  .all())
 
@@ -94,7 +92,7 @@ def org_index():
         if ct is None and ur.place.level == r.level:
             continue
         o = overview[-1] if overview else None
-        if not (o and o.round == r and o.place == ur.place):
+        if not (o and o.round == r and o.place == ur.place and o.contest == ct):
             o = OrgOverview(round=r, place=ur.place, contest=ct)
             overview.append(o)
         o.role_set.add(ur.role)
@@ -111,38 +109,39 @@ def org_index():
 def get_stats(overview: List[OrgOverview]) -> None:
     sess = db.get_session()
 
-    over_by_round_place: Dict[Tuple[int, int], OrgOverview] = {}
+    requests: DefaultDict[Tuple[int, int], List[OrgOverview]] = defaultdict(list)
     rcs_for: List[Tuple[int, int]] = []
     rps_for: List[Tuple[int, int]] = []
     for o in overview:
-        rp = (o.round.round_id, o.place.place_id)
-        over_by_round_place[rp] = o
         if o.contest:
             o.num_contests = 1
             o.contest_states.add(o.contest.state)
+            rp = (o.round.round_id, o.contest.place_id)
         else:
+            rp = (o.round.round_id, o.place.place_id)
             rcs_for.append(rp)
         rps_for.append(rp)
+        requests[rp].append(o)
 
     if rcs_for:
         rcss = (sess.query(db.RegionContestStat)
                     .filter(tuple_(db.RegionContestStat.round_id, db.RegionContestStat.region).in_(rcs_for))
                     .all())
         for rcs in rcss:
-            o = over_by_round_place[(rcs.round_id, rcs.region)]
-            o.num_contests += rcs.count
-            o.contest_states.add(rcs.state)
+            for o in requests[(rcs.round_id, rcs.region)]:
+                o.num_contests += rcs.count
+                o.contest_states.add(rcs.state)
 
     if rps_for:
         rpss = (sess.query(db.RegionParticipantStat)
                     .filter(tuple_(db.RegionParticipantStat.round_id, db.RegionParticipantStat.region).in_(rps_for))
                     .all())
         for rps in rpss:
-            o = over_by_round_place[(rps.round_id, rps.region)]
-            if rps.state == db.PartState.active:
-                o.num_active_pants += rps.count
-            elif rps.state == db.PartState.registered:
-                o.num_unconfirmed_pants += rps.count
+            for o in requests[(rps.round_id, rps.region)]:
+                if rps.state == db.PartState.active:
+                    o.num_active_pants += rps.count
+                elif rps.state == db.PartState.registered:
+                    o.num_unconfirmed_pants += rps.count
 
 
 def filter_overview(overview: List[OrgOverview]) -> List[OrgOverview]:
diff --git a/mo/web/org_contest.py b/mo/web/org_contest.py
index 56d4231beca7af823c32cf2d76f3a607c8753a5e..f9015ea49eff73dcf81bb1f2fb0d90b1fa85a746 100644
--- a/mo/web/org_contest.py
+++ b/mo/web/org_contest.py
@@ -1861,7 +1861,7 @@ def org_contest_scans(ct_id: int, site_id: Optional[int] = None):
     round, contest, site = ctx.round, ctx.contest, ctx.site
     assert contest
 
-    if not ctx.rights.can_upload_solutions():
+    if not (ctx.rights.can_upload_solutions() or ctx.rights.can_upload_feedback()):
         raise werkzeug.exceptions.Forbidden()
 
     sess = db.get_session()
@@ -2108,3 +2108,16 @@ def org_contest_scans_file(ct_id: int, job_id: int, file: str, site_id: Optional
         return send_file(path)
     else:
         raise werkzeug.exceptions.NotFound()
+
+
+@app.route('/org/contest/c/<int:ct_id>/task-statement/zadani.pdf')
+@app.route('/org/contest/c/<int:ct_id>/site/<int:site_id>/task-statement/zadani.pdf')
+@app.route('/org/contest/r/<int:round_id>/task-statement/zadani.pdf')
+def org_task_statement(round_id: Optional[int] = None, ct_id: Optional[int] = None, site_id: Optional[int] = None):
+    ctx = get_context(round_id=round_id, ct_id=ct_id, site_id=site_id)
+
+    if not ctx.rights.can_view_statement():
+        app.logger.warn(f'Organizátor #{g.user.user_id} chce zadání, na které nemá právo')
+        raise werkzeug.exceptions.Forbidden()
+
+    return mo.web.util.send_task_statement(ctx.round)
diff --git a/mo/web/org_round.py b/mo/web/org_round.py
index 799ec890ad3b30bb8b74762a694428a7066e9322..ff7c75198cc140d6d37455d0e19c39b9079c5c88 100644
--- a/mo/web/org_round.py
+++ b/mo/web/org_round.py
@@ -526,17 +526,6 @@ def _time_crossed(first_field: mo_fields.DateTime, second_field: mo_fields.DateT
     return first is not None and second is not None and first > second
 
 
-@app.route('/org/contest/r/<int:round_id>/task-statement/zadani.pdf')
-def org_task_statement(round_id: int):
-    ctx = get_context(round_id=round_id)
-
-    if not ctx.rights.can_view_statement():
-        app.logger.warn(f'Organizátor #{g.user.user_id} chce zadání, na které nemá právo')
-        raise werkzeug.exceptions.Forbidden()
-
-    return mo.web.util.send_task_statement(ctx.round)
-
-
 class StatementEditForm(FlaskForm):
     file = flask_wtf.file.FileField("Soubor", render_kw={'autofocus': True})
     upload = wtforms.SubmitField('Nahrát')
diff --git a/mo/web/templates/org_contest.html b/mo/web/templates/org_contest.html
index 9606dfe6640c497d53eb00df9992ff537dff1c2b..b6316dfe0fac158f765622708fc2ba22d57f2adc 100644
--- a/mo/web/templates/org_contest.html
+++ b/mo/web/templates/org_contest.html
@@ -47,7 +47,7 @@
 	<tr><td>Zadání<td>
 {% if round.tasks_file %}
 	{% if can_view_statement %}
-		<a href='{{ ctx.url_for('org_task_statement', ct_id=None) }}'>stáhnout</a>
+		<a href='{{ ctx.url_for('org_task_statement') }}'>stáhnout</a>
 	{% elif possible_rights_elsewhere %}
 		viz stránka vaší soutěže/místa&nbsp;{{ rights_elsewhere_info() }}
 	{% else %}
diff --git a/mo/web/templates/org_index.html b/mo/web/templates/org_index.html
index 53ad47d0ed2dc671afc6040a1668a2e34d1cd1b7..4d75a3ee68a8914e6598fb782973d4fec9676864 100644
--- a/mo/web/templates/org_index.html
+++ b/mo/web/templates/org_index.html
@@ -15,7 +15,7 @@
 			<th>Kolo
 			<th>Místo
 			<th>Stav
-			<th class='has-tip' title='S=soutěže, U=soutěžící účastníci, P=přihlášky čekající na potvrzení'>Statistiky
+			<th class='has-tip' title='S=soutěže, U=soutěžící účastníci, P=přihlášky čekající na potvrzení. U soutěžních míst se zobrazuje celkový počet účastníků soutěže.'>Statistiky
 			<th>Moje role
 			<th>Odkazy
 		</thead>
@@ -42,7 +42,7 @@
 			{% if o.place == o.contest.place %}
 			<td>{{ o.place.name }}
 			{% else %}
-			<td><i>{{ o.place.name_locative() }}</i>
+			<td>{{ o.contest.place.name }} {{ o.place.name_locative() }}
 			{% endif %}
 		{% else %}
 			<td><i>{{ o.place.name_locative() }}</i>