diff --git a/contracts/management/commands/import_old_contracts.py b/contracts/management/commands/import_old_contracts.py index 53c929456a7037f0d455db7ca258f74189c5f115..23f456c71d02661cf3a5bac7f2013887182c0995 100644 --- a/contracts/management/commands/import_old_contracts.py +++ b/contracts/management/commands/import_old_contracts.py @@ -7,10 +7,19 @@ from datetime import date, datetime import yaml from django.conf import settings from django.core.management.base import BaseCommand +from postal.parser import parse_address from git import Repo -from ...models import Contract, ContractFilingArea, ContractType +from ...models import ( + Contract, + ContractFilingArea, + ContractType, + Contractee, + Signee, + SigneeSignature, + ContracteeSignature, +) class Command(BaseCommand): @@ -25,7 +34,7 @@ class Command(BaseCommand): self.issue_count = 0 self.fatal_error_count = 0 - def add_arguments(self, parser): + def add_arguments(self, parser) -> None: parser.add_argument( "repo_url", type=str, @@ -250,6 +259,103 @@ class Command(BaseCommand): return parsed_metadata + def assign_signing_party_metadata( + self, + slug: str, + contract: Contract, + signing_party: dict + ) -> tuple[Contract|Signee, bool, int]: + issue_count = 0 + + if ( + "jméno" not in signing_party + and "název" not in signing_party + and "název společnosti" not in signing_party + ): + issue_count += 1 + contract.notes += f"Nepojmenovaná smluvní strana, zdrojová data: {signing_party}\n" + + if self.verbosity >= 2: + self.stderr.write( + self.style.NOTICE( + f"Contract {slug} has an unnamed signing party: {signing_party}." + ) + ) + + return issue_count + + name = signing_party.get( + "jméno", + signing_party.get( + "název", + signing_party.get("název společnosti") + ) + ).strip() + + is_contractee = False + + if name.lower() in ( + "česká pirátská strana", + "česká pirástká strana", + "česká pirátkská strana", + "česká pirátská stran", + ): + instance = Contractee() + is_contractee = True + else: + instance = Signee(name=name, address_country="Česká republika") + + for signing_party_key, signing_party_value in signing_party.items(): + if isinstance(signing_party_value, str): + signing_party_value = signing_party_value.strip() + + match signing_party_key: + case "sídlo": + if is_contractee: + continue + + if not isinstance(signing_party_value, str): + issue_count += 1 + contract.notes += f"Špatně zadané sídlo smluvní strany: {signing_party_value}\n" + + if self.verbosity >= 2: + self.stderr.write( + self.style.NOTICE( + f"Contract {slug} has an invalid signing party address: {signing_party_value}." + ) + ) + + continue + + raw_parsed_address_data = parse_address(signing_party_value) + address = {} + + # Convert to a dict first, so that we can access other keys in the loop later + for address_info in raw_parsed_address_data: + address_value, address_key = address_info + address[address_key] = address_value + + instance.address_street_with_number = "" + + if "road" in address: + instance.address_street_with_number = address["road"] + + if "house_number" in address: + instance.address_street_with_number += address["house_number"] + + for address_key, address_value in address.items(): + match address_key: + case "city": + instance.address_district = address_value + case "postcode": + instance.address_zip = address_value + case "IČ": + instance.ico_number = signing_party_value + + instance.save() + + return instance, is_contractee, issue_count + def assign_contract_metadata( self, contract: Contract, @@ -258,6 +364,8 @@ class Command(BaseCommand): ) -> None: filing_area = None types = [] + signees = [] + contractees = [] is_already_imported = False observed_issues_count = 0 @@ -472,8 +580,45 @@ class Command(BaseCommand): ) ) + case "smluvní strany": + if not isinstance(value, list): + observed_issues_count += 1 + contract.notes += f"Špatně zadané smluvní strany, nejsou seznam: {value}\n" + + if self.verbosity >= 2: + self.stderr.write( + self.style.NOTICE( + f"Signing parties on {slug} are not a list: {value}." + ) + ) + + continue + + for signing_party in value: + if not isinstance(signing_party, dict): + observed_issues_count += 1 + contract.notes += f"Špatně zadaná smluvní strana: {signing_party}\n" + + if self.verbosity >= 2: + self.stderr.write( + self.style.NOTICE( + f"Signing party on {slug} is not a dictionary: {value}." + ) + ) + continue + instance, is_contractee, signing_party_issue_count = ( + self.assign_signing_party_metadata(slug, contract, signing_party) + ) + + observed_issues_count += signing_party_issue_count + + if is_contractee: + contractees.append(instance) + else: + signees.append(instance) + if not is_already_imported: if contract.name in (None, "") or ( isinstance(contract.name, str) @@ -499,6 +644,32 @@ class Command(BaseCommand): # Save primary key first contract.save() + for contractee in contractees: + contractee.save() + + signature = ContracteeSignature( + contract=contract, + contractee=contractee, + date=contract.valid_start_date, + ) + + signature.save() + + contract.contractee_signatures.add(signature) + + for signee in signees: + signee.save() + + signature = SigneeSignature( + contract=contract, + signee=signee, + date=contract.valid_start_date, + ) + + signature.save() + + contract.signee_signatures.add(signature) + contract.filing_area = filing_area contract.types.set(types) contract.save() @@ -653,9 +824,16 @@ class Command(BaseCommand): self.stdout.write(self.style.SUCCESS("Finished cloning repository.")) if options["purge"]: - Contract.objects.filter().delete() - ContractType.objects.filter().delete() - ContractFilingArea.objects.filter().delete() + for model in ( + Contract, + ContractType, + ContractFilingArea, + Contractee, + ContracteeSignature, + Signee, + SigneeSignature, + ): + model.objects.filter().delete() if self.verbosity >= 1: self.stdout.write(self.style.SUCCESS("Deleted all previous records.")) diff --git a/contracts/migrations/0054_alter_signee_address_zip_alter_signee_ico_number.py b/contracts/migrations/0054_alter_signee_address_zip_alter_signee_ico_number.py new file mode 100644 index 0000000000000000000000000000000000000000..7a6ea981dc7c481caa8939e22dafcdc9cb5b9232 --- /dev/null +++ b/contracts/migrations/0054_alter_signee_address_zip_alter_signee_ico_number.py @@ -0,0 +1,23 @@ +# Generated by Django 4.1.4 on 2023-04-21 22:51 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('contracts', '0053_alter_contractfile_file'), + ] + + operations = [ + migrations.AlterField( + model_name='signee', + name='address_zip', + field=models.CharField(blank=True, help_text='Veřejné pouze, když typ není nastaven na fyzickou osobu.', max_length=256, null=True, verbose_name='PSČ'), + ), + migrations.AlterField( + model_name='signee', + name='ico_number', + field=models.CharField(blank=True, help_text='U právnických a podnikajících fyzických osob musí být vyplněno. Vyplněním můžeš automaticky načíst data z ARES.', max_length=256, null=True, verbose_name='IČO'), + ), + ] diff --git a/contracts/migrations/0055_alter_contractee_address_zip_and_more.py b/contracts/migrations/0055_alter_contractee_address_zip_and_more.py new file mode 100644 index 0000000000000000000000000000000000000000..ea6c711dbaea760820bac281ddbe866dbe8e9d02 --- /dev/null +++ b/contracts/migrations/0055_alter_contractee_address_zip_and_more.py @@ -0,0 +1,24 @@ +# Generated by Django 4.1.4 on 2023-04-21 22:52 + +import contracts.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('contracts', '0054_alter_signee_address_zip_alter_signee_ico_number'), + ] + + operations = [ + migrations.AlterField( + model_name='contractee', + name='address_zip', + field=models.CharField(default=contracts.models.get_default_contractee_zip, max_length=256, verbose_name='PSČ'), + ), + migrations.AlterField( + model_name='contractee', + name='ico_number', + field=models.CharField(blank=True, default=contracts.models.get_default_contractee_ico_number, max_length=256, null=True, verbose_name='IČO'), + ), + ] diff --git a/contracts/models.py b/contracts/models.py index 72d01d2a5649dcb0ff475234d4d607ae49870e3e..fad812edfbf29c1374396c2956a77ddc3d02b079 100644 --- a/contracts/models.py +++ b/contracts/models.py @@ -156,7 +156,7 @@ class Signee( ) address_zip = models.CharField( - max_length=16, + max_length=256, blank=True, null=True, verbose_name="PSČ", @@ -172,7 +172,7 @@ class Signee( ) ico_number = models.CharField( - max_length=16, + max_length=256, blank=True, null=True, verbose_name="IČO", @@ -323,7 +323,7 @@ class Contractee( ) address_zip = models.CharField( - max_length=16, + max_length=256, default=get_default_contractee_zip, verbose_name="PSČ", ) @@ -335,7 +335,7 @@ class Contractee( ) ico_number = models.CharField( - max_length=16, + max_length=256, blank=True, null=True, default=get_default_contractee_ico_number, diff --git a/requirements/base.txt b/requirements/base.txt index c8f33c620f3939b2097253df401d14bfc05fd049..f2f7e257817b7604206a85ea38a40757152f88ae 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -17,5 +17,6 @@ django-http-exceptions==1.4.0 django-guardian==2.4.0 GitPython==3.1.31 Markdown==3.4.3 +postal==1.1.10 PyJWT==2.6.0 PyYAML==6.0