From df2e4f086a9a0e12dfb77cb0a809cbf07a153984 Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Mon, 11 Dec 2023 20:20:24 +0100
Subject: [PATCH 01/59] Unmanage seminar.Nastaveni
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Sry, nebudu to psát česky :-)
---
 .../migrations/0115_alter_nastaveni_options.py  | 17 +++++++++++++++++
 seminar/models/tvorba.py                        |  1 +
 2 files changed, 18 insertions(+)
 create mode 100644 seminar/migrations/0115_alter_nastaveni_options.py

diff --git a/seminar/migrations/0115_alter_nastaveni_options.py b/seminar/migrations/0115_alter_nastaveni_options.py
new file mode 100644
index 00000000..9153bc4d
--- /dev/null
+++ b/seminar/migrations/0115_alter_nastaveni_options.py
@@ -0,0 +1,17 @@
+# Generated by Django 3.2.23 on 2023-12-11 19:14
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('seminar', '0114_related_name_se_zmenilo_a_django_chce_migraci_tak_dostane_migraci'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='nastaveni',
+            options={'managed': False, 'verbose_name': 'Nastavení semináře'},
+        ),
+    ]
diff --git a/seminar/models/tvorba.py b/seminar/models/tvorba.py
index 1c1a3285..bc5636b8 100644
--- a/seminar/models/tvorba.py
+++ b/seminar/models/tvorba.py
@@ -730,6 +730,7 @@ class Nastaveni(SingletonModel):
 	class Meta:
 		db_table = 'seminar_nastaveni'
 		verbose_name = 'Nastavení semináře'
+		managed = False
 
 #	aktualni_rocnik = models.ForeignKey(Rocnik, verbose_name='aktuální ročník',
 #		null=False, on_delete=models.PROTECT)

From f19726127154d1b946f08d65e4085e2f87178424 Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Mon, 11 Dec 2023 20:20:48 +0100
Subject: [PATCH 02/59] =?UTF-8?q?Opraven=C3=AD=20related=5Fname?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Tohle by mělo být nezávislé, nemůžeme vyrobit foreign key na něco, co už existuje…

ERRORS:
seminar.Nastaveni.aktualni_cislo: (fields.E304) Reverse accessor for 'seminar.Nastaveni.aktualni_cislo' clashes with reverse accessor for 'various.Nastaveni.aktualni_cislo'.
	HINT: Add or change a related_name argument to the definition for 'seminar.Nastaveni.aktualni_cislo' or 'various.Nastaveni.aktualni_cislo'.
various.Nastaveni.aktualni_cislo: (fields.E304) Reverse accessor for 'various.Nastaveni.aktualni_cislo' clashes with reverse accessor for 'seminar.Nastaveni.aktualni_cislo'.
	HINT: Add or change a related_name argument to the definition for 'various.Nastaveni.aktualni_cislo' or 'seminar.Nastaveni.aktualni_cislo'.
---
 seminar/models/tvorba.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/seminar/models/tvorba.py b/seminar/models/tvorba.py
index bc5636b8..8049d8cf 100644
--- a/seminar/models/tvorba.py
+++ b/seminar/models/tvorba.py
@@ -736,7 +736,7 @@ class Nastaveni(SingletonModel):
 #		null=False, on_delete=models.PROTECT)
 
 	aktualni_cislo = models.ForeignKey(Cislo, verbose_name='Aktuální číslo', 
-		null=False, on_delete=models.PROTECT)
+		null=False, on_delete=models.PROTECT, related_name='aktualni_cislo_old')
 
 	cena_sous = models.IntegerField(null=False,
 									verbose_name="Účastnický poplatek za soustředění",

From 6a5390cdf6463ec2908fa8fa4f965b4f82698d93 Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Mon, 11 Dec 2023 20:21:27 +0100
Subject: [PATCH 03/59] =?UTF-8?q?P=C5=99id=C3=A1n=C3=AD=20Nastaveni=20do?=
 =?UTF-8?q?=20various?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 various/migrations/0001_initial.py | 26 ++++++++++++++++++++
 various/models.py                  | 38 +++++++++++++++++++++++++++++-
 2 files changed, 63 insertions(+), 1 deletion(-)
 create mode 100644 various/migrations/0001_initial.py

diff --git a/various/migrations/0001_initial.py b/various/migrations/0001_initial.py
new file mode 100644
index 00000000..5d1232cf
--- /dev/null
+++ b/various/migrations/0001_initial.py
@@ -0,0 +1,26 @@
+# Generated by Django 3.2.23 on 2023-12-11 19:19
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Nastaveni',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('cena_sous', models.IntegerField(default=1000, verbose_name='Účastnický poplatek za soustředění')),
+            ],
+            options={
+                'verbose_name': 'Nastavení semináře',
+                'db_table': 'seminar_nastaveni',
+                'managed': False,
+            },
+        ),
+    ]
diff --git a/various/models.py b/various/models.py
index 71a83623..28a33057 100644
--- a/various/models.py
+++ b/various/models.py
@@ -1,3 +1,39 @@
 from django.db import models
 
-# Create your models here.
+from reversion import revisions as reversion
+from solo.models import SingletonModel
+
+from seminar.models import Cislo
+
+from django.urls import reverse
+
+@reversion.register(ignore_duplicates=True)
+class Nastaveni(SingletonModel):
+
+	class Meta:
+		db_table = 'seminar_nastaveni'
+		verbose_name = 'Nastavení semináře'
+		managed = False
+
+#	aktualni_rocnik = models.ForeignKey(Rocnik, verbose_name='aktuální ročník',
+#		null=False, on_delete=models.PROTECT)
+
+	aktualni_cislo = models.ForeignKey(Cislo, verbose_name='Aktuální číslo', 
+		null=False, on_delete=models.PROTECT)
+
+	cena_sous = models.IntegerField(null=False,
+									verbose_name="Účastnický poplatek za soustředění",
+									default=1000)
+
+	@property
+	def aktualni_rocnik(self):
+		return self.aktualni_cislo.rocnik
+
+	def __str__(self):
+		return 'Nastavení semináře'
+
+	def admin_url(self):
+		return reverse('admin:seminar_nastaveni_change', args=(self.id, ))
+	
+	def verejne(self):
+		return False

From 0bbb860b163809ba1d3e745079cb530ec092d8df Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Mon, 11 Dec 2023 20:25:33 +0100
Subject: [PATCH 04/59] =?UTF-8?q?Zru=C5=A1en=C3=AD=20seminar.Nastaveni?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 seminar/migrations/0116_delete_nastaveni.py | 16 +++++++++++
 seminar/models/__init__.py                  |  2 ++
 seminar/models/tvorba.py                    | 32 ---------------------
 3 files changed, 18 insertions(+), 32 deletions(-)
 create mode 100644 seminar/migrations/0116_delete_nastaveni.py

diff --git a/seminar/migrations/0116_delete_nastaveni.py b/seminar/migrations/0116_delete_nastaveni.py
new file mode 100644
index 00000000..b820ea16
--- /dev/null
+++ b/seminar/migrations/0116_delete_nastaveni.py
@@ -0,0 +1,16 @@
+# Generated by Django 3.2.23 on 2023-12-11 19:25
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('seminar', '0115_alter_nastaveni_options'),
+    ]
+
+    operations = [
+        migrations.DeleteModel(
+            name='Nastaveni',
+        ),
+    ]
diff --git a/seminar/models/__init__.py b/seminar/models/__init__.py
index 34712ee4..6df1478f 100644
--- a/seminar/models/__init__.py
+++ b/seminar/models/__init__.py
@@ -6,3 +6,5 @@ from .soustredeni import *
 from .pomocne import *
 from .treenode import *
 from .novinky import *
+
+from various.models import Nastaveni
diff --git a/seminar/models/tvorba.py b/seminar/models/tvorba.py
index 8049d8cf..41def9c4 100644
--- a/seminar/models/tvorba.py
+++ b/seminar/models/tvorba.py
@@ -722,35 +722,3 @@ class Pohadka(SeminarModelBase):
 		except ObjectDoesNotExist:
 			# Neexistující *Node nemá smysl aktualizovat.
 			pass
-
-
-@reversion.register(ignore_duplicates=True)
-class Nastaveni(SingletonModel):
-
-	class Meta:
-		db_table = 'seminar_nastaveni'
-		verbose_name = 'Nastavení semináře'
-		managed = False
-
-#	aktualni_rocnik = models.ForeignKey(Rocnik, verbose_name='aktuální ročník',
-#		null=False, on_delete=models.PROTECT)
-
-	aktualni_cislo = models.ForeignKey(Cislo, verbose_name='Aktuální číslo', 
-		null=False, on_delete=models.PROTECT, related_name='aktualni_cislo_old')
-
-	cena_sous = models.IntegerField(null=False,
-									verbose_name="Účastnický poplatek za soustředění",
-									default=1000)
-
-	@property
-	def aktualni_rocnik(self):
-		return self.aktualni_cislo.rocnik
-
-	def __str__(self):
-		return 'Nastavení semináře'
-
-	def admin_url(self):
-		return reverse('admin:seminar_nastaveni_change', args=(self.id, ))
-	
-	def verejne(self):
-		return False

From f7382fb9464cf116ae01d306cf5df9664a6a962e Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Mon, 11 Dec 2023 20:28:10 +0100
Subject: [PATCH 05/59] =?UTF-8?q?Zapomenut=C3=A1=20z=C3=A1vislost?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Ještě mi chybí jedna :-/
---
 seminar/migrations/0116_delete_nastaveni.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/seminar/migrations/0116_delete_nastaveni.py b/seminar/migrations/0116_delete_nastaveni.py
index b820ea16..21d90b63 100644
--- a/seminar/migrations/0116_delete_nastaveni.py
+++ b/seminar/migrations/0116_delete_nastaveni.py
@@ -7,6 +7,7 @@ class Migration(migrations.Migration):
 
     dependencies = [
         ('seminar', '0115_alter_nastaveni_options'),
+        ('various', '0001_initial'),
     ]
 
     operations = [

From 158c0e4d90711c19ee83e976e5a70ecc98a49ed5 Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Mon, 11 Dec 2023 20:28:58 +0100
Subject: [PATCH 06/59] =?UTF-8?q?Druh=C3=A1=20zapomenut=C3=A1=20z=C3=A1vis?=
 =?UTF-8?q?lost?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 various/migrations/0001_initial.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/various/migrations/0001_initial.py b/various/migrations/0001_initial.py
index 5d1232cf..08bc1ea0 100644
--- a/various/migrations/0001_initial.py
+++ b/various/migrations/0001_initial.py
@@ -8,6 +8,7 @@ class Migration(migrations.Migration):
     initial = True
 
     dependencies = [
+        ('seminar', '0115_alter_nastaveni_options'),
     ]
 
     operations = [

From ca0bbb124736168855b94eacc951e10cc6a78253 Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Mon, 11 Dec 2023 20:32:22 +0100
Subject: [PATCH 07/59] Manage

---
 .../migrations/0002_alter_nastaveni_options.py | 18 ++++++++++++++++++
 various/models.py                              |  1 -
 2 files changed, 18 insertions(+), 1 deletion(-)
 create mode 100644 various/migrations/0002_alter_nastaveni_options.py

diff --git a/various/migrations/0002_alter_nastaveni_options.py b/various/migrations/0002_alter_nastaveni_options.py
new file mode 100644
index 00000000..6ef9c285
--- /dev/null
+++ b/various/migrations/0002_alter_nastaveni_options.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.2.23 on 2023-12-11 19:30
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('various', '0001_initial'),
+        ('seminar', '0116_delete_nastaveni'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='nastaveni',
+            options={'verbose_name': 'Nastavení semináře'},
+        ),
+    ]
diff --git a/various/models.py b/various/models.py
index 28a33057..6fc48e44 100644
--- a/various/models.py
+++ b/various/models.py
@@ -13,7 +13,6 @@ class Nastaveni(SingletonModel):
 	class Meta:
 		db_table = 'seminar_nastaveni'
 		verbose_name = 'Nastavení semináře'
-		managed = False
 
 #	aktualni_rocnik = models.ForeignKey(Rocnik, verbose_name='aktuální ročník',
 #		null=False, on_delete=models.PROTECT)

From 37586d7433969d4a06b8818338265463dd8d777e Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Mon, 11 Dec 2023 21:31:54 +0100
Subject: [PATCH 08/59] =?UTF-8?q?Opraven=C3=AD=20pr=C3=A1v?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

TODO: je na těch ContentTypech navěšené ještě něco dalšího? Pro nastavení asi ne, ale co ostatní aplikace?
---
 various/migrations/0003_fix_permissions.py | 41 ++++++++++++++++++++++
 1 file changed, 41 insertions(+)
 create mode 100644 various/migrations/0003_fix_permissions.py

diff --git a/various/migrations/0003_fix_permissions.py b/various/migrations/0003_fix_permissions.py
new file mode 100644
index 00000000..2364f160
--- /dev/null
+++ b/various/migrations/0003_fix_permissions.py
@@ -0,0 +1,41 @@
+# Generated by Django 3.2.23 on 2023-12-11 19:40
+
+from django.db import migrations
+
+def oprav_prava_k_nastaveni(apps, schema_editor):
+    # Tohle je trošku hnus, nešlo by to snáz?
+    ContentType = apps.get_model('contenttypes', 'ContentType')
+    Permission = apps.get_model('auth', 'Permission')
+    Group = apps.get_model('auth', 'Group')
+    old_ct = ContentType.objects.get_by_natural_key('seminar', 'nastaveni')
+    new_ct = ContentType.objects.get_by_natural_key('various', 'nastaveni')
+    old_perms = Permission.objects.filter(content_type=old_ct)
+    new_perms = Permission.objects.filter(content_type=new_ct)
+    for g in Group.objects.filter(permissions__in=old_perms):
+        old_codenames = Permission.objects.filter(group__in=[g], content_type=old_ct).values('codename')
+        g.permissions.add(*new_perms.filter(codename__in=old_codenames))
+        g.permissions.remove(*old_perms)
+
+def obnov_prava_k_nastaveni(apps, schema_editor):
+    ContentType = apps.get_model('contenttypes', 'ContentType')
+    Permission = apps.get_model('auth', 'Permission')
+    Group = apps.get_model('auth', 'Group')
+    old_ct = ContentType.objects.get_by_natural_key('seminar', 'nastaveni')
+    new_ct = ContentType.objects.get_by_natural_key('various', 'nastaveni')
+    old_perms = Permission.objects.filter(content_type=old_ct)
+    new_perms = Permission.objects.filter(content_type=new_ct)
+    for g in Group.objects.filter(permissions__in=old_perms):
+        new_codenames = Permission.objects.filter(group__in=[g], content_type=new_ct).values('codename')
+        g.permissions.add(*old_perms.filter(codename__in=new_codenames))
+        g.permissions.remove(*new_perms)
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('various', '0002_alter_nastaveni_options'),
+    ]
+
+    operations = [
+        migrations.RunPython(oprav_prava_k_nastaveni, obnov_prava_k_nastaveni),
+    ]

From f41d5587fc05585da214e122f8926941a1ca95a9 Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Mon, 18 Dec 2023 21:21:17 +0100
Subject: [PATCH 09/59] =?UTF-8?q?Pokus=20o=20hack:=20p=C5=99i=20v=C3=BDrob?=
 =?UTF-8?q?=C4=9B=20modelu=20na=20n=C4=9Bj=20rovnou=20p=C5=99esm=C4=9Bruje?=
 =?UTF-8?q?me=20p=C5=AFvodn=C3=AD=20contenttype.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 various/migrations/0001_initial.py         | 10 ++++++++++
 various/migrations/0003_fix_permissions.py |  1 -
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/various/migrations/0001_initial.py b/various/migrations/0001_initial.py
index 08bc1ea0..1cc24c3d 100644
--- a/various/migrations/0001_initial.py
+++ b/various/migrations/0001_initial.py
@@ -2,6 +2,15 @@
 
 from django.db import migrations, models
 
+def nastav_nove_contenttypes(apps, schema_editor):
+    ContentType = apps.get_model('contenttypes', 'ContentType')
+    old_ct = ContentType.objects.get_by_natural_key('seminar', 'nastaveni')
+    old_ct.update(appname='various')
+
+def nastav_stare_contenttypes(apps, schema_editor):
+    ContentType = apps.get_model('contenttypes', 'ContentType')
+    new_ct = ContentType.objects.get_by_natural_key('various', 'nastaveni')
+    new_ct.update(appname='seminar')
 
 class Migration(migrations.Migration):
 
@@ -24,4 +33,5 @@ class Migration(migrations.Migration):
                 'managed': False,
             },
         ),
+        migrations.RunPython(nastav_nove_contenttypes, nastav_stare_contenttypes),
     ]
diff --git a/various/migrations/0003_fix_permissions.py b/various/migrations/0003_fix_permissions.py
index 2364f160..9c3396f6 100644
--- a/various/migrations/0003_fix_permissions.py
+++ b/various/migrations/0003_fix_permissions.py
@@ -37,5 +37,4 @@ class Migration(migrations.Migration):
     ]
 
     operations = [
-        migrations.RunPython(oprav_prava_k_nastaveni, obnov_prava_k_nastaveni),
     ]

From 39d618834bf3f066544f97b4ff28e56dd42d889b Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Mon, 18 Dec 2023 21:32:58 +0100
Subject: [PATCH 10/59] =?UTF-8?q?fixup!=20Pokus=20o=20hack:=20p=C5=99i=20v?=
 =?UTF-8?q?=C3=BDrob=C4=9B=20modelu=20na=20n=C4=9Bj=20rovnou=20p=C5=99esm?=
 =?UTF-8?q?=C4=9Brujeme=20p=C5=AFvodn=C3=AD=20contenttype.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 various/migrations/0001_initial.py | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/various/migrations/0001_initial.py b/various/migrations/0001_initial.py
index 1cc24c3d..38f2a029 100644
--- a/various/migrations/0001_initial.py
+++ b/various/migrations/0001_initial.py
@@ -4,13 +4,15 @@ from django.db import migrations, models
 
 def nastav_nove_contenttypes(apps, schema_editor):
     ContentType = apps.get_model('contenttypes', 'ContentType')
-    old_ct = ContentType.objects.get_by_natural_key('seminar', 'nastaveni')
-    old_ct.update(appname='various')
+    old_ct = ContentType.objects.filter(app_label='seminar', model='nastaveni')
+    # Pozn: tohle může být prázdné (pokud Django nedostalo signál o dokončených migracích, např. při vyrábění databáze z nuly)
+    # Ale .update to nevadí…
+    old_ct.update(app_label='various')
 
 def nastav_stare_contenttypes(apps, schema_editor):
     ContentType = apps.get_model('contenttypes', 'ContentType')
-    new_ct = ContentType.objects.get_by_natural_key('various', 'nastaveni')
-    new_ct.update(appname='seminar')
+    new_ct = ContentType.objects.filter(app_label='various', model='nastaveni')
+    new_ct.update(app_label='seminar')
 
 class Migration(migrations.Migration):
 

From 7a34fced0ad3e5028f5088044000718b3b7e8b33 Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Tue, 12 Mar 2024 21:27:26 +0100
Subject: [PATCH 11/59] =?UTF-8?q?P=C5=99=C3=ADprava=20na=20zru=C5=A1en?=
 =?UTF-8?q?=C3=AD=20person=C3=A1ln=C3=ADch=20v=C4=9Bc=C3=AD=20ze=20Semin?=
 =?UTF-8?q?=C3=A1=C5=99e?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Modely: Osoba, Řešitel, Organizátor, Škola, Příjemce
---
 korektury/migrations/0021_auto_20240312_2124.py  | 13 +++++++++++++
 personalni/migrations/0002_auto_20240312_2118.py | 13 +++++++++++++
 prednasky/migrations/0013_auto_20240312_2124.py  | 13 +++++++++++++
 seminar/migrations/0117_auto_20240312_2125.py    | 16 ++++++++++++++++
 sifrovacka/migrations/0004_auto_20240312_2124.py | 13 +++++++++++++
 5 files changed, 68 insertions(+)
 create mode 100644 korektury/migrations/0021_auto_20240312_2124.py
 create mode 100644 personalni/migrations/0002_auto_20240312_2118.py
 create mode 100644 prednasky/migrations/0013_auto_20240312_2124.py
 create mode 100644 seminar/migrations/0117_auto_20240312_2125.py
 create mode 100644 sifrovacka/migrations/0004_auto_20240312_2124.py

diff --git a/korektury/migrations/0021_auto_20240312_2124.py b/korektury/migrations/0021_auto_20240312_2124.py
new file mode 100644
index 00000000..ee2eb53d
--- /dev/null
+++ b/korektury/migrations/0021_auto_20240312_2124.py
@@ -0,0 +1,13 @@
+# Generated by Django 4.2.8 on 2024-03-12 20:24
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('korektury', '0020_lepsi_popis_nazvu_PDF_v_adminu'),
+    ]
+
+    operations = [
+    ]
diff --git a/personalni/migrations/0002_auto_20240312_2118.py b/personalni/migrations/0002_auto_20240312_2118.py
new file mode 100644
index 00000000..62a0b0d2
--- /dev/null
+++ b/personalni/migrations/0002_auto_20240312_2118.py
@@ -0,0 +1,13 @@
+# Generated by Django 4.2.8 on 2024-03-12 20:18
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('personalni', '0001_skupiny'),
+    ]
+
+    operations = [
+    ]
diff --git a/prednasky/migrations/0013_auto_20240312_2124.py b/prednasky/migrations/0013_auto_20240312_2124.py
new file mode 100644
index 00000000..ea9ddb2e
--- /dev/null
+++ b/prednasky/migrations/0013_auto_20240312_2124.py
@@ -0,0 +1,13 @@
+# Generated by Django 4.2.8 on 2024-03-12 20:24
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('prednasky', '0012_auto_20190610_2358'),
+    ]
+
+    operations = [
+    ]
diff --git a/seminar/migrations/0117_auto_20240312_2125.py b/seminar/migrations/0117_auto_20240312_2125.py
new file mode 100644
index 00000000..57b2431f
--- /dev/null
+++ b/seminar/migrations/0117_auto_20240312_2125.py
@@ -0,0 +1,16 @@
+# Generated by Django 4.2.8 on 2024-03-12 20:25
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('seminar', '0116_delete_nastaveni'),
+        ('personalni', '0001_skupiny'),
+        ('korektury', '0021_auto_20240312_2124'),
+        ('sifrovacka', '0004_auto_20240312_2124'),
+    ]
+
+    operations = [
+    ]
diff --git a/sifrovacka/migrations/0004_auto_20240312_2124.py b/sifrovacka/migrations/0004_auto_20240312_2124.py
new file mode 100644
index 00000000..8f58cf58
--- /dev/null
+++ b/sifrovacka/migrations/0004_auto_20240312_2124.py
@@ -0,0 +1,13 @@
+# Generated by Django 4.2.8 on 2024-03-12 20:24
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('sifrovacka', '0003_odpoveducastnika_uspech'),
+    ]
+
+    operations = [
+    ]

From ae8040fb04720fc504bf2a017ef909eb62093c15 Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Tue, 12 Mar 2024 21:30:58 +0100
Subject: [PATCH 12/59] =?UTF-8?q?P=C5=99id=C3=A1v=C3=A1m=20(si)=20pozn?=
 =?UTF-8?q?=C3=A1mky=20o=20tom,=20jak=20se=20to=20bastl=C3=AD=20:-)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 split-apps-meta/dummy_migration.py | 6 ++++++
 split-apps-meta/pre.sh             | 3 +++
 2 files changed, 9 insertions(+)
 create mode 100644 split-apps-meta/dummy_migration.py
 create mode 100644 split-apps-meta/pre.sh

diff --git a/split-apps-meta/dummy_migration.py b/split-apps-meta/dummy_migration.py
new file mode 100644
index 00000000..6f9a2c9c
--- /dev/null
+++ b/split-apps-meta/dummy_migration.py
@@ -0,0 +1,6 @@
+from django.db import migrations
+class Migration(migrations.Migration):
+    dependencies = [
+        ('APP', 'MIGR'),
+    ]
+    operations = []
diff --git a/split-apps-meta/pre.sh b/split-apps-meta/pre.sh
new file mode 100644
index 00000000..28dbe9da
--- /dev/null
+++ b/split-apps-meta/pre.sh
@@ -0,0 +1,3 @@
+for app in whatever I want; do
+	./manage.py makemigrations --empty $app
+vim seminar/migrations/whatever.py # add the other as depends.

From b550857a8d9c88f3380e226c73762fb4e3e87099 Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Tue, 12 Mar 2024 21:45:50 +0100
Subject: [PATCH 13/59] Wtf nastaveni.aktualni_cislo nebylo??!?!
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Nějaká bullshit migrace, whatever.
---
 .../0004_nastaveni_aktualni_cislo.py          | 21 +++++++++++++++++++
 various/models.py                             |  8 ++++++-
 2 files changed, 28 insertions(+), 1 deletion(-)
 create mode 100644 various/migrations/0004_nastaveni_aktualni_cislo.py

diff --git a/various/migrations/0004_nastaveni_aktualni_cislo.py b/various/migrations/0004_nastaveni_aktualni_cislo.py
new file mode 100644
index 00000000..feb6bc71
--- /dev/null
+++ b/various/migrations/0004_nastaveni_aktualni_cislo.py
@@ -0,0 +1,21 @@
+# Generated by Django 4.2.8 on 2024-03-12 20:44
+
+from django.db import migrations, models
+import django.db.models.deletion
+import various.models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('seminar', '0118_alter_organizator_options_alter_osoba_options_and_more'),
+        ('various', '0003_fix_permissions'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='nastaveni',
+            name='aktualni_cislo',
+            field=models.ForeignKey(default=various.models._default_aktualni_cislo, on_delete=django.db.models.deletion.PROTECT, to='seminar.cislo', verbose_name='Aktuální číslo'),
+        ),
+    ]
diff --git a/various/models.py b/various/models.py
index 6fc48e44..eb89ab25 100644
--- a/various/models.py
+++ b/various/models.py
@@ -7,6 +7,10 @@ from seminar.models import Cislo
 
 from django.urls import reverse
 
+# Whatever, makemigrations chce default, má ho mít lol.
+def _default_aktualni_cislo():
+	return Cislo.objects.last()
+
 @reversion.register(ignore_duplicates=True)
 class Nastaveni(SingletonModel):
 
@@ -18,7 +22,9 @@ class Nastaveni(SingletonModel):
 #		null=False, on_delete=models.PROTECT)
 
 	aktualni_cislo = models.ForeignKey(Cislo, verbose_name='Aktuální číslo', 
-		null=False, on_delete=models.PROTECT)
+		null=False, on_delete=models.PROTECT,
+		default=_default_aktualni_cislo,
+		)
 
 	cena_sous = models.IntegerField(null=False,
 									verbose_name="Účastnický poplatek za soustředění",

From a1c1b9f280aa57a27441da64d5125afa3de99009 Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Tue, 12 Mar 2024 21:46:50 +0100
Subject: [PATCH 14/59] =?UTF-8?q?Pozn=C3=A1mky=20k=20odmanagov=C3=A1n?=
 =?UTF-8?q?=C3=AD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 split-apps-meta/unmanage.notes | 5 +++++
 1 file changed, 5 insertions(+)
 create mode 100644 split-apps-meta/unmanage.notes

diff --git a/split-apps-meta/unmanage.notes b/split-apps-meta/unmanage.notes
new file mode 100644
index 00000000..a3965cdb
--- /dev/null
+++ b/split-apps-meta/unmanage.notes
@@ -0,0 +1,5 @@
+vim seminar/models/whatever
+	Model.Meta.managed = False
+	Model.field.related_name = Model.related_name + '_old' # až vyrobíme nový objekt, tak nesmí kolidovat.
+		# related_name se vyhodnocuje za běhu, takže pokud nic nespustíme (celý
+		# blok migrací spustíme najednou), tak nám nevadí, že v tuhle chvíli nefunguje půlka views ap :-)

From e3ce5efdd67ccf16a71fcea74c2c1e1eefcb3266 Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Tue, 12 Mar 2024 21:47:02 +0100
Subject: [PATCH 15/59] =?UTF-8?q?Odmanagovan=C3=BD=20semin=C3=A1=C5=99?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 ...or_options_alter_osoba_options_and_more.py | 33 +++++++++++++++++++
 seminar/models/personalni.py                  |  7 +++-
 2 files changed, 39 insertions(+), 1 deletion(-)
 create mode 100644 seminar/migrations/0118_alter_organizator_options_alter_osoba_options_and_more.py

diff --git a/seminar/migrations/0118_alter_organizator_options_alter_osoba_options_and_more.py b/seminar/migrations/0118_alter_organizator_options_alter_osoba_options_and_more.py
new file mode 100644
index 00000000..642820a6
--- /dev/null
+++ b/seminar/migrations/0118_alter_organizator_options_alter_osoba_options_and_more.py
@@ -0,0 +1,33 @@
+# Generated by Django 4.2.8 on 2024-03-12 20:44
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('seminar', '0117_auto_20240312_2125'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='organizator',
+            options={'managed': False, 'ordering': ['-organizuje_do', 'osoba__jmeno', 'osoba__prijmeni'], 'verbose_name': 'Organizátor', 'verbose_name_plural': 'Organizátoři'},
+        ),
+        migrations.AlterModelOptions(
+            name='osoba',
+            options={'managed': False, 'ordering': ['prijmeni', 'jmeno'], 'verbose_name': 'Osoba', 'verbose_name_plural': 'Osoby'},
+        ),
+        migrations.AlterModelOptions(
+            name='prijemce',
+            options={'managed': False, 'verbose_name': 'příjemce', 'verbose_name_plural': 'příjemce'},
+        ),
+        migrations.AlterModelOptions(
+            name='resitel',
+            options={'managed': False, 'ordering': ['osoba'], 'verbose_name': 'Řešitel', 'verbose_name_plural': 'Řešitelé'},
+        ),
+        migrations.AlterModelOptions(
+            name='skola',
+            options={'managed': False, 'ordering': ['mesto', 'nazev'], 'verbose_name': 'Škola', 'verbose_name_plural': 'Školy'},
+        ),
+    ]
diff --git a/seminar/models/personalni.py b/seminar/models/personalni.py
index 61313e87..b245c2a4 100644
--- a/seminar/models/personalni.py
+++ b/seminar/models/personalni.py
@@ -25,6 +25,7 @@ class Osoba(SeminarModelBase):
 		verbose_name = 'Osoba'
 		verbose_name_plural = 'Osoby'
 		ordering = ['prijmeni','jmeno']
+		managed = False
 	
 	id = models.AutoField(primary_key = True)
 
@@ -128,6 +129,7 @@ class Skola(SeminarModelBase):
 		verbose_name = 'Škola'
 		verbose_name_plural = 'Školy'
 		ordering = ['mesto', 'nazev']
+		managed = False
 
 	# Interní ID
 	id = models.AutoField(primary_key = True)
@@ -180,6 +182,7 @@ class Prijemce(SeminarModelBase):
 		db_table = 'seminar_prijemce'
 		verbose_name = 'příjemce'
 		verbose_name_plural = 'příjemce'
+		managed = False
 	
 
 	# Interní ID
@@ -209,6 +212,7 @@ class Resitel(SeminarModelBase):
 		verbose_name = 'Řešitel'
 		verbose_name_plural = 'Řešitelé'
 		ordering = ['osoba']
+		managed = False
 
 	# Interní ID
 	id = models.AutoField(primary_key = True)
@@ -395,7 +399,7 @@ class Resitel(SeminarModelBase):
 
 @reversion.register(ignore_duplicates=True)
 class Organizator(SeminarModelBase):
-	osoba = models.OneToOneField(Osoba, verbose_name='osoba', related_name='org',
+	osoba = models.OneToOneField(Osoba, verbose_name='osoba', related_name='org_old',
 		help_text='osobní údaje organizátora', null=False, blank=False,
 		on_delete=models.PROTECT)
 
@@ -445,3 +449,4 @@ class Organizator(SeminarModelBase):
 		# TODO: Zohledňovat přezdívky?
 		# TODO: Sjednotit s tím, jak se řadí organizátoři v seznau orgů na webu
 		ordering = ['-organizuje_do', 'osoba__jmeno', 'osoba__prijmeni']
+		managed = False

From 559297240d100b4f5b395f08b77189744b9ae7c7 Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Tue, 12 Mar 2024 21:53:17 +0100
Subject: [PATCH 16/59] =?UTF-8?q?Dal=C5=A1=C3=AD=20pozn=C3=A1mky?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 split-apps-meta/create.notes   | 4 ++++
 split-apps-meta/unmanage.notes | 2 ++
 2 files changed, 6 insertions(+)
 create mode 100644 split-apps-meta/create.notes

diff --git a/split-apps-meta/create.notes b/split-apps-meta/create.notes
new file mode 100644
index 00000000..9abae94d
--- /dev/null
+++ b/split-apps-meta/create.notes
@@ -0,0 +1,4 @@
+Prostě zkopírovat vedle, s původními (=správnými) related names.
+makemigrations
+doplnit závislost na unmanage
+migrate
diff --git a/split-apps-meta/unmanage.notes b/split-apps-meta/unmanage.notes
index a3965cdb..b00cb294 100644
--- a/split-apps-meta/unmanage.notes
+++ b/split-apps-meta/unmanage.notes
@@ -3,3 +3,5 @@ vim seminar/models/whatever
 	Model.field.related_name = Model.related_name + '_old' # až vyrobíme nový objekt, tak nesmí kolidovat.
 		# related_name se vyhodnocuje za běhu, takže pokud nic nespustíme (celý
 		# blok migrací spustíme najednou), tak nám nevadí, že v tuhle chvíli nefunguje půlka views ap :-)
+makemigrations, bez úprav
+migrate?

From d4b92854c25cd726add0040d2b729da5dcd6d9a0 Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Tue, 12 Mar 2024 21:57:09 +0100
Subject: [PATCH 17/59] =?UTF-8?q?Pozn=C3=A1mky:=20nezapomenout=20na=20defa?=
 =?UTF-8?q?ultn=C3=AD=20related=5Fnames?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 split-apps-meta/unmanage.notes | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/split-apps-meta/unmanage.notes b/split-apps-meta/unmanage.notes
index b00cb294..4d863a21 100644
--- a/split-apps-meta/unmanage.notes
+++ b/split-apps-meta/unmanage.notes
@@ -3,5 +3,7 @@ vim seminar/models/whatever
 	Model.field.related_name = Model.related_name + '_old' # až vyrobíme nový objekt, tak nesmí kolidovat.
 		# related_name se vyhodnocuje za běhu, takže pokud nic nespustíme (celý
 		# blok migrací spustíme najednou), tak nám nevadí, že v tuhle chvíli nefunguje půlka views ap :-)
+	Nastavit nějak i všechny další závislostní fieldy (mají defaultní related_name!)
+		- ManyToManyF, OneToOneF, ForeignKey, …
 makemigrations, bez úprav
 migrate?

From 8cc5864257e3451d5a8e88422b9b42fcf8447205 Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Tue, 12 Mar 2024 22:09:52 +0100
Subject: [PATCH 18/59] =?UTF-8?q?Je=C5=A1t=C4=9B=20pozn=C3=A1mka=20k=20rel?=
 =?UTF-8?q?ated=5Fnames?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 split-apps-meta/unmanage.notes | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/split-apps-meta/unmanage.notes b/split-apps-meta/unmanage.notes
index 4d863a21..d623425f 100644
--- a/split-apps-meta/unmanage.notes
+++ b/split-apps-meta/unmanage.notes
@@ -5,5 +5,16 @@ vim seminar/models/whatever
 		# blok migrací spustíme najednou), tak nám nevadí, že v tuhle chvíli nefunguje půlka views ap :-)
 	Nastavit nějak i všechny další závislostní fieldy (mají defaultní related_name!)
 		- ManyToManyF, OneToOneF, ForeignKey, …
+	Nevím proč, ale když nebyly unikátní, tak se to potlouklo::
+		seminar.Prijemce.osoba: (fields.E304) Reverse accessor 'Osoba.osoba_old' for 'seminar.Prijemce.osoba' clashes with reverse accessor for 'seminar.Resitel.osoba'.
+			HINT: Add or change a related_name argument to the definition for 'seminar.Prijemce.osoba' or 'seminar.Resitel.osoba'.
+		seminar.Prijemce.osoba: (fields.E305) Reverse query name for 'seminar.Prijemce.osoba' clashes with reverse query name for 'seminar.Resitel.osoba'.
+			HINT: Add or change a related_name argument to the definition for 'seminar.Prijemce.osoba' or 'seminar.Resitel.osoba'.
+		seminar.Resitel.osoba: (fields.E304) Reverse accessor 'Osoba.osoba_old' for 'seminar.Resitel.osoba' clashes with reverse accessor for 'seminar.Prijemce.osoba'.
+			HINT: Add or change a related_name argument to the definition for 'seminar.Resitel.osoba' or 'seminar.Prijemce.osoba'.
+		seminar.Resitel.osoba: (fields.E305) Reverse query name for 'seminar.Resitel.osoba' clashes with reverse query name for 'seminar.Prijemce.osoba'.
+			HINT: Add or change a related_name argument to the definition for 'seminar.Resitel.osoba' or 'seminar.Prijemce.osoba'.
+	Snadné řešení: dočasné related names mít unikátní. Stejně to nikoho nezajímá.
+
 makemigrations, bez úprav
 migrate?

From 17b4a4764c1f42be7ed52e5c7bd94850cc3e7007 Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Tue, 12 Mar 2024 22:10:25 +0100
Subject: [PATCH 19/59] =?UTF-8?q?Vyr=C3=A1b=C3=ADme=20person=C3=A1ln=C3=AD?=
 =?UTF-8?q?=20v=20person=C3=A1ln=C3=ADch=20+=20oprava=20sem/models.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Nezapomenout na závislost v migraci!
---
 personalni/migrations/0003_initial.py | 124 +++++++
 personalni/models.py                  | 453 ++++++++++++++++++++++++++
 seminar/models/personalni.py          |  13 +-
 3 files changed, 584 insertions(+), 6 deletions(-)
 create mode 100644 personalni/migrations/0003_initial.py
 create mode 100644 personalni/models.py

diff --git a/personalni/migrations/0003_initial.py b/personalni/migrations/0003_initial.py
new file mode 100644
index 00000000..3c445cab
--- /dev/null
+++ b/personalni/migrations/0003_initial.py
@@ -0,0 +1,124 @@
+# Generated by Django 4.2.8 on 2024-03-12 21:10
+
+from django.db import migrations, models
+import django.utils.timezone
+import django_countries.fields
+import imagekit.models.fields
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+        ('personalni', '0002_auto_20240312_2118'),
+        ('seminar', '0118_alter_organizator_options_alter_osoba_options_and_more'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Organizator',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('vytvoreno', models.DateTimeField(blank=True, default=django.utils.timezone.now, editable=False, verbose_name='Vytvořeno')),
+                ('organizuje_od', models.DateTimeField(blank=True, null=True, verbose_name='Organizuje od')),
+                ('organizuje_do', models.DateTimeField(blank=True, null=True, verbose_name='Organizuje do')),
+                ('studuje', models.CharField(blank=True, help_text="Např. 'Studuje Obecnou fyziku (Bc.), 3. ročník', 'Vystudovala Diskrétní modely a algoritmy (Mgr.)' nebo 'Přednáší na MFF'", max_length=256, null=True, verbose_name='Studium aj.')),
+                ('strucny_popis_organizatora', models.TextField(blank=True, null=True, verbose_name='Stručný popis organizátora')),
+                ('skola', models.CharField(blank=True, help_text='Škola, např. MFF, VŠCHT, VUT, ... prostě aby se nemuselo psát do studuješkolu, ale jen obor, možnost zobrazit zvlášť', max_length=256, null=True, verbose_name='Škola, kterou studuje')),
+            ],
+            options={
+                'verbose_name': 'Organizátor',
+                'verbose_name_plural': 'Organizátoři',
+                'ordering': ['-organizuje_do', 'osoba__jmeno', 'osoba__prijmeni'],
+                'managed': False,
+            },
+        ),
+        migrations.CreateModel(
+            name='Osoba',
+            fields=[
+                ('id', models.AutoField(primary_key=True, serialize=False)),
+                ('jmeno', models.CharField(max_length=256, verbose_name='jméno')),
+                ('prijmeni', models.CharField(max_length=256, verbose_name='příjmení')),
+                ('prezdivka', models.CharField(blank=True, max_length=256, null=True, verbose_name='přezdívka')),
+                ('pohlavi_muz', models.BooleanField(default=False, verbose_name='pohlaví (muž)')),
+                ('email', models.EmailField(blank=True, default='', max_length=256, verbose_name='e-mail')),
+                ('telefon', models.CharField(blank=True, default='', max_length=256, verbose_name='telefon')),
+                ('datum_narozeni', models.DateField(blank=True, null=True, verbose_name='datum narození')),
+                ('datum_souhlasu_udaje', models.DateField(blank=True, help_text='Datum souhlasu se zpracováním osobních údajů', null=True, verbose_name='datum souhlasu (údaje)')),
+                ('datum_souhlasu_zasilani', models.DateField(blank=True, help_text='Datum souhlasu se zasíláním MFF materiálů', null=True, verbose_name='datum souhlasu (spam)')),
+                ('datum_registrace', models.DateField(default=django.utils.timezone.now, verbose_name='datum registrace do semináře')),
+                ('ulice', models.CharField(blank=True, default='', max_length=256, verbose_name='ulice')),
+                ('mesto', models.CharField(blank=True, default='', max_length=256, verbose_name='město')),
+                ('psc', models.CharField(blank=True, default='', max_length=32, verbose_name='PSČ')),
+                ('stat', django_countries.fields.CountryField(default='CZ', help_text='ISO 3166-1 kód země velkými písmeny (CZ, SK, ...)', max_length=2, verbose_name='stát')),
+                ('jak_se_dozvedeli', models.TextField(blank=True, verbose_name='Jak se dozvěděli')),
+                ('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka k osobě (plain text)', verbose_name='neveřejná poznámka')),
+                ('foto', imagekit.models.fields.ProcessedImageField(blank=True, help_text='Vlož fotografii osoby o libovolné velikosti', null=True, upload_to='image_osoby/velke/%Y/', verbose_name='Fotografie osoby')),
+            ],
+            options={
+                'verbose_name': 'Osoba',
+                'verbose_name_plural': 'Osoby',
+                'db_table': 'seminar_osoby',
+                'ordering': ['prijmeni', 'jmeno'],
+                'managed': False,
+            },
+        ),
+        migrations.CreateModel(
+            name='Prijemce',
+            fields=[
+                ('id', models.AutoField(primary_key=True, serialize=False)),
+                ('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka k příemci čísel (plain text)', verbose_name='neveřejná poznámka')),
+                ('zasilat_cislo_emailem', models.BooleanField(default=False, help_text='True pokud chce příjemce dostávat číslo emailem', verbose_name='zasílat číslo emailem')),
+            ],
+            options={
+                'verbose_name': 'příjemce',
+                'verbose_name_plural': 'příjemce',
+                'db_table': 'seminar_prijemce',
+                'managed': False,
+            },
+        ),
+        migrations.CreateModel(
+            name='Resitel',
+            fields=[
+                ('id', models.AutoField(primary_key=True, serialize=False)),
+                ('prezdivka_resitele', models.CharField(blank=True, max_length=256, null=True, unique=True, verbose_name='přezdívka řešitele')),
+                ('rok_maturity', models.IntegerField(blank=True, null=True, verbose_name='rok maturity')),
+                ('zasilat', models.CharField(choices=[('domu', 'Domů'), ('do_skoly', 'Do školy'), ('nikam', 'Nezasílat papírově')], default='domu', max_length=32, verbose_name='kam zasílat')),
+                ('zasilat_cislo_emailem', models.BooleanField(default=False, help_text='True pokud chce řešitel dostávat číslo emailem', verbose_name='zasílat číslo emailem')),
+                ('zasilat_cislo_papirove', models.BooleanField(default=True, help_text='True pokud chce řešitel dostávat číslo papírově', verbose_name='zasílat číslo papírově')),
+                ('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka k řešiteli (plain text)', verbose_name='neveřejná poznámka')),
+            ],
+            options={
+                'verbose_name': 'Řešitel',
+                'verbose_name_plural': 'Řešitelé',
+                'db_table': 'seminar_resitele',
+                'ordering': ['osoba'],
+                'managed': False,
+            },
+        ),
+        migrations.CreateModel(
+            name='Skola',
+            fields=[
+                ('id', models.AutoField(primary_key=True, serialize=False)),
+                ('aesop_id', models.CharField(blank=True, default='', help_text='Aesopi ID typu "izo:..." nebo "aesop:..."', max_length=32, verbose_name='Aesop ID')),
+                ('izo', models.CharField(blank=True, help_text='IZO školy (jen české školy)', max_length=32, verbose_name='IZO')),
+                ('nazev', models.CharField(help_text='Celý název školy', max_length=256, verbose_name='název')),
+                ('kratky_nazev', models.CharField(blank=True, help_text='Zkrácený název pro zobrazení ve výsledkovce', max_length=256, verbose_name='zkrácený název')),
+                ('ulice', models.CharField(max_length=256, verbose_name='ulice')),
+                ('mesto', models.CharField(max_length=256, verbose_name='město')),
+                ('psc', models.CharField(max_length=32, verbose_name='PSČ')),
+                ('stat', django_countries.fields.CountryField(default='CZ', help_text='ISO 3166-1 kód země velkými písmeny (CZ, SK, ...)', max_length=2, verbose_name='stát')),
+                ('je_zs', models.BooleanField(default=True, verbose_name='základní stupeň')),
+                ('je_ss', models.BooleanField(default=True, verbose_name='střední stupeň')),
+                ('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka ke škole (plain text)', verbose_name='neveřejná poznámka')),
+            ],
+            options={
+                'verbose_name': 'Škola',
+                'verbose_name_plural': 'Školy',
+                'db_table': 'seminar_skoly',
+                'ordering': ['mesto', 'nazev'],
+                'managed': False,
+            },
+        ),
+    ]
diff --git a/personalni/models.py b/personalni/models.py
new file mode 100644
index 00000000..743e18b8
--- /dev/null
+++ b/personalni/models.py
@@ -0,0 +1,453 @@
+# -*- coding: utf-8 -*-
+import logging
+
+from django.db import models
+from django.utils import timezone
+from django.conf import settings
+from django.core.exceptions import ValidationError
+from imagekit.models import ImageSpecField, ProcessedImageField
+from imagekit.processors import ResizeToFit, Transpose
+
+from django_countries.fields import CountryField
+
+from reversion import revisions as reversion
+
+from seminar.models.base import SeminarModelBase
+
+logger = logging.getLogger(__name__)
+
+
+@reversion.register(ignore_duplicates=True)
+class Osoba(SeminarModelBase):
+	
+	class Meta:
+		db_table = 'seminar_osoby'
+		verbose_name = 'Osoba'
+		verbose_name_plural = 'Osoby'
+		ordering = ['prijmeni','jmeno']
+		managed = False
+	
+	id = models.AutoField(primary_key = True)
+
+	jmeno = models.CharField('jméno', max_length=256)
+
+	prijmeni = models.CharField('příjmení', max_length=256)
+
+	prezdivka = models.CharField('přezdívka', blank=True, null=True, max_length=256)
+
+	# User, pokud má na webu účet
+	user = models.OneToOneField(settings.AUTH_USER_MODEL, blank=True, null=True, 
+				verbose_name='uživatel', on_delete=models.DO_NOTHING)
+
+	# Pohlaví. Že ho neznáme se snad nestane (a ušetří to práci při programování)
+	pohlavi_muz = models.BooleanField('pohlaví (muž)', default=False)
+
+	email = models.EmailField('e-mail', max_length=256, blank=True, default='')
+
+	telefon = models.CharField('telefon', max_length=256, blank=True, default='')
+
+	datum_narozeni = models.DateField('datum narození', blank=True, null=True)
+
+	# NULL dokud nedali souhlas
+	datum_souhlasu_udaje = models.DateField('datum souhlasu (údaje)', blank=True, null=True,
+		help_text='Datum souhlasu se zpracováním osobních údajů')
+
+	# NULL dokud nedali souhlas
+	datum_souhlasu_zasilani = models.DateField('datum souhlasu (spam)', blank=True, null=True,
+		help_text='Datum souhlasu se zasíláním MFF materiálů')
+
+	# Alespoň odhad (rok či i měsíc)
+	datum_registrace = models.DateField('datum registrace do semináře', default=timezone.now)
+
+	# Ulice může být i jen číslo
+	ulice = models.CharField('ulice', max_length=256, blank=True, default='')
+
+	mesto = models.CharField('město', max_length=256, blank=True, default='')
+
+	psc = models.CharField('PSČ', max_length=32, blank=True, default='')
+
+	# ISO 3166-1 dvojznakovy kod zeme velkym pismem (CZ, SK)
+	# Ekvivalentní s CharField(max_length=2, default='CZ', ...)
+	stat = CountryField('stát', default='CZ',
+		help_text='ISO 3166-1 kód země velkými písmeny (CZ, SK, ...)')
+
+	jak_se_dozvedeli = models.TextField('Jak se dozvěděli', blank=True)
+
+	poznamka = models.TextField('neveřejná poznámka', blank=True,
+		help_text='Neveřejná poznámka k osobě (plain text)')
+
+	foto = ProcessedImageField(verbose_name='Fotografie osoby',
+			upload_to='image_osoby/velke/%Y/', null = True, blank = True,
+			help_text = 'Vlož fotografii osoby o libovolné velikosti',
+			processors=[
+				Transpose(Transpose.AUTO),
+				ResizeToFit(500, 500, upscale=False)
+			],
+			options={'quality': 95})
+	foto_male = ImageSpecField(source='foto',
+			processors=[
+				ResizeToFit(200, 200, upscale=False)
+			],
+			options={'quality': 95})
+
+	# má OneToOneField nejvýše s:
+	# Resitel
+	# Prijemce
+	# Organizator
+
+	def plne_jmeno(self):
+		return '{} {}'.format(self.jmeno, self.prijmeni)
+
+	def inicial_krestni(self):
+		jmena = self.jmeno.split()
+		return " ".join(['{}.'.format(jmeno[0]) for jmeno in jmena])
+
+	def __str__(self):
+		return self.plne_jmeno()
+
+	# Overridujeme save Osoby, aby když si změní e-mail, aby se projevil i v
+	# Userovi (a tak se dal poslat mail s resetem hesla)
+	def save(self, *args, **kwargs):
+		if self.user is not None:
+			u = self.user
+			# U svatého tučňáka, prosím ať tohle funguje.
+			# (Takhle se kódit asi nemá...)
+			u.email = self.email
+			u.save()
+		super().save()
+
+#
+# Mělo by být částečně vytaženo z Aesopa
+# viz https://ovvp.mff.cuni.cz/wiki/aesop/export-skol.
+#
+
+@reversion.register(ignore_duplicates=True)
+class Skola(SeminarModelBase):
+
+	class Meta:
+		db_table = 'seminar_skoly'
+		verbose_name = 'Škola'
+		verbose_name_plural = 'Školy'
+		ordering = ['mesto', 'nazev']
+		managed = False
+
+	# Interní ID
+	id = models.AutoField(primary_key = True)
+
+	# Aesopi ID "izo:..." nebo "aesop:..."
+	# NULL znamená v exportu do aesopa "ufo"
+	aesop_id = models.CharField('Aesop ID', max_length=32, blank=True, default='',
+		help_text='Aesopi ID typu "izo:..." nebo "aesop:..."')
+
+	# IZO školy (jen české školy)
+	izo = models.CharField('IZO', max_length=32, blank=True,
+		help_text='IZO školy (jen české školy)')
+
+	# Celý název školy
+	nazev = models.CharField('název', max_length=256,
+		help_text='Celý název školy')
+
+	# Zkraceny nazev pro zobrazení ve výsledkovce, volitelné.
+	# Není v Aesopovi, musíme vytvářet sami.
+	kratky_nazev = models.CharField('zkrácený název', max_length=256, blank=True,
+		help_text="Zkrácený název pro zobrazení ve výsledkovce")
+
+	# Ulice může být jen číslo
+	ulice = models.CharField('ulice', max_length=256)
+
+	mesto = models.CharField('město', max_length=256)
+
+	psc = models.CharField('PSČ', max_length=32)
+
+	# ISO 3166-1 dvojznakovy kod zeme velkym pismem (CZ, SK)
+	# Ekvivalentní s CharField(max_length=2, default='CZ', ...)
+	stat = CountryField('stát', default='CZ',
+		help_text='ISO 3166-1 kód země velkými písmeny (CZ, SK, ...)')
+
+	# Jaké vzdělání škpla poskytuje?
+	je_zs = models.BooleanField('základní stupeň', default=True)
+	je_ss = models.BooleanField('střední stupeň', default=True)
+
+	poznamka = models.TextField('neveřejná poznámka', blank=True,
+		help_text='Neveřejná poznámka ke škole (plain text)')
+	
+	kontaktni_osoba = models.ForeignKey(Osoba, verbose_name='Kontaktní osoba', 
+			blank=True, null=True, on_delete=models.SET_NULL)
+
+	def __str__(self):
+		return '{}, {}, {}'.format(self.nazev, self.ulice, self.mesto)
+
+class Prijemce(SeminarModelBase):
+	class Meta:
+		db_table = 'seminar_prijemce'
+		verbose_name = 'příjemce'
+		verbose_name_plural = 'příjemce'
+		managed = False
+	
+
+	# Interní ID
+	id = models.AutoField(primary_key = True)
+
+	poznamka = models.TextField('neveřejná poznámka', blank=True,
+		help_text='Neveřejná poznámka k příemci čísel (plain text)')
+
+	osoba = models.OneToOneField(Osoba, verbose_name='komu', blank=False, null=False,
+		help_text='Které osobě či na jakou adresu se mají zasílat čísla',
+		on_delete=models.CASCADE)
+
+	zasilat_cislo_emailem = models.BooleanField('zasílat číslo emailem', help_text='True pokud chce příjemce dostávat číslo emailem', default=False)
+
+	# FIXME: možná chceme něco jako vazbu na osobu XOR školu a počet kusů k zaslání
+	# FIXME: a možná taky posílání na mail a možná taky přes něj chceme posílat i řešitelům
+
+	def __str__(self):
+		return self.osoba.plne_jmeno()
+	
+
+@reversion.register(ignore_duplicates=True)
+class Resitel(SeminarModelBase):
+
+	class Meta:
+		db_table = 'seminar_resitele'
+		verbose_name = 'Řešitel'
+		verbose_name_plural = 'Řešitelé'
+		ordering = ['osoba']
+		managed = False
+
+	# Interní ID
+	id = models.AutoField(primary_key = True)
+
+	prezdivka_resitele = models.CharField('přezdívka řešitele', blank=True, null=True, max_length=256, unique=True)
+
+	osoba = models.OneToOneField(Osoba, blank=False, null=False, verbose_name='osoba',
+		on_delete=models.PROTECT)
+	
+
+	skola = models.ForeignKey(Skola, blank=True, null=True, verbose_name='škola',
+		on_delete=models.SET_NULL)
+
+	# Očekávaný rok maturity a vyřazení z aktivních řešitelů
+	rok_maturity = models.IntegerField('rok maturity', blank=True, null=True)
+
+	ZASILAT_DOMU = 'domu'
+	ZASILAT_DO_SKOLY = 'do_skoly'
+	ZASILAT_NIKAM = 'nikam'
+	ZASILAT_CHOICES = [
+		(ZASILAT_DOMU, 'Domů'),
+		(ZASILAT_DO_SKOLY, 'Do školy'),
+		(ZASILAT_NIKAM, 'Nezasílat papírově'),
+		]
+
+	zasilat = models.CharField('kam zasílat', max_length=32, choices=ZASILAT_CHOICES, blank=False, default=ZASILAT_DOMU)
+
+	zasilat_cislo_emailem = models.BooleanField('zasílat číslo emailem', help_text='True pokud chce řešitel dostávat číslo emailem', default=False)
+
+	zasilat_cislo_papirove = models.BooleanField('zasílat číslo papírově', help_text='True pokud chce řešitel dostávat číslo papírově', default=True)
+
+	poznamka = models.TextField('neveřejná poznámka', blank=True,
+		help_text='Neveřejná poznámka k řešiteli (plain text)')
+
+
+	def export_row(self):
+		"Slovnik pro pouziti v AESOP exportu"
+		return {
+			'id': self.id,
+			'name': self.osoba.jmeno,
+			'surname': self.osoba.prijmeni,
+			'gender': 'M' if self.osoba.pohlavi_muz else 'F',
+			'born': self.osoba.datum_narozeni.isoformat() if self.osoba.datum_narozeni else '',
+			'email': self.osoba.email,
+			'end-year': self.rok_maturity or '',
+
+			'street': self.osoba.ulice,
+			'town': self.osoba.mesto,
+			'postcode': self.osoba.psc,
+			'country': self.osoba.stat,
+
+			'spam-flag': 'Y' if self.osoba.datum_souhlasu_zasilani else '',
+			'spam-date': self.osoba.datum_souhlasu_zasilani.isoformat() if self.osoba.datum_souhlasu_zasilani else '',
+
+			'school': self.skola.aesop_id if self.skola else '',
+			'school-name': str(self.skola) if self.skola else 'Skola neni znama',
+			}
+
+	def rocnik(self, rocnik):
+		"""Vrati skolni rocnik resitele pro zadany Rocnik.
+				Vraci '' pro neznamy rok maturity resitele, Z* pro ekvivalent ZŠ."""
+		if self.rok_maturity is None:
+			return ''
+		rozdil = 5 - (self.rok_maturity - rocnik.prvni_rok)
+		if rozdil >= 1:
+			return str(rozdil)
+		else:
+			return 'Z' + str(rozdil + 9)
+
+	def vsechny_body(self):
+		"Spočítá body odjakživa."
+		vsechna_reseni = self.reseni_set.all()
+		from .odevzdavatko import Hodnoceni
+		vsechna_hodnoceni = Hodnoceni.objects.filter(
+			reseni__in=vsechna_reseni)
+		return sum(h.body for h in list(vsechna_hodnoceni) if h.body is not None)
+
+
+	def get_titul(self, body=None):
+		"Vrati titul jako řetězec."
+		
+		# Nejprve si zadefinujeme titul
+		from enum import Enum
+		from functools import total_ordering
+		@total_ordering
+		class Titul(Enum):
+			""" Třída reprezentující možné tituly. Hodnoty jsou dvojice (dolní hranice, stringifikace). """
+			nic =  (0, '')
+			bc =   (20, 'Bc.')
+			mgr =  (50, 'Mgr.')
+			dr =   (100, 'Dr.')
+			doc =  (200, 'Doc.')
+			prof = (500, 'Prof.')
+			akad = (1000, 'Akad.')
+
+			def __lt__(self, other):
+				return True if self.value[0] < other.value[0] else False
+			def __eq__(self, other): # Měla by být implicitní, ale klidně explicitně.
+				return True if self.value[0] == other.value[0] else False
+
+			def __str__(self):
+				return self.value[1]
+
+			@classmethod
+			def z_bodu(cls, body):
+				aktualni = cls.nic
+				# TODO: ověřit, že to funguje
+				for titul in cls: # Kdyžtak použít __members__.items()
+					if titul.value[0] <= body:
+						aktualni = titul
+					else:
+						break
+				return aktualni
+
+		# Hledáme body v databázi
+		# V listopadu 2020 jsme se na filosofické schůzce shodli o změně hranic titulů:
+		#  - body z 25. ročníku a dříve byly shledány dvakrát hodnotnějšími
+		#  - proto se započítávají dvojnásobně a byly posunuté hranice titulů
+		#  - staré tituly se ale nemají odebrat, pokud řešitel v t.č. minulém (26.) ročníku měl titul, má ho mít pořád.
+		from .odevzdavatko import Hodnoceni
+		hodnoceni_do_25_rocniku = Hodnoceni.objects.filter(deadline_body__cislo__rocnik__rocnik__lte=25,reseni__in=self.reseni_set.all())
+		novejsi_hodnoceni = Hodnoceni.objects.filter(reseni__in=self.reseni_set.all()).difference(hodnoceni_do_25_rocniku)
+
+		def body_z_hodnoceni(hh : list):
+			return sum(h.body for h in hh if h.body is not None)
+
+		stare_body = body_z_hodnoceni(hodnoceni_do_25_rocniku)
+		if body is None:
+			nove_body = body_z_hodnoceni(novejsi_hodnoceni)
+		else:
+			# Zjistíme, kolik bodů jsou staré, tedy hodnotnější
+			nove_body = max(0, body - stare_body) # Všechny body nad počet původních hodnotnějších
+			stare_body = min(stare_body, body) # Skutečný počet hodnotnějších bodů
+		logicke_body = 2*stare_body + nove_body
+
+	
+		# Titul se určí následovně:
+		#  - Pokud se řeší body, které jsou starší, než do 26 ročníku (včetně), dáváme tituly postaru.
+		#  - Jinak dáváme tituly po novu...
+		#  - ... ale titul se nesmí odebrat, pokud se zmenšil.
+		def titul_do_26_rocniku(body):
+			""" Původní hranice bodů za tituly """
+			if body < 10:
+				return Titul.nic
+			elif body < 20:
+				return Titul.bc
+			elif body < 50:
+				return Titul.mgr
+			elif body < 100:
+				return Titul.dr
+			elif body < 200:
+				return Titul.doc
+			elif body < 500:
+				return Titul.prof
+			else:
+				return Titul.akad
+
+		from .odevzdavatko import Hodnoceni
+		hodnoceni_do_26_rocniku = Hodnoceni.objects.filter(deadline_body__cislo__rocnik__rocnik__lte=26,reseni__in=self.reseni_set.all())
+		novejsi_body = body_z_hodnoceni(
+			Hodnoceni.objects.filter(reseni__in=self.reseni_set.all())
+			.difference(hodnoceni_do_26_rocniku)
+			)
+		starsi_body = body_z_hodnoceni(hodnoceni_do_26_rocniku)
+		if body is not None:
+			# Ještě z toho vybereme ty správně staré body
+			novejsi_body = max(0, body - starsi_body)
+			starsi_body = min(starsi_body, body)
+
+		# Titul pro 26. ročník
+		stary_titul = titul_do_26_rocniku(starsi_body)
+		# Titul podle aktuálních pravidel
+		novy_titul = Titul.z_bodu(logicke_body)
+
+		if novejsi_body == 0:
+			# Žádné nové body -- titul podle starých pravidel
+			return str(stary_titul)
+		return str(max(novy_titul, stary_titul))
+
+
+	def __str__(self):
+		return self.osoba.plne_jmeno()
+
+
+@reversion.register(ignore_duplicates=True)
+class Organizator(SeminarModelBase):
+
+	class Meta:
+		verbose_name = 'Organizátor'
+		verbose_name_plural = 'Organizátoři'
+		# Řadí aktivní orgy na začátek, pod tím v pořadí od nejstarších neaktivní orgy.
+		# TODO: Chtěl bych spíš mít nejstarší orgy dole.
+		# TODO: Zohledňovat přezdívky?
+		# TODO: Sjednotit s tím, jak se řadí organizátoři v seznau orgů na webu
+		ordering = ['-organizuje_do', 'osoba__jmeno', 'osoba__prijmeni']
+		managed = False
+
+	osoba = models.OneToOneField(Osoba, verbose_name='osoba', related_name='org',
+		help_text='osobní údaje organizátora', null=False, blank=False,
+		on_delete=models.PROTECT)
+
+	vytvoreno = models.DateTimeField(
+		'Vytvořeno',
+		default=timezone.now,
+		blank=True,
+		editable=False
+	)
+
+	# Ne, date to nebude. SQLite: invalid literal for int() with base 10: b'17 23:00:00'
+	organizuje_od = models.DateTimeField('Organizuje od', blank=True, null=True)
+	
+	organizuje_do = models.DateTimeField('Organizuje do', blank=True, null=True)
+
+	studuje = models.CharField('Studium aj.', max_length = 256,
+			null = True, blank = True,
+			help_text="Např. 'Studuje Obecnou fyziku (Bc.), 3. ročník', "
+			"'Vystudovala Diskrétní modely a algoritmy (Mgr.)' nebo "
+			"'Přednáší na MFF'")
+
+	strucny_popis_organizatora = models.TextField('Stručný popis organizátora',
+			null = True, blank = True)
+
+	skola = models.CharField('Škola, kterou studuje', max_length = 256, null=True, blank=True,
+		help_text="Škola, např. MFF, VŠCHT, VUT, ... prostě aby se nemuselo psát do studuje"
+		"školu, ale jen obor, možnost zobrazit zvlášť")
+
+	def clean(self):
+		if self.organizuje_od and self.organizuje_do and (self.organizuje_od > self.organizuje_do):
+			raise ValidationError("Organizátor nemůže skončit s organizováním dříve než začal!")
+		super().clean()
+
+	def __str__(self):
+		if self.osoba.prezdivka:
+			return "{} '{}' {}".format(self.osoba.jmeno,
+				self.osoba.prezdivka,
+				self.osoba.prijmeni)
+		else:
+			return "{} {}".format(self.osoba.jmeno, self.osoba.prijmeni)
diff --git a/seminar/models/personalni.py b/seminar/models/personalni.py
index b245c2a4..5286e1be 100644
--- a/seminar/models/personalni.py
+++ b/seminar/models/personalni.py
@@ -37,7 +37,8 @@ class Osoba(SeminarModelBase):
 
 	# User, pokud má na webu účet
 	user = models.OneToOneField(settings.AUTH_USER_MODEL, blank=True, null=True, 
-				verbose_name='uživatel', on_delete=models.DO_NOTHING)
+				verbose_name='uživatel', on_delete=models.DO_NOTHING,
+				related_name='user_old')
 
 	# Pohlaví. Že ho neznáme se snad nestane (a ušetří to práci při programování)
 	pohlavi_muz = models.BooleanField('pohlaví (muž)', default=False)
@@ -172,7 +173,7 @@ class Skola(SeminarModelBase):
 		help_text='Neveřejná poznámka ke škole (plain text)')
 	
 	kontaktni_osoba = models.ForeignKey(Osoba, verbose_name='Kontaktní osoba', 
-			blank=True, null=True, on_delete=models.SET_NULL)
+			blank=True, null=True, on_delete=models.SET_NULL, related_name='kontaktni_osoba_old')
 
 	def __str__(self):
 		return '{}, {}, {}'.format(self.nazev, self.ulice, self.mesto)
@@ -193,7 +194,7 @@ class Prijemce(SeminarModelBase):
 
 	osoba = models.OneToOneField(Osoba, verbose_name='komu', blank=False, null=False,
 		help_text='Které osobě či na jakou adresu se mají zasílat čísla',
-		on_delete=models.CASCADE)
+		on_delete=models.CASCADE, related_name='osobad_old1')
 
 	zasilat_cislo_emailem = models.BooleanField('zasílat číslo emailem', help_text='True pokud chce příjemce dostávat číslo emailem', default=False)
 
@@ -220,11 +221,11 @@ class Resitel(SeminarModelBase):
 	prezdivka_resitele = models.CharField('přezdívka řešitele', blank=True, null=True, max_length=256, unique=True)
 
 	osoba = models.OneToOneField(Osoba, blank=False, null=False, verbose_name='osoba',
-		on_delete=models.PROTECT)
+		on_delete=models.PROTECT, related_name='osoba_old2')
 	
 
 	skola = models.ForeignKey(Skola, blank=True, null=True, verbose_name='škola',
-		on_delete=models.SET_NULL)
+		on_delete=models.SET_NULL, related_name='skola_old3')
 
 	# Očekávaný rok maturity a vyřazení z aktivních řešitelů
 	rok_maturity = models.IntegerField('rok maturity', blank=True, null=True)
@@ -399,7 +400,7 @@ class Resitel(SeminarModelBase):
 
 @reversion.register(ignore_duplicates=True)
 class Organizator(SeminarModelBase):
-	osoba = models.OneToOneField(Osoba, verbose_name='osoba', related_name='org_old',
+	osoba = models.OneToOneField(Osoba, verbose_name='osoba', related_name='org_old4',
 		help_text='osobní údaje organizátora', null=False, blank=False,
 		on_delete=models.PROTECT)
 

From 4182bd542edcc1928de46608f4e4cba071795452 Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Tue, 12 Mar 2024 22:15:12 +0100
Subject: [PATCH 20/59] =?UTF-8?q?Oprava=20nejv=C3=ADc=20haluz=20migrace,?=
 =?UTF-8?q?=20wtf,=20nev=C3=ADm=20co=20d=C4=9Bl=C3=A1m?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Best commit.
---
 various/models.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/various/models.py b/various/models.py
index eb89ab25..f551a652 100644
--- a/various/models.py
+++ b/various/models.py
@@ -9,7 +9,7 @@ from django.urls import reverse
 
 # Whatever, makemigrations chce default, má ho mít lol.
 def _default_aktualni_cislo():
-	return Cislo.objects.last()
+	return Cislo.objects.last().id
 
 @reversion.register(ignore_duplicates=True)
 class Nastaveni(SingletonModel):

From 457236c21a5e754d69f137362a16248c0567f002 Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Tue, 12 Mar 2024 22:25:32 +0100
Subject: [PATCH 21/59] Vzor migrace pro create

---
 split-apps-meta/create-ct-hack.py | 52 +++++++++++++++++++++++++++++++
 split-apps-meta/create.notes      |  1 +
 2 files changed, 53 insertions(+)
 create mode 100644 split-apps-meta/create-ct-hack.py

diff --git a/split-apps-meta/create-ct-hack.py b/split-apps-meta/create-ct-hack.py
new file mode 100644
index 00000000..fb603f6f
--- /dev/null
+++ b/split-apps-meta/create-ct-hack.py
@@ -0,0 +1,52 @@
+# Správná migrace vypadá takto:
+# Generated by Django 3.2.23 on 2023-12-11 19:19
+
+def nastav_nove_contenttypes(apps, schema_editor):
+    ContentType = apps.get_model('contenttypes', 'ContentType')
+    old_ct = ContentType.objects.filter(app_label='seminar', model='nastaveni')
+    # Pozn: tohle může být prázdné (pokud Django nedostalo signál o dokončených migracích, např. při vyrábění databáze z nuly)
+    # Ale .update to nevadí…
+    old_ct.update(app_label='various')
+
+def nastav_stare_contenttypes(apps, schema_editor):
+    ContentType = apps.get_model('contenttypes', 'ContentType')
+    new_ct = ContentType.objects.filter(app_label='various', model='nastaveni')
+    new_ct.update(app_label='seminar')
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+        ('seminar', '0115_alter_nastaveni_options'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Nastaveni',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('cena_sous', models.IntegerField(default=1000, verbose_name='Účastnický poplatek za soustředění')),
+            ],
+            options={
+                'verbose_name': 'Nastavení semináře',
+                'db_table': 'seminar_nastaveni',
+                'managed': False,
+            },
+        ),
+        migrations.RunPython(nastav_nove_contenttypes, nastav_stare_contenttypes),
+    ]
+
+
+
+#Hack: zkrácení
+def nastav_nove_contenttypes(apps, schema_editor):
+    ContentType = apps.get_model('contenttypes', 'ContentType')
+    for m in ('resitel', 'organizator', 'osoba', 'skola', 'prijemce'):
+        ContentType.objects.filter(app_label='seminar', model=m).update(app_label='personalni')
+
+def nastav_stare_contenttypes(apps, schema_editor):
+    ContentType = apps.get_model('contenttypes', 'ContentType')
+    for m in ('resitel', 'organizator', 'osoba', 'skola', 'prijemce'):
+        ContentType.objects.filter(app_label='personalni', model=m).update(app_label='seminar')
+
diff --git a/split-apps-meta/create.notes b/split-apps-meta/create.notes
index 9abae94d..43872d68 100644
--- a/split-apps-meta/create.notes
+++ b/split-apps-meta/create.notes
@@ -1,4 +1,5 @@
 Prostě zkopírovat vedle, s původními (=správnými) related names.
 makemigrations
+! Doplnit hack kolem content-types
 doplnit závislost na unmanage
 migrate

From a23daf8b9713a30751766580a060a3ee4b4b7ff9 Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Tue, 19 Mar 2024 21:35:52 +0100
Subject: [PATCH 22/59] =?UTF-8?q?Dal=C5=A1=C3=AD=20pokus=20o=20migraci,=20?=
 =?UTF-8?q?I=20guess=20(po=20t=C3=BDdnu,=20lol)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 personalni/migrations/0003_initial.py | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/personalni/migrations/0003_initial.py b/personalni/migrations/0003_initial.py
index 3c445cab..fc586ff6 100644
--- a/personalni/migrations/0003_initial.py
+++ b/personalni/migrations/0003_initial.py
@@ -5,6 +5,17 @@ import django.utils.timezone
 import django_countries.fields
 import imagekit.models.fields
 
+def nastav_nove_contenttypes(apps, schema_editor):
+    ContentType = apps.get_model('contenttypes', 'ContentType')
+    for m in ('resitel', 'organizator', 'osoba', 'skola', 'prijemce'):
+        oct = ContentType.objects.filter(app_label='seminar', model=m)
+        oct.update(app_label='personalni')
+
+def nastav_stare_contenttypes(apps, schema_editor):
+    ContentType = apps.get_model('contenttypes', 'ContentType')
+    for m in ('resitel', 'organizator', 'osoba', 'skola', 'prijemce'):
+        nct = ContentType.objects.filter(app_label='personalni', model=m)
+        nct.update(app_label='seminar')
 
 class Migration(migrations.Migration):
 
@@ -16,6 +27,7 @@ class Migration(migrations.Migration):
     ]
 
     operations = [
+        migrations.RunPython(nastav_nove_contenttypes, nastav_stare_contenttypes),
         migrations.CreateModel(
             name='Organizator',
             fields=[

From 46f1d3d42ddb23d8e0e30c9b061801520eb2c421 Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Tue, 19 Mar 2024 22:15:29 +0100
Subject: [PATCH 23/59] I did something!

---
 various/migrations/0001_initial.py            |  1 +
 .../0004_nastaveni_aktualni_cislo.py          | 21 -------------------
 various/models.py                             |  7 +------
 3 files changed, 2 insertions(+), 27 deletions(-)
 delete mode 100644 various/migrations/0004_nastaveni_aktualni_cislo.py

diff --git a/various/migrations/0001_initial.py b/various/migrations/0001_initial.py
index 38f2a029..43b77171 100644
--- a/various/migrations/0001_initial.py
+++ b/various/migrations/0001_initial.py
@@ -28,6 +28,7 @@ class Migration(migrations.Migration):
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                 ('cena_sous', models.IntegerField(default=1000, verbose_name='Účastnický poplatek za soustředění')),
+                ('aktualni_cislo', models.ForeignKey(null=True, on_delete=models.deletion.PROTECT, to='seminar.cislo', verbose_name='Aktuální číslo')),
             ],
             options={
                 'verbose_name': 'Nastavení semináře',
diff --git a/various/migrations/0004_nastaveni_aktualni_cislo.py b/various/migrations/0004_nastaveni_aktualni_cislo.py
deleted file mode 100644
index feb6bc71..00000000
--- a/various/migrations/0004_nastaveni_aktualni_cislo.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# Generated by Django 4.2.8 on 2024-03-12 20:44
-
-from django.db import migrations, models
-import django.db.models.deletion
-import various.models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('seminar', '0118_alter_organizator_options_alter_osoba_options_and_more'),
-        ('various', '0003_fix_permissions'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='nastaveni',
-            name='aktualni_cislo',
-            field=models.ForeignKey(default=various.models._default_aktualni_cislo, on_delete=django.db.models.deletion.PROTECT, to='seminar.cislo', verbose_name='Aktuální číslo'),
-        ),
-    ]
diff --git a/various/models.py b/various/models.py
index f551a652..17632c46 100644
--- a/various/models.py
+++ b/various/models.py
@@ -7,10 +7,6 @@ from seminar.models import Cislo
 
 from django.urls import reverse
 
-# Whatever, makemigrations chce default, má ho mít lol.
-def _default_aktualni_cislo():
-	return Cislo.objects.last().id
-
 @reversion.register(ignore_duplicates=True)
 class Nastaveni(SingletonModel):
 
@@ -22,8 +18,7 @@ class Nastaveni(SingletonModel):
 #		null=False, on_delete=models.PROTECT)
 
 	aktualni_cislo = models.ForeignKey(Cislo, verbose_name='Aktuální číslo', 
-		null=False, on_delete=models.PROTECT,
-		default=_default_aktualni_cislo,
+		null=True, blank=False, on_delete=models.PROTECT,
 		)
 
 	cena_sous = models.IntegerField(null=False,

From 1f0e6cccf60b7f49a545fbf3bb939a4f1a722a2a Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Tue, 19 Mar 2024 22:47:11 +0100
Subject: [PATCH 24/59] =?UTF-8?q?Dal=C5=A1=C3=AD=20pozn=C3=A1mky?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 split-apps-meta/relink.notes   | 4 ++++
 split-apps-meta/unmanage.notes | 1 +
 2 files changed, 5 insertions(+)
 create mode 100644 split-apps-meta/relink.notes

diff --git a/split-apps-meta/relink.notes b/split-apps-meta/relink.notes
new file mode 100644
index 00000000..54d70260
--- /dev/null
+++ b/split-apps-meta/relink.notes
@@ -0,0 +1,4 @@
+Přepsat všechny odkazy v sousedních aplikacích na novou aplikaci
+Naincludovat nové modely v seminar.models kvůli views
+makemigrations
+migrate
diff --git a/split-apps-meta/unmanage.notes b/split-apps-meta/unmanage.notes
index d623425f..846b7314 100644
--- a/split-apps-meta/unmanage.notes
+++ b/split-apps-meta/unmanage.notes
@@ -15,6 +15,7 @@ vim seminar/models/whatever
 		seminar.Resitel.osoba: (fields.E305) Reverse query name for 'seminar.Resitel.osoba' clashes with reverse query name for 'seminar.Prijemce.osoba'.
 			HINT: Add or change a related_name argument to the definition for 'seminar.Resitel.osoba' or 'seminar.Prijemce.osoba'.
 	Snadné řešení: dočasné related names mít unikátní. Stejně to nikoho nezajímá.
+	!! Zkontrolovat, že všechno má nastavenou db_table (jinak se to potom pokusí vybastlit jméno tabulky podle aplikace…)
 
 makemigrations, bez úprav
 migrate?

From 9e21b2ca1a88f08897e7b715fcee6d331efd0595 Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Tue, 19 Mar 2024 22:47:45 +0100
Subject: [PATCH 25/59] relink

---
 ...tor_alter_korekturovanepdf_org_and_more.py | 30 +++++++
 korektury/models.py                           |  2 +-
 .../migrations/0014_alter_prednaska_org.py    | 20 +++++
 prednasky/models.py                           |  3 +-
 .../0119_alter_konfera_ucastnici_and_more.py  | 85 +++++++++++++++++++
 seminar/models/__init__.py                    |  2 +-
 seminar/models/novinky.py                     |  4 +-
 seminar/models/odevzdavatko.py                |  6 +-
 seminar/models/soustredeni.py                 | 14 +--
 seminar/models/treenode.py                    |  4 +-
 seminar/models/tvorba.py                      | 12 +--
 .../0005_alter_odpoveducastnika_resitel.py    | 20 +++++
 sifrovacka/models.py                          |  2 +-
 13 files changed, 180 insertions(+), 24 deletions(-)
 create mode 100644 korektury/migrations/0022_alter_komentar_autor_alter_korekturovanepdf_org_and_more.py
 create mode 100644 prednasky/migrations/0014_alter_prednaska_org.py
 create mode 100644 seminar/migrations/0119_alter_konfera_ucastnici_and_more.py
 create mode 100644 sifrovacka/migrations/0005_alter_odpoveducastnika_resitel.py

diff --git a/korektury/migrations/0022_alter_komentar_autor_alter_korekturovanepdf_org_and_more.py b/korektury/migrations/0022_alter_komentar_autor_alter_korekturovanepdf_org_and_more.py
new file mode 100644
index 00000000..4e063a89
--- /dev/null
+++ b/korektury/migrations/0022_alter_komentar_autor_alter_korekturovanepdf_org_and_more.py
@@ -0,0 +1,30 @@
+# Generated by Django 4.2.11 on 2024-03-19 21:35
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('personalni', '0003_initial'),
+        ('korektury', '0021_auto_20240312_2124'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='komentar',
+            name='autor',
+            field=models.ForeignKey(blank=True, help_text='Autor komentáře', null=True, on_delete=django.db.models.deletion.SET_NULL, to='personalni.organizator'),
+        ),
+        migrations.AlterField(
+            model_name='korekturovanepdf',
+            name='org',
+            field=models.ForeignKey(blank=True, default=None, help_text='Zodpovědný organizátor za obsah', null=True, on_delete=django.db.models.deletion.SET_NULL, to='personalni.organizator'),
+        ),
+        migrations.AlterField(
+            model_name='oprava',
+            name='autor',
+            field=models.ForeignKey(blank=True, help_text='Autor opravy', null=True, on_delete=django.db.models.deletion.SET_NULL, to='personalni.organizator'),
+        ),
+    ]
diff --git a/korektury/models.py b/korektury/models.py
index c9d47dfa..ca1bb877 100644
--- a/korektury/models.py
+++ b/korektury/models.py
@@ -20,7 +20,7 @@ from django.core.exceptions import ObjectDoesNotExist
 from django.utils.functional import cached_property
 from django.utils.text import get_valid_filename
 
-from seminar.models import Organizator
+from personalni.models import Organizator
 
 import subprocess
 from reversion import revisions as reversion
diff --git a/prednasky/migrations/0014_alter_prednaska_org.py b/prednasky/migrations/0014_alter_prednaska_org.py
new file mode 100644
index 00000000..09742377
--- /dev/null
+++ b/prednasky/migrations/0014_alter_prednaska_org.py
@@ -0,0 +1,20 @@
+# Generated by Django 4.2.11 on 2024-03-19 21:35
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('personalni', '0003_initial'),
+        ('prednasky', '0013_auto_20240312_2124'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='prednaska',
+            name='org',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='personalni.organizator'),
+        ),
+    ]
diff --git a/prednasky/models.py b/prednasky/models.py
index dcf44cbc..7045f4d5 100644
--- a/prednasky/models.py
+++ b/prednasky/models.py
@@ -2,7 +2,8 @@
 
 from django.db import models
 
-from seminar.models import Organizator, Soustredeni
+from seminar.models import Soustredeni
+from personalni.models import Organizator
 
 STAV_NAVRH = 1
 STAV_BUDE = 2
diff --git a/seminar/migrations/0119_alter_konfera_ucastnici_and_more.py b/seminar/migrations/0119_alter_konfera_ucastnici_and_more.py
new file mode 100644
index 00000000..31457997
--- /dev/null
+++ b/seminar/migrations/0119_alter_konfera_ucastnici_and_more.py
@@ -0,0 +1,85 @@
+# Generated by Django 4.2.11 on 2024-03-19 21:35
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('personalni', '0003_initial'),
+        ('seminar', '0118_alter_organizator_options_alter_osoba_options_and_more'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='konfera',
+            name='ucastnici',
+            field=models.ManyToManyField(help_text='Seznam účastníků konfery', through='seminar.Konfery_Ucastnici', to='personalni.resitel', verbose_name='účastníci konfery'),
+        ),
+        migrations.AlterField(
+            model_name='konfery_ucastnici',
+            name='resitel',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='personalni.resitel', verbose_name='řešitel'),
+        ),
+        migrations.AlterField(
+            model_name='novinky',
+            name='autor',
+            field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='personalni.organizator', verbose_name='Autor novinky'),
+        ),
+        migrations.AlterField(
+            model_name='orgtextnode',
+            name='organizator',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='personalni.organizator', verbose_name='Organizátor'),
+        ),
+        migrations.AlterField(
+            model_name='pohadka',
+            name='autor',
+            field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='personalni.organizator', verbose_name='Autor pohádky'),
+        ),
+        migrations.AlterField(
+            model_name='problem',
+            name='autor',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='autor_problemu_%(class)s', to='personalni.organizator', verbose_name='autor problému'),
+        ),
+        migrations.AlterField(
+            model_name='problem',
+            name='garant',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='garant_problemu_%(class)s', to='personalni.organizator', verbose_name='garant zadaného problému'),
+        ),
+        migrations.AlterField(
+            model_name='problem',
+            name='opravovatele',
+            field=models.ManyToManyField(blank=True, related_name='opravovatele_%(class)s', to='personalni.organizator', verbose_name='opravovatelé'),
+        ),
+        migrations.AlterField(
+            model_name='reseni',
+            name='resitele',
+            field=models.ManyToManyField(help_text='Seznam autorů řešení', through='seminar.Reseni_Resitele', to='personalni.resitel', verbose_name='autoři řešení'),
+        ),
+        migrations.AlterField(
+            model_name='reseni_resitele',
+            name='resitele',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='personalni.resitel', verbose_name='řešitel'),
+        ),
+        migrations.AlterField(
+            model_name='soustredeni',
+            name='organizatori',
+            field=models.ManyToManyField(help_text='Seznam organizátorů soustředění', through='seminar.Soustredeni_Organizatori', to='personalni.organizator', verbose_name='Organizátoři soustředění'),
+        ),
+        migrations.AlterField(
+            model_name='soustredeni',
+            name='ucastnici',
+            field=models.ManyToManyField(help_text='Seznam účastníků soustředění', through='seminar.Soustredeni_Ucastnici', to='personalni.resitel', verbose_name='účastníci soustředění'),
+        ),
+        migrations.AlterField(
+            model_name='soustredeni_organizatori',
+            name='organizator',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='personalni.organizator', verbose_name='organizátor'),
+        ),
+        migrations.AlterField(
+            model_name='soustredeni_ucastnici',
+            name='resitel',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='personalni.resitel', verbose_name='řešitel'),
+        ),
+    ]
diff --git a/seminar/models/__init__.py b/seminar/models/__init__.py
index 6df1478f..e3026a2c 100644
--- a/seminar/models/__init__.py
+++ b/seminar/models/__init__.py
@@ -1,10 +1,10 @@
 from .tvorba import *
 from .odevzdavatko import *
 from .base import *
-from .personalni import *
 from .soustredeni import *
 from .pomocne import *
 from .treenode import *
 from .novinky import *
 
 from various.models import Nastaveni
+from personalni.models import Organizator, Resitel, Skola, Prijemce, Osoba
diff --git a/seminar/models/novinky.py b/seminar/models/novinky.py
index cee674a8..b67bdfe6 100644
--- a/seminar/models/novinky.py
+++ b/seminar/models/novinky.py
@@ -4,7 +4,7 @@ from imagekit.processors import ResizeToFit
 
 from reversion import revisions as reversion
 
-from . import personalni as pm
+from personalni.models import Organizator
 
 @reversion.register(ignore_duplicates=True)
 class Novinky(models.Model):
@@ -26,7 +26,7 @@ class Novinky(models.Model):
 								  ],
 								  options={'quality': 95})
 
-	autor = models.ForeignKey(pm.Organizator, verbose_name='Autor novinky', null=True,
+	autor = models.ForeignKey(Organizator, verbose_name='Autor novinky', null=True,
 							  on_delete=models.SET_NULL)
 
 	zverejneno = models.BooleanField('Zveřejněno', default=False)
diff --git a/seminar/models/odevzdavatko.py b/seminar/models/odevzdavatko.py
index 744fe38c..b0dec663 100644
--- a/seminar/models/odevzdavatko.py
+++ b/seminar/models/odevzdavatko.py
@@ -10,11 +10,11 @@ from django.utils import timezone
 from django.conf import settings
 
 from seminar.models import tvorba as am
-from seminar.models import personalni as pm
 from seminar.models import treenode as tm
 from seminar.models import base as bm
 
 from seminar.utils import vzorecek_na_prepocet, inverze_vzorecku_na_prepocet
+from personalni.models import Resitel
 
 
 @reversion.register(ignore_duplicates=True)
@@ -34,7 +34,7 @@ class Reseni(bm.SeminarModelBase):
 	problem = models.ManyToManyField(am.Problem, verbose_name='problém', help_text='Problém',
 									 through='Hodnoceni')
 
-	resitele = models.ManyToManyField(pm.Resitel, verbose_name='autoři řešení',
+	resitele = models.ManyToManyField(Resitel, verbose_name='autoři řešení',
 									  help_text='Seznam autorů řešení', through='Reseni_Resitele')
 
 
@@ -229,7 +229,7 @@ class Reseni_Resitele(models.Model):
 	# Interní ID
 	id = models.AutoField(primary_key = True)
 
-	resitele = models.ForeignKey(pm.Resitel, verbose_name='řešitel', on_delete=models.PROTECT)
+	resitele = models.ForeignKey(Resitel, verbose_name='řešitel', on_delete=models.PROTECT)
 
 	reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE)
 
diff --git a/seminar/models/soustredeni.py b/seminar/models/soustredeni.py
index 03ff5909..275224a3 100644
--- a/seminar/models/soustredeni.py
+++ b/seminar/models/soustredeni.py
@@ -8,7 +8,7 @@ from reversion import revisions as reversion
 
 from django.conf import settings
 
-from . import personalni as pm
+from personalni.models import Resitel, Organizator
 
 from .base import SeminarModelBase
 from seminar.models import tvorba as am
@@ -42,10 +42,10 @@ class Soustredeni(SeminarModelBase):
 	misto = models.CharField('místo soustředění', max_length=256, blank=True, default='',
 		help_text='Místo (název obce, volitelně též objektu')
 
-	ucastnici = models.ManyToManyField(pm.Resitel, verbose_name='účastníci soustředění',
+	ucastnici = models.ManyToManyField(Resitel, verbose_name='účastníci soustředění',
 		help_text='Seznam účastníků soustředění', through='Soustredeni_Ucastnici')
 
-	organizatori = models.ManyToManyField(pm.Organizator,
+	organizatori = models.ManyToManyField(Organizator,
 			verbose_name='Organizátoři soustředění',
 			help_text='Seznam organizátorů soustředění',
 			through='Soustredeni_Organizatori')
@@ -92,7 +92,7 @@ class Soustredeni_Ucastnici(SeminarModelBase):
 	# Interní ID
 	id = models.AutoField(primary_key = True)
 
-	resitel = models.ForeignKey(pm.Resitel, verbose_name='řešitel', on_delete=models.PROTECT)
+	resitel = models.ForeignKey(Resitel, verbose_name='řešitel', on_delete=models.PROTECT)
 
 	soustredeni = models.ForeignKey(Soustredeni, verbose_name='soustředění',
 		on_delete=models.PROTECT)
@@ -118,7 +118,7 @@ class Soustredeni_Organizatori(SeminarModelBase):
 	# Interní ID
 	id = models.AutoField(primary_key = True)
 
-	organizator = models.ForeignKey(pm.Organizator, verbose_name='organizátor',
+	organizator = models.ForeignKey(Organizator, verbose_name='organizátor',
 		on_delete=models.PROTECT)
 
 	soustredeni = models.ForeignKey(Soustredeni, verbose_name='soustředění',
@@ -163,7 +163,7 @@ class Konfera(am.Problem):
 								help_text='Abstrakt konfery tak, jak byl uveden ve sborníku')
 
 	# FIXME: Umíme omezit jen na účastníky daného soustřeďka?
-	ucastnici = models.ManyToManyField(pm.Resitel, verbose_name='účastníci konfery',
+	ucastnici = models.ManyToManyField(Resitel, verbose_name='účastníci konfery',
 									   help_text='Seznam účastníků konfery', through='Konfery_Ucastnici')
 
 	soustredeni = models.ForeignKey(Soustredeni, verbose_name='soustředění',
@@ -204,7 +204,7 @@ class Konfery_Ucastnici(models.Model):
 	# Interní ID
 	id = models.AutoField(primary_key = True)
 
-	resitel = models.ForeignKey(pm.Resitel, verbose_name='řešitel', on_delete=models.PROTECT)
+	resitel = models.ForeignKey(Resitel, verbose_name='řešitel', on_delete=models.PROTECT)
 
 	konfera = models.ForeignKey(Konfera, verbose_name='konfera', on_delete=models.CASCADE)
 
diff --git a/seminar/models/treenode.py b/seminar/models/treenode.py
index 50261d1a..735501c0 100644
--- a/seminar/models/treenode.py
+++ b/seminar/models/treenode.py
@@ -9,7 +9,7 @@ from unidecode import unidecode # Používám pro získání ID odkazu (ještě
 
 from polymorphic.models import PolymorphicModel
 
-from . import personalni as pm
+from personalni.models import Organizator
 
 from .pomocne import Text
 
@@ -166,7 +166,7 @@ class OrgTextNode(TreeNode):
 		verbose_name = 'Organizátorský článek (Node)'
 		verbose_name_plural = 'Organizátorské články (Node)'
 	
-	organizator = models.ForeignKey(pm.Organizator,
+	organizator = models.ForeignKey(Organizator,
 		null=False,
 		blank=False,
 		on_delete=models.DO_NOTHING,
diff --git a/seminar/models/tvorba.py b/seminar/models/tvorba.py
index 41def9c4..1dd9db8e 100644
--- a/seminar/models/tvorba.py
+++ b/seminar/models/tvorba.py
@@ -34,7 +34,7 @@ from polymorphic.models import PolymorphicModel
 from django.core.mail import EmailMessage
 from seminar.utils import aktivniResitele
 
-from . import personalni as pm
+from personalni.models import Prijemce, Organizator
 
 from .base import SeminarModelBase
 
@@ -306,7 +306,7 @@ class Cislo(SeminarModelBase):
 			  resitele_vsichni.filter(zasilat_cislo_papirove=True))
 
 		paticka_prijemce = "---\nPokud tyto e-maily nechcete nadále dostávat, prosíme, ozvěte se nám na mam@matfyz.cz."
-		posli(predmet, text_mailu + paticka_prijemce, pm.Prijemce.objects.filter(zasilat_cislo_emailem=True))
+		posli(predmet, text_mailu + paticka_prijemce, Prijemce.objects.filter(zasilat_cislo_emailem=True))
 
 	def save(self, *args, **kwargs):
 		super().save(*args, **kwargs)
@@ -462,15 +462,15 @@ class Problem(SeminarModelBase,PolymorphicModel):
 	poznamka = models.TextField('org poznámky (HTML)', blank=True,
 		help_text='Neveřejný návrh úlohy, návrh řešení, text zadání, poznámky ...')
 
-	autor = models.ForeignKey(pm.Organizator, verbose_name='autor problému',
+	autor = models.ForeignKey(Organizator, verbose_name='autor problému',
 		related_name='autor_problemu_%(class)s', null=True, blank=True,
 		on_delete=models.SET_NULL)
 
-	garant = models.ForeignKey(pm.Organizator, verbose_name='garant zadaného problému',
+	garant = models.ForeignKey(Organizator, verbose_name='garant zadaného problému',
 		related_name='garant_problemu_%(class)s', null=True, blank=True,
 		on_delete=models.SET_NULL)
 
-	opravovatele = models.ManyToManyField(pm.Organizator, verbose_name='opravovatelé',
+	opravovatele = models.ManyToManyField(Organizator, verbose_name='opravovatelé',
 		blank=True, related_name='opravovatele_%(class)s')
 
 	kod = models.CharField('lokální kód', max_length=32, blank=True, default='',
@@ -691,7 +691,7 @@ class Pohadka(SeminarModelBase):
 	id = models.AutoField(primary_key=True)
 
 	autor = models.ForeignKey(
-		pm.Organizator,
+		Organizator,
 		verbose_name="Autor pohádky",
 
 		# Při nahrávání z TeXu není vyplnění vyžadováno, v adminu je
diff --git a/sifrovacka/migrations/0005_alter_odpoveducastnika_resitel.py b/sifrovacka/migrations/0005_alter_odpoveducastnika_resitel.py
new file mode 100644
index 00000000..34b50d4b
--- /dev/null
+++ b/sifrovacka/migrations/0005_alter_odpoveducastnika_resitel.py
@@ -0,0 +1,20 @@
+# Generated by Django 4.2.11 on 2024-03-19 21:35
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('personalni', '0003_initial'),
+        ('sifrovacka', '0004_auto_20240312_2124'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='odpoveducastnika',
+            name='resitel',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='personalni.resitel'),
+        ),
+    ]
diff --git a/sifrovacka/models.py b/sifrovacka/models.py
index 6517c2e0..5bad397f 100644
--- a/sifrovacka/models.py
+++ b/sifrovacka/models.py
@@ -1,7 +1,7 @@
 from django.db import models
 from django.utils import timezone
 
-from seminar.models.personalni import Resitel
+from personalni.models import Resitel
 
 
 # Create your models here.

From 53f40e193d06553e220735d34e86095263099f59 Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Tue, 19 Mar 2024 22:48:42 +0100
Subject: [PATCH 26/59] =?UTF-8?q?Dopln=C4=9Bn=C3=AD=20chyb=C4=9Bj=C3=ADc?=
 =?UTF-8?q?=C3=ADho=20jm=C3=A9na=20tabulky=20(do=20p=C5=99edchoz=C3=AD=20m?=
 =?UTF-8?q?igrace,=20proto=C5=BEe=20yolo)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 personalni/migrations/0003_initial.py | 1 +
 personalni/models.py                  | 1 +
 2 files changed, 2 insertions(+)

diff --git a/personalni/migrations/0003_initial.py b/personalni/migrations/0003_initial.py
index fc586ff6..e026182c 100644
--- a/personalni/migrations/0003_initial.py
+++ b/personalni/migrations/0003_initial.py
@@ -42,6 +42,7 @@ class Migration(migrations.Migration):
             options={
                 'verbose_name': 'Organizátor',
                 'verbose_name_plural': 'Organizátoři',
+                'db_table': 'seminar_organizator',
                 'ordering': ['-organizuje_do', 'osoba__jmeno', 'osoba__prijmeni'],
                 'managed': False,
             },
diff --git a/personalni/models.py b/personalni/models.py
index 743e18b8..cff2899e 100644
--- a/personalni/models.py
+++ b/personalni/models.py
@@ -407,6 +407,7 @@ class Organizator(SeminarModelBase):
 		# TODO: Chtěl bych spíš mít nejstarší orgy dole.
 		# TODO: Zohledňovat přezdívky?
 		# TODO: Sjednotit s tím, jak se řadí organizátoři v seznau orgů na webu
+		db_table = 'seminar_organizator'
 		ordering = ['-organizuje_do', 'osoba__jmeno', 'osoba__prijmeni']
 		managed = False
 

From e083f74a835991958ff131d72337bd743c184e37 Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Tue, 19 Mar 2024 22:49:32 +0100
Subject: [PATCH 27/59] =?UTF-8?q?Dal=C5=A1=C3=AD=20pozn=C3=A1mka?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 split-apps-meta/relink.notes | 1 +
 1 file changed, 1 insertion(+)

diff --git a/split-apps-meta/relink.notes b/split-apps-meta/relink.notes
index 54d70260..5465c47d 100644
--- a/split-apps-meta/relink.notes
+++ b/split-apps-meta/relink.notes
@@ -1,4 +1,5 @@
 Přepsat všechny odkazy v sousedních aplikacích na novou aplikaci
 Naincludovat nové modely v seminar.models kvůli views
 makemigrations
+	Zkontrolovat přítomnost závislosti na create!
 migrate

From ace30c931e3d4168be0ca5a76bae8b160103e115 Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Tue, 19 Mar 2024 22:57:51 +0100
Subject: [PATCH 28/59] =?UTF-8?q?Neimportovat=20z=20mazan=C3=A9ho=20modulu?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 sifrovacka/views.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sifrovacka/views.py b/sifrovacka/views.py
index 9c4af3ed..960dc366 100644
--- a/sifrovacka/views.py
+++ b/sifrovacka/views.py
@@ -4,7 +4,7 @@ from django.views.generic import FormView, ListView
 from seminar.views import formularOKView
 from .forms import SifrovackaForm
 from .models import OdpovedUcastnika, SpravnaOdpoved
-from seminar.models.personalni import Resitel
+from seminar.models import Resitel
 
 
 # Create your views here.

From 858b5ce054ffc8f9a9e7fd9193eeaac18aeff3d3 Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Tue, 19 Mar 2024 22:58:15 +0100
Subject: [PATCH 29/59] =?UTF-8?q?Smaz=C3=A1n=C3=AD,=20makemigrations?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 ...oba_user_remove_prijemce_osoba_and_more.py |  48 ++
 seminar/models/personalni.py                  | 453 ------------------
 2 files changed, 48 insertions(+), 453 deletions(-)
 create mode 100644 seminar/migrations/0120_remove_osoba_user_remove_prijemce_osoba_and_more.py
 delete mode 100644 seminar/models/personalni.py

diff --git a/seminar/migrations/0120_remove_osoba_user_remove_prijemce_osoba_and_more.py b/seminar/migrations/0120_remove_osoba_user_remove_prijemce_osoba_and_more.py
new file mode 100644
index 00000000..6e4f4612
--- /dev/null
+++ b/seminar/migrations/0120_remove_osoba_user_remove_prijemce_osoba_and_more.py
@@ -0,0 +1,48 @@
+# Generated by Django 4.2.11 on 2024-03-19 21:57
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('seminar', '0119_alter_konfera_ucastnici_and_more'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='osoba',
+            name='user',
+        ),
+        migrations.RemoveField(
+            model_name='prijemce',
+            name='osoba',
+        ),
+        migrations.RemoveField(
+            model_name='resitel',
+            name='osoba',
+        ),
+        migrations.RemoveField(
+            model_name='resitel',
+            name='skola',
+        ),
+        migrations.RemoveField(
+            model_name='skola',
+            name='kontaktni_osoba',
+        ),
+        migrations.DeleteModel(
+            name='Organizator',
+        ),
+        migrations.DeleteModel(
+            name='Osoba',
+        ),
+        migrations.DeleteModel(
+            name='Prijemce',
+        ),
+        migrations.DeleteModel(
+            name='Resitel',
+        ),
+        migrations.DeleteModel(
+            name='Skola',
+        ),
+    ]
diff --git a/seminar/models/personalni.py b/seminar/models/personalni.py
deleted file mode 100644
index 5286e1be..00000000
--- a/seminar/models/personalni.py
+++ /dev/null
@@ -1,453 +0,0 @@
-# -*- coding: utf-8 -*-
-import logging
-
-from django.db import models
-from django.utils import timezone
-from django.conf import settings
-from django.core.exceptions import ValidationError
-from imagekit.models import ImageSpecField, ProcessedImageField
-from imagekit.processors import ResizeToFit, Transpose
-
-from django_countries.fields import CountryField
-
-from reversion import revisions as reversion
-
-from .base import SeminarModelBase
-
-logger = logging.getLogger(__name__)
-
-
-@reversion.register(ignore_duplicates=True)
-class Osoba(SeminarModelBase):
-	
-	class Meta:
-		db_table = 'seminar_osoby'
-		verbose_name = 'Osoba'
-		verbose_name_plural = 'Osoby'
-		ordering = ['prijmeni','jmeno']
-		managed = False
-	
-	id = models.AutoField(primary_key = True)
-
-	jmeno = models.CharField('jméno', max_length=256)
-
-	prijmeni = models.CharField('příjmení', max_length=256)
-
-	prezdivka = models.CharField('přezdívka', blank=True, null=True, max_length=256)
-
-	# User, pokud má na webu účet
-	user = models.OneToOneField(settings.AUTH_USER_MODEL, blank=True, null=True, 
-				verbose_name='uživatel', on_delete=models.DO_NOTHING,
-				related_name='user_old')
-
-	# Pohlaví. Že ho neznáme se snad nestane (a ušetří to práci při programování)
-	pohlavi_muz = models.BooleanField('pohlaví (muž)', default=False)
-
-	email = models.EmailField('e-mail', max_length=256, blank=True, default='')
-
-	telefon = models.CharField('telefon', max_length=256, blank=True, default='')
-
-	datum_narozeni = models.DateField('datum narození', blank=True, null=True)
-
-	# NULL dokud nedali souhlas
-	datum_souhlasu_udaje = models.DateField('datum souhlasu (údaje)', blank=True, null=True,
-		help_text='Datum souhlasu se zpracováním osobních údajů')
-
-	# NULL dokud nedali souhlas
-	datum_souhlasu_zasilani = models.DateField('datum souhlasu (spam)', blank=True, null=True,
-		help_text='Datum souhlasu se zasíláním MFF materiálů')
-
-	# Alespoň odhad (rok či i měsíc)
-	datum_registrace = models.DateField('datum registrace do semináře', default=timezone.now)
-
-	# Ulice může být i jen číslo
-	ulice = models.CharField('ulice', max_length=256, blank=True, default='')
-
-	mesto = models.CharField('město', max_length=256, blank=True, default='')
-
-	psc = models.CharField('PSČ', max_length=32, blank=True, default='')
-
-	# ISO 3166-1 dvojznakovy kod zeme velkym pismem (CZ, SK)
-	# Ekvivalentní s CharField(max_length=2, default='CZ', ...)
-	stat = CountryField('stát', default='CZ',
-		help_text='ISO 3166-1 kód země velkými písmeny (CZ, SK, ...)')
-
-	jak_se_dozvedeli = models.TextField('Jak se dozvěděli', blank=True)
-
-	poznamka = models.TextField('neveřejná poznámka', blank=True,
-		help_text='Neveřejná poznámka k osobě (plain text)')
-
-	foto = ProcessedImageField(verbose_name='Fotografie osoby',
-			upload_to='image_osoby/velke/%Y/', null = True, blank = True,
-			help_text = 'Vlož fotografii osoby o libovolné velikosti',
-			processors=[
-				Transpose(Transpose.AUTO),
-				ResizeToFit(500, 500, upscale=False)
-			],
-			options={'quality': 95})
-	foto_male = ImageSpecField(source='foto',
-			processors=[
-				ResizeToFit(200, 200, upscale=False)
-			],
-			options={'quality': 95})
-
-	# má OneToOneField nejvýše s:
-	# Resitel
-	# Prijemce
-	# Organizator
-
-	def plne_jmeno(self):
-		return '{} {}'.format(self.jmeno, self.prijmeni)
-
-	def inicial_krestni(self):
-		jmena = self.jmeno.split()
-		return " ".join(['{}.'.format(jmeno[0]) for jmeno in jmena])
-
-	def __str__(self):
-		return self.plne_jmeno()
-
-	# Overridujeme save Osoby, aby když si změní e-mail, aby se projevil i v
-	# Userovi (a tak se dal poslat mail s resetem hesla)
-	def save(self, *args, **kwargs):
-		if self.user is not None:
-			u = self.user
-			# U svatého tučňáka, prosím ať tohle funguje.
-			# (Takhle se kódit asi nemá...)
-			u.email = self.email
-			u.save()
-		super().save()
-
-#
-# Mělo by být částečně vytaženo z Aesopa
-# viz https://ovvp.mff.cuni.cz/wiki/aesop/export-skol.
-#
-
-@reversion.register(ignore_duplicates=True)
-class Skola(SeminarModelBase):
-
-	class Meta:
-		db_table = 'seminar_skoly'
-		verbose_name = 'Škola'
-		verbose_name_plural = 'Školy'
-		ordering = ['mesto', 'nazev']
-		managed = False
-
-	# Interní ID
-	id = models.AutoField(primary_key = True)
-
-	# Aesopi ID "izo:..." nebo "aesop:..."
-	# NULL znamená v exportu do aesopa "ufo"
-	aesop_id = models.CharField('Aesop ID', max_length=32, blank=True, default='',
-		help_text='Aesopi ID typu "izo:..." nebo "aesop:..."')
-
-	# IZO školy (jen české školy)
-	izo = models.CharField('IZO', max_length=32, blank=True,
-		help_text='IZO školy (jen české školy)')
-
-	# Celý název školy
-	nazev = models.CharField('název', max_length=256,
-		help_text='Celý název školy')
-
-	# Zkraceny nazev pro zobrazení ve výsledkovce, volitelné.
-	# Není v Aesopovi, musíme vytvářet sami.
-	kratky_nazev = models.CharField('zkrácený název', max_length=256, blank=True,
-		help_text="Zkrácený název pro zobrazení ve výsledkovce")
-
-	# Ulice může být jen číslo
-	ulice = models.CharField('ulice', max_length=256)
-
-	mesto = models.CharField('město', max_length=256)
-
-	psc = models.CharField('PSČ', max_length=32)
-
-	# ISO 3166-1 dvojznakovy kod zeme velkym pismem (CZ, SK)
-	# Ekvivalentní s CharField(max_length=2, default='CZ', ...)
-	stat = CountryField('stát', default='CZ',
-		help_text='ISO 3166-1 kód země velkými písmeny (CZ, SK, ...)')
-
-	# Jaké vzdělání škpla poskytuje?
-	je_zs = models.BooleanField('základní stupeň', default=True)
-	je_ss = models.BooleanField('střední stupeň', default=True)
-
-	poznamka = models.TextField('neveřejná poznámka', blank=True,
-		help_text='Neveřejná poznámka ke škole (plain text)')
-	
-	kontaktni_osoba = models.ForeignKey(Osoba, verbose_name='Kontaktní osoba', 
-			blank=True, null=True, on_delete=models.SET_NULL, related_name='kontaktni_osoba_old')
-
-	def __str__(self):
-		return '{}, {}, {}'.format(self.nazev, self.ulice, self.mesto)
-
-class Prijemce(SeminarModelBase):
-	class Meta:
-		db_table = 'seminar_prijemce'
-		verbose_name = 'příjemce'
-		verbose_name_plural = 'příjemce'
-		managed = False
-	
-
-	# Interní ID
-	id = models.AutoField(primary_key = True)
-
-	poznamka = models.TextField('neveřejná poznámka', blank=True,
-		help_text='Neveřejná poznámka k příemci čísel (plain text)')
-
-	osoba = models.OneToOneField(Osoba, verbose_name='komu', blank=False, null=False,
-		help_text='Které osobě či na jakou adresu se mají zasílat čísla',
-		on_delete=models.CASCADE, related_name='osobad_old1')
-
-	zasilat_cislo_emailem = models.BooleanField('zasílat číslo emailem', help_text='True pokud chce příjemce dostávat číslo emailem', default=False)
-
-	# FIXME: možná chceme něco jako vazbu na osobu XOR školu a počet kusů k zaslání
-	# FIXME: a možná taky posílání na mail a možná taky přes něj chceme posílat i řešitelům
-
-	def __str__(self):
-		return self.osoba.plne_jmeno()
-	
-
-@reversion.register(ignore_duplicates=True)
-class Resitel(SeminarModelBase):
-
-	class Meta:
-		db_table = 'seminar_resitele'
-		verbose_name = 'Řešitel'
-		verbose_name_plural = 'Řešitelé'
-		ordering = ['osoba']
-		managed = False
-
-	# Interní ID
-	id = models.AutoField(primary_key = True)
-
-	prezdivka_resitele = models.CharField('přezdívka řešitele', blank=True, null=True, max_length=256, unique=True)
-
-	osoba = models.OneToOneField(Osoba, blank=False, null=False, verbose_name='osoba',
-		on_delete=models.PROTECT, related_name='osoba_old2')
-	
-
-	skola = models.ForeignKey(Skola, blank=True, null=True, verbose_name='škola',
-		on_delete=models.SET_NULL, related_name='skola_old3')
-
-	# Očekávaný rok maturity a vyřazení z aktivních řešitelů
-	rok_maturity = models.IntegerField('rok maturity', blank=True, null=True)
-
-	ZASILAT_DOMU = 'domu'
-	ZASILAT_DO_SKOLY = 'do_skoly'
-	ZASILAT_NIKAM = 'nikam'
-	ZASILAT_CHOICES = [
-		(ZASILAT_DOMU, 'Domů'),
-		(ZASILAT_DO_SKOLY, 'Do školy'),
-		(ZASILAT_NIKAM, 'Nezasílat papírově'),
-		]
-
-	zasilat = models.CharField('kam zasílat', max_length=32, choices=ZASILAT_CHOICES, blank=False, default=ZASILAT_DOMU)
-
-	zasilat_cislo_emailem = models.BooleanField('zasílat číslo emailem', help_text='True pokud chce řešitel dostávat číslo emailem', default=False)
-
-	zasilat_cislo_papirove = models.BooleanField('zasílat číslo papírově', help_text='True pokud chce řešitel dostávat číslo papírově', default=True)
-
-	poznamka = models.TextField('neveřejná poznámka', blank=True,
-		help_text='Neveřejná poznámka k řešiteli (plain text)')
-
-
-	def export_row(self):
-		"Slovnik pro pouziti v AESOP exportu"
-		return {
-			'id': self.id,
-			'name': self.osoba.jmeno,
-			'surname': self.osoba.prijmeni,
-			'gender': 'M' if self.osoba.pohlavi_muz else 'F',
-			'born': self.osoba.datum_narozeni.isoformat() if self.osoba.datum_narozeni else '',
-			'email': self.osoba.email,
-			'end-year': self.rok_maturity or '',
-
-			'street': self.osoba.ulice,
-			'town': self.osoba.mesto,
-			'postcode': self.osoba.psc,
-			'country': self.osoba.stat,
-
-			'spam-flag': 'Y' if self.osoba.datum_souhlasu_zasilani else '',
-			'spam-date': self.osoba.datum_souhlasu_zasilani.isoformat() if self.osoba.datum_souhlasu_zasilani else '',
-
-			'school': self.skola.aesop_id if self.skola else '',
-			'school-name': str(self.skola) if self.skola else 'Skola neni znama',
-			}
-
-	def rocnik(self, rocnik):
-		"""Vrati skolni rocnik resitele pro zadany Rocnik.
-				Vraci '' pro neznamy rok maturity resitele, Z* pro ekvivalent ZŠ."""
-		if self.rok_maturity is None:
-			return ''
-		rozdil = 5 - (self.rok_maturity - rocnik.prvni_rok)
-		if rozdil >= 1:
-			return str(rozdil)
-		else:
-			return 'Z' + str(rozdil + 9)
-
-	def vsechny_body(self):
-		"Spočítá body odjakživa."
-		vsechna_reseni = self.reseni_set.all()
-		from .odevzdavatko import Hodnoceni
-		vsechna_hodnoceni = Hodnoceni.objects.filter(
-			reseni__in=vsechna_reseni)
-		return sum(h.body for h in list(vsechna_hodnoceni) if h.body is not None)
-
-
-	def get_titul(self, body=None):
-		"Vrati titul jako řetězec."
-		
-		# Nejprve si zadefinujeme titul
-		from enum import Enum
-		from functools import total_ordering
-		@total_ordering
-		class Titul(Enum):
-			""" Třída reprezentující možné tituly. Hodnoty jsou dvojice (dolní hranice, stringifikace). """
-			nic =  (0, '')
-			bc =   (20, 'Bc.')
-			mgr =  (50, 'Mgr.')
-			dr =   (100, 'Dr.')
-			doc =  (200, 'Doc.')
-			prof = (500, 'Prof.')
-			akad = (1000, 'Akad.')
-
-			def __lt__(self, other):
-				return True if self.value[0] < other.value[0] else False
-			def __eq__(self, other): # Měla by být implicitní, ale klidně explicitně.
-				return True if self.value[0] == other.value[0] else False
-
-			def __str__(self):
-				return self.value[1]
-
-			@classmethod
-			def z_bodu(cls, body):
-				aktualni = cls.nic
-				# TODO: ověřit, že to funguje
-				for titul in cls: # Kdyžtak použít __members__.items()
-					if titul.value[0] <= body:
-						aktualni = titul
-					else:
-						break
-				return aktualni
-
-		# Hledáme body v databázi
-		# V listopadu 2020 jsme se na filosofické schůzce shodli o změně hranic titulů:
-		#  - body z 25. ročníku a dříve byly shledány dvakrát hodnotnějšími
-		#  - proto se započítávají dvojnásobně a byly posunuté hranice titulů
-		#  - staré tituly se ale nemají odebrat, pokud řešitel v t.č. minulém (26.) ročníku měl titul, má ho mít pořád.
-		from .odevzdavatko import Hodnoceni
-		hodnoceni_do_25_rocniku = Hodnoceni.objects.filter(deadline_body__cislo__rocnik__rocnik__lte=25,reseni__in=self.reseni_set.all())
-		novejsi_hodnoceni = Hodnoceni.objects.filter(reseni__in=self.reseni_set.all()).difference(hodnoceni_do_25_rocniku)
-
-		def body_z_hodnoceni(hh : list):
-			return sum(h.body for h in hh if h.body is not None)
-
-		stare_body = body_z_hodnoceni(hodnoceni_do_25_rocniku)
-		if body is None:
-			nove_body = body_z_hodnoceni(novejsi_hodnoceni)
-		else:
-			# Zjistíme, kolik bodů jsou staré, tedy hodnotnější
-			nove_body = max(0, body - stare_body) # Všechny body nad počet původních hodnotnějších
-			stare_body = min(stare_body, body) # Skutečný počet hodnotnějších bodů
-		logicke_body = 2*stare_body + nove_body
-
-	
-		# Titul se určí následovně:
-		#  - Pokud se řeší body, které jsou starší, než do 26 ročníku (včetně), dáváme tituly postaru.
-		#  - Jinak dáváme tituly po novu...
-		#  - ... ale titul se nesmí odebrat, pokud se zmenšil.
-		def titul_do_26_rocniku(body):
-			""" Původní hranice bodů za tituly """
-			if body < 10:
-				return Titul.nic
-			elif body < 20:
-				return Titul.bc
-			elif body < 50:
-				return Titul.mgr
-			elif body < 100:
-				return Titul.dr
-			elif body < 200:
-				return Titul.doc
-			elif body < 500:
-				return Titul.prof
-			else:
-				return Titul.akad
-
-		from .odevzdavatko import Hodnoceni
-		hodnoceni_do_26_rocniku = Hodnoceni.objects.filter(deadline_body__cislo__rocnik__rocnik__lte=26,reseni__in=self.reseni_set.all())
-		novejsi_body = body_z_hodnoceni(
-			Hodnoceni.objects.filter(reseni__in=self.reseni_set.all())
-			.difference(hodnoceni_do_26_rocniku)
-			)
-		starsi_body = body_z_hodnoceni(hodnoceni_do_26_rocniku)
-		if body is not None:
-			# Ještě z toho vybereme ty správně staré body
-			novejsi_body = max(0, body - starsi_body)
-			starsi_body = min(starsi_body, body)
-
-		# Titul pro 26. ročník
-		stary_titul = titul_do_26_rocniku(starsi_body)
-		# Titul podle aktuálních pravidel
-		novy_titul = Titul.z_bodu(logicke_body)
-
-		if novejsi_body == 0:
-			# Žádné nové body -- titul podle starých pravidel
-			return str(stary_titul)
-		return str(max(novy_titul, stary_titul))
-
-
-	def __str__(self):
-		return self.osoba.plne_jmeno()
-
-
-@reversion.register(ignore_duplicates=True)
-class Organizator(SeminarModelBase):
-	osoba = models.OneToOneField(Osoba, verbose_name='osoba', related_name='org_old4',
-		help_text='osobní údaje organizátora', null=False, blank=False,
-		on_delete=models.PROTECT)
-
-	vytvoreno = models.DateTimeField(
-		'Vytvořeno',
-		default=timezone.now,
-		blank=True,
-		editable=False
-	)
-
-	# Ne, date to nebude. SQLite: invalid literal for int() with base 10: b'17 23:00:00'
-	organizuje_od = models.DateTimeField('Organizuje od', blank=True, null=True)
-	
-	organizuje_do = models.DateTimeField('Organizuje do', blank=True, null=True)
-
-	studuje = models.CharField('Studium aj.', max_length = 256,
-			null = True, blank = True,
-			help_text="Např. 'Studuje Obecnou fyziku (Bc.), 3. ročník', "
-			"'Vystudovala Diskrétní modely a algoritmy (Mgr.)' nebo "
-			"'Přednáší na MFF'")
-
-	strucny_popis_organizatora = models.TextField('Stručný popis organizátora',
-			null = True, blank = True)
-
-	skola = models.CharField('Škola, kterou studuje', max_length = 256, null=True, blank=True,
-		help_text="Škola, např. MFF, VŠCHT, VUT, ... prostě aby se nemuselo psát do studuje"
-		"školu, ale jen obor, možnost zobrazit zvlášť")
-
-	def clean(self):
-		if self.organizuje_od and self.organizuje_do and (self.organizuje_od > self.organizuje_do):
-			raise ValidationError("Organizátor nemůže skončit s organizováním dříve než začal!")
-		super().clean()
-
-	def __str__(self):
-		if self.osoba.prezdivka:
-			return "{} '{}' {}".format(self.osoba.jmeno,
-				self.osoba.prezdivka,
-				self.osoba.prijmeni)
-		else:
-			return "{} {}".format(self.osoba.jmeno, self.osoba.prijmeni)
-
-	class Meta:
-		verbose_name = 'Organizátor'
-		verbose_name_plural = 'Organizátoři'
-		# Řadí aktivní orgy na začátek, pod tím v pořadí od nejstarších neaktivní orgy.
-		# TODO: Chtěl bych spíš mít nejstarší orgy dole.
-		# TODO: Zohledňovat přezdívky?
-		# TODO: Sjednotit s tím, jak se řadí organizátoři v seznau orgů na webu
-		ordering = ['-organizuje_do', 'osoba__jmeno', 'osoba__prijmeni']
-		managed = False

From 9c1c393395be0491e3ccd52a692566056d5bec59 Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Tue, 19 Mar 2024 23:11:15 +0100
Subject: [PATCH 30/59] delete notes

---
 split-apps-meta/delete.notes | 8 ++++++++
 1 file changed, 8 insertions(+)
 create mode 100644 split-apps-meta/delete.notes

diff --git a/split-apps-meta/delete.notes b/split-apps-meta/delete.notes
new file mode 100644
index 00000000..64c15c0a
--- /dev/null
+++ b/split-apps-meta/delete.notes
@@ -0,0 +1,8 @@
+Prostě to smazat.
+makemigrations
+	Kromě triviální smazání modelů se smažou i jejich vazby, to je snad OK.
+		(Hypotéza: kvůli konzistenčním kontrolám v DB?)
+migrate
+a doufat :-P
+
+(Pozor: pokud něco (JSON serializace, data/… atp.) má starý identifikátor modelu, tak se to teď rozbije.)

From b17b97e4e4e5299b55a597e487b1133b79b6d602 Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Tue, 26 Mar 2024 22:09:12 +0100
Subject: [PATCH 31/59] OrgSkolyAutocompleteTestCase: jde vypnout
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Když máme jiný model, tak se nepotkají typy dumpů.

Cherry-pickable.
---
 api/tests/test_skola_autocomplete.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/api/tests/test_skola_autocomplete.py b/api/tests/test_skola_autocomplete.py
index 9fc4aee6..36df97e8 100644
--- a/api/tests/test_skola_autocomplete.py
+++ b/api/tests/test_skola_autocomplete.py
@@ -1,9 +1,10 @@
-from django.test import TestCase
+from django.test import TestCase, tag
 from django.urls import reverse
 import seminar.models as m
 import seminar.views as v
 from seminar.utils import sync_skoly
 
+@tag('stejny-model-na-produkci')
 class OrgSkolyAutocompleteTestCase(TestCase):
 	@classmethod
 	def setUpClass(cls):

From 54db53805a87e3d117df184f66f51fc6c7780be4 Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Tue, 26 Mar 2024 22:16:59 +0100
Subject: [PATCH 32/59] Nezapomenout na vazby

---
 split-apps-meta/delete.notes | 1 +
 1 file changed, 1 insertion(+)

diff --git a/split-apps-meta/delete.notes b/split-apps-meta/delete.notes
index 64c15c0a..dad0e6d4 100644
--- a/split-apps-meta/delete.notes
+++ b/split-apps-meta/delete.notes
@@ -2,6 +2,7 @@ Prostě to smazat.
 makemigrations
 	Kromě triviální smazání modelů se smažou i jejich vazby, to je snad OK.
 		(Hypotéza: kvůli konzistenčním kontrolám v DB?)
+Doplnit vazby na relinky a vznik nového modelu
 migrate
 a doufat :-P
 

From 1a3dd5d6b233aa646e3797002655fe2407e939ed Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Tue, 26 Mar 2024 22:17:53 +0100
Subject: [PATCH 33/59] =?UTF-8?q?Jak=20za=C5=99=C3=ADdit,=20=C5=BEe=20mode?=
 =?UTF-8?q?l=20bude=20managed?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 split-apps-meta/manage.notes | 3 +++
 1 file changed, 3 insertions(+)
 create mode 100644 split-apps-meta/manage.notes

diff --git a/split-apps-meta/manage.notes b/split-apps-meta/manage.notes
new file mode 100644
index 00000000..3cf5a8d2
--- /dev/null
+++ b/split-apps-meta/manage.notes
@@ -0,0 +1,3 @@
+1. smazat `managed = False`
+2. makemigrations
+3. migrate

From b4b41b949966c1cf8ed194de8c90fb06e6c5c278 Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Tue, 26 Mar 2024 22:18:12 +0100
Subject: [PATCH 34/59] =?UTF-8?q?P=C5=99id=C3=A1n=C3=AD=20z=C3=A1vislost?=
 =?UTF-8?q?=C3=AD=20do=20delete?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../0120_remove_osoba_user_remove_prijemce_osoba_and_more.py  | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/seminar/migrations/0120_remove_osoba_user_remove_prijemce_osoba_and_more.py b/seminar/migrations/0120_remove_osoba_user_remove_prijemce_osoba_and_more.py
index 6e4f4612..1bbfcff0 100644
--- a/seminar/migrations/0120_remove_osoba_user_remove_prijemce_osoba_and_more.py
+++ b/seminar/migrations/0120_remove_osoba_user_remove_prijemce_osoba_and_more.py
@@ -7,6 +7,10 @@ class Migration(migrations.Migration):
 
     dependencies = [
         ('seminar', '0119_alter_konfera_ucastnici_and_more'),
+        ('personalni', '0003_initial'),
+        ('korektury', '0022_alter_komentar_autor_alter_korekturovanepdf_org_and_more'),
+        ('prednasky', '0014_alter_prednaska_org'),
+        ('sifrovacka', '0005_alter_odpoveducastnika_resitel'),
     ]
 
     operations = [

From a17914f49ce6b09995369f933192dd0c6f3ab8ca Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Tue, 26 Mar 2024 22:18:25 +0100
Subject: [PATCH 35/59] Personalni: managed = True + makemigrations

---
 ...or_options_alter_osoba_options_and_more.py | 33 +++++++++++++++++++
 personalni/models.py                          |  5 ---
 2 files changed, 33 insertions(+), 5 deletions(-)
 create mode 100644 personalni/migrations/0004_alter_organizator_options_alter_osoba_options_and_more.py

diff --git a/personalni/migrations/0004_alter_organizator_options_alter_osoba_options_and_more.py b/personalni/migrations/0004_alter_organizator_options_alter_osoba_options_and_more.py
new file mode 100644
index 00000000..8abbf0a0
--- /dev/null
+++ b/personalni/migrations/0004_alter_organizator_options_alter_osoba_options_and_more.py
@@ -0,0 +1,33 @@
+# Generated by Django 4.2.11 on 2024-03-26 21:11
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('personalni', '0003_initial'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='organizator',
+            options={'ordering': ['-organizuje_do', 'osoba__jmeno', 'osoba__prijmeni'], 'verbose_name': 'Organizátor', 'verbose_name_plural': 'Organizátoři'},
+        ),
+        migrations.AlterModelOptions(
+            name='osoba',
+            options={'ordering': ['prijmeni', 'jmeno'], 'verbose_name': 'Osoba', 'verbose_name_plural': 'Osoby'},
+        ),
+        migrations.AlterModelOptions(
+            name='prijemce',
+            options={'verbose_name': 'příjemce', 'verbose_name_plural': 'příjemce'},
+        ),
+        migrations.AlterModelOptions(
+            name='resitel',
+            options={'ordering': ['osoba'], 'verbose_name': 'Řešitel', 'verbose_name_plural': 'Řešitelé'},
+        ),
+        migrations.AlterModelOptions(
+            name='skola',
+            options={'ordering': ['mesto', 'nazev'], 'verbose_name': 'Škola', 'verbose_name_plural': 'Školy'},
+        ),
+    ]
diff --git a/personalni/models.py b/personalni/models.py
index cff2899e..7ede2d08 100644
--- a/personalni/models.py
+++ b/personalni/models.py
@@ -25,7 +25,6 @@ class Osoba(SeminarModelBase):
 		verbose_name = 'Osoba'
 		verbose_name_plural = 'Osoby'
 		ordering = ['prijmeni','jmeno']
-		managed = False
 	
 	id = models.AutoField(primary_key = True)
 
@@ -129,7 +128,6 @@ class Skola(SeminarModelBase):
 		verbose_name = 'Škola'
 		verbose_name_plural = 'Školy'
 		ordering = ['mesto', 'nazev']
-		managed = False
 
 	# Interní ID
 	id = models.AutoField(primary_key = True)
@@ -182,7 +180,6 @@ class Prijemce(SeminarModelBase):
 		db_table = 'seminar_prijemce'
 		verbose_name = 'příjemce'
 		verbose_name_plural = 'příjemce'
-		managed = False
 	
 
 	# Interní ID
@@ -212,7 +209,6 @@ class Resitel(SeminarModelBase):
 		verbose_name = 'Řešitel'
 		verbose_name_plural = 'Řešitelé'
 		ordering = ['osoba']
-		managed = False
 
 	# Interní ID
 	id = models.AutoField(primary_key = True)
@@ -409,7 +405,6 @@ class Organizator(SeminarModelBase):
 		# TODO: Sjednotit s tím, jak se řadí organizátoři v seznau orgů na webu
 		db_table = 'seminar_organizator'
 		ordering = ['-organizuje_do', 'osoba__jmeno', 'osoba__prijmeni']
-		managed = False
 
 	osoba = models.OneToOneField(Osoba, verbose_name='osoba', related_name='org',
 		help_text='osobní údaje organizátora', null=False, blank=False,

From 49f9b052852a84a107e5fdb402ac77310dd24b87 Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Tue, 26 Mar 2024 22:19:02 +0100
Subject: [PATCH 36/59] =?UTF-8?q?nezapomenout=20vazby=E2=80=A6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 split-apps-meta/manage.notes | 1 +
 1 file changed, 1 insertion(+)

diff --git a/split-apps-meta/manage.notes b/split-apps-meta/manage.notes
index 3cf5a8d2..c6d4b189 100644
--- a/split-apps-meta/manage.notes
+++ b/split-apps-meta/manage.notes
@@ -1,3 +1,4 @@
 1. smazat `managed = False`
 2. makemigrations
+	Vazba na delete!
 3. migrate

From d21ac37187001b65aeb0bb74adb1540af6c985ad Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Tue, 26 Mar 2024 22:19:38 +0100
Subject: [PATCH 37/59] vazba

---
 ...004_alter_organizator_options_alter_osoba_options_and_more.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/personalni/migrations/0004_alter_organizator_options_alter_osoba_options_and_more.py b/personalni/migrations/0004_alter_organizator_options_alter_osoba_options_and_more.py
index 8abbf0a0..6065e58f 100644
--- a/personalni/migrations/0004_alter_organizator_options_alter_osoba_options_and_more.py
+++ b/personalni/migrations/0004_alter_organizator_options_alter_osoba_options_and_more.py
@@ -7,6 +7,7 @@ class Migration(migrations.Migration):
 
     dependencies = [
         ('personalni', '0003_initial'),
+        ('seminar', '0120_remove_osoba_user_remove_prijemce_osoba_and_more'),
     ]
 
     operations = [

From 9b12681e4526b96ea2ca3853f26d1c1ef3b5947d Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Tue, 26 Mar 2024 22:23:40 +0100
Subject: [PATCH 38/59] =?UTF-8?q?pomigra=C4=8Dn=C3=AD=20safeguardy?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 split-apps-meta/post | 2 ++
 1 file changed, 2 insertions(+)
 create mode 100644 split-apps-meta/post

diff --git a/split-apps-meta/post b/split-apps-meta/post
new file mode 100644
index 00000000..0fa01c0d
--- /dev/null
+++ b/split-apps-meta/post
@@ -0,0 +1,2 @@
+dummy migrace v novém modelu
+dummy migrace v semináři a ostatních, závisející na té nové migraci.

From e0d45ddf5d7ce558e6bcef59ba5996d3baf00ab1 Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Tue, 26 Mar 2024 22:26:33 +0100
Subject: [PATCH 39/59] =?UTF-8?q?Pomigra=C4=8Dn=C3=AD=20migrace?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

lol wording
---
 .../migrations/0023_personalni_post_migrate.py     | 14 ++++++++++++++
 .../migrations/0005_personalni_post_migrate.py     | 13 +++++++++++++
 .../migrations/0015_personalni_post_migrate.py     | 14 ++++++++++++++
 seminar/migrations/0121_personalni_post_migrate.py | 14 ++++++++++++++
 .../migrations/0006_personalni_post_migrate.py     | 14 ++++++++++++++
 5 files changed, 69 insertions(+)
 create mode 100644 korektury/migrations/0023_personalni_post_migrate.py
 create mode 100644 personalni/migrations/0005_personalni_post_migrate.py
 create mode 100644 prednasky/migrations/0015_personalni_post_migrate.py
 create mode 100644 seminar/migrations/0121_personalni_post_migrate.py
 create mode 100644 sifrovacka/migrations/0006_personalni_post_migrate.py

diff --git a/korektury/migrations/0023_personalni_post_migrate.py b/korektury/migrations/0023_personalni_post_migrate.py
new file mode 100644
index 00000000..a92ec89d
--- /dev/null
+++ b/korektury/migrations/0023_personalni_post_migrate.py
@@ -0,0 +1,14 @@
+# Generated by Django 4.2.11 on 2024-03-26 21:25
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('korektury', '0022_alter_komentar_autor_alter_korekturovanepdf_org_and_more'),
+        ('peronalni', '0005_personalni_post_migrate'),
+    ]
+
+    operations = [
+    ]
diff --git a/personalni/migrations/0005_personalni_post_migrate.py b/personalni/migrations/0005_personalni_post_migrate.py
new file mode 100644
index 00000000..72d1571a
--- /dev/null
+++ b/personalni/migrations/0005_personalni_post_migrate.py
@@ -0,0 +1,13 @@
+# Generated by Django 4.2.11 on 2024-03-26 21:25
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('personalni', '0004_alter_organizator_options_alter_osoba_options_and_more'),
+    ]
+
+    operations = [
+    ]
diff --git a/prednasky/migrations/0015_personalni_post_migrate.py b/prednasky/migrations/0015_personalni_post_migrate.py
new file mode 100644
index 00000000..7bb596d6
--- /dev/null
+++ b/prednasky/migrations/0015_personalni_post_migrate.py
@@ -0,0 +1,14 @@
+# Generated by Django 4.2.11 on 2024-03-26 21:25
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('prednasky', '0014_alter_prednaska_org'),
+        ('peronalni', '0005_personalni_post_migrate'),
+    ]
+
+    operations = [
+    ]
diff --git a/seminar/migrations/0121_personalni_post_migrate.py b/seminar/migrations/0121_personalni_post_migrate.py
new file mode 100644
index 00000000..f84328a5
--- /dev/null
+++ b/seminar/migrations/0121_personalni_post_migrate.py
@@ -0,0 +1,14 @@
+# Generated by Django 4.2.11 on 2024-03-26 21:25
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('seminar', '0120_remove_osoba_user_remove_prijemce_osoba_and_more'),
+        ('peronalni', '0005_personalni_post_migrate'),
+    ]
+
+    operations = [
+    ]
diff --git a/sifrovacka/migrations/0006_personalni_post_migrate.py b/sifrovacka/migrations/0006_personalni_post_migrate.py
new file mode 100644
index 00000000..25775ae4
--- /dev/null
+++ b/sifrovacka/migrations/0006_personalni_post_migrate.py
@@ -0,0 +1,14 @@
+# Generated by Django 4.2.11 on 2024-03-26 21:25
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('sifrovacka', '0005_alter_odpoveducastnika_resitel'),
+        ('peronalni', '0005_personalni_post_migrate'),
+    ]
+
+    operations = [
+    ]

From 51f730fe71d9017a45d123ecf6352e7c77c644df Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Tue, 26 Mar 2024 22:27:48 +0100
Subject: [PATCH 40/59] =?UTF-8?q?typo=E2=80=A6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 korektury/migrations/0023_personalni_post_migrate.py  | 2 +-
 prednasky/migrations/0015_personalni_post_migrate.py  | 2 +-
 seminar/migrations/0121_personalni_post_migrate.py    | 2 +-
 sifrovacka/migrations/0006_personalni_post_migrate.py | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/korektury/migrations/0023_personalni_post_migrate.py b/korektury/migrations/0023_personalni_post_migrate.py
index a92ec89d..048ece7a 100644
--- a/korektury/migrations/0023_personalni_post_migrate.py
+++ b/korektury/migrations/0023_personalni_post_migrate.py
@@ -7,7 +7,7 @@ class Migration(migrations.Migration):
 
     dependencies = [
         ('korektury', '0022_alter_komentar_autor_alter_korekturovanepdf_org_and_more'),
-        ('peronalni', '0005_personalni_post_migrate'),
+        ('personalni', '0005_personalni_post_migrate'),
     ]
 
     operations = [
diff --git a/prednasky/migrations/0015_personalni_post_migrate.py b/prednasky/migrations/0015_personalni_post_migrate.py
index 7bb596d6..04553e8f 100644
--- a/prednasky/migrations/0015_personalni_post_migrate.py
+++ b/prednasky/migrations/0015_personalni_post_migrate.py
@@ -7,7 +7,7 @@ class Migration(migrations.Migration):
 
     dependencies = [
         ('prednasky', '0014_alter_prednaska_org'),
-        ('peronalni', '0005_personalni_post_migrate'),
+        ('personalni', '0005_personalni_post_migrate'),
     ]
 
     operations = [
diff --git a/seminar/migrations/0121_personalni_post_migrate.py b/seminar/migrations/0121_personalni_post_migrate.py
index f84328a5..51840894 100644
--- a/seminar/migrations/0121_personalni_post_migrate.py
+++ b/seminar/migrations/0121_personalni_post_migrate.py
@@ -7,7 +7,7 @@ class Migration(migrations.Migration):
 
     dependencies = [
         ('seminar', '0120_remove_osoba_user_remove_prijemce_osoba_and_more'),
-        ('peronalni', '0005_personalni_post_migrate'),
+        ('personalni', '0005_personalni_post_migrate'),
     ]
 
     operations = [
diff --git a/sifrovacka/migrations/0006_personalni_post_migrate.py b/sifrovacka/migrations/0006_personalni_post_migrate.py
index 25775ae4..cf2c8ad5 100644
--- a/sifrovacka/migrations/0006_personalni_post_migrate.py
+++ b/sifrovacka/migrations/0006_personalni_post_migrate.py
@@ -7,7 +7,7 @@ class Migration(migrations.Migration):
 
     dependencies = [
         ('sifrovacka', '0005_alter_odpoveducastnika_resitel'),
-        ('peronalni', '0005_personalni_post_migrate'),
+        ('personalni', '0005_personalni_post_migrate'),
     ]
 
     operations = [

From 026efe2467646fc8aa95eba5c7f67da103ee310a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= <jonas.havelka@volny.cz>
Date: Sun, 14 Apr 2024 15:09:03 +0200
Subject: [PATCH 41/59] =?UTF-8?q?N=C3=A1pov=C4=9Bdy=20v=20=C5=A1ifrova?=
 =?UTF-8?q?=C4=8Dce?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 sifrovacka/admin.py                           |  4 +-
 sifrovacka/forms.py                           | 14 +++-
 .../0004_napoveda_napovezenoucastnikovi.py    | 65 +++++++++++++++++++
 sifrovacka/models.py                          | 17 +++++
 sifrovacka/templates/sifrovacka/napoveda.html | 48 ++++++++++++++
 .../templates/sifrovacka/napovedy_list.html   | 23 +++++++
 .../templates/sifrovacka/sifrovacka.html      |  2 +
 sifrovacka/urls.py                            | 12 +++-
 sifrovacka/views.py                           | 27 +++++++-
 9 files changed, 206 insertions(+), 6 deletions(-)
 create mode 100644 sifrovacka/migrations/0004_napoveda_napovezenoucastnikovi.py
 create mode 100644 sifrovacka/templates/sifrovacka/napoveda.html
 create mode 100644 sifrovacka/templates/sifrovacka/napovedy_list.html

diff --git a/sifrovacka/admin.py b/sifrovacka/admin.py
index 71d191d4..5099c241 100644
--- a/sifrovacka/admin.py
+++ b/sifrovacka/admin.py
@@ -1,8 +1,10 @@
 from django.contrib import admin
 
-from .models import OdpovedUcastnika, SpravnaOdpoved
+from .models import OdpovedUcastnika, SpravnaOdpoved, NapovezenoUcastnikovi, Napoveda
 
 # Register your models here.
 
 admin.site.register(OdpovedUcastnika)
 admin.site.register(SpravnaOdpoved)
+admin.site.register(Napoveda)
+admin.site.register(NapovezenoUcastnikovi)
diff --git a/sifrovacka/forms.py b/sifrovacka/forms.py
index e3eba7c7..8a1e16d1 100644
--- a/sifrovacka/forms.py
+++ b/sifrovacka/forms.py
@@ -1,6 +1,6 @@
 from django.core.exceptions import ValidationError
 from django.forms import ModelForm, Textarea
-from .models import OdpovedUcastnika, SpravnaOdpoved
+from .models import OdpovedUcastnika, SpravnaOdpoved, NapovezenoUcastnikovi, Napoveda
 
 
 class SifrovackaForm(ModelForm):
@@ -16,3 +16,15 @@ class SifrovackaForm(ModelForm):
 		if SpravnaOdpoved.objects.filter(sifra=sifra).count() == 0:
 			raise ValidationError("Tohle číslo šifry v databázi nemáme. Zkontrolujte si ho prosím.")
 		return sifra
+
+
+class NapovedaForm(ModelForm):
+	class Meta:
+		model = NapovezenoUcastnikovi
+		fields = ["sifra",]
+
+	def clean_sifra(self):
+		sifra = self.cleaned_data.get('sifra')
+		if Napoveda.objects.filter(sifra=sifra).count() == 0:
+			raise ValidationError("K tomuto číslu šifry nemáme nápovědu. Zkonstolujte si ho prosím.")
+		return sifra
diff --git a/sifrovacka/migrations/0004_napoveda_napovezenoucastnikovi.py b/sifrovacka/migrations/0004_napoveda_napovezenoucastnikovi.py
new file mode 100644
index 00000000..cba7ae8f
--- /dev/null
+++ b/sifrovacka/migrations/0004_napoveda_napovezenoucastnikovi.py
@@ -0,0 +1,65 @@
+# Generated by Django 4.2.8 on 2024-04-14 12:57
+
+from django.db import migrations, models
+import django.db.models.deletion
+import django.utils.timezone
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        (
+            "seminar",
+            "0114_related_name_se_zmenilo_a_django_chce_migraci_tak_dostane_migraci",
+        ),
+        ("sifrovacka", "0003_odpoveducastnika_uspech"),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name="Napoveda",
+            fields=[
+                (
+                    "id",
+                    models.AutoField(
+                        auto_created=True,
+                        primary_key=True,
+                        serialize=False,
+                        verbose_name="ID",
+                    ),
+                ),
+                ("text", models.TextField()),
+                ("sifra", models.IntegerField()),
+            ],
+        ),
+        migrations.CreateModel(
+            name="NapovezenoUcastnikovi",
+            fields=[
+                (
+                    "id",
+                    models.AutoField(
+                        auto_created=True,
+                        primary_key=True,
+                        serialize=False,
+                        verbose_name="ID",
+                    ),
+                ),
+                ("sifra", models.IntegerField(verbose_name="Číslo šifry")),
+                (
+                    "timestamp",
+                    models.DateTimeField(
+                        default=django.utils.timezone.now, verbose_name="Timestamp"
+                    ),
+                ),
+                (
+                    "resitel",
+                    models.ForeignKey(
+                        on_delete=django.db.models.deletion.CASCADE,
+                        to="seminar.resitel",
+                    ),
+                ),
+            ],
+            options={
+                "ordering": ["-timestamp"],
+            },
+        ),
+    ]
diff --git a/sifrovacka/models.py b/sifrovacka/models.py
index 6517c2e0..beabd0a7 100644
--- a/sifrovacka/models.py
+++ b/sifrovacka/models.py
@@ -25,3 +25,20 @@ class SpravnaOdpoved(models.Model):
 
 	def __str__(self):
 		return f"{self.sifra}: {self.odpoved}"
+
+
+class NapovezenoUcastnikovi(models.Model):
+	class Meta:
+		ordering = ["-timestamp"]
+
+	resitel = models.ForeignKey(Resitel, blank=False, null=False, on_delete=models.CASCADE)
+	sifra = models.IntegerField("Číslo šifry", blank=False, null=False,)
+	timestamp = models.DateTimeField("Timestamp", blank=False, null=False, default=timezone.now)
+
+
+class Napoveda(models.Model):
+	text = models.TextField(blank=False, null=False,)
+	sifra = models.IntegerField(blank=False, null=False,)
+
+	def __str__(self):
+		return f"{self.sifra}: {self.text}"
diff --git a/sifrovacka/templates/sifrovacka/napoveda.html b/sifrovacka/templates/sifrovacka/napoveda.html
new file mode 100644
index 00000000..88944216
--- /dev/null
+++ b/sifrovacka/templates/sifrovacka/napoveda.html
@@ -0,0 +1,48 @@
+{% extends "base.html" %}
+
+{% block content %}
+
+  <br>
+
+  <h1>{% block nadpis1a %}M&Mí šifrovačka{% endblock nadpis1a %}</h1>
+
+  <br>
+
+  <h2>Získat nápovědu k šifře:</h2>
+
+  <form action="{% url 'sifrovacka_napoveda' %}" method="post">
+    <table class="form">
+      {{form.non_field_errors}}
+      {% for field in form %}
+        <tr>
+          <td>
+            <label class="field-label{% if field.field.required %} field-required{% endif %}" for="{{ field.id_for_label }}">
+              {{ field.label }}
+            </label>
+
+          </td>
+
+          <td {% if field.help_text %} class="field-with-comment"{% endif %}>
+            {{ field }}
+            <span class="field-comment">{{ field.help_text|safe }}</span>
+          </td>
+
+        </tr>
+
+
+        {% if field.errors %}
+          <tr>
+            <td colspan="2"><span class="field-error">{{ field.errors }}</span></td>
+          </tr>
+        {% endif %}
+      {% endfor %}
+    </table>
+
+    {% csrf_token %}
+
+    <input type="submit" value="Chci nápovědu">
+  </form>
+
+  <a href="{% url 'sifrovacka' %}">Nechceme nápovědu, známe řešení!</a>
+
+{% endblock content %}
diff --git a/sifrovacka/templates/sifrovacka/napovedy_list.html b/sifrovacka/templates/sifrovacka/napovedy_list.html
new file mode 100644
index 00000000..dc729947
--- /dev/null
+++ b/sifrovacka/templates/sifrovacka/napovedy_list.html
@@ -0,0 +1,23 @@
+{% extends "base.html" %}
+
+{% block content %}
+
+  <h1>{% block nadpis1a %}Šifrovačka vzaté nápovědy{% endblock nadpis1a %}</h1>
+
+  <table class="dosla_reseni">
+    <tr>
+      <th>Timestamp</th>
+      <th>Řešitel</th>
+      <th>Šifra</th>
+    </tr>
+
+    {% for u in object_list %}
+      <tr>
+        <td>{{ u.timestamp }}</td>
+        <td>{{ u.resitel }}</td>
+        <td>{{ u.sifra }}</td>
+      </tr>
+    {% endfor %}
+  </table>
+
+{% endblock content %}
diff --git a/sifrovacka/templates/sifrovacka/sifrovacka.html b/sifrovacka/templates/sifrovacka/sifrovacka.html
index 4e0cc15a..74943406 100644
--- a/sifrovacka/templates/sifrovacka/sifrovacka.html
+++ b/sifrovacka/templates/sifrovacka/sifrovacka.html
@@ -43,4 +43,6 @@
     <input type="submit" value="Tak pravím!">
   </form>
 
+  <a href="{% url 'sifrovacka_napoveda' %}">Získat nápovědu</a>
+
 {% endblock content %}
diff --git a/sifrovacka/urls.py b/sifrovacka/urls.py
index a7af5e54..916d73ec 100644
--- a/sifrovacka/urls.py
+++ b/sifrovacka/urls.py
@@ -1,7 +1,7 @@
 from django.urls import path
 
 from seminar.utils import org_required, resitel_or_org_required
-from .views import SifrovackaView, SifrovackaListView
+from .views import SifrovackaView, SifrovackaListView, NapovedaView, NapovedaListView
 
 urlpatterns = [
 	path(
@@ -14,4 +14,14 @@ urlpatterns = [
 		org_required(SifrovackaListView.as_view()),
 		name='sifrovacka_odpovedi'
 	),
+	path(
+		'napoveda/',
+		resitel_or_org_required(NapovedaView.as_view()),
+		name='sifrovacka_napoveda'
+	),
+	path(
+		'napovedy/',
+		org_required(NapovedaListView.as_view()),
+		name='sifrovacka_napovedy'
+	),
 ]
diff --git a/sifrovacka/views.py b/sifrovacka/views.py
index 9c4af3ed..5537344b 100644
--- a/sifrovacka/views.py
+++ b/sifrovacka/views.py
@@ -2,8 +2,8 @@ from django.urls import reverse
 from django.views.generic import FormView, ListView
 
 from seminar.views import formularOKView
-from .forms import SifrovackaForm
-from .models import OdpovedUcastnika, SpravnaOdpoved
+from .forms import SifrovackaForm, NapovedaForm
+from .models import OdpovedUcastnika, SpravnaOdpoved, Napoveda, NapovezenoUcastnikovi
 from seminar.models.personalni import Resitel
 
 
@@ -24,10 +24,31 @@ class SifrovackaView(FormView):
 
 		instance.uspech = True
 		instance.save()
-		
+
 		return formularOKView(self.request, f'<h1>{sifra.skryty_text}</h1> <p><a href="{reverse("sifrovacka")}">Odevzdat další.</a></p><br><br><br>')
 
 
 class SifrovackaListView(ListView):
 	template_name = 'sifrovacka/odpovedi_list.html'
 	model = OdpovedUcastnika
+
+
+class NapovedaView(FormView):
+	template_name = 'sifrovacka/napoveda.html'
+	form_class = NapovedaForm
+
+	def form_valid(self, form):
+		instance = form.save(commit=False)
+		resitel = Resitel.objects.get(osoba__user=self.request.user)
+		instance.resitel = resitel
+
+		if NapovezenoUcastnikovi.objects.filter(resitel=resitel, sifra=instance.sifra).first() is None:
+			instance.save()
+
+		napoveda = Napoveda.objects.filter(sifra=instance.sifra).first()
+		return formularOKView(self.request, f'<h1>Nápověda k šifře číslo {instance.sifra} je:</h1><p>{napoveda.text}</p> <p><a href="{reverse("sifrovacka")}">Odevzdat řešení.</a></p><br><br><br>')
+
+
+class NapovedaListView(ListView):
+	template_name = 'sifrovacka/napovedy_list.html'
+	model = NapovezenoUcastnikovi

From 9a93f954906d1eeaccc2a447d0c6c73a1f7a2445 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= <jonas.havelka@volny.cz>
Date: Sun, 21 Apr 2024 21:06:56 +0200
Subject: [PATCH 42/59] =?UTF-8?q?fix:=20N=C3=A1pov=C4=9Bdy=20v=20=C5=A1ifr?=
 =?UTF-8?q?ova=C4=8Dce?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 sifrovacka/forms.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sifrovacka/forms.py b/sifrovacka/forms.py
index 8a1e16d1..3ae75cc1 100644
--- a/sifrovacka/forms.py
+++ b/sifrovacka/forms.py
@@ -26,5 +26,5 @@ class NapovedaForm(ModelForm):
 	def clean_sifra(self):
 		sifra = self.cleaned_data.get('sifra')
 		if Napoveda.objects.filter(sifra=sifra).count() == 0:
-			raise ValidationError("K tomuto číslu šifry nemáme nápovědu. Zkonstolujte si ho prosím.")
+			raise ValidationError("K tomuto číslu šifry nemáme nápovědu. Zkontrolujte si ho prosím.")
 		return sifra

From d09a08f53fc5c9c028ac4295f0908fe7e4a9441e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= <jonas.havelka@volny.cz>
Date: Mon, 22 Apr 2024 23:27:29 +0200
Subject: [PATCH 43/59] =?UTF-8?q?fix:=20P=C5=99eskakov=C3=A1n=C3=AD=20v=20?=
 =?UTF-8?q?=C5=A1ifrova=C4=8Dce=20(je=20tam=20faaaaakt=20hodn=C4=9B=20sn?=
 =?UTF-8?q?=C4=9Bhu)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 sifrovacka/templates/sifrovacka/napoveda.html |  4 +-
 .../templates/sifrovacka/preskoceni.html      | 54 +++++++++++++++++++
 .../templates/sifrovacka/sifrovacka.html      |  4 +-
 sifrovacka/urls.py                            |  7 ++-
 sifrovacka/views.py                           | 16 ++++++
 5 files changed, 82 insertions(+), 3 deletions(-)
 create mode 100644 sifrovacka/templates/sifrovacka/preskoceni.html

diff --git a/sifrovacka/templates/sifrovacka/napoveda.html b/sifrovacka/templates/sifrovacka/napoveda.html
index 88944216..cce48ed8 100644
--- a/sifrovacka/templates/sifrovacka/napoveda.html
+++ b/sifrovacka/templates/sifrovacka/napoveda.html
@@ -43,6 +43,8 @@
     <input type="submit" value="Chci nápovědu">
   </form>
 
-  <a href="{% url 'sifrovacka' %}">Nechceme nápovědu, známe řešení!</a>
+  <p><a href="{% url 'sifrovacka' %}">Nechceme nápovědu, známe řešení!</a></p>
+
+  <p><a href="{% url 'sifrovacka_preskoceni' %}">Přeskoč šifru</a></p>
 
 {% endblock content %}
diff --git a/sifrovacka/templates/sifrovacka/preskoceni.html b/sifrovacka/templates/sifrovacka/preskoceni.html
new file mode 100644
index 00000000..64e777ea
--- /dev/null
+++ b/sifrovacka/templates/sifrovacka/preskoceni.html
@@ -0,0 +1,54 @@
+{% extends "base.html" %}
+
+{% block content %}
+
+  <br>
+
+  <h1>{% block nadpis1a %}M&Mí šifrovačka{% endblock nadpis1a %}</h1>
+
+  <br>
+
+  <h2>Přeskočit šifru:</h2>
+
+  <form action="{% url 'sifrovacka_preskoceni' %}" method="post">
+    <table class="form">
+      {{form.non_field_errors}}
+      {% for field in form %}
+        {% if field.id_for_label != "id_odpoved" %}
+          <tr>
+            <td>
+              <label class="field-label{% if field.field.required %} field-required{% endif %}" for="{{ field.id_for_label }}">
+                {{ field.label }}
+              </label>
+
+            </td>
+
+            <td {% if field.help_text %} class="field-with-comment"{% endif %}>
+              {{ field }}
+              <span class="field-comment">{{ field.help_text|safe }}</span>
+            </td>
+
+          </tr>
+
+
+          {% if field.errors %}
+            <tr>
+              <td colspan="2"><span class="field-error">{{ field.errors }}</span></td>
+            </tr>
+          {% endif %}
+        {% else %}
+          {{ field.as_hidden }}
+        {% endif %}
+      {% endfor %}
+    </table>
+
+    {% csrf_token %}
+
+    <input type="submit" value="Chceme další stanoviště bez vyřešení šifry">
+  </form>
+
+  <p><a href="{% url 'sifrovacka' %}">Nechceme přeskočit, známe řešení!</a></p>
+
+  <p><a href="{% url 'sifrovacka_napoveda' %}">Nechceme přeskakovat, ale nápověda by se šikla.</a></p>
+
+{% endblock content %}
diff --git a/sifrovacka/templates/sifrovacka/sifrovacka.html b/sifrovacka/templates/sifrovacka/sifrovacka.html
index 74943406..1ce95454 100644
--- a/sifrovacka/templates/sifrovacka/sifrovacka.html
+++ b/sifrovacka/templates/sifrovacka/sifrovacka.html
@@ -43,6 +43,8 @@
     <input type="submit" value="Tak pravím!">
   </form>
 
-  <a href="{% url 'sifrovacka_napoveda' %}">Získat nápovědu</a>
+  <p><a href="{% url 'sifrovacka_napoveda' %}">Získat nápovědu</a></p>
+
+  <p><a href="{% url 'sifrovacka_preskoceni' %}">Přeskoč šifru</a></p>
 
 {% endblock content %}
diff --git a/sifrovacka/urls.py b/sifrovacka/urls.py
index 916d73ec..85f9c4cc 100644
--- a/sifrovacka/urls.py
+++ b/sifrovacka/urls.py
@@ -1,7 +1,7 @@
 from django.urls import path
 
 from seminar.utils import org_required, resitel_or_org_required
-from .views import SifrovackaView, SifrovackaListView, NapovedaView, NapovedaListView
+from .views import SifrovackaView, SifrovackaListView, NapovedaView, NapovedaListView, PreskoceniView
 
 urlpatterns = [
 	path(
@@ -24,4 +24,9 @@ urlpatterns = [
 		org_required(NapovedaListView.as_view()),
 		name='sifrovacka_napovedy'
 	),
+	path(
+		'preskoceni/',
+		resitel_or_org_required(PreskoceniView.as_view()),
+		name='sifrovacka_preskoceni'
+	),
 ]
diff --git a/sifrovacka/views.py b/sifrovacka/views.py
index 5537344b..59f3deec 100644
--- a/sifrovacka/views.py
+++ b/sifrovacka/views.py
@@ -52,3 +52,19 @@ class NapovedaView(FormView):
 class NapovedaListView(ListView):
 	template_name = 'sifrovacka/napovedy_list.html'
 	model = NapovezenoUcastnikovi
+
+
+class PreskoceniView(FormView):
+	template_name = 'sifrovacka/preskoceni.html'
+	form_class = SifrovackaForm
+	initial = {"odpoved": "=======PŘESKOČENO======="}
+
+	def form_valid(self, form):
+		instance = form.save(commit=False)
+		instance.odpoved = "=======PŘESKOČENO======="
+		resitel = Resitel.objects.get(osoba__user=self.request.user)
+		instance.resitel = resitel
+		instance.save()
+		sifra = SpravnaOdpoved.objects.filter(sifra=instance.sifra).first()  # FIXME co když je více "správných" odpovědí?
+
+		return formularOKView(self.request, f'<h1>{sifra.skryty_text}</h1> <p><a href="{reverse("sifrovacka")}">Zpět na odevzdávátko.</a></p><br><br><br>')

From 4eb6746c75f73fbe90cfc901e321d43e0f0ba674 Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Tue, 30 Apr 2024 21:05:10 +0200
Subject: [PATCH 44/59] =?UTF-8?q?Chyb=C4=9Bj=C3=ADc=C3=AD=20ForeignKeys/1-?=
 =?UTF-8?q?2-1Keys?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 personalni/migrations/0003_initial.py | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/personalni/migrations/0003_initial.py b/personalni/migrations/0003_initial.py
index e026182c..4103295e 100644
--- a/personalni/migrations/0003_initial.py
+++ b/personalni/migrations/0003_initial.py
@@ -5,6 +5,9 @@ import django.utils.timezone
 import django_countries.fields
 import imagekit.models.fields
 
+from django.conf import settings
+import django.db.models.deletion
+
 def nastav_nove_contenttypes(apps, schema_editor):
     ContentType = apps.get_model('contenttypes', 'ContentType')
     for m in ('resitel', 'organizator', 'osoba', 'skola', 'prijemce'):
@@ -38,6 +41,7 @@ class Migration(migrations.Migration):
                 ('studuje', models.CharField(blank=True, help_text="Např. 'Studuje Obecnou fyziku (Bc.), 3. ročník', 'Vystudovala Diskrétní modely a algoritmy (Mgr.)' nebo 'Přednáší na MFF'", max_length=256, null=True, verbose_name='Studium aj.')),
                 ('strucny_popis_organizatora', models.TextField(blank=True, null=True, verbose_name='Stručný popis organizátora')),
                 ('skola', models.CharField(blank=True, help_text='Škola, např. MFF, VŠCHT, VUT, ... prostě aby se nemuselo psát do studuješkolu, ale jen obor, možnost zobrazit zvlášť', max_length=256, null=True, verbose_name='Škola, kterou studuje')),
+                ('osoba', models.OneToOneField(help_text='osobní údaje organizátora', on_delete=django.db.models.deletion.PROTECT, related_name='org', to='personalni.osoba', verbose_name='osoba')),
             ],
             options={
                 'verbose_name': 'Organizátor',
@@ -68,6 +72,7 @@ class Migration(migrations.Migration):
                 ('jak_se_dozvedeli', models.TextField(blank=True, verbose_name='Jak se dozvěděli')),
                 ('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka k osobě (plain text)', verbose_name='neveřejná poznámka')),
                 ('foto', imagekit.models.fields.ProcessedImageField(blank=True, help_text='Vlož fotografii osoby o libovolné velikosti', null=True, upload_to='image_osoby/velke/%Y/', verbose_name='Fotografie osoby')),
+                ('user', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to=settings.AUTH_USER_MODEL, verbose_name='uživatel')),
             ],
             options={
                 'verbose_name': 'Osoba',
@@ -83,6 +88,7 @@ class Migration(migrations.Migration):
                 ('id', models.AutoField(primary_key=True, serialize=False)),
                 ('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka k příemci čísel (plain text)', verbose_name='neveřejná poznámka')),
                 ('zasilat_cislo_emailem', models.BooleanField(default=False, help_text='True pokud chce příjemce dostávat číslo emailem', verbose_name='zasílat číslo emailem')),
+                ('osoba', models.OneToOneField(help_text='Které osobě či na jakou adresu se mají zasílat čísla', on_delete=django.db.models.deletion.CASCADE, to='personalni.osoba', verbose_name='komu')),
             ],
             options={
                 'verbose_name': 'příjemce',
@@ -101,6 +107,8 @@ class Migration(migrations.Migration):
                 ('zasilat_cislo_emailem', models.BooleanField(default=False, help_text='True pokud chce řešitel dostávat číslo emailem', verbose_name='zasílat číslo emailem')),
                 ('zasilat_cislo_papirove', models.BooleanField(default=True, help_text='True pokud chce řešitel dostávat číslo papírově', verbose_name='zasílat číslo papírově')),
                 ('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka k řešiteli (plain text)', verbose_name='neveřejná poznámka')),
+                ('osoba', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, to='personalni.osoba', verbose_name='osoba')),
+                ('skola', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='personalni.skola', verbose_name='škola')),
             ],
             options={
                 'verbose_name': 'Řešitel',
@@ -125,6 +133,7 @@ class Migration(migrations.Migration):
                 ('je_zs', models.BooleanField(default=True, verbose_name='základní stupeň')),
                 ('je_ss', models.BooleanField(default=True, verbose_name='střední stupeň')),
                 ('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka ke škole (plain text)', verbose_name='neveřejná poznámka')),
+                ('kontaktni_osoba', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='personalni.osoba', verbose_name='Kontaktní osoba')),
             ],
             options={
                 'verbose_name': 'Škola',

From 8ab25545a493258cf0c198353b2c07306bd681d1 Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Tue, 30 Apr 2024 21:07:11 +0200
Subject: [PATCH 45/59] =?UTF-8?q?Pozn=C3=A1mky=20k=20FK/1to1F=20v=20creaet?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 split-apps-meta/create.notes | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/split-apps-meta/create.notes b/split-apps-meta/create.notes
index 43872d68..8d3174e3 100644
--- a/split-apps-meta/create.notes
+++ b/split-apps-meta/create.notes
@@ -1,5 +1,6 @@
 Prostě zkopírovat vedle, s původními (=správnými) related names.
 makemigrations
-! Doplnit hack kolem content-types
+! Doplnit hack kolem content-types (jako první operace při migraci)
+! Doplnit ForeignKeys (TODO: jak? Já jsem je ukradl až zpětně…)
 doplnit závislost na unmanage
 migrate

From 03589e484c3b84b0b289f80fbba38c187697ee26 Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Tue, 30 Apr 2024 21:34:54 +0200
Subject: [PATCH 46/59] =?UTF-8?q?Fix=20import=C5=AF=20v=20personalni.model?=
 =?UTF-8?q?s.Resitel?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Lokální, přehlédl jsem je…
---
 personalni/models.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/personalni/models.py b/personalni/models.py
index 7ede2d08..b78ab4c1 100644
--- a/personalni/models.py
+++ b/personalni/models.py
@@ -281,7 +281,7 @@ class Resitel(SeminarModelBase):
 	def vsechny_body(self):
 		"Spočítá body odjakživa."
 		vsechna_reseni = self.reseni_set.all()
-		from .odevzdavatko import Hodnoceni
+		from seminar.odevzdavatko import Hodnoceni
 		vsechna_hodnoceni = Hodnoceni.objects.filter(
 			reseni__in=vsechna_reseni)
 		return sum(h.body for h in list(vsechna_hodnoceni) if h.body is not None)
@@ -328,7 +328,7 @@ class Resitel(SeminarModelBase):
 		#  - body z 25. ročníku a dříve byly shledány dvakrát hodnotnějšími
 		#  - proto se započítávají dvojnásobně a byly posunuté hranice titulů
 		#  - staré tituly se ale nemají odebrat, pokud řešitel v t.č. minulém (26.) ročníku měl titul, má ho mít pořád.
-		from .odevzdavatko import Hodnoceni
+		from seminar.odevzdavatko import Hodnoceni
 		hodnoceni_do_25_rocniku = Hodnoceni.objects.filter(deadline_body__cislo__rocnik__rocnik__lte=25,reseni__in=self.reseni_set.all())
 		novejsi_hodnoceni = Hodnoceni.objects.filter(reseni__in=self.reseni_set.all()).difference(hodnoceni_do_25_rocniku)
 
@@ -366,7 +366,7 @@ class Resitel(SeminarModelBase):
 			else:
 				return Titul.akad
 
-		from .odevzdavatko import Hodnoceni
+		from seminar.odevzdavatko import Hodnoceni
 		hodnoceni_do_26_rocniku = Hodnoceni.objects.filter(deadline_body__cislo__rocnik__rocnik__lte=26,reseni__in=self.reseni_set.all())
 		novejsi_body = body_z_hodnoceni(
 			Hodnoceni.objects.filter(reseni__in=self.reseni_set.all())

From 77d392f14f3e4722ae0b0473d3650ac13c9be1b4 Mon Sep 17 00:00:00 2001
From: Pavel 'LEdoian' Turinsky <ledoian@matfyz.cz>
Date: Tue, 30 Apr 2024 21:36:59 +0200
Subject: [PATCH 47/59] =?UTF-8?q?fixup!=20Fix=20import=C5=AF=20v=20persona?=
 =?UTF-8?q?lni.models.Resitel?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 personalni/models.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/personalni/models.py b/personalni/models.py
index b78ab4c1..dfcc7372 100644
--- a/personalni/models.py
+++ b/personalni/models.py
@@ -281,7 +281,7 @@ class Resitel(SeminarModelBase):
 	def vsechny_body(self):
 		"Spočítá body odjakživa."
 		vsechna_reseni = self.reseni_set.all()
-		from seminar.odevzdavatko import Hodnoceni
+		from seminar.models.odevzdavatko import Hodnoceni
 		vsechna_hodnoceni = Hodnoceni.objects.filter(
 			reseni__in=vsechna_reseni)
 		return sum(h.body for h in list(vsechna_hodnoceni) if h.body is not None)
@@ -328,7 +328,7 @@ class Resitel(SeminarModelBase):
 		#  - body z 25. ročníku a dříve byly shledány dvakrát hodnotnějšími
 		#  - proto se započítávají dvojnásobně a byly posunuté hranice titulů
 		#  - staré tituly se ale nemají odebrat, pokud řešitel v t.č. minulém (26.) ročníku měl titul, má ho mít pořád.
-		from seminar.odevzdavatko import Hodnoceni
+		from seminar.models.odevzdavatko import Hodnoceni
 		hodnoceni_do_25_rocniku = Hodnoceni.objects.filter(deadline_body__cislo__rocnik__rocnik__lte=25,reseni__in=self.reseni_set.all())
 		novejsi_hodnoceni = Hodnoceni.objects.filter(reseni__in=self.reseni_set.all()).difference(hodnoceni_do_25_rocniku)
 
@@ -366,7 +366,7 @@ class Resitel(SeminarModelBase):
 			else:
 				return Titul.akad
 
-		from seminar.odevzdavatko import Hodnoceni
+		from seminar.models.odevzdavatko import Hodnoceni
 		hodnoceni_do_26_rocniku = Hodnoceni.objects.filter(deadline_body__cislo__rocnik__rocnik__lte=26,reseni__in=self.reseni_set.all())
 		novejsi_body = body_z_hodnoceni(
 			Hodnoceni.objects.filter(reseni__in=self.reseni_set.all())

From 6ab00392cca19542f4f332bb7a70553ad887b276 Mon Sep 17 00:00:00 2001
From: "Pavel \"LEdoian\" Turinsky" <ledoian@matfyz.cz>
Date: Tue, 30 Apr 2024 22:20:36 +0200
Subject: [PATCH 48/59] =?UTF-8?q?Pou=C5=BE=C3=ADt=20p=C5=99=C3=ADmo=20nov?=
 =?UTF-8?q?=C3=BD=20model?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

… když už můžeme
---
 sifrovacka/views.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sifrovacka/views.py b/sifrovacka/views.py
index 0428e043..2ad352da 100644
--- a/sifrovacka/views.py
+++ b/sifrovacka/views.py
@@ -4,7 +4,7 @@ from django.views.generic import FormView, ListView
 from seminar.views import formularOKView
 from .forms import SifrovackaForm, NapovedaForm
 from .models import OdpovedUcastnika, SpravnaOdpoved, Napoveda, NapovezenoUcastnikovi
-from seminar.models import Resitel
+from personalni.models import Resitel
 
 
 # Create your views here.

From 676b94ca1bf354bcbfb75a17f8bb2c7ea204641a Mon Sep 17 00:00:00 2001
From: "Pavel \"LEdoian\" Turinsky" <ledoian@matfyz.cz>
Date: Tue, 30 Apr 2024 22:27:06 +0200
Subject: [PATCH 49/59] =?UTF-8?q?Oprava=20po=C5=99ad=C3=AD=20migrac=C3=AD?=
 =?UTF-8?q?=20v=20=C5=A0ifrova=C4=8Dce?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 sifrovacka/migrations/0004_auto_20240312_2124.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sifrovacka/migrations/0004_auto_20240312_2124.py b/sifrovacka/migrations/0004_auto_20240312_2124.py
index 8f58cf58..252268a6 100644
--- a/sifrovacka/migrations/0004_auto_20240312_2124.py
+++ b/sifrovacka/migrations/0004_auto_20240312_2124.py
@@ -6,7 +6,7 @@ from django.db import migrations
 class Migration(migrations.Migration):
 
     dependencies = [
-        ('sifrovacka', '0003_odpoveducastnika_uspech'),
+        ('sifrovacka', '0004_napoveda_napovezenoucastnikovi'),
     ]
 
     operations = [

From 99c00954651d1dc5f49150e77d6cf6a0ce5f8d6b Mon Sep 17 00:00:00 2001
From: "Pavel \"LEdoian\" Turinsky" <ledoian@matfyz.cz>
Date: Tue, 30 Apr 2024 23:00:28 +0200
Subject: [PATCH 50/59] =?UTF-8?q?Fix=20migrac=C3=AD:=20p=C5=99ibyl=20FK=20?=
 =?UTF-8?q?na=20masteru=20b=C4=9Bhem=20p=C5=99ekop=C3=A1v=C3=A1n=C3=AD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 sifrovacka/migrations/0005_alter_odpoveducastnika_resitel.py | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/sifrovacka/migrations/0005_alter_odpoveducastnika_resitel.py b/sifrovacka/migrations/0005_alter_odpoveducastnika_resitel.py
index 34b50d4b..d21d65d6 100644
--- a/sifrovacka/migrations/0005_alter_odpoveducastnika_resitel.py
+++ b/sifrovacka/migrations/0005_alter_odpoveducastnika_resitel.py
@@ -17,4 +17,9 @@ class Migration(migrations.Migration):
             name='resitel',
             field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='personalni.resitel'),
         ),
+        migrations.AlterField(
+            model_name='napovezenoucastnikovi',
+            name='resitel',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='personalni.resitel'),
+        ),
     ]

From cdc147259589d445fc881a78febec15e95912088 Mon Sep 17 00:00:00 2001
From: "Pavel \"LEdoian\" Turinsky" <ledoian@matfyz.cz>
Date: Wed, 1 May 2024 00:46:31 +0200
Subject: [PATCH 51/59] =?UTF-8?q?Pre,=20unmanage,=20create.=20Snad=20ve=20?=
 =?UTF-8?q?fin=C3=A1ln=C3=AD=20podob=C4=9B.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../migrations/0011_pre_split_soustredeni.py  |  13 ++
 .../migrations/0006_pre_split_soustredeni.py  |  13 ++
 .../migrations/0016_pre_split_soustredeni.py  |  13 ++
 .../migrations/0122_pre_split_soustredeni.py  |  16 ++
 .../migrations/0123_soustredeni_unmanage.py   |  33 +++
 seminar/models/soustredeni.py                 |  25 +-
 .../migrations/0001_split_from_seminar.py     | 119 ++++++++++
 soustredeni/models.py                         | 221 ++++++++++++++++++
 split-apps-meta/create.notes                  |   3 +-
 split-apps-meta/unmanage.notes                |   3 +
 10 files changed, 453 insertions(+), 6 deletions(-)
 create mode 100644 galerie/migrations/0011_pre_split_soustredeni.py
 create mode 100644 personalni/migrations/0006_pre_split_soustredeni.py
 create mode 100644 prednasky/migrations/0016_pre_split_soustredeni.py
 create mode 100644 seminar/migrations/0122_pre_split_soustredeni.py
 create mode 100644 seminar/migrations/0123_soustredeni_unmanage.py
 create mode 100644 soustredeni/migrations/0001_split_from_seminar.py
 create mode 100644 soustredeni/models.py

diff --git a/galerie/migrations/0011_pre_split_soustredeni.py b/galerie/migrations/0011_pre_split_soustredeni.py
new file mode 100644
index 00000000..a038ca44
--- /dev/null
+++ b/galerie/migrations/0011_pre_split_soustredeni.py
@@ -0,0 +1,13 @@
+# Generated by Django 4.2.11 on 2024-04-30 21:53
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('galerie', '0010_auto_20200819_0947'),
+    ]
+
+    operations = [
+    ]
diff --git a/personalni/migrations/0006_pre_split_soustredeni.py b/personalni/migrations/0006_pre_split_soustredeni.py
new file mode 100644
index 00000000..ce2b4de1
--- /dev/null
+++ b/personalni/migrations/0006_pre_split_soustredeni.py
@@ -0,0 +1,13 @@
+# Generated by Django 4.2.11 on 2024-04-30 21:53
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('personalni', '0005_personalni_post_migrate'),
+    ]
+
+    operations = [
+    ]
diff --git a/prednasky/migrations/0016_pre_split_soustredeni.py b/prednasky/migrations/0016_pre_split_soustredeni.py
new file mode 100644
index 00000000..77ade0d0
--- /dev/null
+++ b/prednasky/migrations/0016_pre_split_soustredeni.py
@@ -0,0 +1,13 @@
+# Generated by Django 4.2.11 on 2024-04-30 21:53
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('prednasky', '0015_personalni_post_migrate'),
+    ]
+
+    operations = [
+    ]
diff --git a/seminar/migrations/0122_pre_split_soustredeni.py b/seminar/migrations/0122_pre_split_soustredeni.py
new file mode 100644
index 00000000..58e9d479
--- /dev/null
+++ b/seminar/migrations/0122_pre_split_soustredeni.py
@@ -0,0 +1,16 @@
+# Generated by Django 4.2.11 on 2024-04-30 21:54
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('seminar', '0121_personalni_post_migrate'),
+        ('personalni', '0006_pre_split_soustredeni'),
+        ('galerie', '0011_pre_split_soustredeni'),
+        ('prednasky', '0016_pre_split_soustredeni'),
+    ]
+
+    operations = [
+    ]
diff --git a/seminar/migrations/0123_soustredeni_unmanage.py b/seminar/migrations/0123_soustredeni_unmanage.py
new file mode 100644
index 00000000..a7b87ff3
--- /dev/null
+++ b/seminar/migrations/0123_soustredeni_unmanage.py
@@ -0,0 +1,33 @@
+# Generated by Django 4.2.11 on 2024-04-30 22:17
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('seminar', '0122_pre_split_soustredeni'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='konfera',
+            options={'managed': False, 'verbose_name': 'Konfera', 'verbose_name_plural': 'Konfery'},
+        ),
+        migrations.AlterModelOptions(
+            name='konfery_ucastnici',
+            options={'managed': False, 'ordering': ['konfera', 'resitel'], 'verbose_name': 'Účast na konfeře', 'verbose_name_plural': 'Účasti na konfeře'},
+        ),
+        migrations.AlterModelOptions(
+            name='soustredeni',
+            options={'managed': False, 'ordering': ['-rocnik__rocnik', '-datum_zacatku'], 'verbose_name': 'Soustředění', 'verbose_name_plural': 'Soustředění'},
+        ),
+        migrations.AlterModelOptions(
+            name='soustredeni_organizatori',
+            options={'managed': False, 'ordering': ['soustredeni', 'organizator'], 'verbose_name': 'Účast organizátorů na soustředění', 'verbose_name_plural': 'Účasti organizátorů na soustředění'},
+        ),
+        migrations.AlterModelOptions(
+            name='soustredeni_ucastnici',
+            options={'managed': False, 'ordering': ['soustredeni', 'resitel'], 'verbose_name': 'Účast na soustředění', 'verbose_name_plural': 'Účasti na soustředění'},
+        ),
+    ]
diff --git a/seminar/models/soustredeni.py b/seminar/models/soustredeni.py
index 275224a3..3f761444 100644
--- a/seminar/models/soustredeni.py
+++ b/seminar/models/soustredeni.py
@@ -20,6 +20,7 @@ logger = logging.getLogger(__name__)
 class Soustredeni(SeminarModelBase):
 
 	class Meta:
+		managed = False
 		db_table = 'seminar_soustredeni'
 		verbose_name = 'Soustředění'
 		verbose_name_plural = 'Soustředění'
@@ -28,7 +29,7 @@ class Soustredeni(SeminarModelBase):
 	# Interní ID
 	id = models.AutoField(primary_key = True)
 
-	rocnik = models.ForeignKey(am.Rocnik, verbose_name='ročník', related_name='soustredeni',
+	rocnik = models.ForeignKey(am.Rocnik, verbose_name='ročník', related_name='soustredeni_old',
 		on_delete=models.PROTECT)
 
 	datum_zacatku = models.DateField('datum začátku', blank=True, null=True,
@@ -43,9 +44,11 @@ class Soustredeni(SeminarModelBase):
 		help_text='Místo (název obce, volitelně též objektu')
 
 	ucastnici = models.ManyToManyField(Resitel, verbose_name='účastníci soustředění',
+		related_name='soustredeni_old',
 		help_text='Seznam účastníků soustředění', through='Soustredeni_Ucastnici')
 
 	organizatori = models.ManyToManyField(Organizator,
+			related_name='soustredeni_old',
 			verbose_name='Organizátoři soustředění',
 			help_text='Seznam organizátorů soustředění',
 			through='Soustredeni_Organizatori')
@@ -84,6 +87,7 @@ class Soustredeni_Ucastnici(SeminarModelBase):
 # zmena dedicnosti z models.Model na SeminarModelBase, potencialni vznik bugu
 
 	class Meta:
+		managed = False
 		db_table = 'seminar_soustredeni_ucastnici'
 		verbose_name = 'Účast na soustředění'
 		verbose_name_plural = 'Účasti na soustředění'
@@ -92,9 +96,12 @@ class Soustredeni_Ucastnici(SeminarModelBase):
 	# Interní ID
 	id = models.AutoField(primary_key = True)
 
-	resitel = models.ForeignKey(Resitel, verbose_name='řešitel', on_delete=models.PROTECT)
+	resitel = models.ForeignKey(Resitel, verbose_name='řešitel',
+		related_name='sous_ucastnici',
+		 on_delete=models.PROTECT)
 
 	soustredeni = models.ForeignKey(Soustredeni, verbose_name='soustředění',
+		related_name='sous_ucastnici',
 		on_delete=models.PROTECT)
 
 	poznamka = models.TextField('neveřejná poznámka', blank=True,
@@ -110,6 +117,7 @@ class Soustredeni_Organizatori(SeminarModelBase):
 # zmena dedicnosti z models.Model na SeminarModelBase, potencialni vznik bugu
 
 	class Meta:
+		managed = False
 		db_table = 'seminar_soustredeni_organizatori'
 		verbose_name = 'Účast organizátorů na soustředění'
 		verbose_name_plural = 'Účasti organizátorů na soustředění'
@@ -119,9 +127,11 @@ class Soustredeni_Organizatori(SeminarModelBase):
 	id = models.AutoField(primary_key = True)
 
 	organizator = models.ForeignKey(Organizator, verbose_name='organizátor',
+		related_name='sous_orgove',
 		on_delete=models.PROTECT)
 
 	soustredeni = models.ForeignKey(Soustredeni, verbose_name='soustředění',
+		related_name='sous_orgove',
 		on_delete=models.PROTECT)
 
 	poznamka = models.TextField('neveřejná poznámka', blank=True,
@@ -152,10 +162,13 @@ def generate_filename_konfera(self, filename):
 @reversion.register(ignore_duplicates=True)
 class Konfera(am.Problem):
 	class Meta:
+		managed = False
 		db_table = 'seminar_konfera'
 		verbose_name = 'Konfera'
 		verbose_name_plural = 'Konfery'
 
+	problem_ptr_old = models.OneToOneField(am.Problem, parent_link=True, related_name='konfera_old', on_delete=models.PROTECT, primary_key=True)
+
 	anotace = models.TextField('anotace', blank=True,
 							   help_text='Popis, o čem bude konfera.')
 
@@ -164,10 +177,11 @@ class Konfera(am.Problem):
 
 	# FIXME: Umíme omezit jen na účastníky daného soustřeďka?
 	ucastnici = models.ManyToManyField(Resitel, verbose_name='účastníci konfery',
+									related_name='konfera_old',
 									   help_text='Seznam účastníků konfery', through='Konfery_Ucastnici')
 
 	soustredeni = models.ForeignKey(Soustredeni, verbose_name='soustředění',
-									related_name='konfery', on_delete = models.SET_NULL, null=True)
+									related_name='konfery_old', on_delete = models.SET_NULL, null=True)
 
 	TYP_VELETRH = 'veletrh'
 	TYP_PREZENTACE = 'prezentace'
@@ -196,6 +210,7 @@ class Konfera(am.Problem):
 class Konfery_Ucastnici(models.Model):
 
 	class Meta:
+		managed = False
 		db_table = 'seminar_konfery_ucastnici'
 		verbose_name = 'Účast na konfeře'
 		verbose_name_plural = 'Účasti na konfeře'
@@ -204,9 +219,9 @@ class Konfery_Ucastnici(models.Model):
 	# Interní ID
 	id = models.AutoField(primary_key = True)
 
-	resitel = models.ForeignKey(Resitel, verbose_name='řešitel', on_delete=models.PROTECT)
+	resitel = models.ForeignKey(Resitel, verbose_name='řešitel', on_delete=models.PROTECT, related_name='konf_uc')
 
-	konfera = models.ForeignKey(Konfera, verbose_name='konfera', on_delete=models.CASCADE)
+	konfera = models.ForeignKey(Konfera, verbose_name='konfera', on_delete=models.CASCADE, related_name='konf_uc')
 
 	poznamka = models.TextField('neveřejná poznámka', blank=True,
 		help_text='Neveřejná poznámka k účasti (plain text)')
diff --git a/soustredeni/migrations/0001_split_from_seminar.py b/soustredeni/migrations/0001_split_from_seminar.py
new file mode 100644
index 00000000..745f62e7
--- /dev/null
+++ b/soustredeni/migrations/0001_split_from_seminar.py
@@ -0,0 +1,119 @@
+# Generated by Django 4.2.11 on 2024-04-30 22:53
+
+from django.db import migrations, models
+import django.db.models.deletion
+import soustredeni.models
+
+def nastav_nove_contenttypes(apps, schema_editor):
+    ContentType = apps.get_model('contenttypes', 'ContentType')
+    for m in ('konfera', 'soustredeni', 'soustredeni_ucastnici', 'soustredeni_organizatori', 'konfery_ucastnici'):
+        oct = ContentType.objects.filter(app_label='seminar', model=m)
+        oct.update(app_label='soustredeni')
+
+def nastav_stare_contenttypes(apps, schema_editor):
+    ContentType = apps.get_model('contenttypes', 'ContentType')
+    for m in ('konfera', 'soustredeni', 'soustredeni_ucastnici', 'soustredeni_organizatori', 'konfery_ucastnici'):
+        nct = ContentType.objects.filter(app_label='soustredeni', model=m)
+        nct.update(app_label='seminar')
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+        ('seminar', '0123_soustredeni_unmanage'),
+    ]
+
+    operations = [
+        migrations.RunPython(nastav_nove_contenttypes, nastav_stare_contenttypes),
+
+        migrations.CreateModel(
+            name='Konfera',
+            fields=[
+                ('problem_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='seminar.problem')),
+                ('anotace', models.TextField(blank=True, help_text='Popis, o čem bude konfera.', verbose_name='anotace')),
+                ('abstrakt', models.TextField(blank=True, help_text='Abstrakt konfery tak, jak byl uveden ve sborníku', verbose_name='abstrakt')),
+                ('typ_prezentace', models.CharField(choices=[('veletrh', 'Veletrh (postery)'), ('prezentace', 'Prezentace (přednáška)')], default='veletrh', max_length=16, verbose_name='typ prezentace')),
+                ('prezentace', models.FileField(blank=True, help_text='Prezentace nebo fotka posteru', upload_to=soustredeni.models.generate_filename_konfera, verbose_name='prezentace')),
+                ('materialy', models.FileField(blank=True, help_text='Další materiály ke konfeře zabalené do jednoho souboru', upload_to=soustredeni.models.generate_filename_konfera, verbose_name='materialy')),
+                ('soustredeni', models.ForeignKey(to='soustredeni.soustredeni', verbose_name='soustředění', on_delete=models.PROTECT)),
+            ],
+            options={
+                'verbose_name': 'Konfera',
+                'verbose_name_plural': 'Konfery',
+                'db_table': 'seminar_konfera',
+                'managed': False,
+            },
+            bases=('seminar.problem',),
+        ),
+        migrations.CreateModel(
+            name='Konfery_Ucastnici',
+            fields=[
+                ('id', models.AutoField(primary_key=True, serialize=False)),
+                ('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka k účasti (plain text)', verbose_name='neveřejná poznámka')),
+                ('resitel', models.ForeignKey(to='personalni.resitel', verbose_name='řešitel', on_delete=models.PROTECT)),
+                ('konfera', models.ForeignKey(to='soustredeni.konfera', verbose_name='konfera', on_delete=models.CASCADE)),
+            ],
+            options={
+                'verbose_name': 'Účast na konfeře',
+                'verbose_name_plural': 'Účasti na konfeře',
+                'db_table': 'seminar_konfery_ucastnici',
+                'ordering': ['konfera', 'resitel'],
+                'managed': False,
+            },
+        ),
+        migrations.CreateModel(
+            name='Soustredeni',
+            fields=[
+                ('id', models.AutoField(primary_key=True, serialize=False)),
+                ('datum_zacatku', models.DateField(blank=True, help_text='První den soustředění', null=True, verbose_name='datum začátku')),
+                ('datum_konce', models.DateField(blank=True, help_text='Poslední den soustředění', null=True, verbose_name='datum konce')),
+                ('verejne_db', models.BooleanField(db_column='verejne', default=False, verbose_name='soustředění zveřejněno')),
+                ('misto', models.CharField(blank=True, default='', help_text='Místo (název obce, volitelně též objektu', max_length=256, verbose_name='místo soustředění')),
+                ('text', models.TextField(blank=True, default='', verbose_name='text k soustředění (HTML)')),
+                ('typ', models.CharField(choices=[('jarni', 'Jarní soustředění'), ('podzimni', 'Podzimní soustředění'), ('vikend', 'Víkendový sraz'), ('vylet', 'Výlet')], default='podzimni', max_length=16, verbose_name='typ akce')),
+                ('exportovat', models.BooleanField(db_column='exportovat', default=False, help_text='Exportuje se jen podle tohoto flagu (ne veřejnosti)', verbose_name='export do AESOPa')),
+                ('rocnik', models.ForeignKey(to='seminar.rocnik', verbose_name='ročník', related_name='soustredeni', on_delete=models.PROTECT)),
+            ],
+            options={
+                'verbose_name': 'Soustředění',
+                'verbose_name_plural': 'Soustředění',
+                'db_table': 'seminar_soustredeni',
+                'ordering': ['-rocnik__rocnik', '-datum_zacatku'],
+                'managed': False,
+            },
+        ),
+        migrations.CreateModel(
+            name='Soustredeni_Organizatori',
+            fields=[
+                ('id', models.AutoField(primary_key=True, serialize=False)),
+                ('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka k účasti organizátora (plain text)', verbose_name='neveřejná poznámka')),
+                ('organizator', models.ForeignKey(to='personalni.organizator', verbose_name='organizátor', on_delete=models.PROTECT)),
+                ('soustredeni', models.ForeignKey(to='soustredeni.soustredeni', verbose_name='soustředění', on_delete=models.PROTECT)),
+            ],
+            options={
+                'verbose_name': 'Účast organizátorů na soustředění',
+                'verbose_name_plural': 'Účasti organizátorů na soustředění',
+                'db_table': 'seminar_soustredeni_organizatori',
+                'ordering': ['soustredeni', 'organizator'],
+                'managed': False,
+            },
+        ),
+        migrations.CreateModel(
+            name='Soustredeni_Ucastnici',
+            fields=[
+                ('id', models.AutoField(primary_key=True, serialize=False)),
+                ('poznamka', models.TextField(blank=True, help_text='Neveřejná poznámka k účasti (plain text)', verbose_name='neveřejná poznámka')),
+                ('resitel', models.ForeignKey(to='personalni.resitel', verbose_name='řešitel', on_delete=models.PROTECT)),
+                ('soustredeni', models.ForeignKey(to='soustredeni.soustredeni', verbose_name='soustředění', on_delete=models.PROTECT)),
+            ],
+            options={
+                'verbose_name': 'Účast na soustředění',
+                'verbose_name_plural': 'Účasti na soustředění',
+                'db_table': 'seminar_soustredeni_ucastnici',
+                'ordering': ['soustredeni', 'resitel'],
+                'managed': False,
+            },
+        ),
+    ]
diff --git a/soustredeni/models.py b/soustredeni/models.py
new file mode 100644
index 00000000..4429e2bf
--- /dev/null
+++ b/soustredeni/models.py
@@ -0,0 +1,221 @@
+# -*- coding: utf-8 -*-
+import logging
+import os
+
+from django.db import models
+from django.urls import reverse
+from reversion import revisions as reversion
+
+from django.conf import settings
+
+from personalni.models import Resitel, Organizator
+
+from seminar.models.base import SeminarModelBase
+from seminar.models import tvorba as am
+
+logger = logging.getLogger(__name__)
+
+
+@reversion.register(ignore_duplicates=True)
+class Soustredeni(SeminarModelBase):
+
+	class Meta:
+		managed = False
+		db_table = 'seminar_soustredeni'
+		verbose_name = 'Soustředění'
+		verbose_name_plural = 'Soustředění'
+		ordering = ['-rocnik__rocnik', '-datum_zacatku']
+
+	# Interní ID
+	id = models.AutoField(primary_key = True)
+
+	rocnik = models.ForeignKey(am.Rocnik, verbose_name='ročník', related_name='soustredeni',
+		on_delete=models.PROTECT)
+
+	datum_zacatku = models.DateField('datum začátku', blank=True, null=True,
+		help_text='První den soustředění')
+
+	datum_konce = models.DateField('datum konce', blank=True, null=True,
+		help_text='Poslední den soustředění')
+
+	verejne_db = models.BooleanField('soustředění zveřejněno', db_column='verejne', default=False)
+
+	misto = models.CharField('místo soustředění', max_length=256, blank=True, default='',
+		help_text='Místo (název obce, volitelně též objektu')
+
+	ucastnici = models.ManyToManyField(Resitel, verbose_name='účastníci soustředění',
+		help_text='Seznam účastníků soustředění', through='Soustredeni_Ucastnici')
+
+	organizatori = models.ManyToManyField(Organizator,
+			verbose_name='Organizátoři soustředění',
+			help_text='Seznam organizátorů soustředění',
+			through='Soustredeni_Organizatori')
+
+	text = models.TextField('text k soustředění (HTML)', blank=True, default='')
+
+	TYP_JARNI = 'jarni'
+	TYP_PODZIMNI = 'podzimni'
+	TYP_VIKEND = 'vikend'
+	TYP_VYLET = 'vylet'
+	TYP_CHOICES = [
+		(TYP_JARNI, 'Jarní soustředění'),
+		(TYP_PODZIMNI, 'Podzimní soustředění'),
+		(TYP_VIKEND, 'Víkendový sraz'),
+		(TYP_VYLET, 'Výlet'),
+		]
+	typ = models.CharField('typ akce', max_length=16, choices=TYP_CHOICES, blank=False, default=TYP_PODZIMNI)
+
+	exportovat = models.BooleanField('export do AESOPa', db_column='exportovat', default=False,
+			help_text='Exportuje se jen podle tohoto flagu (ne veřejnosti)')
+
+	def __str__(self):
+		return '{} ({})'.format(self.misto, self.datum_zacatku)
+
+	def verejne(self):
+		return self.verejne_db
+	verejne.boolean = True
+
+	def verejne_url(self):
+		#return reverse('seminar_soustredeni', kwargs={'pk': self.id})
+		return reverse('seminar_seznam_soustredeni')
+
+
+@reversion.register(ignore_duplicates=True)
+class Soustredeni_Ucastnici(SeminarModelBase):
+# zmena dedicnosti z models.Model na SeminarModelBase, potencialni vznik bugu
+
+	class Meta:
+		managed = False
+		db_table = 'seminar_soustredeni_ucastnici'
+		verbose_name = 'Účast na soustředění'
+		verbose_name_plural = 'Účasti na soustředění'
+		ordering = ['soustredeni', 'resitel']
+
+	# Interní ID
+	id = models.AutoField(primary_key = True)
+
+	resitel = models.ForeignKey(Resitel, verbose_name='řešitel', on_delete=models.PROTECT)
+
+	soustredeni = models.ForeignKey(Soustredeni, verbose_name='soustředění',
+		on_delete=models.PROTECT)
+
+	poznamka = models.TextField('neveřejná poznámka', blank=True,
+		help_text='Neveřejná poznámka k účasti (plain text)')
+
+	def __str__(self):
+		return '{} na {}'.format(self.resitel, self.soustredeni)
+		# NOTE: Poteciální DB HOG bez select_related
+
+
+@reversion.register(ignore_duplicates=True)
+class Soustredeni_Organizatori(SeminarModelBase):
+# zmena dedicnosti z models.Model na SeminarModelBase, potencialni vznik bugu
+
+	class Meta:
+		managed = False
+		db_table = 'seminar_soustredeni_organizatori'
+		verbose_name = 'Účast organizátorů na soustředění'
+		verbose_name_plural = 'Účasti organizátorů na soustředění'
+		ordering = ['soustredeni', 'organizator']
+
+	# Interní ID
+	id = models.AutoField(primary_key = True)
+
+	organizator = models.ForeignKey(Organizator, verbose_name='organizátor',
+		on_delete=models.PROTECT)
+
+	soustredeni = models.ForeignKey(Soustredeni, verbose_name='soustředění',
+		on_delete=models.PROTECT)
+
+	poznamka = models.TextField('neveřejná poznámka', blank=True,
+		help_text='Neveřejná poznámka k účasti organizátora (plain text)')
+
+	def __str__(self):
+		return '{} na {}'.format(self.organizator, self.soustredeni)
+		# NOTE: Poteciální DB HOG bez select_related
+
+
+# FIXME cycle import
+
+
+# Django neumí jednoduše serializovat partial nebo třídu s __call__
+# (https://docs.djangoproject.com/en/1.8/topics/migrations/),
+# neprojdou pak migrace. Takže rozlišení funkcí generujících názvy souboru
+# podle adresáře řešíme takto.
+
+##
+def generate_filename_konfera(self, filename):
+	return os.path.join(
+		settings.SEMINAR_KONFERY_DIR,
+		am.aux_generate_filename(self, filename)
+	)
+
+##
+
+@reversion.register(ignore_duplicates=True)
+class Konfera(am.Problem):
+	class Meta:
+		managed = False
+		db_table = 'seminar_konfera'
+		verbose_name = 'Konfera'
+		verbose_name_plural = 'Konfery'
+
+	anotace = models.TextField('anotace', blank=True,
+							   help_text='Popis, o čem bude konfera.')
+
+	abstrakt = models.TextField('abstrakt', blank=True,
+								help_text='Abstrakt konfery tak, jak byl uveden ve sborníku')
+
+	# FIXME: Umíme omezit jen na účastníky daného soustřeďka?
+	ucastnici = models.ManyToManyField(Resitel, verbose_name='účastníci konfery',
+									   help_text='Seznam účastníků konfery', through='Konfery_Ucastnici')
+
+	soustredeni = models.ForeignKey(Soustredeni, verbose_name='soustředění',
+									related_name='konfery', on_delete = models.SET_NULL, null=True)
+
+	TYP_VELETRH = 'veletrh'
+	TYP_PREZENTACE = 'prezentace'
+	TYP_CHOICES = [
+		(TYP_VELETRH, 'Veletrh (postery)'),
+		(TYP_PREZENTACE, 'Prezentace (přednáška)'),
+	]
+	typ_prezentace = models.CharField('typ prezentace', max_length=16, choices=TYP_CHOICES,
+									  blank=False, default=TYP_VELETRH)
+
+	prezentace = models.FileField('prezentace',help_text = 'Prezentace nebo fotka posteru',
+								  upload_to = generate_filename_konfera, blank=True)
+
+	materialy = models.FileField('materialy',
+								 help_text = 'Další materiály ke konfeře zabalené do jednoho souboru',
+								 upload_to = generate_filename_konfera, blank=True)
+
+	def __str__(self):
+		return "{}: ({})".format(self.nazev, self.soustredeni)
+
+	def cislo_node(self):
+		return None
+
+
+@reversion.register(ignore_duplicates=True)
+class Konfery_Ucastnici(models.Model):
+
+	class Meta:
+		managed = False
+		db_table = 'seminar_konfery_ucastnici'
+		verbose_name = 'Účast na konfeře'
+		verbose_name_plural = 'Účasti na konfeře'
+		ordering = ['konfera', 'resitel']
+
+	# Interní ID
+	id = models.AutoField(primary_key = True)
+
+	resitel = models.ForeignKey(Resitel, verbose_name='řešitel', on_delete=models.PROTECT)
+
+	konfera = models.ForeignKey(Konfera, verbose_name='konfera', on_delete=models.CASCADE)
+
+	poznamka = models.TextField('neveřejná poznámka', blank=True,
+		help_text='Neveřejná poznámka k účasti (plain text)')
+
+	def __str__(self):
+		return '{} na {}'.format(self.resitel, self.konfera)
+		# NOTE: Poteciální DB HOG bez select_related
diff --git a/split-apps-meta/create.notes b/split-apps-meta/create.notes
index 8d3174e3..606f76d8 100644
--- a/split-apps-meta/create.notes
+++ b/split-apps-meta/create.notes
@@ -1,6 +1,7 @@
 Prostě zkopírovat vedle, s původními (=správnými) related names.
+	(Případně opravit *všechny* relativní importy)
 makemigrations
 ! Doplnit hack kolem content-types (jako první operace při migraci)
-! Doplnit ForeignKeys (TODO: jak? Já jsem je ukradl až zpětně…)
+! Doplnit ForeignKeys (Vypadá to, že se dá vesměs zkopírovat předpis z models.py, jen místo prvního fieldu dát `to='app.model'. Dokonce asi funguje použít už novou aplikaci pro vazby v rámci aplikace.)
 doplnit závislost na unmanage
 migrate
diff --git a/split-apps-meta/unmanage.notes b/split-apps-meta/unmanage.notes
index 846b7314..bce419bf 100644
--- a/split-apps-meta/unmanage.notes
+++ b/split-apps-meta/unmanage.notes
@@ -16,6 +16,9 @@ vim seminar/models/whatever
 			HINT: Add or change a related_name argument to the definition for 'seminar.Resitel.osoba' or 'seminar.Prijemce.osoba'.
 	Snadné řešení: dočasné related names mít unikátní. Stejně to nikoho nezajímá.
 	!! Zkontrolovat, že všechno má nastavenou db_table (jinak se to potom pokusí vybastlit jméno tabulky podle aplikace…)
+	Pro tip: related names nejsou součástí DB schématu, takže když se tohle opraví později (typicky při create spadne makemigrations), nevadí to a nemělo by být potřeba měnit migrace).
+	Pro multi-table inheritance je potřeba explicitně přidat 1to1Field s parent_link=True (<https://docs.djangoproject.com/en/5.0/topics/db/models/#specifying-the-parent-link-field>)
+		Je potřeba to udělat správně (třeba nemít FK), migrace potřeba není, protože je to stejně unmanaged…
 
 makemigrations, bez úprav
 migrate?

From e3771f865d70835e6e84e7200399be0373908216 Mon Sep 17 00:00:00 2001
From: "Pavel \"LEdoian\" Turinsky" <ledoian@matfyz.cz>
Date: Wed, 1 May 2024 15:37:54 +0200
Subject: [PATCH 52/59] =?UTF-8?q?Relink=20=E2=80=93=20post,=20asi=20done?=
 =?UTF-8?q?=3F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 galerie/migrations/0012_soustredeni_relink.py |  20 ++
 .../migrations/0013_post_split_soustredeni.py |  14 ++
 galerie/models.py                             |   2 +-
 .../migrations/0017_soustredeni_relink.py     |  20 ++
 .../migrations/0018_post_split_soustredeni.py |  14 ++
 prednasky/models.py                           |   2 +-
 .../0124_remove_sous_from_seminar.py          |  67 +++++
 .../migrations/0125_post_split_soustredeni.py |  14 ++
 seminar/models/__init__.py                    |   5 +-
 seminar/models/soustredeni.py                 | 231 ------------------
 soustredeni/admin.py                          |   2 +-
 .../migrations/0001_split_from_seminar.py     |   5 +-
 .../migrations/0002_manage_soustredeni.py     |  34 +++
 .../migrations/0003_post_split_soustredeni.py |  13 +
 soustredeni/models.py                         |   5 -
 split-apps-meta/create.notes                  |   2 +
 16 files changed, 209 insertions(+), 241 deletions(-)
 create mode 100644 galerie/migrations/0012_soustredeni_relink.py
 create mode 100644 galerie/migrations/0013_post_split_soustredeni.py
 create mode 100644 prednasky/migrations/0017_soustredeni_relink.py
 create mode 100644 prednasky/migrations/0018_post_split_soustredeni.py
 create mode 100644 seminar/migrations/0124_remove_sous_from_seminar.py
 create mode 100644 seminar/migrations/0125_post_split_soustredeni.py
 delete mode 100644 seminar/models/soustredeni.py
 create mode 100644 soustredeni/migrations/0002_manage_soustredeni.py
 create mode 100644 soustredeni/migrations/0003_post_split_soustredeni.py

diff --git a/galerie/migrations/0012_soustredeni_relink.py b/galerie/migrations/0012_soustredeni_relink.py
new file mode 100644
index 00000000..0b8cbaff
--- /dev/null
+++ b/galerie/migrations/0012_soustredeni_relink.py
@@ -0,0 +1,20 @@
+# Generated by Django 4.2.11 on 2024-05-01 13:07
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('soustredeni', '0001_split_from_seminar'),
+        ('galerie', '0011_pre_split_soustredeni'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='galerie',
+            name='soustredeni',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='soustredeni.soustredeni'),
+        ),
+    ]
diff --git a/galerie/migrations/0013_post_split_soustredeni.py b/galerie/migrations/0013_post_split_soustredeni.py
new file mode 100644
index 00000000..de3cfa60
--- /dev/null
+++ b/galerie/migrations/0013_post_split_soustredeni.py
@@ -0,0 +1,14 @@
+# Generated by Django 4.2.11 on 2024-05-01 13:35
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('galerie', '0012_soustredeni_relink'),
+        ('soustredeni', '0003_post_split_soustredeni'),
+    ]
+
+    operations = [
+    ]
diff --git a/galerie/models.py b/galerie/models.py
index 78551969..96225101 100644
--- a/galerie/models.py
+++ b/galerie/models.py
@@ -7,7 +7,7 @@ from imagekit.processors import ResizeToFit, Transpose
 
 import os
 
-from seminar.models import Soustredeni
+from soustredeni.models import Soustredeni
 
 VZDY=0
 ORG=1
diff --git a/prednasky/migrations/0017_soustredeni_relink.py b/prednasky/migrations/0017_soustredeni_relink.py
new file mode 100644
index 00000000..6542b51d
--- /dev/null
+++ b/prednasky/migrations/0017_soustredeni_relink.py
@@ -0,0 +1,20 @@
+# Generated by Django 4.2.11 on 2024-05-01 13:07
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('soustredeni', '0001_split_from_seminar'),
+        ('prednasky', '0016_pre_split_soustredeni'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='seznam',
+            name='soustredeni',
+            field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.PROTECT, to='soustredeni.soustredeni'),
+        ),
+    ]
diff --git a/prednasky/migrations/0018_post_split_soustredeni.py b/prednasky/migrations/0018_post_split_soustredeni.py
new file mode 100644
index 00000000..bd59819d
--- /dev/null
+++ b/prednasky/migrations/0018_post_split_soustredeni.py
@@ -0,0 +1,14 @@
+# Generated by Django 4.2.11 on 2024-05-01 13:35
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('prednasky', '0017_soustredeni_relink'),
+        ('soustredeni', '0003_post_split_soustredeni'),
+    ]
+
+    operations = [
+    ]
diff --git a/prednasky/models.py b/prednasky/models.py
index 7045f4d5..54764381 100644
--- a/prednasky/models.py
+++ b/prednasky/models.py
@@ -2,7 +2,7 @@
 
 from django.db import models
 
-from seminar.models import Soustredeni
+from soustredeni.models import Soustredeni
 from personalni.models import Organizator
 
 STAV_NAVRH = 1
diff --git a/seminar/migrations/0124_remove_sous_from_seminar.py b/seminar/migrations/0124_remove_sous_from_seminar.py
new file mode 100644
index 00000000..9acc3bc4
--- /dev/null
+++ b/seminar/migrations/0124_remove_sous_from_seminar.py
@@ -0,0 +1,67 @@
+# Generated by Django 4.2.11 on 2024-05-01 13:13
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('seminar', '0123_soustredeni_unmanage'),
+        ('soustredeni', '0001_split_from_seminar'),
+        ('galerie', '0012_soustredeni_relink'),
+        ('prednasky', '0017_soustredeni_relink'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='konfery_ucastnici',
+            name='konfera',
+        ),
+        migrations.RemoveField(
+            model_name='konfery_ucastnici',
+            name='resitel',
+        ),
+        migrations.RemoveField(
+            model_name='soustredeni',
+            name='organizatori',
+        ),
+        migrations.RemoveField(
+            model_name='soustredeni',
+            name='rocnik',
+        ),
+        migrations.RemoveField(
+            model_name='soustredeni',
+            name='ucastnici',
+        ),
+        migrations.RemoveField(
+            model_name='soustredeni_organizatori',
+            name='organizator',
+        ),
+        migrations.RemoveField(
+            model_name='soustredeni_organizatori',
+            name='soustredeni',
+        ),
+        migrations.RemoveField(
+            model_name='soustredeni_ucastnici',
+            name='resitel',
+        ),
+        migrations.RemoveField(
+            model_name='soustredeni_ucastnici',
+            name='soustredeni',
+        ),
+        migrations.DeleteModel(
+            name='Konfera',
+        ),
+        migrations.DeleteModel(
+            name='Konfery_Ucastnici',
+        ),
+        migrations.DeleteModel(
+            name='Soustredeni',
+        ),
+        migrations.DeleteModel(
+            name='Soustredeni_Organizatori',
+        ),
+        migrations.DeleteModel(
+            name='Soustredeni_Ucastnici',
+        ),
+    ]
diff --git a/seminar/migrations/0125_post_split_soustredeni.py b/seminar/migrations/0125_post_split_soustredeni.py
new file mode 100644
index 00000000..3d8c8c58
--- /dev/null
+++ b/seminar/migrations/0125_post_split_soustredeni.py
@@ -0,0 +1,14 @@
+# Generated by Django 4.2.11 on 2024-05-01 13:35
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('seminar', '0124_remove_sous_from_seminar'),
+        ('soustredeni', '0003_post_split_soustredeni'),
+    ]
+
+    operations = [
+    ]
diff --git a/seminar/models/__init__.py b/seminar/models/__init__.py
index e3026a2c..121aeb04 100644
--- a/seminar/models/__init__.py
+++ b/seminar/models/__init__.py
@@ -1,10 +1,13 @@
 from .tvorba import *
 from .odevzdavatko import *
 from .base import *
-from .soustredeni import *
 from .pomocne import *
 from .treenode import *
 from .novinky import *
 
 from various.models import Nastaveni
 from personalni.models import Organizator, Resitel, Skola, Prijemce, Osoba
+from soustredeni.models import Soustredeni, Soustredeni_Ucastnici, Soustredeni_Organizatori, Konfera, Konfery_Ucastnici
+
+# Kvůli migr. 0041
+from soustredeni.models import generate_filename_konfera
diff --git a/seminar/models/soustredeni.py b/seminar/models/soustredeni.py
deleted file mode 100644
index 3f761444..00000000
--- a/seminar/models/soustredeni.py
+++ /dev/null
@@ -1,231 +0,0 @@
-# -*- coding: utf-8 -*-
-import logging
-import os
-
-from django.db import models
-from django.urls import reverse
-from reversion import revisions as reversion
-
-from django.conf import settings
-
-from personalni.models import Resitel, Organizator
-
-from .base import SeminarModelBase
-from seminar.models import tvorba as am
-
-logger = logging.getLogger(__name__)
-
-
-@reversion.register(ignore_duplicates=True)
-class Soustredeni(SeminarModelBase):
-
-	class Meta:
-		managed = False
-		db_table = 'seminar_soustredeni'
-		verbose_name = 'Soustředění'
-		verbose_name_plural = 'Soustředění'
-		ordering = ['-rocnik__rocnik', '-datum_zacatku']
-
-	# Interní ID
-	id = models.AutoField(primary_key = True)
-
-	rocnik = models.ForeignKey(am.Rocnik, verbose_name='ročník', related_name='soustredeni_old',
-		on_delete=models.PROTECT)
-
-	datum_zacatku = models.DateField('datum začátku', blank=True, null=True,
-		help_text='První den soustředění')
-
-	datum_konce = models.DateField('datum konce', blank=True, null=True,
-		help_text='Poslední den soustředění')
-
-	verejne_db = models.BooleanField('soustředění zveřejněno', db_column='verejne', default=False)
-
-	misto = models.CharField('místo soustředění', max_length=256, blank=True, default='',
-		help_text='Místo (název obce, volitelně též objektu')
-
-	ucastnici = models.ManyToManyField(Resitel, verbose_name='účastníci soustředění',
-		related_name='soustredeni_old',
-		help_text='Seznam účastníků soustředění', through='Soustredeni_Ucastnici')
-
-	organizatori = models.ManyToManyField(Organizator,
-			related_name='soustredeni_old',
-			verbose_name='Organizátoři soustředění',
-			help_text='Seznam organizátorů soustředění',
-			through='Soustredeni_Organizatori')
-
-	text = models.TextField('text k soustředění (HTML)', blank=True, default='')
-
-	TYP_JARNI = 'jarni'
-	TYP_PODZIMNI = 'podzimni'
-	TYP_VIKEND = 'vikend'
-	TYP_VYLET = 'vylet'
-	TYP_CHOICES = [
-		(TYP_JARNI, 'Jarní soustředění'),
-		(TYP_PODZIMNI, 'Podzimní soustředění'),
-		(TYP_VIKEND, 'Víkendový sraz'),
-		(TYP_VYLET, 'Výlet'),
-		]
-	typ = models.CharField('typ akce', max_length=16, choices=TYP_CHOICES, blank=False, default=TYP_PODZIMNI)
-
-	exportovat = models.BooleanField('export do AESOPa', db_column='exportovat', default=False,
-			help_text='Exportuje se jen podle tohoto flagu (ne veřejnosti)')
-
-	def __str__(self):
-		return '{} ({})'.format(self.misto, self.datum_zacatku)
-
-	def verejne(self):
-		return self.verejne_db
-	verejne.boolean = True
-
-	def verejne_url(self):
-		#return reverse('seminar_soustredeni', kwargs={'pk': self.id})
-		return reverse('seminar_seznam_soustredeni')
-
-
-@reversion.register(ignore_duplicates=True)
-class Soustredeni_Ucastnici(SeminarModelBase):
-# zmena dedicnosti z models.Model na SeminarModelBase, potencialni vznik bugu
-
-	class Meta:
-		managed = False
-		db_table = 'seminar_soustredeni_ucastnici'
-		verbose_name = 'Účast na soustředění'
-		verbose_name_plural = 'Účasti na soustředění'
-		ordering = ['soustredeni', 'resitel']
-
-	# Interní ID
-	id = models.AutoField(primary_key = True)
-
-	resitel = models.ForeignKey(Resitel, verbose_name='řešitel',
-		related_name='sous_ucastnici',
-		 on_delete=models.PROTECT)
-
-	soustredeni = models.ForeignKey(Soustredeni, verbose_name='soustředění',
-		related_name='sous_ucastnici',
-		on_delete=models.PROTECT)
-
-	poznamka = models.TextField('neveřejná poznámka', blank=True,
-		help_text='Neveřejná poznámka k účasti (plain text)')
-
-	def __str__(self):
-		return '{} na {}'.format(self.resitel, self.soustredeni)
-		# NOTE: Poteciální DB HOG bez select_related
-
-
-@reversion.register(ignore_duplicates=True)
-class Soustredeni_Organizatori(SeminarModelBase):
-# zmena dedicnosti z models.Model na SeminarModelBase, potencialni vznik bugu
-
-	class Meta:
-		managed = False
-		db_table = 'seminar_soustredeni_organizatori'
-		verbose_name = 'Účast organizátorů na soustředění'
-		verbose_name_plural = 'Účasti organizátorů na soustředění'
-		ordering = ['soustredeni', 'organizator']
-
-	# Interní ID
-	id = models.AutoField(primary_key = True)
-
-	organizator = models.ForeignKey(Organizator, verbose_name='organizátor',
-		related_name='sous_orgove',
-		on_delete=models.PROTECT)
-
-	soustredeni = models.ForeignKey(Soustredeni, verbose_name='soustředění',
-		related_name='sous_orgove',
-		on_delete=models.PROTECT)
-
-	poznamka = models.TextField('neveřejná poznámka', blank=True,
-		help_text='Neveřejná poznámka k účasti organizátora (plain text)')
-
-	def __str__(self):
-		return '{} na {}'.format(self.organizator, self.soustredeni)
-		# NOTE: Poteciální DB HOG bez select_related
-
-
-# FIXME cycle import
-
-
-# Django neumí jednoduše serializovat partial nebo třídu s __call__
-# (https://docs.djangoproject.com/en/1.8/topics/migrations/),
-# neprojdou pak migrace. Takže rozlišení funkcí generujících názvy souboru
-# podle adresáře řešíme takto.
-
-##
-def generate_filename_konfera(self, filename):
-	return os.path.join(
-		settings.SEMINAR_KONFERY_DIR,
-		am.aux_generate_filename(self, filename)
-	)
-
-##
-
-@reversion.register(ignore_duplicates=True)
-class Konfera(am.Problem):
-	class Meta:
-		managed = False
-		db_table = 'seminar_konfera'
-		verbose_name = 'Konfera'
-		verbose_name_plural = 'Konfery'
-
-	problem_ptr_old = models.OneToOneField(am.Problem, parent_link=True, related_name='konfera_old', on_delete=models.PROTECT, primary_key=True)
-
-	anotace = models.TextField('anotace', blank=True,
-							   help_text='Popis, o čem bude konfera.')
-
-	abstrakt = models.TextField('abstrakt', blank=True,
-								help_text='Abstrakt konfery tak, jak byl uveden ve sborníku')
-
-	# FIXME: Umíme omezit jen na účastníky daného soustřeďka?
-	ucastnici = models.ManyToManyField(Resitel, verbose_name='účastníci konfery',
-									related_name='konfera_old',
-									   help_text='Seznam účastníků konfery', through='Konfery_Ucastnici')
-
-	soustredeni = models.ForeignKey(Soustredeni, verbose_name='soustředění',
-									related_name='konfery_old', on_delete = models.SET_NULL, null=True)
-
-	TYP_VELETRH = 'veletrh'
-	TYP_PREZENTACE = 'prezentace'
-	TYP_CHOICES = [
-		(TYP_VELETRH, 'Veletrh (postery)'),
-		(TYP_PREZENTACE, 'Prezentace (přednáška)'),
-	]
-	typ_prezentace = models.CharField('typ prezentace', max_length=16, choices=TYP_CHOICES,
-									  blank=False, default=TYP_VELETRH)
-
-	prezentace = models.FileField('prezentace',help_text = 'Prezentace nebo fotka posteru',
-								  upload_to = generate_filename_konfera, blank=True)
-
-	materialy = models.FileField('materialy',
-								 help_text = 'Další materiály ke konfeře zabalené do jednoho souboru',
-								 upload_to = generate_filename_konfera, blank=True)
-
-	def __str__(self):
-		return "{}: ({})".format(self.nazev, self.soustredeni)
-
-	def cislo_node(self):
-		return None
-
-
-@reversion.register(ignore_duplicates=True)
-class Konfery_Ucastnici(models.Model):
-
-	class Meta:
-		managed = False
-		db_table = 'seminar_konfery_ucastnici'
-		verbose_name = 'Účast na konfeře'
-		verbose_name_plural = 'Účasti na konfeře'
-		ordering = ['konfera', 'resitel']
-
-	# Interní ID
-	id = models.AutoField(primary_key = True)
-
-	resitel = models.ForeignKey(Resitel, verbose_name='řešitel', on_delete=models.PROTECT, related_name='konf_uc')
-
-	konfera = models.ForeignKey(Konfera, verbose_name='konfera', on_delete=models.CASCADE, related_name='konf_uc')
-
-	poznamka = models.TextField('neveřejná poznámka', blank=True,
-		help_text='Neveřejná poznámka k účasti (plain text)')
-
-	def __str__(self):
-		return '{} na {}'.format(self.resitel, self.konfera)
-		# NOTE: Poteciální DB HOG bez select_related
diff --git a/soustredeni/admin.py b/soustredeni/admin.py
index c6f048db..048b9ec3 100644
--- a/soustredeni/admin.py
+++ b/soustredeni/admin.py
@@ -2,7 +2,7 @@ from django.contrib import admin
 from django.forms import widgets
 from django.db import models
 
-from seminar.models import soustredeni as m
+import soustredeni.models as m
 
 
 class SoustredeniUcastniciInline(admin.TabularInline):
diff --git a/soustredeni/migrations/0001_split_from_seminar.py b/soustredeni/migrations/0001_split_from_seminar.py
index 745f62e7..c5e432d3 100644
--- a/soustredeni/migrations/0001_split_from_seminar.py
+++ b/soustredeni/migrations/0001_split_from_seminar.py
@@ -37,7 +37,8 @@ class Migration(migrations.Migration):
                 ('typ_prezentace', models.CharField(choices=[('veletrh', 'Veletrh (postery)'), ('prezentace', 'Prezentace (přednáška)')], default='veletrh', max_length=16, verbose_name='typ prezentace')),
                 ('prezentace', models.FileField(blank=True, help_text='Prezentace nebo fotka posteru', upload_to=soustredeni.models.generate_filename_konfera, verbose_name='prezentace')),
                 ('materialy', models.FileField(blank=True, help_text='Další materiály ke konfeře zabalené do jednoho souboru', upload_to=soustredeni.models.generate_filename_konfera, verbose_name='materialy')),
-                ('soustredeni', models.ForeignKey(to='soustredeni.soustredeni', verbose_name='soustředění', on_delete=models.PROTECT)),
+                ('soustredeni', models.ForeignKey(to='soustredeni.soustredeni', verbose_name='soustředění', on_delete=models.SET_NULL, null=True, related_name='konfery')),
+                ('ucastnici', models.ManyToManyField(help_text='Seznam účastníků konfery', through='soustredeni.Konfery_Ucastnici', to='personalni.resitel', verbose_name='účastníci konfery')),
             ],
             options={
                 'verbose_name': 'Konfera',
@@ -75,6 +76,8 @@ class Migration(migrations.Migration):
                 ('typ', models.CharField(choices=[('jarni', 'Jarní soustředění'), ('podzimni', 'Podzimní soustředění'), ('vikend', 'Víkendový sraz'), ('vylet', 'Výlet')], default='podzimni', max_length=16, verbose_name='typ akce')),
                 ('exportovat', models.BooleanField(db_column='exportovat', default=False, help_text='Exportuje se jen podle tohoto flagu (ne veřejnosti)', verbose_name='export do AESOPa')),
                 ('rocnik', models.ForeignKey(to='seminar.rocnik', verbose_name='ročník', related_name='soustredeni', on_delete=models.PROTECT)),
+                ('organizatori', models.ManyToManyField(help_text='Seznam organizátorů soustředění', through='soustredeni.Soustredeni_Organizatori', to='personalni.organizator', verbose_name='Organizátoři soustředění')),
+                ('ucastnici', models.ManyToManyField(help_text='Seznam účastníků soustředění', through='soustredeni.Soustredeni_Ucastnici', to='personalni.resitel', verbose_name='účastníci soustředění')),
             ],
             options={
                 'verbose_name': 'Soustředění',
diff --git a/soustredeni/migrations/0002_manage_soustredeni.py b/soustredeni/migrations/0002_manage_soustredeni.py
new file mode 100644
index 00000000..1a535eb0
--- /dev/null
+++ b/soustredeni/migrations/0002_manage_soustredeni.py
@@ -0,0 +1,34 @@
+# Generated by Django 4.2.11 on 2024-05-01 13:18
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('soustredeni', '0001_split_from_seminar'),
+        ('seminar', '0124_remove_sous_from_seminar'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='konfera',
+            options={'verbose_name': 'Konfera', 'verbose_name_plural': 'Konfery'},
+        ),
+        migrations.AlterModelOptions(
+            name='konfery_ucastnici',
+            options={'ordering': ['konfera', 'resitel'], 'verbose_name': 'Účast na konfeře', 'verbose_name_plural': 'Účasti na konfeře'},
+        ),
+        migrations.AlterModelOptions(
+            name='soustredeni',
+            options={'ordering': ['-rocnik__rocnik', '-datum_zacatku'], 'verbose_name': 'Soustředění', 'verbose_name_plural': 'Soustředění'},
+        ),
+        migrations.AlterModelOptions(
+            name='soustredeni_organizatori',
+            options={'ordering': ['soustredeni', 'organizator'], 'verbose_name': 'Účast organizátorů na soustředění', 'verbose_name_plural': 'Účasti organizátorů na soustředění'},
+        ),
+        migrations.AlterModelOptions(
+            name='soustredeni_ucastnici',
+            options={'ordering': ['soustredeni', 'resitel'], 'verbose_name': 'Účast na soustředění', 'verbose_name_plural': 'Účasti na soustředění'},
+        ),
+    ]
diff --git a/soustredeni/migrations/0003_post_split_soustredeni.py b/soustredeni/migrations/0003_post_split_soustredeni.py
new file mode 100644
index 00000000..ad1821ee
--- /dev/null
+++ b/soustredeni/migrations/0003_post_split_soustredeni.py
@@ -0,0 +1,13 @@
+# Generated by Django 4.2.11 on 2024-05-01 13:35
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('soustredeni', '0002_manage_soustredeni'),
+    ]
+
+    operations = [
+    ]
diff --git a/soustredeni/models.py b/soustredeni/models.py
index 4429e2bf..78582565 100644
--- a/soustredeni/models.py
+++ b/soustredeni/models.py
@@ -20,7 +20,6 @@ logger = logging.getLogger(__name__)
 class Soustredeni(SeminarModelBase):
 
 	class Meta:
-		managed = False
 		db_table = 'seminar_soustredeni'
 		verbose_name = 'Soustředění'
 		verbose_name_plural = 'Soustředění'
@@ -85,7 +84,6 @@ class Soustredeni_Ucastnici(SeminarModelBase):
 # zmena dedicnosti z models.Model na SeminarModelBase, potencialni vznik bugu
 
 	class Meta:
-		managed = False
 		db_table = 'seminar_soustredeni_ucastnici'
 		verbose_name = 'Účast na soustředění'
 		verbose_name_plural = 'Účasti na soustředění'
@@ -112,7 +110,6 @@ class Soustredeni_Organizatori(SeminarModelBase):
 # zmena dedicnosti z models.Model na SeminarModelBase, potencialni vznik bugu
 
 	class Meta:
-		managed = False
 		db_table = 'seminar_soustredeni_organizatori'
 		verbose_name = 'Účast organizátorů na soustředění'
 		verbose_name_plural = 'Účasti organizátorů na soustředění'
@@ -155,7 +152,6 @@ def generate_filename_konfera(self, filename):
 @reversion.register(ignore_duplicates=True)
 class Konfera(am.Problem):
 	class Meta:
-		managed = False
 		db_table = 'seminar_konfera'
 		verbose_name = 'Konfera'
 		verbose_name_plural = 'Konfery'
@@ -200,7 +196,6 @@ class Konfera(am.Problem):
 class Konfery_Ucastnici(models.Model):
 
 	class Meta:
-		managed = False
 		db_table = 'seminar_konfery_ucastnici'
 		verbose_name = 'Účast na konfeře'
 		verbose_name_plural = 'Účasti na konfeře'
diff --git a/split-apps-meta/create.notes b/split-apps-meta/create.notes
index 606f76d8..a48a17f8 100644
--- a/split-apps-meta/create.notes
+++ b/split-apps-meta/create.notes
@@ -3,5 +3,7 @@ Prostě zkopírovat vedle, s původními (=správnými) related names.
 makemigrations
 ! Doplnit hack kolem content-types (jako první operace při migraci)
 ! Doplnit ForeignKeys (Vypadá to, že se dá vesměs zkopírovat předpis z models.py, jen místo prvního fieldu dát `to='app.model'. Dokonce asi funguje použít už novou aplikaci pro vazby v rámci aplikace.)
+	To samé s ManyToManyFieldy (through= musí taky být 'app.model')
+(Zdá se, že jde dobastlit tuhle migraci polozpětně – doplnit co chybělo až podle toho, co vygeneruje migrace po zamanagování nového modelu.)
 doplnit závislost na unmanage
 migrate

From 8c4bf4d19a99980e3e3430c26bc759d18a9df8d8 Mon Sep 17 00:00:00 2001
From: "Pavel \"LEdoian\" Turinsky" <ledoian@matfyz.cz>
Date: Wed, 1 May 2024 15:48:33 +0200
Subject: [PATCH 53/59] =?UTF-8?q?Oprava=20jmen=20model=C5=AF=20v=20org=20p?=
 =?UTF-8?q?r=C3=A1vech?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 deploy_v2/admin_org_prava.json | 70 +++++++++++++++++-----------------
 1 file changed, 35 insertions(+), 35 deletions(-)

diff --git a/deploy_v2/admin_org_prava.json b/deploy_v2/admin_org_prava.json
index 3ef169a5..01de7476 100644
--- a/deploy_v2/admin_org_prava.json
+++ b/deploy_v2/admin_org_prava.json
@@ -271,42 +271,42 @@
 	},
 	{
 		"codename": "add_konfera",
-		"ct_app_label": "seminar",
+		"ct_app_label": "soustredeni",
 		"ct_model": "konfera"
 	},
 	{
 		"codename": "change_konfera",
-		"ct_app_label": "seminar",
+		"ct_app_label": "soustredeni",
 		"ct_model": "konfera"
 	},
 	{
 		"codename": "delete_konfera",
-		"ct_app_label": "seminar",
+		"ct_app_label": "soustredeni",
 		"ct_model": "konfera"
 	},
 	{
 		"codename": "view_konfera",
-		"ct_app_label": "seminar",
+		"ct_app_label": "soustredeni",
 		"ct_model": "konfera"
 	},
 	{
 		"codename": "add_konfery_ucastnici",
-		"ct_app_label": "seminar",
+		"ct_app_label": "soustredeni",
 		"ct_model": "konfery_ucastnici"
 	},
 	{
 		"codename": "change_konfery_ucastnici",
-		"ct_app_label": "seminar",
+		"ct_app_label": "soustredeni",
 		"ct_model": "konfery_ucastnici"
 	},
 	{
 		"codename": "delete_konfery_ucastnici",
-		"ct_app_label": "seminar",
+		"ct_app_label": "soustredeni",
 		"ct_model": "konfery_ucastnici"
 	},
 	{
 		"codename": "view_konfery_ucastnici",
-		"ct_app_label": "seminar",
+		"ct_app_label": "soustredeni",
 		"ct_model": "konfery_ucastnici"
 	},
 	{
@@ -351,22 +351,22 @@
 	},
 	{
 		"codename": "change_organizator",
-		"ct_app_label": "seminar",
+		"ct_app_label": "personalni",
 		"ct_model": "organizator"
 	},
 	{
 		"codename": "view_organizator",
-		"ct_app_label": "seminar",
+		"ct_app_label": "personalni",
 		"ct_model": "organizator"
 	},
 	{
 		"codename": "change_osoba",
-		"ct_app_label": "seminar",
+		"ct_app_label": "personalni",
 		"ct_model": "osoba"
 	},
 	{
 		"codename": "view_osoba",
-		"ct_app_label": "seminar",
+		"ct_app_label": "personalni",
 		"ct_model": "osoba"
 	},
 	{
@@ -391,22 +391,22 @@
 	},
 	{
 		"codename": "add_prijemce",
-		"ct_app_label": "seminar",
+		"ct_app_label": "personalni",
 		"ct_model": "prijemce"
 	},
 	{
 		"codename": "change_prijemce",
-		"ct_app_label": "seminar",
+		"ct_app_label": "personalni",
 		"ct_model": "prijemce"
 	},
 	{
 		"codename": "delete_prijemce",
-		"ct_app_label": "seminar",
+		"ct_app_label": "personalni",
 		"ct_model": "prijemce"
 	},
 	{
 		"codename": "view_prijemce",
-		"ct_app_label": "seminar",
+		"ct_app_label": "personalni",
 		"ct_model": "prijemce"
 	},
 	{
@@ -431,12 +431,12 @@
 	},
 	{
 		"codename": "change_resitel",
-		"ct_app_label": "seminar",
+		"ct_app_label": "personalni",
 		"ct_model": "resitel"
 	},
 	{
 		"codename": "view_resitel",
-		"ct_app_label": "seminar",
+		"ct_app_label": "personalni",
 		"ct_model": "resitel"
 	},
 	{
@@ -461,82 +461,82 @@
 	},
 	{
 		"codename": "add_skola",
-		"ct_app_label": "seminar",
+		"ct_app_label": "personalni",
 		"ct_model": "skola"
 	},
 	{
 		"codename": "change_skola",
-		"ct_app_label": "seminar",
+		"ct_app_label": "personalni",
 		"ct_model": "skola"
 	},
 	{
 		"codename": "delete_skola",
-		"ct_app_label": "seminar",
+		"ct_app_label": "personalni",
 		"ct_model": "skola"
 	},
 	{
 		"codename": "view_skola",
-		"ct_app_label": "seminar",
+		"ct_app_label": "personalni",
 		"ct_model": "skola"
 	},
 	{
 		"codename": "add_soustredeni",
-		"ct_app_label": "seminar",
+		"ct_app_label": "soustredeni",
 		"ct_model": "soustredeni"
 	},
 	{
 		"codename": "change_soustredeni",
-		"ct_app_label": "seminar",
+		"ct_app_label": "soustredeni",
 		"ct_model": "soustredeni"
 	},
 	{
 		"codename": "delete_soustredeni",
-		"ct_app_label": "seminar",
+		"ct_app_label": "soustredeni",
 		"ct_model": "soustredeni"
 	},
 	{
 		"codename": "view_soustredeni",
-		"ct_app_label": "seminar",
+		"ct_app_label": "soustredeni",
 		"ct_model": "soustredeni"
 	},
 	{
 		"codename": "add_soustredeni_organizatori",
-		"ct_app_label": "seminar",
+		"ct_app_label": "soustredeni",
 		"ct_model": "soustredeni_organizatori"
 	},
 	{
 		"codename": "change_soustredeni_organizatori",
-		"ct_app_label": "seminar",
+		"ct_app_label": "soustredeni",
 		"ct_model": "soustredeni_organizatori"
 	},
 	{
 		"codename": "delete_soustredeni_organizatori",
-		"ct_app_label": "seminar",
+		"ct_app_label": "soustredeni",
 		"ct_model": "soustredeni_organizatori"
 	},
 	{
 		"codename": "view_soustredeni_organizatori",
-		"ct_app_label": "seminar",
+		"ct_app_label": "soustredeni",
 		"ct_model": "soustredeni_organizatori"
 	},
 	{
 		"codename": "add_soustredeni_ucastnici",
-		"ct_app_label": "seminar",
+		"ct_app_label": "soustredeni",
 		"ct_model": "soustredeni_ucastnici"
 	},
 	{
 		"codename": "change_soustredeni_ucastnici",
-		"ct_app_label": "seminar",
+		"ct_app_label": "soustredeni",
 		"ct_model": "soustredeni_ucastnici"
 	},
 	{
 		"codename": "delete_soustredeni_ucastnici",
-		"ct_app_label": "seminar",
+		"ct_app_label": "soustredeni",
 		"ct_model": "soustredeni_ucastnici"
 	},
 	{
 		"codename": "view_soustredeni_ucastnici",
-		"ct_app_label": "seminar",
+		"ct_app_label": "soustredeni",
 		"ct_model": "soustredeni_ucastnici"
 	},
 	{
@@ -619,4 +619,4 @@
 		"ct_app_label": "taggit",
 		"ct_model": "taggeditem"
 	}
-]
\ No newline at end of file
+]

From e075a9e749613c4bf9081e065af5b394371e28fd Mon Sep 17 00:00:00 2001
From: "Pavel \"LEdoian\" Turinsky" <ledoian@matfyz.cz>
Date: Wed, 1 May 2024 16:27:39 +0200
Subject: [PATCH 54/59] =?UTF-8?q?Oprava=20jmen=20model=C5=AF=20po=20odst?=
 =?UTF-8?q?=C5=99elu=20person=C3=A1li=C3=AD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 deploy_v2/admin_org_prava.json | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/deploy_v2/admin_org_prava.json b/deploy_v2/admin_org_prava.json
index 3ef169a5..0bc582fb 100644
--- a/deploy_v2/admin_org_prava.json
+++ b/deploy_v2/admin_org_prava.json
@@ -351,22 +351,22 @@
 	},
 	{
 		"codename": "change_organizator",
-		"ct_app_label": "seminar",
+		"ct_app_label": "personalni",
 		"ct_model": "organizator"
 	},
 	{
 		"codename": "view_organizator",
-		"ct_app_label": "seminar",
+		"ct_app_label": "personalni",
 		"ct_model": "organizator"
 	},
 	{
 		"codename": "change_osoba",
-		"ct_app_label": "seminar",
+		"ct_app_label": "personalni",
 		"ct_model": "osoba"
 	},
 	{
 		"codename": "view_osoba",
-		"ct_app_label": "seminar",
+		"ct_app_label": "personalni",
 		"ct_model": "osoba"
 	},
 	{
@@ -391,22 +391,22 @@
 	},
 	{
 		"codename": "add_prijemce",
-		"ct_app_label": "seminar",
+		"ct_app_label": "personalni",
 		"ct_model": "prijemce"
 	},
 	{
 		"codename": "change_prijemce",
-		"ct_app_label": "seminar",
+		"ct_app_label": "personalni",
 		"ct_model": "prijemce"
 	},
 	{
 		"codename": "delete_prijemce",
-		"ct_app_label": "seminar",
+		"ct_app_label": "personalni",
 		"ct_model": "prijemce"
 	},
 	{
 		"codename": "view_prijemce",
-		"ct_app_label": "seminar",
+		"ct_app_label": "personalni",
 		"ct_model": "prijemce"
 	},
 	{
@@ -431,12 +431,12 @@
 	},
 	{
 		"codename": "change_resitel",
-		"ct_app_label": "seminar",
+		"ct_app_label": "personalni",
 		"ct_model": "resitel"
 	},
 	{
 		"codename": "view_resitel",
-		"ct_app_label": "seminar",
+		"ct_app_label": "personalni",
 		"ct_model": "resitel"
 	},
 	{
@@ -461,22 +461,22 @@
 	},
 	{
 		"codename": "add_skola",
-		"ct_app_label": "seminar",
+		"ct_app_label": "personalni",
 		"ct_model": "skola"
 	},
 	{
 		"codename": "change_skola",
-		"ct_app_label": "seminar",
+		"ct_app_label": "personalni",
 		"ct_model": "skola"
 	},
 	{
 		"codename": "delete_skola",
-		"ct_app_label": "seminar",
+		"ct_app_label": "personalni",
 		"ct_model": "skola"
 	},
 	{
 		"codename": "view_skola",
-		"ct_app_label": "seminar",
+		"ct_app_label": "personalni",
 		"ct_model": "skola"
 	},
 	{

From 9f08ec332faa94ea591f84915ba6507c0b176f72 Mon Sep 17 00:00:00 2001
From: "Pavel \"LEdoian\" Turinsky" <ledoian@matfyz.cz>
Date: Wed, 1 May 2024 16:31:54 +0200
Subject: [PATCH 55/59] =?UTF-8?q?Oprava=20jmen=20model=C5=AF=20po=20odst?=
 =?UTF-8?q?=C5=99elu=20nastaven=C3=AD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 deploy_v2/admin_org_prava.json | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/deploy_v2/admin_org_prava.json b/deploy_v2/admin_org_prava.json
index 0bc582fb..bfac6c77 100644
--- a/deploy_v2/admin_org_prava.json
+++ b/deploy_v2/admin_org_prava.json
@@ -311,22 +311,22 @@
 	},
 	{
 		"codename": "add_nastaveni",
-		"ct_app_label": "seminar",
+		"ct_app_label": "various",
 		"ct_model": "nastaveni"
 	},
 	{
 		"codename": "change_nastaveni",
-		"ct_app_label": "seminar",
+		"ct_app_label": "various",
 		"ct_model": "nastaveni"
 	},
 	{
 		"codename": "delete_nastaveni",
-		"ct_app_label": "seminar",
+		"ct_app_label": "various",
 		"ct_model": "nastaveni"
 	},
 	{
 		"codename": "view_nastaveni",
-		"ct_app_label": "seminar",
+		"ct_app_label": "various",
 		"ct_model": "nastaveni"
 	},
 	{
@@ -619,4 +619,4 @@
 		"ct_app_label": "taggit",
 		"ct_model": "taggeditem"
 	}
-]
\ No newline at end of file
+]

From 6c59c3c2ed1169b0e3e3c5d421a7eec043adf873 Mon Sep 17 00:00:00 2001
From: "Pavel \"LEdoian\" Turinsky" <ledoian@matfyz.cz>
Date: Wed, 1 May 2024 16:31:54 +0200
Subject: [PATCH 56/59] =?UTF-8?q?Oprava=20jmen=20model=C5=AF=20po=20odst?=
 =?UTF-8?q?=C5=99elu=20nastaven=C3=AD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 deploy_v2/admin_org_prava.json | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/deploy_v2/admin_org_prava.json b/deploy_v2/admin_org_prava.json
index 01de7476..9caa6eb2 100644
--- a/deploy_v2/admin_org_prava.json
+++ b/deploy_v2/admin_org_prava.json
@@ -311,22 +311,22 @@
 	},
 	{
 		"codename": "add_nastaveni",
-		"ct_app_label": "seminar",
+		"ct_app_label": "various",
 		"ct_model": "nastaveni"
 	},
 	{
 		"codename": "change_nastaveni",
-		"ct_app_label": "seminar",
+		"ct_app_label": "various",
 		"ct_model": "nastaveni"
 	},
 	{
 		"codename": "delete_nastaveni",
-		"ct_app_label": "seminar",
+		"ct_app_label": "various",
 		"ct_model": "nastaveni"
 	},
 	{
 		"codename": "view_nastaveni",
-		"ct_app_label": "seminar",
+		"ct_app_label": "various",
 		"ct_model": "nastaveni"
 	},
 	{

From 7569447baaf9f51a3efd44b0cd3cdb75aa93b8f0 Mon Sep 17 00:00:00 2001
From: "Pavel \"LEdoian\" Turinsky" <ledoian@matfyz.cz>
Date: Mon, 13 May 2024 22:12:51 +0200
Subject: [PATCH 57/59] Oprava assertEquals na assertEqual
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

první je deprecated a v Py3.12 už nefunguje.
---
 various/tests.py | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/various/tests.py b/various/tests.py
index 0abf4e26..0bb094fb 100644
--- a/various/tests.py
+++ b/various/tests.py
@@ -6,18 +6,18 @@ class MailTagsTest(TestCase):
 	"""Testuje template tagy ohledně mailů."""
 	def test_maillink(self):
 		# Tohle nedává smysl dělit do víc funkcí, bylo by v nich víc boilerplatu než užitečného kódu.
-		self.assertEquals(maillink('Hello', to='some@body.test'), r'<a href="mailto:some@body.test">Hello</a>')
-		self.assertEquals(maillink('Hello', to=['some@body.test']), r'<a href="mailto:some@body.test">Hello</a>')
-		self.assertEquals(
+		self.assertEqual(maillink('Hello', to='some@body.test'), r'<a href="mailto:some@body.test">Hello</a>')
+		self.assertEqual(maillink('Hello', to=['some@body.test']), r'<a href="mailto:some@body.test">Hello</a>')
+		self.assertEqual(
 			maillink('Hello', to=['alice@test.test', 'bob@jinde.test']),
 			r'<a href="mailto:alice@test.test,bob@jinde.test">Hello</a>',
 			)
-		self.assertEquals(
+		self.assertEqual(
 			maillink('Hello', to='some@body.test', attrs='class="trida" id="id"'),
 			r'<a href="mailto:some@body.test" class="trida" id="id">Hello</a>',
 			)
 		# Následující test toho testuje moc zároveň, měly by předcházet dedikované testy… (kašlu na ně :-P)
-		self.assertEquals(
+		self.assertEqual(
 			maillink('Text odkazu', to='prijemce@wtf.test', subject="Předmět", body="Čau"),
 			r'<a href="mailto:prijemce@wtf.test?subject=P%C5%99edm%C4%9Bt&body=%C4%8Cau">Text odkazu</a>',
 			)
@@ -25,10 +25,10 @@ class MailTagsTest(TestCase):
 		self.assertRaises(TypeError, lambda: maillink()) # Nemá text, takže to shodí python
 
 	def test_mailurl(self):
-		self.assertEquals(mailurl(to='some@body.test'), r'mailto:some@body.test')
-		self.assertEquals(mailurl(to=['some@body.test']), r'mailto:some@body.test')
-		self.assertEquals(mailurl(to=['alice@test.test', 'bob@jinde.test']), r'mailto:alice@test.test,bob@jinde.test')
-		self.assertEquals(
+		self.assertEqual(mailurl(to='some@body.test'), r'mailto:some@body.test')
+		self.assertEqual(mailurl(to=['some@body.test']), r'mailto:some@body.test')
+		self.assertEqual(mailurl(to=['alice@test.test', 'bob@jinde.test']), r'mailto:alice@test.test,bob@jinde.test')
+		self.assertEqual(
 			mailurl(to='some@body.test', body='Tělo', subject='Předmět'),
 			r'mailto:some@body.test?subject=P%C5%99edm%C4%9Bt&body=T%C4%9Blo',
 			)
@@ -48,7 +48,7 @@ class MailTagsTest(TestCase):
 			# TODO: Vyzkoušet i víc adresátů. (Nepamatuji si z hlavy syntaxi…)
 			r'{% maillink "Text" to="alice@test.test" subject="Oprava řešení" %}'
 			)
-		self.assertEquals(
+		self.assertEqual(
 			render_template(template),
 			r'<a href="mailto:alice@test.test?subject=Oprava%20%C5%99e%C5%A1en%C3%AD">Text</a>',
 			)
@@ -57,4 +57,4 @@ class MailTagsTest(TestCase):
 			r'{% load mail %}'
 			r'{% mailurl to="alice@test.test" subject="Čau Alice" %}'
 			)
-		self.assertEquals(render_template(mailurltemplate), r'mailto:alice@test.test?subject=%C4%8Cau%20Alice')
+		self.assertEqual(render_template(mailurltemplate), r'mailto:alice@test.test?subject=%C4%8Cau%20Alice')

From e618d390de20365dec01fe3cb1317d8b487932d9 Mon Sep 17 00:00:00 2001
From: "Pavel \"LEdoian\" Turinsky" <ledoian@matfyz.cz>
Date: Mon, 13 May 2024 23:44:26 +0200
Subject: [PATCH 58/59] =?UTF-8?q?Orgorozcestn=C3=ADk=20pou=C5=BE=C3=ADv?=
 =?UTF-8?q?=C3=A1=20urls,=20ne=20hardcodovan=C3=A9=20cesty?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../personalni/profil/orgorozcestnik.html      | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/personalni/templates/personalni/profil/orgorozcestnik.html b/personalni/templates/personalni/profil/orgorozcestnik.html
index 34c4daba..46a26826 100644
--- a/personalni/templates/personalni/profil/orgorozcestnik.html
+++ b/personalni/templates/personalni/profil/orgorozcestnik.html
@@ -13,18 +13,18 @@
 		<li>soustředění</li>
 	</ul>
 	</li>
-	<li><a href="/admin/seminar/novinky/add/"><strong>přidat novinku</strong></a> na web</li>
+	<li><a href="{% url 'admin:seminar_novinky_add' %}"><strong>přidat novinku</strong></a> na web</li>
 </ul>
 
 <hr />
 <h2><strong>Tvorba čísla</strong></h2>
 
 <ul>
-	<li><a href="/admin/seminar/problem/add/"><strong>přidat téma</strong></a></li>
+	<li><a href="{% url 'admin:seminar_problem_add' %}"><strong>přidat téma</strong></a></li>
 	<li><strong>korektury</strong>
 	<ul>
-		<li><a href="/korektury/">korekturování</a></li>
-		<li><a href="/admin/korektury/korekturovanepdf/add/">přidat pdf k opravám</a></li>
+		<li><a href="{% url 'korektury_list' %}">korekturování</a></li>
+		<li><a href="{% url 'admin:korektury_korekturovanepdf_add' %}">přidat pdf k opravám</a></li>
 	</ul>
 	</li>
 	<li>
@@ -70,15 +70,15 @@
 <h2><strong>Soustředění</strong></h2>
 
 <ul>
-	<li><a href="/admin/seminar/soustredeni/add/">přidat soustředění</a></li>
+	<li><a href="{% url 'admin:soustredeni_soustredeni_add' %}">přidat soustředění</a></li>
 	<li><strong>přednášky</strong>
 
 	<ul>
-		<li><a href="/admin/prednasky/prednaska/">vypisování přednášek</a></li>
+		<li><a href="{% url 'admin:prednasky_prednaska_add' %}">vypisování přednášek</a></li>
 		<li>hlasování o přednáškách</li>
 	</ul>
 	</li>
-	<li><a href="/soustredeni/probehlo/">proběhlá soustředění</a>
+	<li><a href="{% url 'seminar_seznam_soustredeni' %}">proběhlá soustředění</a>
 	<ul>
 		<li>vytvoření galerie</li>
 		<li>stažení seznamu účastníků</li>
@@ -91,7 +91,7 @@
 <h2><strong>Můj profil</strong></h2>
 
 <ul>
-	<li><a href="/admin/seminar/organizator/{{ organizator.id }}/change/"><strong>upravit </strong></a></li>
+	<li><a href="{% url 'admin:personalni_organizator_change' organizator.id %}"><strong>upravit </strong></a></li>
 </ul>
 
 <hr/>
@@ -108,6 +108,6 @@
 </ul>
 
 <hr />
-<p>Nemůžeš najít, co hledáš? Může to být v <a href="/admin/">administračním rozhraní webu</a>.</p>
+<p>Nemůžeš najít, co hledáš? Může to být v <a href="{% url 'admin:index' %}">administračním rozhraní webu</a>.</p>
 {% endblock content %}
 

From 2ce45b3d897abf7a7874924f00ff025c7688194c Mon Sep 17 00:00:00 2001
From: "Pavel \"LEdoian\" Turinsky" <ledoian@matfyz.cz>
Date: Mon, 13 May 2024 23:44:54 +0200
Subject: [PATCH 59/59] =?UTF-8?q?Co=20dal=C5=A1=C3=ADho=20nezapomenout=20p?=
 =?UTF-8?q?=C5=99i=20odst=C5=99elu=20aplikac=C3=AD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 split-apps-meta/other | 6 ++++++
 1 file changed, 6 insertions(+)
 create mode 100644 split-apps-meta/other

diff --git a/split-apps-meta/other b/split-apps-meta/other
new file mode 100644
index 00000000..68d320e2
--- /dev/null
+++ b/split-apps-meta/other
@@ -0,0 +1,6 @@
+admin
+práva
+aplikovat práva
+orgorozcestník a další reverzované urls
+
+výhledově smazat ze seminare