diff --git a/mo/imports.py b/mo/imports.py
index 4cf8deede795e083e67eb21f7d97b0bd1a6c1408..8e894710bdca8d88b1a5d845b368d1d40a2c33e0 100644
--- a/mo/imports.py
+++ b/mo/imports.py
@@ -5,7 +5,7 @@ import io
 import re
 from sqlalchemy import and_
 from sqlalchemy.orm import joinedload, Query
-from typing import List, Optional, Any, Dict, Type, Union
+from typing import List, Optional, Any, Dict, Type, Union, Set
 
 import mo.config as config
 import mo.csv
@@ -57,6 +57,7 @@ class Import:
     user: db.User
     round: Optional[db.Round]
     contest: Optional[db.Contest]
+    only_region: Optional[db.Place]
     task: Optional[db.Task]         # pro Import bodů
     allow_add_del: bool             # pro Import bodů: je povoleno zakládat/mazat řešení
     fmt: FileFormat
@@ -231,19 +232,30 @@ class Import:
             self.cnt_new_participations += 1
         return pion
 
+    def place_is_allowed(self, place: db.Place) -> bool:
+        if self.contest is not None and self.contest.place_id != place.place_id:
+            return False
+        if self.only_region is not None and not self.gatekeeper.is_ancestor_of(self.only_region, place):
+            return False
+        return True
+
     def obtain_contest(self, oblast: Optional[db.Place], allow_none: bool = False):
+        assert self.round
+        if oblast is not None and not self.place_is_allowed(oblast):
+            return self.error('Oblast neodpovídá té, do které se importuje')
         if self.contest:
             contest = self.contest
-            if oblast is not None and oblast.place_id != contest.place.place_id:
-                return self.error('Oblast neodpovídá té, do které se importuje')
         else:
+            # Zde mluvíme o oblastech, místo abychom používali place_levels,
+            # protože sloupec má ve jménu oblast a také je potřeba rozlišovat školu
+            # účastníka a školu jako oblast.
             if oblast is None:
                 if not allow_none:
-                    self.error('Je nutné uvést ' + self.round.get_level().name)
+                    self.error('Je nutné uvést kód oblasti')
                 return None
             contest = db.get_session().query(db.Contest).filter_by(round=self.round, place=oblast).one_or_none()
             if contest is None:
-                return self.error('V ' + self.round.get_level().name_locative("uvedeném", "uvedené", "uvedeném") + ' toto kolo neprobíhá')
+                return self.error('V uvedené oblasti toto kolo neprobíhá')
 
         return contest
 
@@ -281,6 +293,8 @@ class Import:
             args.append(f'round=#{self.round.round_id}')
         if self.contest is not None:
             args.append(f'contest=#{self.contest.contest_id}')
+        if self.only_region is not None:
+            args.append(f'region=#{self.only_region.place_id}')
         if self.task is not None:
             args.append(f'task=#{self.task.task_id}')
 
@@ -301,17 +315,21 @@ class Import:
                 args.append(f'{key}={val}')
         logger.info('Import: Hotovo (%s)', " ".join(args))
 
+        details = self.log_details.copy()
+        if self.only_region:
+            details['region'] = self.only_region.place_id
+
         if self.contest is not None:
             mo.util.log(
                 type=db.LogType.contest,
                 what=self.contest.contest_id,
-                details=self.log_details,
+                details=details,
             )
         elif self.round is not None:
             mo.util.log(
                 type=db.LogType.round,
                 what=self.round.round_id,
-                details=self.log_details,
+                details=details,
             )
         else:
             assert False
@@ -588,6 +606,9 @@ class PointsImport(Import):
             query = query.filter(db.Participation.contest_id == self.contest.master_contest_id)
         else:
             contest_query = sess.query(db.Contest.master_contest_id).filter_by(round=self.round)
+            if self.only_region:
+                assert self.round
+                contest_query = db.filter_place_nth_parent(contest_query, db.Contest.place_id, self.round.level - self.only_region.level, self.only_region.place_id)
             query = query.filter(db.Participation.contest_id.in_(contest_query.subquery()))
 
         return query
@@ -615,7 +636,13 @@ class PointsImport(Import):
         query = self._pion_sol_query().filter(db.Participation.user_id == user_id)
         pion_sols = query.all()
         if not pion_sols:
-            return self.error('Soutěžící nenalezen v tomto kole')
+            if self.contest is not None:
+                msg = self.round.get_level().name_locative('tomto', 'této', 'tomto')
+            elif self.only_region is not None:
+                msg = self.only_region.get_level().name_locative('tomto', 'této', 'tomto')
+            else:
+                msg = 'tomto kole'
+            return self.error(f'Soutěžící nenalezen v {msg}')
         elif len(pion_sols) > 1:
             return self.error('Soutěžící v tomto kole soutěží vícekrát, neumím zpracovat')
         pion, sol = pion_sols[0]
@@ -625,10 +652,6 @@ class PointsImport(Import):
         else:
             contest = sess.query(db.Contest).filter_by(round=self.round, master_contest_id=pion.contest_id).one()
 
-        if self.contest is not None:
-            if contest != self.contest:
-                return self.error('Soutěžící nesoutěží v ' + self.round.get_level().name_locative('tomto', 'této', 'tomto'))
-
         rights = self.gatekeeper.rights_for_contest(contest)
         if not rights.can_edit_points():
             return self.error('Nemáte právo na úpravu bodů')
@@ -709,6 +732,7 @@ def create_import(user: db.User,
                   fmt: FileFormat,
                   round: Optional[db.Round] = None,
                   contest: Optional[db.Contest] = None,
+                  only_region: Optional[db.Place] = None,
                   task: Optional[db.Task] = None,
                   allow_add_del: bool = False):
     imp: Import
@@ -726,6 +750,7 @@ def create_import(user: db.User,
     imp.user = user
     imp.round = round
     imp.contest = contest
+    imp.only_region = only_region
     imp.task = task
     imp.allow_add_del = allow_add_del
     imp.fmt = fmt
diff --git a/mo/web/org_contest.py b/mo/web/org_contest.py
index 7b8379bae55cc03b0a595e1c38478b8fe2337fbf..7fb44d42d14c7c92a5a15ebad315637fb0983a14 100644
--- a/mo/web/org_contest.py
+++ b/mo/web/org_contest.py
@@ -433,8 +433,9 @@ class ImportForm(FlaskForm):
 
 @app.route('/org/contest/c/<int:ct_id>/import', methods=('GET', 'POST'))
 @app.route('/org/contest/r/<int:round_id>/import', methods=('GET', 'POST'))
-def org_generic_import(round_id: Optional[int] = None, ct_id: Optional[int] = None):
-    ctx = get_context(round_id=round_id, ct_id=ct_id, right_needed=Right.manage_contest)
+@app.route('/org/contest/r/<int:round_id>/h/<int:hier_id>/import', methods=('GET', 'POST'))
+def org_generic_import(round_id: Optional[int] = None, hier_id: Optional[int] = None, ct_id: Optional[int] = None):
+    ctx = get_context(round_id=round_id, hier_id=hier_id, ct_id=ct_id, right_needed=Right.manage_contest)
     round, contest = ctx.master_round, ctx.master_contest
 
     form = ImportForm()
@@ -442,7 +443,7 @@ def org_generic_import(round_id: Optional[int] = None, ct_id: Optional[int] = No
     warnings = []
     if form.validate_on_submit():
         fmt = form.fmt.data
-        imp = create_import(user=g.user, type=form.typ.data, fmt=fmt, round=round, contest=contest)
+        imp = create_import(user=g.user, type=form.typ.data, fmt=fmt, round=round, contest=contest, only_region=ctx.hier_place)
         if form.submit.data:
             if form.file.data is not None:
                 file = form.file.data.stream
@@ -1308,9 +1309,10 @@ class BatchPointsForm(FlaskForm):
 
 @app.route('/org/contest/c/<int:ct_id>/task/<int:task_id>/batch-points', methods=('GET', 'POST'))
 @app.route('/org/contest/r/<int:round_id>/task/<int:task_id>/batch-points', methods=('GET', 'POST'))
-def org_generic_batch_points(task_id: int, round_id: Optional[int] = None, ct_id: Optional[int] = None):
-    ctx = get_context(round_id=round_id, ct_id=ct_id, task_id=task_id)
-    round, contest, task = ctx.round, ctx.contest, ctx.task
+@app.route('/org/contest/r/<int:round_id>/h/<int:hier_id>/task/<int:task_id>/batch-points', methods=('GET', 'POST'))
+def org_generic_batch_points(task_id: int, round_id: Optional[int] = None, hier_id: Optional[int] = None, ct_id: Optional[int] = None):
+    ctx = get_context(round_id=round_id, hier_id=hier_id, ct_id=ct_id, task_id=task_id)
+    round, hier_place, contest, task = ctx.round, ctx.hier_place, ctx.contest, ctx.task
 
     if not ctx.rights.can_edit_points():
         raise werkzeug.exceptions.Forbidden()
@@ -1320,7 +1322,7 @@ def org_generic_batch_points(task_id: int, round_id: Optional[int] = None, ct_id
     warnings = []
     if form.validate_on_submit():
         fmt = form.fmt.data
-        imp = create_import(user=g.user, type=ImportType.points, fmt=fmt, round=round, contest=contest, task=task, allow_add_del=form.add_del_sols.data)
+        imp = create_import(user=g.user, type=ImportType.points, fmt=fmt, round=round, only_region=hier_place, contest=contest, task=task, allow_add_del=form.add_del_sols.data)
         if form.submit.data:
             if form.file.data is not None:
                 file = form.file.data.stream
diff --git a/mo/web/templates/org_generic_import.html b/mo/web/templates/org_generic_import.html
index 2d9c95049172ec5b0badb5a406a004b7853b303e..6ed927d607648ab8660340faeb70a481ebc5831b 100644
--- a/mo/web/templates/org_generic_import.html
+++ b/mo/web/templates/org_generic_import.html
@@ -33,7 +33,7 @@ Import dat do {% if contest %}soutěže {{ contest.place.name_locative() }}{% el
 Detaily fungování importu najdete v <a href='{{ url_for('doc_import') }}'>dokumentaci</a>.
 
 {% if not contest %}
-<p><em>Pozor, zde se importuje do všech oblastí najednou, takže je nutné uvádět
+<p><em>Pozor, zde se importuje do více soutěží najednou, takže je nutné uvádět
 kód oblasti. Nechcete raději importovat do konkrétní oblasti?</em>
 {% endif %}