diff --git a/mo/web/org_export.py b/mo/web/org_export.py
index 904aaf256bff7bb3e3728f677c7149cd2285d452..79baa2ace6fef2ba682d5426eb9fd58d39ce83b2 100644
--- a/mo/web/org_export.py
+++ b/mo/web/org_export.py
@@ -143,37 +143,62 @@ def org_export_z_stats(year: int, seq: int):
 
     regions = (sess.query(db.Place)
                .filter_by(level=1)
+               .order_by(db.Place.code)
                .all())
 
     rounds = (sess.query(db.Round)
               .filter(db.Round.category.like('Z%'))
               .filter_by(year=year, seq=seq)
+              .order_by(db.Round.category)
               .all())
 
-    print(rounds)
-
-    table = {}
+    data = {}
 
     for rnd in rounds:
-        # FIXME: Check rights
-        reg = aliased(db.Place)
-        print('=========', rnd.round_code())
-        pions = (sess.query(reg.place_id, reg.code, func.count(db.Participation.user_id))
-                 .select_from(db.Participation)
-                 .join(db.Contest, db.Contest.contest_id == db.Participation.contest_id)
-                 .filter(db.Contest.round == rnd)
-                 .join(db.RegionDescendant, db.RegionDescendant.descendant == db.Contest.place_id)
-                 .join(reg, reg.place_id == db.RegionDescendant.region)
-                 .filter(reg.level == 1)
-                 .filter(sess.query(db.Solution)
-                         .with_entities(func.sum(db.Solution.points))
-                         .select_from(db.Solution)
-                         .filter(db.Solution.user_id == db.Participation.user_id)
-                         .join(db.Task, db.Task.round == rnd)
-                         .filter(db.Solution.task_id == db.Task.task_id)
-                         .scalar_subquery() > 0)
-                 .group_by(reg.place_id)
-                 .all())
-        print(pions)
-
-    return ""
+        if not g.gatekeeper.rights_for_round(rnd).have_right(Right.view_contestants):
+            continue
+        for successful in [False, True]:
+            min_pts = rnd.points_step
+            if successful and rnd.score_successful_limit is not None:
+                min_pts = rnd.score_successful_limit
+            reg = aliased(db.Place)
+            stats = (sess.query(reg, func.count(db.Participation.user_id))
+                     .select_from(db.Participation)
+                     .join(db.Contest, db.Contest.contest_id == db.Participation.contest_id)
+                     .filter(db.Contest.round == rnd)
+                     .join(db.RegionDescendant, db.RegionDescendant.descendant == db.Contest.place_id)
+                     .join(reg, reg.place_id == db.RegionDescendant.region)
+                     .filter(reg.level == 1)
+                     .filter(sess.query(db.Solution)
+                             .with_entities(func.sum(db.Solution.points))
+                             .select_from(db.Solution)
+                             .filter(db.Solution.user_id == db.Participation.user_id)
+                             .join(db.Task, db.Task.round == rnd)
+                             .filter(db.Solution.task_id == db.Task.task_id)
+                             .scalar_subquery() >= min_pts)
+                     .group_by(reg.place_id)
+                     .all())
+            for reg, cnt in stats:
+                data[rnd, reg, successful] = cnt
+
+    columns = [Column(key='kraj', name='kraj')]
+    for rnd in rounds:
+        columns.append(Column(key=f'total-{rnd.category}', name=f'{rnd.category}-celkem'))
+        columns.append(Column(key=f'succ-{rnd.category}', name=f'{rnd.category}-úspěšní'))
+
+    rows = []
+    for reg in regions:
+        keys = {'kraj': reg.name}
+        for rnd in rounds:
+            keys[f'total-{rnd.category}'] = data.get((rnd, reg, False), 0)
+            keys[f'succ-{rnd.category}'] = data.get((rnd, reg, True), 0)
+        rows.append(Row(keys=keys))
+
+    table = Table(
+        id="z-stats",
+        columns=columns,
+        rows=rows,
+        filename=f'stats-z-{year}-{seq}',
+    )
+
+    return table.send_as(format)