diff --git a/contracts/admin.py b/contracts/admin.py index e020611ab798557099a241de696b2aae3c94aea1..264fb42788d820632c060a502af93b91e4ecc925 100644 --- a/contracts/admin.py +++ b/contracts/admin.py @@ -1,6 +1,7 @@ import copy import typing +from admin_auto_filters.filters import AutocompleteFilterFactory from django.contrib import admin from django.contrib.auth.models import Permission from django.db import models @@ -168,11 +169,6 @@ class ContractAdmin( ): form = ContractAdminForm - ordering = ( - "-created_on", - "-updated_on", - "-name", - ) search_fields = ("name",) readonly_fields = ( @@ -364,12 +360,15 @@ class ContractAdmin( return super().has_change_permission(request, obj) list_filter = ( - "types", + AutocompleteFilterFactory("Typ", "types"), + AutocompleteFilterFactory("Spisovna", "filing_area"), + AutocompleteFilterFactory("Problém", "issues"), + AutocompleteFilterFactory("Naše smluvná strana", "contractee_signatures__contractee"), + AutocompleteFilterFactory("Jiná smluvní strana", "signee_signatures__signee"), "is_approved", "is_valid", "is_public", "paper_form_state", - "issues", ("all_parties_sign_date", DateRangeFilter), ("valid_start_date", DateRangeFilter), ("valid_end_date", DateRangeFilter), @@ -385,19 +384,16 @@ class ContractAdmin( class ContractTypeAdmin(MarkdownxGuardedModelAdmin): model = ContractType - ordering = ("name",) search_fields = ("name",) class ContractIssueAdmin(MarkdownxGuardedModelAdmin): model = ContractIssue - ordering = ("name",) search_fields = ("name",) class ContractFilingAreaAdmin(MarkdownxGuardedModelAdmin): model = ContractFilingArea - ordering = ("name",) search_fields = ( "name", "person_responsible", @@ -425,7 +421,6 @@ class ContracteeAdmin(OwnPermissionsMixin, MarkdownxGuardedModelAdmin): "name", "department", ) - ordering = ("name",) class SigneeAdmin(OwnPermissionsMixin, MarkdownxGuardedModelAdmin): @@ -446,7 +441,6 @@ class SigneeAdmin(OwnPermissionsMixin, MarkdownxGuardedModelAdmin): "name", "department", ) - ordering = ("name",) form = SigneeAdminForm diff --git a/contracts/management/commands/import_old_contracts.py b/contracts/management/commands/import_old_contracts.py index 7a9aa42d8b8feeb7735fea25ba6918fe865abf6d..74c4a69d44ac3880adf75d1be44e2ff8587e90f2 100644 --- a/contracts/management/commands/import_old_contracts.py +++ b/contracts/management/commands/import_old_contracts.py @@ -69,6 +69,10 @@ class Command(BaseCommand): type_name = string.capwords(type_name) patterns = ( + ( + r"\s\s+", + " " + ), (r" O ", " o "), (r" S ", " s "), (r" K ", " k "), @@ -140,10 +144,157 @@ class Command(BaseCommand): return type_name + def normalize_department(self, type_name: str) -> str: + type_name = type_name.strip() + + patterns = ( + ( + r"\s\s+", + " " + ), + ( + r"^Kraské sdružení Praha$", + "Krajské sdružení Praha" + ), + ( + r"^republikové předsednictvo$", + "Republikové předsednictvo" + ), + ( + r"^KS ", + "Krajské sdružení " + ), + ( + r"^(MS |místní sdružení )", + "Místní sdružení " + ), + ( + r"^(Ústecký kraj|Ustecký kraj)$", + "Krajské sdružení Ústecký kraj" + ), + ( + r"^Moravskoslezský kraj$", + "Krajské sdružení Moravskoslezský kraj", + ), + ( + r"^Karlovarský kraj$", + "Krajské sdružení Karlovarský kraj", + ), + ( + r"Jihočeská kraj", + "Jihočeský kraj", + ), + ( + r"^(Krajského |krajské |Kajské )", + "Krajské " + ), + ( + "^Poslanecký klub České pirátské strany$", + "Poslanecký klub" + ), + ( + r"Středočeký", + "Středočeský" + ), + ( + r"^Zahraničního odboru$", + "Zahraniční odbor" + ), + ( + r"JčK", + "Jihočeský kraj" + ), + ( + r"SčK", + "Středočeský kraj" + ), + ( + r"(ÚsK|UsK)", + "Ústecký kraj" + ), + ( + r"JmK", + "Jihomoravský kraj" + ), + ( + r"PaK", + "Pardubický kraj" + ), + ( + r"KhK", + "Královehradecký kraj" + ), + ( + r"(Prsonální|personální)", + "Personální" + ), + ( + r"^administrativní ", + "Administrativní " + ), + ( + r"technický", + "Technický" + ), + ( + r"Mediálni", + "Technický" + ), + ( + r"^řešitel ", + "Řešitel " + ), + ( + r"^předsednictvo ", + "Předsednictvo " + ), + ( + r"olomoucký", + "Olomoucký" + ), + ( + r"^místní ", + "Místní " + ), + ( + r"^celostátní ", + "Celostátní " + ), + ( + r"odbor", + "Odbor" + ), + ( + r"PKS", + "Předsednictvo krajského sdružení" + ), + ( + r"( KS | Krajského sdružení )", + " krajského sdružení " + ), + ( + r"^(Předsednictvo krajského sdružení |Předsednictvo |Místní předsednictvo )", + "" + ), + ( + r"ého kraje$", + "ý kraj" + ) + ) + + for pattern in patterns: + type_name = re.sub(pattern[0], pattern[1], type_name) + + return type_name + def normalize_filing_area(self, area_name: str) -> str: area_name = string.capwords(area_name) patterns = ( + ( + r"\s\s+", + " " + ), ( r"^(Cenrální Spisovna|Censtrální Spisovna|Centrála|Centrálách Archiv Str" r"any|Centrála Strany|Centrální Achiv Strany|Centrální Archiv|Centralni " @@ -303,6 +454,10 @@ class Command(BaseCommand): ).strip() patterns = ( + ( + r"\s\s+", + " " + ), ( r"^(1\. Pirátská s\.r\.o|1\.Pirátská s\.r\.o\.)$", "1. Pirátská s.r.o." @@ -322,7 +477,8 @@ class Command(BaseCommand): ( ( r"^(Křesťanská a demokratická unie – Československá strana lidová|" - r"Křesťansko demokratická unie – Československá strana lidová)$" + r"Křesťansko demokratická unie – Československá strana lidová|Křes" + r"ťanská a demokratická unie - Československá strana lidová)$" ), "Křesťanská a demokratická unie – Československá strana lidová" ), @@ -369,6 +525,34 @@ class Command(BaseCommand): ( r" (KUdláčková|Kudlláčková)$", " Kudláčková" + ), + ( + r"^Jiří knotek$", + "Jiří Knotek" + ), + ( + r"^JIří Roubíček$", + "Jiří Roubíček" + ), + ( + r"^Koalice Vlasta\. z\.s\.$", + "Koalice Vlasta, z.s." + ), + ( + r"^Mikuáš ", + "Mikuláš " + ), + ( + r"^Strana zelených$", + "Strana Zelených" + ), + ( + r"^Systemický institut s\.r\.o\.$", + "Systemický institut, s.r.o." + ), + ( + r"^Adéla hradilová$", + "Adéla Hradilová" ) ) @@ -428,7 +612,7 @@ class Command(BaseCommand): instance.address_street_with_number = "" if "road" in address: - instance.address_street_with_number = address["road"] + instance.address_street_with_number = string.capwords(address["road"]) if "house_number" in address: instance.address_street_with_number += f" {address['house_number']}" @@ -436,12 +620,21 @@ class Command(BaseCommand): for address_key, address_value in address.items(): match address_key: case "city": - instance.address_district = address_value + if "district" not in address: + instance.address_district = string.capwords(address_value) + case "house": + if "city" not in address and "district" not in address: + instance.address_district = string.capwords(address_value) + case "city_district": + instance.address_district = string.capwords(address_value) case "postcode": - instance.address_zip = address_value + instance.address_zip = string.capwords(address_value) self.normalization_count += 1 case "IČ": + if is_contractee: + continue + if not isinstance(signing_party_value, int|str): issue_count += 1 contract.notes += f"Špatně zadané IČO smluvní strany: {signing_party_value}\n" @@ -479,6 +672,23 @@ class Command(BaseCommand): ) self.normalization_count += 1 + function = None + + if "funkce" in signing_party: + if isinstance(signing_party["funkce"], str): + function = signing_party["funkce"] + else: + issue_count += 1 + contract.notes += f"Špatně zadaná funkce zástupce smluvní strany: {signing_party['funkce']}\n" + + if self.verbosity >= 2: + self.stderr.write( + self.style.NOTICE( + f"Contract {slug} has an invalid signing party " + f"representative function: {signing_party['funkce']}." + ) + ) + representatives.append( representative_model( name=signing_party_value @@ -512,11 +722,40 @@ class Command(BaseCommand): name=signing_party_value ) ) + case "orgán": + if not isinstance(signing_party_value, str): + issue_count += 1 + contract.notes += f"Špatně zadaný orgán smluvní strany: {signing_party_value}\n" + + if self.verbosity >= 2: + self.stderr.write( + self.style.NOTICE( + f"Contract {slug} has an invalid signing party " + f"department: {signing_party_value}." + ) + ) + + continue + + if signing_party_value in ( + "Česká Pirátská strana", + "Česká pirátská strana", + ): + # Irrelevant + continue + + instance.department = self.normalize_department(signing_party_value) + self.normalization_count += 1 # Do our best to merge signing parties together. existing_instance = model.objects.filter( ( models.Q(name=instance.name) + & ( + models.Q(department=instance.department) + if instance.department is not None + else models.Q(department__isnull=True) + ) & ( ( models.Q(address_street_with_number=instance.address_street_with_number) @@ -534,7 +773,14 @@ class Command(BaseCommand): ) ) | ( - models.Q(ico_number=instance.ico_number) + ( + models.Q(ico_number=instance.ico_number) + & ( + models.Q(department=instance.department) + if instance.department is not None + else models.Q(department__isnull=True) + ) + ) if instance.ico_number is not None else models.Value(False) ) diff --git a/contracts/migrations/0057_alter_contract_options_alter_contractee_options_and_more.py b/contracts/migrations/0057_alter_contract_options_alter_contractee_options_and_more.py new file mode 100644 index 0000000000000000000000000000000000000000..78898b509d833d2bcee3aaf617b7c950d072f7c1 --- /dev/null +++ b/contracts/migrations/0057_alter_contract_options_alter_contractee_options_and_more.py @@ -0,0 +1,37 @@ +# Generated by Django 4.1.4 on 2023-04-23 10:28 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('contracts', '0056_rename_contractee_signature_contracteesignaturerepresentative_signature_and_more'), + ] + + operations = [ + migrations.AlterModelOptions( + name='contract', + options={'ordering': ('-created_on', '-updated_on', '-name'), 'permissions': [('approve', 'Schválit / zrušit schválení'), ('view_confidential', 'Zobrazit tajné informace'), ('edit_when_approved', 'Upravit schválené'), ('delete_when_approved', 'Odstranit schválené'), ('edit_others', 'Upravit cizí'), ('delete_others', 'Odstranit cizí'), ('can_edit_contract_settings', 'Can edit Smlouva settings')], 'verbose_name': 'Smlouva', 'verbose_name_plural': 'Smlouvy'}, + ), + migrations.AlterModelOptions( + name='contractee', + options={'ordering': ['-name', '-department'], 'permissions': [('edit_others', 'Upravit cizí'), ('delete_others', 'Odstranit cizí')], 'verbose_name': 'Naše smluvní strana', 'verbose_name_plural': 'Naše smluvní strany'}, + ), + migrations.AlterModelOptions( + name='contractfilingarea', + options={'ordering': ['-name'], 'verbose_name': 'Spisovna', 'verbose_name_plural': 'Spisovny'}, + ), + migrations.AlterModelOptions( + name='contractissue', + options={'ordering': ['-name'], 'verbose_name': 'Problém se smlouvou', 'verbose_name_plural': 'Problémy se smlouvami'}, + ), + migrations.AlterModelOptions( + name='contracttype', + options={'ordering': ['-name'], 'verbose_name': 'Typ smlouvy', 'verbose_name_plural': 'Typy smluv'}, + ), + migrations.AlterModelOptions( + name='signee', + options={'ordering': ['-name', '-department'], 'permissions': [('edit_others', 'Upravit cizí'), ('delete_others', 'Odstranit cizí')], 'verbose_name': 'Jiná smluvní strana', 'verbose_name_plural': 'Ostatní smluvní strany'}, + ), + ] diff --git a/contracts/migrations/0058_alter_contract_options_alter_contractee_options_and_more.py b/contracts/migrations/0058_alter_contract_options_alter_contractee_options_and_more.py new file mode 100644 index 0000000000000000000000000000000000000000..c17f7b0ea88c220834059e244d1bdf97a1881fba --- /dev/null +++ b/contracts/migrations/0058_alter_contract_options_alter_contractee_options_and_more.py @@ -0,0 +1,37 @@ +# Generated by Django 4.1.4 on 2023-04-23 10:29 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('contracts', '0057_alter_contract_options_alter_contractee_options_and_more'), + ] + + operations = [ + migrations.AlterModelOptions( + name='contract', + options={'ordering': ('-created_on', '-updated_on', 'name'), 'permissions': [('approve', 'Schválit / zrušit schválení'), ('view_confidential', 'Zobrazit tajné informace'), ('edit_when_approved', 'Upravit schválené'), ('delete_when_approved', 'Odstranit schválené'), ('edit_others', 'Upravit cizí'), ('delete_others', 'Odstranit cizí'), ('can_edit_contract_settings', 'Can edit Smlouva settings')], 'verbose_name': 'Smlouva', 'verbose_name_plural': 'Smlouvy'}, + ), + migrations.AlterModelOptions( + name='contractee', + options={'ordering': ['name', 'department'], 'permissions': [('edit_others', 'Upravit cizí'), ('delete_others', 'Odstranit cizí')], 'verbose_name': 'Naše smluvní strana', 'verbose_name_plural': 'Naše smluvní strany'}, + ), + migrations.AlterModelOptions( + name='contractfilingarea', + options={'ordering': ['name'], 'verbose_name': 'Spisovna', 'verbose_name_plural': 'Spisovny'}, + ), + migrations.AlterModelOptions( + name='contractissue', + options={'ordering': ['name'], 'verbose_name': 'Problém se smlouvou', 'verbose_name_plural': 'Problémy se smlouvami'}, + ), + migrations.AlterModelOptions( + name='contracttype', + options={'ordering': ['name'], 'verbose_name': 'Typ smlouvy', 'verbose_name_plural': 'Typy smluv'}, + ), + migrations.AlterModelOptions( + name='signee', + options={'ordering': ['name', 'department'], 'permissions': [('edit_others', 'Upravit cizí'), ('delete_others', 'Odstranit cizí')], 'verbose_name': 'Jiná smluvní strana', 'verbose_name_plural': 'Ostatní smluvní strany'}, + ), + ] diff --git a/contracts/models.py b/contracts/models.py index 2d2b963f962afedda67f5731e77ae2c9ee3cc7dc..aaad5e1e6f86af244bee092cf7f36c26375d277e 100644 --- a/contracts/models.py +++ b/contracts/models.py @@ -274,6 +274,8 @@ class Signee( verbose_name = "Jiná smluvní strana" verbose_name_plural = "Ostatní smluvní strany" + ordering = ["name", "department"] + permissions = OwnPermissionsMixin.Meta.permissions @@ -367,6 +369,8 @@ class Contractee( verbose_name = "Naše smluvní strana" verbose_name_plural = "Naše smluvní strany" + ordering = ["name", "department"] + permissions = OwnPermissionsMixin.Meta.permissions @@ -383,6 +387,8 @@ class ContractType(ContractCountMixin, NameStrMixin, models.Model): class Meta: app_label = "contracts" + ordering = ["name"] + verbose_name = "Typ smlouvy" verbose_name_plural = "Typy smluv" @@ -400,6 +406,8 @@ class ContractIssue(ContractCountMixin, NameStrMixin, models.Model): class Meta: app_label = "contracts" + ordering = ["name"] + verbose_name = "Problém se smlouvou" verbose_name_plural = "Problémy se smlouvami" @@ -422,6 +430,8 @@ class ContractFilingArea(ContractCountMixin, NameStrMixin, models.Model): class Meta: app_label = "contracts" + ordering = ["name"] + verbose_name = "Spisovna" verbose_name_plural = "Spisovny" @@ -789,6 +799,12 @@ class Contract(NameStrMixin, models.Model): verbose_name = "Smlouva" verbose_name_plural = "Smlouvy" + ordering = ( + "-created_on", + "-updated_on", + "name", + ) + permissions = [ ("approve", "Schválit / zrušit schválení"), ("view_confidential", "Zobrazit tajné informace"), diff --git a/registry/settings/base.py b/registry/settings/base.py index 15f0ca7001cdb133c88efd3666e6fdba61f893ea..c2f29bd217dd4272ac6a7fdeb5727ef3a17861a3 100644 --- a/registry/settings/base.py +++ b/registry/settings/base.py @@ -53,6 +53,7 @@ INSTALLED_APPS = [ "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", + "admin_auto_filters", "dbsettings", "nested_admin", "rangefilter", diff --git a/requirements/base.txt b/requirements/base.txt index f2f7e257817b7604206a85ea38a40757152f88ae..e4fe2396bd4a6f5a08f1182a3859258f20f97548 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -1,5 +1,6 @@ clamd==1.0.2 django==4.1.4 +django-admin-autocomplete-filter==0.7.1 django-admin-index==2.0.2 django-admin-interface==0.24.2 django-admin-rangefilter==0.9.0