diff --git a/TODO b/TODO
new file mode 100644
index 0000000000000000000000000000000000000000..57aa6fa87ca044d40c13d31df85426df8d09be54
--- /dev/null
+++ b/TODO
@@ -0,0 +1,15 @@
+### Dořešit v importu škol ###
+
+WARNING: Obec Černovice je podle rejstříku v okrese CZ0632, podle RUIAN je na výběr ['CZ0641', 'CZ0321', 'CZ0422', 'CZ0633'] => dořešit ručně!
+WARNING: Obec Zlín je podle rejstříku v okrese CZ0721, ale pod RUIAN v CZ0724 => preferuji RUIAN
+WARNING: Obec Ostrava je podle rejstříku v okrese CZ0108, ale pod RUIAN v CZ0806 => preferuji RUIAN
+WARNING: Obec Brno je podle rejstříku v okrese CZ0643, ale pod RUIAN v CZ0642 => preferuji RUIAN
+WARNING: Obec Brno je podle rejstříku v okrese CZ0643, ale pod RUIAN v CZ0642 => preferuji RUIAN
+WARNING: Obec Řepice je podle rejstříku v okrese CZ0313, ale pod RUIAN v CZ0316 => preferuji RUIAN
+WARNING: Obec Chrudim je podle rejstříku v okrese CZ0532, ale pod RUIAN v CZ0531 => preferuji RUIAN
+WARNING: Obec Chlumec je podle rejstříku v okrese CZ0426, podle RUIAN je na výběr ['CZ0427', 'CZ0312'] => dořešit ručně!
+WARNING: Obec Hranice je podle rejstříku v okrese CZ0712, podle RUIAN je na výběr ['CZ0311', 'CZ0411', 'CZ0714'] => dořešit ručně!
+WARNING: Obec Úvaly je podle rejstříku v okrese CZ0203, ale pod RUIAN v CZ0209 => preferuji RUIAN
+WARNING: Obec Dobřichovice je podle rejstříku v okrese CZ0202, ale pod RUIAN v CZ020A => preferuji RUIAN
+WARNING: Obec Pchery je podle rejstříku v okrese CZ0204, ale pod RUIAN v CZ0203 => preferuji RUIAN
+WARNING: Obec Poděbrady je podle rejstříku v okrese CZ0204, ale pod RUIAN v CZ0208 => preferuji RUIAN
diff --git a/bin/dump-regions b/bin/dump-regions
new file mode 100755
index 0000000000000000000000000000000000000000..51ffa604d6680d6bcd81aad472e7068f836c00ea
--- /dev/null
+++ b/bin/dump-regions
@@ -0,0 +1,28 @@
+#!/usr/bin/env python3
+# Show all regions in the DB
+
+import mo.db as db
+from collections import defaultdict
+
+session = db.get_session()
+
+root = None
+children = defaultdict(list)
+
+
+def walk(place, expected_level=0):
+    indent = '\t' * place.level
+    print(f"{indent}{place.name} [{place.type.name}] #{place.place_id} NUTS={place.nuts}")
+    assert place.level == expected_level
+    for c in sorted(children[place.place_id], key=lambda p: p.name):
+        walk(c, expected_level + 1)
+
+
+for place in session.query(db.Place).all():
+    if place.parent:
+        children[place.parent].append(place)
+    else:
+        assert not root
+        root = place
+
+walk(root)
diff --git a/bin/init-schools b/bin/init-schools
index 9e94fadbb2785e675cf83e6c7810835ff08c8975..343fa843f1470aed41ff97de961084683e27d356 100755
--- a/bin/init-schools
+++ b/bin/init-schools
@@ -1,12 +1,19 @@
 #!/usr/bin/env python3
-# Initialize schools from parsed school register
-# Uses db/skoly/parsed/*.tsv
-
-from typing import List, Dict
+# Naplní databázi školami a obcemi, v nichž školy sídlí
+# Používá db/skoly/parsed/*.tsv
+#
+# Pozor, zrada: rejstřík škol je sice rozdělený do okresů dle NUTS/LAU,
+# ale školy tam řadí podle úřadu, u nějž je škole registrovaná, což vůbec
+# nemusí odpovídat skutečnému sídlu školy. Proto si poněkud magicky pomáháme
+# číselníkem obcí z RUIANu.
+
+from typing import List, Dict, DefaultDict
 import sys
 from pathlib import Path
 import mo.db as db
 import re
+import csv
+from collections import defaultdict
 
 session = db.get_session()
 new_town_cnt = 0
@@ -14,9 +21,11 @@ new_school_cnt = 0
 
 
 def import_schools(path: Path, nuts: str):
-    # XXX: The school register uses several invalid NUTS codes :( Fix them!
+    # XXX: Rejstřík škol používá několik chybných/obsoletních NUTS kódů :(
     nuts = re.sub('^CZ011', 'CZ010', nuts)
     nuts = re.sub('^CZ021', 'CZ020', nuts)
+    nuts = re.sub('^CZ061', 'CZ063', nuts)
+    nuts = re.sub('^CZ062', 'CZ064', nuts)
     nuts = re.sub('^CZ081', 'CZ080', nuts)
 
     with path.open('r') as file:
@@ -43,8 +52,8 @@ def import_schools(path: Path, nuts: str):
 
             addr = make_address(misto, ulice, cp, co)
             addr2 = make_address(misto2, ulice2, cp2, co2)
-            if addr != addr2:
-                print(f"WARNING: Address mismatch, check regions: <{addr}> != <{addr2}>", file=sys.stderr)
+            # if addr != addr2:
+            #     print(f"WARNING: Škola má dvě různé adresy: <{addr}> != <{addr2}>", file=sys.stderr)
 
             town = lookup_town(misto2, nuts)
             print(town)
@@ -82,10 +91,32 @@ def make_address(misto: str, ulice: str, cp: str, co: str) -> str:
 
 
 def lookup_town(misto: str, region_nuts: str) -> db.Place:
-    town = session.query(db.Place).filter_by(level=3, name=misto).first()
+    ruian_nuts = ruian_obec_to_okres_nuts[misto]
+    region = None
+
+    if region_nuts in ruian_nuts:
+        nuts = region_nuts
+    elif not ruian_nuts:
+        if misto.startswith('Praha '):
+            # XXX: Pražské obvody nejsou v RUIANu
+            region = session.query(db.Place).filter_by(level=2, name=misto).first()
+            assert region
+        else:
+            nuts = region_nuts
+            print(f"WARNING: Obec {misto} není v RUIAN", file=sys.stderr)
+    elif len(ruian_nuts) == 1:
+        nuts = ruian_nuts[0]
+        print(f"WARNING: Obec {misto} je podle rejstříku v okrese {region_nuts}, ale pod RUIAN v {nuts} => preferuji RUIAN", file=sys.stderr)
+    else:
+        nuts = region_nuts
+        print(f"WARNING: Obec {misto} je podle rejstříku v okrese {region_nuts}, podle RUIAN je na výběr {ruian_nuts} => dořešit ručně!", file=sys.stderr)
+
+    if not region:
+        region = session.query(db.Place).filter_by(level=2, nuts=nuts).first()
+        assert region
+
+    town = session.query(db.Place).filter_by(level=3, parent=region.place_id, name=misto).first()
     if town is None:
-        region = session.query(db.Place).filter_by(level=2, nuts=region_nuts).first()
-        assert region is not None, f"Failed to find region with NUTS code {region_nuts}"
         town = db.Place(level=3, parent=region.place_id, name=misto, type=db.PlaceType.region)
         session.add(town)
         session.flush()
@@ -94,6 +125,40 @@ def lookup_town(misto: str, region_nuts: str) -> db.Place:
     return town
 
 
+def load_ruian_csv(name):
+    with open(name) as file:
+        reader = csv.reader(file, delimiter=';')
+        rows = list(reader)
+        columns = {}
+        i = 0
+        for h in rows[0]:
+            columns[h] = i
+            i += 1
+        return columns, rows[1:]
+
+
+ruian_obec_to_okres_nuts: DefaultDict[str, List[str]] = defaultdict(list)
+
+
+def load_ruian():
+    ocols, okresy = load_ruian_csv('db/ruian/UI_OKRES.csv')
+    okres_by_id: Dict[int, List[str]] = {}
+    for o in okresy:
+        id = int(o[ocols['KOD']])
+        assert id not in okres_by_id
+        okres_by_id[id] = o
+
+    mcols, mesta = load_ruian_csv('db/ruian/UI_OBEC.csv')
+    for m in mesta:
+        jmeno = m[mcols['NAZEV']]
+        oid = int(m[mcols['OKRES_KOD']])
+        okres = okres_by_id[oid]
+        # print(f"{jmeno} -> {okres}")
+        ruian_obec_to_okres_nuts[jmeno].append(okres[ocols['NUTS_LAU']])
+
+
+load_ruian()
+
 for path in Path('db/skoly/parsed').glob('*.tsv'):
     m = re.fullmatch(r'^[A-Z]-(CZ\w+)\.tsv', path.name)
     assert m is not None
@@ -101,4 +166,4 @@ for path in Path('db/skoly/parsed').glob('*.tsv'):
     import_schools(path, nuts)
 
 session.commit()
-print(f"Imported {new_school_cnt} schools, created {new_town_cnt} new towns.")
+print(f"Importováno {new_school_cnt} škol, založeno {new_town_cnt} nových obcí.")