From adb981019bbaa1dd14fa4f58866cd8eaf7ca001d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tom=C3=A1=C5=A1?= <git@imaniti.org>
Date: Fri, 3 Feb 2023 23:02:23 +0900
Subject: [PATCH] add pre-commit hooks, wip readme, run hooks

---
 .gitignore                                    |   2 +
 .pre-commit-config.yaml                       |  26 +
 Makefile                                      |   5 +
 README.md                                     |  39 ++
 manage.py                                     |   4 +-
 registry/asgi.py                              |   2 +-
 registry/settings/base.py                     |   7 +-
 registry/urls.py                              |   2 +-
 registry/wsgi.py                              |   2 +-
 requirements/base.txt                         |   2 +-
 shared/apps.py                                |   4 +-
 shared/migrations/0001_initial.py             | 606 ++++++++++++++----
 ...contractlocalsigner_department_and_more.py |  79 ++-
 shared/models.py                              |  56 +-
 14 files changed, 624 insertions(+), 212 deletions(-)
 create mode 100644 .pre-commit-config.yaml
 create mode 100644 README.md

diff --git a/.gitignore b/.gitignore
index d8265e7..e5a12d8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,6 @@
 .env
 __pycache__/
+staticfiles
 node_modules/*
 shared/static/*
+webpack-stats.json
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..7d3057f
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,26 @@
+default_language_version:
+  python: python3.10
+
+exclude: snapshots/
+repos:
+  - repo: https://github.com/pre-commit/pre-commit-hooks
+    rev: v4.4.0
+    hooks:
+      - id: trailing-whitespace
+        exclude: ^.*\.md$
+      - id: end-of-file-fixer
+      - id: debug-statements
+      - id: mixed-line-ending
+        args: [--fix=lf]
+      - id: detect-private-key
+      - id: check-merge-conflict
+
+  - repo: https://github.com/timothycrosley/isort
+    rev: 5.12.0
+    hooks:
+      - id: isort
+
+  - repo: https://github.com/psf/black
+    rev: 23.1.0
+    hooks:
+      - id: black
diff --git a/Makefile b/Makefile
index c530d3d..10de836 100644
--- a/Makefile
+++ b/Makefile
@@ -30,6 +30,11 @@ install: venv
 	${VENV}/bin/nodeenv --python-virtualenv --node=19.3.0
 	${VENV}/bin/npm install
 
+install-hooks:
+	pre-commit install --install-hooks
+
+hooks:
+	pre-commit run -a
 
 build: venv
 	${VENV}/bin/npm run build
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..93b1e17
--- /dev/null
+++ b/README.md
@@ -0,0 +1,39 @@
+# Contract registry
+
+The Czech Pirate Party's transparent evidence system for its contracts and
+information about them.
+
+## Basic requirements
+- `make`
+- `python`, 3.9 or newer
+- `python-virtualenv`
+- A PostgreSQL server
+
+## Setup
+
+Copy `env.example` to `.env`.
+
+Set the ``DATABASE_URL`` environment variable in `.env` to one you can access your database server with. The format should be  as per RFC 1738, such as `postgresql://login:password@localhost:5432/database_name`. It's also important to change the ``SECRET_KEY`` variable.
+
+Then, run the following commands:
+
+```bash
+make venv     # Create virtual environment
+make install  # Install Python and Node.js dependencies
+make build    # Build static files
+```
+
+## Running
+
+To run with the default settings on the port set in `Makefile`, run:
+
+```bash
+make run
+```
+
+For more customization, it's better practice to run the server starting command directly:
+
+```bash
+source .venv/bin/activate   # Load the virtual environment
+python manage.py runserver  # [ Your settings here ]
+```
diff --git a/manage.py b/manage.py
index 0e1442b..45b9a48 100755
--- a/manage.py
+++ b/manage.py
@@ -6,7 +6,7 @@ import sys
 
 def main():
     """Run administrative tasks."""
-    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'registry.settings')
+    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "registry.settings")
     try:
         from django.core.management import execute_from_command_line
     except ImportError as exc:
@@ -18,5 +18,5 @@ def main():
     execute_from_command_line(sys.argv)
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
diff --git a/registry/asgi.py b/registry/asgi.py
index e1d1af0..97f0093 100644
--- a/registry/asgi.py
+++ b/registry/asgi.py
@@ -11,6 +11,6 @@ import os
 
 from django.core.asgi import get_asgi_application
 
-os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'registry.settings')
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "registry.settings")
 
 application = get_asgi_application()
diff --git a/registry/settings/base.py b/registry/settings/base.py
index a7b7745..93c9200 100644
--- a/registry/settings/base.py
+++ b/registry/settings/base.py
@@ -13,8 +13,8 @@ https://docs.djangoproject.com/en/4.0/ref/settings/
 import os
 import pathlib
 
-import environ
 import dj_database_url
+import environ
 
 # Build paths inside the project like this: BASE_DIR / 'subdir'.
 BASE_DIR = pathlib.Path(__file__).parents[2]
@@ -45,7 +45,6 @@ INSTALLED_APPS = [
     "django.contrib.sessions",
     "django.contrib.messages",
     "django.contrib.staticfiles",
-
     "shared",
 ]
 
@@ -83,9 +82,7 @@ WSGI_APPLICATION = "registry.wsgi.application"
 # Database
 # https://docs.djangoproject.com/en/4.0/ref/settings/#databases
 
-DATABASES = {
-    "default": dj_database_url.config(conn_max_age=600)
-}
+DATABASES = {"default": dj_database_url.config(conn_max_age=600)}
 
 
 # Password validation
diff --git a/registry/urls.py b/registry/urls.py
index 13c37f7..417ef51 100644
--- a/registry/urls.py
+++ b/registry/urls.py
@@ -17,5 +17,5 @@ from django.contrib import admin
 from django.urls import path
 
 urlpatterns = [
-    path('admin/', admin.site.urls),
+    path("admin/", admin.site.urls),
 ]
diff --git a/registry/wsgi.py b/registry/wsgi.py
index 06fb229..bf93aed 100644
--- a/registry/wsgi.py
+++ b/registry/wsgi.py
@@ -11,6 +11,6 @@ import os
 
 from django.core.wsgi import get_wsgi_application
 
-os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'registry.settings')
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "registry.settings")
 
 application = get_wsgi_application()
diff --git a/requirements/base.txt b/requirements/base.txt
index 51af94f..9ebad7d 100644
--- a/requirements/base.txt
+++ b/requirements/base.txt
@@ -6,6 +6,6 @@ nodeenv==1.7.0
 pirates==0.6.0
 django-markdownx==4.0.0b1
 django-environ==0.9.0
-django-http-exceptions==1.4.0 
+django-http-exceptions==1.4.0
 django-guardian==2.4.0
 django-countries==7.5.1
diff --git a/shared/apps.py b/shared/apps.py
index 87efe00..5bfd568 100644
--- a/shared/apps.py
+++ b/shared/apps.py
@@ -2,5 +2,5 @@ from django.apps import AppConfig
 
 
 class SharedConfig(AppConfig):
-    default_auto_field = 'django.db.models.BigAutoField'
-    name = 'shared'
+    default_auto_field = "django.db.models.BigAutoField"
+    name = "shared"
diff --git a/shared/migrations/0001_initial.py b/shared/migrations/0001_initial.py
index cbeff1c..0d45e6a 100644
--- a/shared/migrations/0001_initial.py
+++ b/shared/migrations/0001_initial.py
@@ -1,199 +1,567 @@
 # Generated by Django 4.1.4 on 2023-02-03 04:50
 
-from django.conf import settings
-from django.db import migrations, models
 import django.db.models.deletion
 import django.utils.timezone
 import django_countries.fields
 import markdownx.models
+from django.conf import settings
+from django.db import migrations, models
 
 
 class Migration(migrations.Migration):
-
     initial = True
 
     dependencies = [
-        ('auth', '0012_alter_user_first_name_max_length'),
+        ("auth", "0012_alter_user_first_name_max_length"),
     ]
 
     operations = [
         migrations.CreateModel(
-            name='User',
+            name="User",
             fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
-                ('sso_id', models.CharField(error_messages={'unique': 'A user with that SSO ID already exists.'}, max_length=150, unique=True, verbose_name='SSO ID')),
-                ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
-                ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
-                ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
-                ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
-                ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
-                ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
-                ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
-                ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
+                (
+                    "id",
+                    models.BigAutoField(
+                        auto_created=True,
+                        primary_key=True,
+                        serialize=False,
+                        verbose_name="ID",
+                    ),
+                ),
+                (
+                    "is_superuser",
+                    models.BooleanField(
+                        default=False,
+                        help_text="Designates that this user has all permissions without explicitly assigning them.",
+                        verbose_name="superuser status",
+                    ),
+                ),
+                (
+                    "sso_id",
+                    models.CharField(
+                        error_messages={
+                            "unique": "A user with that SSO ID already exists."
+                        },
+                        max_length=150,
+                        unique=True,
+                        verbose_name="SSO ID",
+                    ),
+                ),
+                (
+                    "first_name",
+                    models.CharField(
+                        blank=True, max_length=150, verbose_name="first name"
+                    ),
+                ),
+                (
+                    "last_name",
+                    models.CharField(
+                        blank=True, max_length=150, verbose_name="last name"
+                    ),
+                ),
+                (
+                    "email",
+                    models.EmailField(
+                        blank=True, max_length=254, verbose_name="email address"
+                    ),
+                ),
+                (
+                    "is_staff",
+                    models.BooleanField(
+                        default=False,
+                        help_text="Designates whether the user can log into this admin site.",
+                        verbose_name="staff status",
+                    ),
+                ),
+                (
+                    "is_active",
+                    models.BooleanField(
+                        default=True,
+                        help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.",
+                        verbose_name="active",
+                    ),
+                ),
+                (
+                    "date_joined",
+                    models.DateTimeField(
+                        default=django.utils.timezone.now, verbose_name="date joined"
+                    ),
+                ),
+                (
+                    "groups",
+                    models.ManyToManyField(
+                        blank=True,
+                        help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.",
+                        related_name="user_set",
+                        related_query_name="user",
+                        to="auth.group",
+                        verbose_name="groups",
+                    ),
+                ),
+                (
+                    "user_permissions",
+                    models.ManyToManyField(
+                        blank=True,
+                        help_text="Specific permissions for this user.",
+                        related_name="user_set",
+                        related_query_name="user",
+                        to="auth.permission",
+                        verbose_name="user permissions",
+                    ),
+                ),
             ],
             options={
-                'verbose_name': 'user',
-                'verbose_name_plural': 'users',
-                'abstract': False,
+                "verbose_name": "user",
+                "verbose_name_plural": "users",
+                "abstract": False,
             },
         ),
         migrations.CreateModel(
-            name='Contract',
+            name="Contract",
             fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('type', models.CharField(choices=[('primary', 'Hlavní'), ('amendment', 'Dodatek'), ('framework_order', 'Objednávka u rámcové smlouvy')], default='primary', max_length=15, verbose_name='Typ')),
-                ('contains_nda', models.BooleanField(default=False, verbose_name='Obsahuje NDA')),
-                ('is_anonymized', models.BooleanField(default=False, verbose_name='Je anonymizovaná')),
-                ('external_signer_signature_date', models.DateField(verbose_name='Datum podpisu druhé strany')),
-                ('local_signer_signature_date', models.DateField(verbose_name='Datum podpisu naší strany')),
-                ('all_parties_sign_date', models.DateField(verbose_name='Datum podpisu všech stran')),
-                ('valid_start_date', models.DateField(verbose_name='Začátek účinnosti')),
-                ('valid_end_date', models.DateField(verbose_name='Začátek platnosti')),
-                ('legal_state', models.CharField(choices=[('valid', 'Platná'), ('effective', 'Účinná'), ('not_effective', 'Neúčinná'), ('invalid', 'Neplatná')], max_length=13, verbose_name='Stav právního ujednání')),
-                ('public_state', models.CharField(choices=[('unknown', 'Nová'), ('yes', 'Zveřejněná'), ('no', 'Neveřejná')], max_length=7, verbose_name='Veřejnost smlouvy')),
-                ('paper_form_state', models.CharField(choices=[('sent', 'Odeslaná'), ('stored', 'Uložená'), ('to_shred', 'Ke skartaci'), ('shredded', 'Skartovaná')], max_length=8, verbose_name='Stav papírové formy')),
-                ('publishing_rejection_comment', models.CharField(blank=True, max_length=65536, null=True, verbose_name='Důvod nezveřejnění')),
-                ('tender_url', models.URLField(blank=True, max_length=256, null=True, verbose_name='Odkaz na výběrové řízení')),
-                ('identifier', models.CharField(max_length=128, verbose_name='Identifikační číslo')),
-                ('summary', models.CharField(blank=True, max_length=65536, null=True, verbose_name='Rekapitulace')),
-                ('contract_file', models.FileField(upload_to='', verbose_name='Smlouva (PDF)')),
-                ('expected_cost_total', models.IntegerField(verbose_name='Očekáváná celková cena')),
-                ('expected_cost_year', models.IntegerField(verbose_name='Očekáváná cena za rok')),
-                ('expected_cost_month', models.IntegerField(verbose_name='Očekáváná cena za měsíc')),
-                ('expected_cost_hour', models.IntegerField(verbose_name='Očekáváná cena za hodinu')),
-                ('intent_url', models.URLField(blank=True, max_length=256, null=True, verbose_name='Odkaz na záměr')),
-                ('agreement_url', models.URLField(blank=True, max_length=256, null=True, verbose_name='Odkaz na schválení')),
+                (
+                    "id",
+                    models.BigAutoField(
+                        auto_created=True,
+                        primary_key=True,
+                        serialize=False,
+                        verbose_name="ID",
+                    ),
+                ),
+                (
+                    "type",
+                    models.CharField(
+                        choices=[
+                            ("primary", "Hlavní"),
+                            ("amendment", "Dodatek"),
+                            ("framework_order", "Objednávka u rámcové smlouvy"),
+                        ],
+                        default="primary",
+                        max_length=15,
+                        verbose_name="Typ",
+                    ),
+                ),
+                (
+                    "contains_nda",
+                    models.BooleanField(default=False, verbose_name="Obsahuje NDA"),
+                ),
+                (
+                    "is_anonymized",
+                    models.BooleanField(default=False, verbose_name="Je anonymizovaná"),
+                ),
+                (
+                    "external_signer_signature_date",
+                    models.DateField(verbose_name="Datum podpisu druhé strany"),
+                ),
+                (
+                    "local_signer_signature_date",
+                    models.DateField(verbose_name="Datum podpisu naší strany"),
+                ),
+                (
+                    "all_parties_sign_date",
+                    models.DateField(verbose_name="Datum podpisu všech stran"),
+                ),
+                (
+                    "valid_start_date",
+                    models.DateField(verbose_name="Začátek účinnosti"),
+                ),
+                ("valid_end_date", models.DateField(verbose_name="Začátek platnosti")),
+                (
+                    "legal_state",
+                    models.CharField(
+                        choices=[
+                            ("valid", "Platná"),
+                            ("effective", "Účinná"),
+                            ("not_effective", "Neúčinná"),
+                            ("invalid", "Neplatná"),
+                        ],
+                        max_length=13,
+                        verbose_name="Stav právního ujednání",
+                    ),
+                ),
+                (
+                    "public_state",
+                    models.CharField(
+                        choices=[
+                            ("unknown", "Nová"),
+                            ("yes", "Zveřejněná"),
+                            ("no", "Neveřejná"),
+                        ],
+                        max_length=7,
+                        verbose_name="Veřejnost smlouvy",
+                    ),
+                ),
+                (
+                    "paper_form_state",
+                    models.CharField(
+                        choices=[
+                            ("sent", "Odeslaná"),
+                            ("stored", "Uložená"),
+                            ("to_shred", "Ke skartaci"),
+                            ("shredded", "Skartovaná"),
+                        ],
+                        max_length=8,
+                        verbose_name="Stav papírové formy",
+                    ),
+                ),
+                (
+                    "publishing_rejection_comment",
+                    models.CharField(
+                        blank=True,
+                        max_length=65536,
+                        null=True,
+                        verbose_name="Důvod nezveřejnění",
+                    ),
+                ),
+                (
+                    "tender_url",
+                    models.URLField(
+                        blank=True,
+                        max_length=256,
+                        null=True,
+                        verbose_name="Odkaz na výběrové řízení",
+                    ),
+                ),
+                (
+                    "identifier",
+                    models.CharField(
+                        max_length=128, verbose_name="Identifikační číslo"
+                    ),
+                ),
+                (
+                    "summary",
+                    models.CharField(
+                        blank=True,
+                        max_length=65536,
+                        null=True,
+                        verbose_name="Rekapitulace",
+                    ),
+                ),
+                (
+                    "contract_file",
+                    models.FileField(upload_to="", verbose_name="Smlouva (PDF)"),
+                ),
+                (
+                    "expected_cost_total",
+                    models.IntegerField(verbose_name="Očekáváná celková cena"),
+                ),
+                (
+                    "expected_cost_year",
+                    models.IntegerField(verbose_name="Očekáváná cena za rok"),
+                ),
+                (
+                    "expected_cost_month",
+                    models.IntegerField(verbose_name="Očekáváná cena za měsíc"),
+                ),
+                (
+                    "expected_cost_hour",
+                    models.IntegerField(verbose_name="Očekáváná cena za hodinu"),
+                ),
+                (
+                    "intent_url",
+                    models.URLField(
+                        blank=True,
+                        max_length=256,
+                        null=True,
+                        verbose_name="Odkaz na záměr",
+                    ),
+                ),
+                (
+                    "agreement_url",
+                    models.URLField(
+                        blank=True,
+                        max_length=256,
+                        null=True,
+                        verbose_name="Odkaz na schválení",
+                    ),
+                ),
             ],
             options={
-                'verbose_name': 'Smlouva',
-                'verbose_name_plural': 'Smlouvy',
+                "verbose_name": "Smlouva",
+                "verbose_name_plural": "Smlouvy",
             },
         ),
         migrations.CreateModel(
-            name='ContractExternalSigner',
+            name="ContractExternalSigner",
             fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('name', models.CharField(max_length=256, verbose_name='Jméno')),
-                ('is_legal_entity', models.BooleanField(verbose_name='Je právnická osoba')),
-                ('address_street_with_number', models.CharField(max_length=256, verbose_name='Ulice, č.p.')),
-                ('address_district', models.CharField(max_length=256, verbose_name='Obec')),
-                ('address_zip', models.CharField(max_length=16, verbose_name='PSČ')),
-                ('address_country', django_countries.fields.CountryField(max_length=2, verbose_name='Země')),
-                ('ico_number', models.CharField(blank=True, max_length=16, null=True, verbose_name='IČO')),
-                ('date_of_birth', models.DateField(blank=True, null=True, verbose_name='Datum narození')),
-                ('representative_name', models.CharField(blank=True, max_length=256, null=True, verbose_name='Zástupce')),
-                ('representative_role', models.CharField(blank=True, max_length=256, null=True, verbose_name='Funkce zástupce')),
-                ('department', models.CharField(blank=True, max_length=128, null=True, verbose_name='Organizační složka')),
+                (
+                    "id",
+                    models.BigAutoField(
+                        auto_created=True,
+                        primary_key=True,
+                        serialize=False,
+                        verbose_name="ID",
+                    ),
+                ),
+                ("name", models.CharField(max_length=256, verbose_name="Jméno")),
+                (
+                    "is_legal_entity",
+                    models.BooleanField(verbose_name="Je právnická osoba"),
+                ),
+                (
+                    "address_street_with_number",
+                    models.CharField(max_length=256, verbose_name="Ulice, č.p."),
+                ),
+                (
+                    "address_district",
+                    models.CharField(max_length=256, verbose_name="Obec"),
+                ),
+                ("address_zip", models.CharField(max_length=16, verbose_name="PSČ")),
+                (
+                    "address_country",
+                    django_countries.fields.CountryField(
+                        max_length=2, verbose_name="Země"
+                    ),
+                ),
+                (
+                    "ico_number",
+                    models.CharField(
+                        blank=True, max_length=16, null=True, verbose_name="IČO"
+                    ),
+                ),
+                (
+                    "date_of_birth",
+                    models.DateField(
+                        blank=True, null=True, verbose_name="Datum narození"
+                    ),
+                ),
+                (
+                    "representative_name",
+                    models.CharField(
+                        blank=True, max_length=256, null=True, verbose_name="Zástupce"
+                    ),
+                ),
+                (
+                    "representative_role",
+                    models.CharField(
+                        blank=True,
+                        max_length=256,
+                        null=True,
+                        verbose_name="Funkce zástupce",
+                    ),
+                ),
+                (
+                    "department",
+                    models.CharField(
+                        blank=True,
+                        max_length=128,
+                        null=True,
+                        verbose_name="Organizační složka",
+                    ),
+                ),
             ],
             options={
-                'verbose_name': 'Druhá smluvní strana',
-                'verbose_name_plural': 'Druhé smluvní strany',
+                "verbose_name": "Druhá smluvní strana",
+                "verbose_name_plural": "Druhé smluvní strany",
             },
         ),
         migrations.CreateModel(
-            name='ContractFilingArea',
+            name="ContractFilingArea",
             fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('name', models.CharField(max_length=32, verbose_name='Jméno')),
-                ('person_responsible', models.CharField(max_length=256, verbose_name='Odpovědná osoba')),
+                (
+                    "id",
+                    models.BigAutoField(
+                        auto_created=True,
+                        primary_key=True,
+                        serialize=False,
+                        verbose_name="ID",
+                    ),
+                ),
+                ("name", models.CharField(max_length=32, verbose_name="Jméno")),
+                (
+                    "person_responsible",
+                    models.CharField(max_length=256, verbose_name="Odpovědná osoba"),
+                ),
             ],
             options={
-                'verbose_name': 'Spisovna',
-                'verbose_name_plural': 'Spisovny',
+                "verbose_name": "Spisovna",
+                "verbose_name_plural": "Spisovny",
             },
         ),
         migrations.CreateModel(
-            name='ContractIssue',
+            name="ContractIssue",
             fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('name', models.CharField(max_length=32, verbose_name='Jméno')),
+                (
+                    "id",
+                    models.BigAutoField(
+                        auto_created=True,
+                        primary_key=True,
+                        serialize=False,
+                        verbose_name="ID",
+                    ),
+                ),
+                ("name", models.CharField(max_length=32, verbose_name="Jméno")),
             ],
             options={
-                'verbose_name': 'Problém se smlouvou',
-                'verbose_name_plural': 'Problémy se smlouvou',
+                "verbose_name": "Problém se smlouvou",
+                "verbose_name_plural": "Problémy se smlouvou",
             },
         ),
         migrations.CreateModel(
-            name='ContractLocalSigner',
+            name="ContractLocalSigner",
             fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('name', models.CharField(max_length=256, verbose_name='Jméno')),
-                ('address_street_with_number', models.CharField(max_length=256, verbose_name='Ulice, č.p.')),
-                ('address_district', models.CharField(max_length=256, verbose_name='Obec')),
-                ('address_zip', models.CharField(max_length=16, verbose_name='PSČ')),
-                ('address_country', django_countries.fields.CountryField(max_length=2, verbose_name='Země')),
-                ('ico_number', models.CharField(blank=True, max_length=16, null=True, verbose_name='IČO')),
-                ('color', models.CharField(max_length=6, verbose_name='Barva')),
+                (
+                    "id",
+                    models.BigAutoField(
+                        auto_created=True,
+                        primary_key=True,
+                        serialize=False,
+                        verbose_name="ID",
+                    ),
+                ),
+                ("name", models.CharField(max_length=256, verbose_name="Jméno")),
+                (
+                    "address_street_with_number",
+                    models.CharField(max_length=256, verbose_name="Ulice, č.p."),
+                ),
+                (
+                    "address_district",
+                    models.CharField(max_length=256, verbose_name="Obec"),
+                ),
+                ("address_zip", models.CharField(max_length=16, verbose_name="PSČ")),
+                (
+                    "address_country",
+                    django_countries.fields.CountryField(
+                        max_length=2, verbose_name="Země"
+                    ),
+                ),
+                (
+                    "ico_number",
+                    models.CharField(
+                        blank=True, max_length=16, null=True, verbose_name="IČO"
+                    ),
+                ),
+                ("color", models.CharField(max_length=6, verbose_name="Barva")),
             ],
             options={
-                'verbose_name': 'Naše smluvní strana',
-                'verbose_name_plural': 'Naše smlouvní strany',
+                "verbose_name": "Naše smluvní strana",
+                "verbose_name_plural": "Naše smlouvní strany",
             },
         ),
         migrations.CreateModel(
-            name='ContractSubtype',
+            name="ContractSubtype",
             fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('name', models.CharField(max_length=32, verbose_name='Jméno')),
+                (
+                    "id",
+                    models.BigAutoField(
+                        auto_created=True,
+                        primary_key=True,
+                        serialize=False,
+                        verbose_name="ID",
+                    ),
+                ),
+                ("name", models.CharField(max_length=32, verbose_name="Jméno")),
             ],
             options={
-                'verbose_name': 'Podtyp smlouvy',
-                'verbose_name_plural': 'Podtypy smlouvy',
+                "verbose_name": "Podtyp smlouvy",
+                "verbose_name_plural": "Podtypy smlouvy",
             },
         ),
         migrations.CreateModel(
-            name='ContractNote',
+            name="ContractNote",
             fields=[
-                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('created_date', models.DateTimeField(verbose_name='Datum vytvoření')),
-                ('content', markdownx.models.MarkdownxField(verbose_name='Obsah')),
-                ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='contract_notes', to=settings.AUTH_USER_MODEL, verbose_name='Autor')),
-                ('contract', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shared.contract')),
+                (
+                    "id",
+                    models.BigAutoField(
+                        auto_created=True,
+                        primary_key=True,
+                        serialize=False,
+                        verbose_name="ID",
+                    ),
+                ),
+                ("created_date", models.DateTimeField(verbose_name="Datum vytvoření")),
+                ("content", markdownx.models.MarkdownxField(verbose_name="Obsah")),
+                (
+                    "author",
+                    models.ForeignKey(
+                        on_delete=django.db.models.deletion.CASCADE,
+                        related_name="contract_notes",
+                        to=settings.AUTH_USER_MODEL,
+                        verbose_name="Autor",
+                    ),
+                ),
+                (
+                    "contract",
+                    models.ForeignKey(
+                        on_delete=django.db.models.deletion.CASCADE,
+                        to="shared.contract",
+                    ),
+                ),
             ],
             options={
-                'verbose_name': 'Poznámka ke smlouvě',
-                'verbose_name_plural': 'Poznámky ke smlouvě',
+                "verbose_name": "Poznámka ke smlouvě",
+                "verbose_name_plural": "Poznámky ke smlouvě",
             },
         ),
         migrations.AddField(
-            model_name='contract',
-            name='external_signer',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shared.contractexternalsigner'),
+            model_name="contract",
+            name="external_signer",
+            field=models.ForeignKey(
+                on_delete=django.db.models.deletion.CASCADE,
+                to="shared.contractexternalsigner",
+            ),
         ),
         migrations.AddField(
-            model_name='contract',
-            name='filing_area',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='shared.contractfilingarea'),
+            model_name="contract",
+            name="filing_area",
+            field=models.ForeignKey(
+                blank=True,
+                null=True,
+                on_delete=django.db.models.deletion.CASCADE,
+                to="shared.contractfilingarea",
+            ),
         ),
         migrations.AddField(
-            model_name='contract',
-            name='issues',
-            field=models.ManyToManyField(to='shared.contractissue'),
+            model_name="contract",
+            name="issues",
+            field=models.ManyToManyField(to="shared.contractissue"),
         ),
         migrations.AddField(
-            model_name='contract',
-            name='local_signer',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shared.contractlocalsigner'),
+            model_name="contract",
+            name="local_signer",
+            field=models.ForeignKey(
+                on_delete=django.db.models.deletion.CASCADE,
+                to="shared.contractlocalsigner",
+            ),
         ),
         migrations.AddField(
-            model_name='contract',
-            name='primary_contract',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='shared.contract', verbose_name='Hlavní smlouva'),
+            model_name="contract",
+            name="primary_contract",
+            field=models.ForeignKey(
+                blank=True,
+                null=True,
+                on_delete=django.db.models.deletion.CASCADE,
+                to="shared.contract",
+                verbose_name="Hlavní smlouva",
+            ),
         ),
         migrations.AddField(
-            model_name='contract',
-            name='public_status_set_by',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='public_status_altered_contracts', to=settings.AUTH_USER_MODEL, verbose_name='Zveřejněno / nezveřejněno uživatelem'),
+            model_name="contract",
+            name="public_status_set_by",
+            field=models.ForeignKey(
+                on_delete=django.db.models.deletion.CASCADE,
+                related_name="public_status_altered_contracts",
+                to=settings.AUTH_USER_MODEL,
+                verbose_name="Zveřejněno / nezveřejněno uživatelem",
+            ),
         ),
         migrations.AddField(
-            model_name='contract',
-            name='subtype',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shared.contractsubtype', verbose_name='Podtyp'),
+            model_name="contract",
+            name="subtype",
+            field=models.ForeignKey(
+                on_delete=django.db.models.deletion.CASCADE,
+                to="shared.contractsubtype",
+                verbose_name="Podtyp",
+            ),
         ),
         migrations.AddField(
-            model_name='contract',
-            name='uploaded_by',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='uploaded_contracts', to=settings.AUTH_USER_MODEL, verbose_name='Nahráno uživatelem'),
+            model_name="contract",
+            name="uploaded_by",
+            field=models.ForeignKey(
+                on_delete=django.db.models.deletion.CASCADE,
+                related_name="uploaded_contracts",
+                to=settings.AUTH_USER_MODEL,
+                verbose_name="Nahráno uživatelem",
+            ),
         ),
     ]
diff --git a/shared/migrations/0002_contractlocalsigner_department_and_more.py b/shared/migrations/0002_contractlocalsigner_department_and_more.py
index 83f3534..010d1a2 100644
--- a/shared/migrations/0002_contractlocalsigner_department_and_more.py
+++ b/shared/migrations/0002_contractlocalsigner_department_and_more.py
@@ -1,59 +1,78 @@
 # Generated by Django 4.1.4 on 2023-02-03 05:01
 
-from django.db import migrations, models
 import django_countries.fields
+from django.db import migrations, models
 
 
 class Migration(migrations.Migration):
-
     dependencies = [
-        ('shared', '0001_initial'),
+        ("shared", "0001_initial"),
     ]
 
     operations = [
         migrations.AddField(
-            model_name='contractlocalsigner',
-            name='department',
-            field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Organizační složka'),
+            model_name="contractlocalsigner",
+            name="department",
+            field=models.CharField(
+                blank=True, max_length=128, null=True, verbose_name="Organizační složka"
+            ),
         ),
         migrations.AddField(
-            model_name='contractlocalsigner',
-            name='representative_name',
-            field=models.CharField(blank=True, max_length=256, null=True, verbose_name='Zástupce'),
+            model_name="contractlocalsigner",
+            name="representative_name",
+            field=models.CharField(
+                blank=True, max_length=256, null=True, verbose_name="Zástupce"
+            ),
         ),
         migrations.AddField(
-            model_name='contractlocalsigner',
-            name='representative_role',
-            field=models.CharField(blank=True, max_length=256, null=True, verbose_name='Funkce zástupce'),
+            model_name="contractlocalsigner",
+            name="representative_role",
+            field=models.CharField(
+                blank=True, max_length=256, null=True, verbose_name="Funkce zástupce"
+            ),
         ),
         migrations.AlterField(
-            model_name='contractlocalsigner',
-            name='address_country',
-            field=django_countries.fields.CountryField(default='CZ', max_length=2, verbose_name='Země'),
+            model_name="contractlocalsigner",
+            name="address_country",
+            field=django_countries.fields.CountryField(
+                default="CZ", max_length=2, verbose_name="Země"
+            ),
         ),
         migrations.AlterField(
-            model_name='contractlocalsigner',
-            name='address_district',
-            field=models.CharField(default='Praha 2', max_length=256, verbose_name='Obec'),
+            model_name="contractlocalsigner",
+            name="address_district",
+            field=models.CharField(
+                default="Praha 2", max_length=256, verbose_name="Obec"
+            ),
         ),
         migrations.AlterField(
-            model_name='contractlocalsigner',
-            name='address_street_with_number',
-            field=models.CharField(default='Na Moráni 360/3', max_length=256, verbose_name='Ulice, č.p.'),
+            model_name="contractlocalsigner",
+            name="address_street_with_number",
+            field=models.CharField(
+                default="Na Moráni 360/3", max_length=256, verbose_name="Ulice, č.p."
+            ),
         ),
         migrations.AlterField(
-            model_name='contractlocalsigner',
-            name='address_zip',
-            field=models.CharField(default='128 00', max_length=16, verbose_name='PSČ'),
+            model_name="contractlocalsigner",
+            name="address_zip",
+            field=models.CharField(default="128 00", max_length=16, verbose_name="PSČ"),
         ),
         migrations.AlterField(
-            model_name='contractlocalsigner',
-            name='ico_number',
-            field=models.CharField(blank=True, default='71339698', max_length=16, null=True, verbose_name='IČO'),
+            model_name="contractlocalsigner",
+            name="ico_number",
+            field=models.CharField(
+                blank=True,
+                default="71339698",
+                max_length=16,
+                null=True,
+                verbose_name="IČO",
+            ),
         ),
         migrations.AlterField(
-            model_name='contractlocalsigner',
-            name='name',
-            field=models.CharField(default='Česká pirátská strana', max_length=256, verbose_name='Jméno'),
+            model_name="contractlocalsigner",
+            name="name",
+            field=models.CharField(
+                default="Česká pirátská strana", max_length=256, verbose_name="Jméno"
+            ),
         ),
     ]
diff --git a/shared/models.py b/shared/models.py
index c1dd879..6e90b49 100644
--- a/shared/models.py
+++ b/shared/models.py
@@ -1,6 +1,5 @@
 from django.conf import settings
 from django.db import models
-
 from django_countries.fields import CountryField
 from markdownx.models import MarkdownxField
 from pirates import models as pirates_models
@@ -16,12 +15,10 @@ class ContractExternalSigner(models.Model):
         verbose_name="Jméno",
     )
 
-
     is_legal_entity = models.BooleanField(
         verbose_name="Je právnická osoba",
     )
 
-
     address_street_with_number = models.CharField(
         max_length=256,
         verbose_name="Ulice, č.p.",
@@ -41,7 +38,6 @@ class ContractExternalSigner(models.Model):
         verbose_name="Země",
     )
 
-
     ico_number = models.CharField(
         max_length=16,
         blank=True,
@@ -55,7 +51,6 @@ class ContractExternalSigner(models.Model):
         verbose_name="Datum narození",
     )  # WARNING: Legal entity status dependent!
 
-
     representative_name = models.CharField(
         max_length=256,
         blank=True,
@@ -70,7 +65,6 @@ class ContractExternalSigner(models.Model):
         verbose_name="Funkce zástupce",
     )
 
-
     department = models.CharField(
         max_length=128,
         blank=True,
@@ -78,7 +72,6 @@ class ContractExternalSigner(models.Model):
         verbose_name="Organizační složka",
     )
 
-
     class Meta:
         verbose_name = "Druhá smluvní strana"
         verbose_name_plural = "Druhé smluvní strany"
@@ -91,7 +84,6 @@ class ContractLocalSigner(models.Model):
         verbose_name="Jméno",
     )
 
-
     address_street_with_number = models.CharField(
         max_length=256,
         default=settings.DEFAULT_LCOAL_SIGNER_STREET,
@@ -115,7 +107,6 @@ class ContractLocalSigner(models.Model):
         verbose_name="Země",
     )
 
-
     ico_number = models.CharField(
         max_length=16,
         blank=True,
@@ -124,7 +115,6 @@ class ContractLocalSigner(models.Model):
         verbose_name="IČO",
     )
 
-
     representative_name = models.CharField(
         max_length=256,
         blank=True,
@@ -139,7 +129,6 @@ class ContractLocalSigner(models.Model):
         verbose_name="Funkce zástupce",
     )
 
-
     department = models.CharField(
         max_length=128,
         blank=True,
@@ -147,14 +136,12 @@ class ContractLocalSigner(models.Model):
         verbose_name="Organizační složka",
     )
 
-
     # TODO: Input validation
     color = models.CharField(
-        max_length=6, # e.g. "ffffff"
+        max_length=6,  # e.g. "ffffff"
         verbose_name="Barva",
     )
 
-
     class Meta:
         verbose_name = "Naše smluvní strana"
         verbose_name_plural = "Naše smlouvní strany"
@@ -166,7 +153,6 @@ class ContractSubtype(models.Model):
         verbose_name="Jméno",
     )
 
-
     class Meta:
         verbose_name = "Podtyp smlouvy"
         verbose_name_plural = "Podtypy smlouvy"
@@ -178,7 +164,6 @@ class ContractIssue(models.Model):
         verbose_name="Jméno",
     )
 
-
     class Meta:
         verbose_name = "Problém se smlouvou"
         verbose_name_plural = "Problémy se smlouvou"
@@ -195,7 +180,6 @@ class ContractFilingArea(models.Model):
         verbose_name="Odpovědná osoba",
     )
 
-
     class Meta:
         verbose_name = "Spisovna"
         verbose_name_plural = "Spisovny"
@@ -220,7 +204,6 @@ class Contract(models.Model):
         verbose_name="Podtyp",
     )
 
-
     contains_nda = models.BooleanField(
         default=False,
         verbose_name="Obsahuje NDA",
@@ -231,7 +214,6 @@ class Contract(models.Model):
         verbose_name="Je anonymizovaná",
     )  # WARNING: Seems to only be used for amendments
 
-
     external_signer = models.ForeignKey(
         ContractExternalSigner,
         on_delete=models.CASCADE,
@@ -255,15 +237,11 @@ class Contract(models.Model):
         verbose_name="Datum podpisu všech stran",
     )  # WARNING: Exclude in admin, autofill
 
-
-    valid_start_date = models.DateField(
-        verbose_name="Začátek účinnosti"
-    )
+    valid_start_date = models.DateField(verbose_name="Začátek účinnosti")
     valid_end_date = models.DateField(
         verbose_name="Začátek platnosti",
     )
 
-
     uploaded_by = models.ForeignKey(
         User,
         on_delete=models.CASCADE,
@@ -271,7 +249,6 @@ class Contract(models.Model):
         verbose_name="Nahráno uživatelem",
     )
 
-
     class LegalStates(models.TextChoices):
         VALID = "valid", "Platná"
         EFFECTIVE = "effective", "Účinná"
@@ -307,7 +284,6 @@ class Contract(models.Model):
         verbose_name="Stav papírové formy",
     )
 
-
     public_status_set_by = models.ForeignKey(
         User,
         on_delete=models.CASCADE,
@@ -315,7 +291,6 @@ class Contract(models.Model):
         verbose_name="Zveřejněno / nezveřejněno uživatelem",
     )
 
-
     publishing_rejection_comment = models.CharField(
         max_length=65536,
         blank=True,
@@ -323,7 +298,6 @@ class Contract(models.Model):
         verbose_name="Důvod nezveřejnění",
     )
 
-
     tender_url = models.URLField(
         max_length=256,
         blank=True,
@@ -331,16 +305,13 @@ class Contract(models.Model):
         verbose_name="Odkaz na výběrové řízení",
     )
 
-
     identifier = models.CharField(
         max_length=128,
         verbose_name="Identifikační číslo",
     )
 
-
     issues = models.ManyToManyField(ContractIssue)
 
-
     summary = models.CharField(
         max_length=65536,
         blank=True,
@@ -348,7 +319,6 @@ class Contract(models.Model):
         verbose_name="Rekapitulace",
     )
 
-
     contract_file = models.FileField(
         verbose_name="Smlouva (PDF)",
     )
@@ -361,23 +331,13 @@ class Contract(models.Model):
         verbose_name="Hlavní smlouva",
     )  # WARNING: Dependent on the type!
 
+    expected_cost_total = models.IntegerField(verbose_name="Očekáváná celková cena")
 
-    expected_cost_total = models.IntegerField(
-        verbose_name="Očekáváná celková cena"
-    )
+    expected_cost_year = models.IntegerField(verbose_name="Očekáváná cena za rok")
 
-    expected_cost_year = models.IntegerField(
-        verbose_name="Očekáváná cena za rok"
-    )
-
-    expected_cost_month = models.IntegerField(
-        verbose_name="Očekáváná cena za měsíc"
-    )
-
-    expected_cost_hour = models.IntegerField(
-        verbose_name="Očekáváná cena za hodinu"
-    )
+    expected_cost_month = models.IntegerField(verbose_name="Očekáváná cena za měsíc")
 
+    expected_cost_hour = models.IntegerField(verbose_name="Očekáváná cena za hodinu")
 
     intent_url = models.URLField(
         max_length=256,
@@ -393,7 +353,6 @@ class Contract(models.Model):
         verbose_name="Odkaz na schválení",
     )  # WARNING: Dependent on the type!
 
-
     filing_area = models.ForeignKey(
         ContractFilingArea,
         on_delete=models.CASCADE,
@@ -401,7 +360,6 @@ class Contract(models.Model):
         null=True,
     )  # WARNING: Dependent on the type!
 
-
     class Meta:
         verbose_name = "Smlouva"
         verbose_name_plural = "Smlouvy"
@@ -413,7 +371,6 @@ class ContractNote(models.Model):
         on_delete=models.CASCADE,
     )
 
-
     author = models.ForeignKey(
         User,
         related_name="contract_notes",
@@ -429,7 +386,6 @@ class ContractNote(models.Model):
         verbose_name="Obsah",
     )
 
-
     class Meta:
         verbose_name = "Poznámka ke smlouvě"
         verbose_name_plural = "Poznámky ke smlouvě"
-- 
GitLab