From f2d8433c56a524ec9af259f8c2e32f1d908c6ece Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Valenta?= <git@imaniti.org> Date: Fri, 21 Apr 2023 13:20:34 +0200 Subject: [PATCH] old registry import normalization --- Makefile | 4 +- .../commands/import_old_contracts.py | 216 ++++++++++++++++-- contracts/views.py | 1 - 3 files changed, 201 insertions(+), 20 deletions(-) diff --git a/Makefile b/Makefile index f0b56fe..7c3c25a 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,9 @@ #!/usr/bin/make -f -PYTHON = python3.10 +PYTHON = python3.11 VENV = .venv PORT = 8013 -SETTINGS = registry.settings.production +SETTINGS = registry.settings.dev .PHONY: help venv install install-hooks hooks build run shell migrations migrate diff --git a/contracts/management/commands/import_old_contracts.py b/contracts/management/commands/import_old_contracts.py index 3c406a1..53c9294 100644 --- a/contracts/management/commands/import_old_contracts.py +++ b/contracts/management/commands/import_old_contracts.py @@ -1,5 +1,6 @@ import io import os +import re import string from datetime import date, datetime @@ -48,9 +49,173 @@ class Command(BaseCommand): parser.add_argument( "--purge", action="store_true", - help="Purge all previous contracts before the import.", + help="Purge all previous contracts, types and filing areas before the import.", ) + def normalize_type(self, type_name: str) -> str: + type_name = string.capwords(type_name) + + patterns = ( + (r" O ", " o "), + (r" S ", " s "), + (r" K ", " k "), + (r" V ", " v "), + (r"Nda", "NDA"), + (r"Dds", "DDS"), + (r"^Dohoda o Podpoře.*$", "Dohoda o Podpoře"), + ( + r"^(Dohoda o Ochraně Důvěrných Informací \(nda\)|Dohoda o Ochraně Důvě" + r"rných Dat|Dohoda o Ochraně Důvěrných Informací|Smlouva o Zachování D" + r"ůvěrnosti Informací|Smlouva o Zachování Důvěrnosti Informací \(nda\)" + ")$", + "NDA", + ), + (r"^(Darovací.*|Ds)$", "Darovací Smlouva"), + (r".*Dodatek.*", "Dodatek"), + ( + r"^(Dpp|Dohoda o Provední Práce|Dohoda o Proveení Práce)$", + "Dohoda o Provedení Práce", + ), + (r"^Dpč$", "Dohoda o Pracovní Činnosti"), + (r"^NS$", "Nájemní Smlouva"), + (r"^Koaliční Smlouva.*$", "Koaliční Smlouva"), + (r"^Pronajem$", "Pronájem"), + ( + r"^(Rácová Smlouva|Rámcová Kupní Smlouva|Rámcová Smlouva|Rámcová Smlouva- Do" + r"datek|Ramcova Smlouva o Najmu Kopií Filmů A Rámcová Smlouva Podlicenční|Rs" + r")$", + "Rámcová Smlouva", + ), + ( + r"^(Oof|Oosf|Osf|Potvrzení o Funkci|Potvrzeni o Stranicke Funkci|Potvrzení o" + r"Stranické Funkci)", + "Osvědčení o Stranické Funkci", + ), + (r"^Smlouva o Bezúročné Zápůjčce.*$", "Bezúročná Zápůjčka Peněz"), + ( + r"^(Smlouva o Dilo|Smlouva o Dílo A Veřejná Licenční Smlouva|Smlouva o Dílo - " + r"Dodatek|Sod)$", + "Smlouva o Dílo", + ), + (r"^Smlouva o Nájmu.*$", "Smlouva o Nájmu"), + ( + r"^Smlouva o Poskytovani Pravnich Sluzeb$", + "Smlouva o Poskytování Právních Služeb", + ), + ("\.$", ""), + (r"^Ks$", "Koaliční Smlouva"), + (r"^Pm$", "Plná moc"), + (r"Pronajmu", "Pronájmu"), + (r"^(Zl .*|(Zadavací|Zdávací) list)$", "Zadávací list"), + (r"Hpp", "Hlavní Pracovní Poměr"), + (r"^Sops - Smlouva o Poskytnutí Služeb", "Smlouva o Poskytnutí Služeb"), + (r"^Suv$", "Smlouva o Uměleckém Vystoupení"), + ( + r"^(Příkaz|Příkazní|Prikazni Smlouva|Příkazní Smlouva|Příkazní Smlouva, Bude" + r"Fakturováno|Příkazní Smlouva (dodatek)|Příkazní Smlouva - Dodatek|Příkazní" + r"Smlouva Dodatek|Příkazní Smlouva- Dodatek|Příkazní Smlouva - Dodatek Č\. 1" + r"|Příkazní Smlouva - Dodatek Č\.1|Příkazní Smlouva Dodatek Č\. 1|Příkazní S" + r"mlouva Dodatek Č\.1|Příkazní Smlouva s Dodavatelem|Příkazní Smlouva- Ukonč" + r"ení|Příkazní Smlouvy|Příkazní Smlova|Příkazní Smluva|Příkazní Smuva|Příkaz" + r"ní Spisovna|Příkzaní Smlouva|Ps)$", + "Příkazní smlouva", + ), + ) + + 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"^(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 " + r"Archiv Strany|Centrální Archiv Strany|Centrální Skupina|Centrální Spid" + r"ovna|Centrální Spisivna|Centrální Spisovna|Centrální Spísovna|Centráln" + r"í Spisovna Strany|Centrální Sposovna|Centrální Sposovná|Centrální Úlož" + r"na|Centrální Úschovna|Česká Pirítská Strana, Na Moráni 360\/3, Praha 2" + r"|Cnterální Spisovna|Archiv|Archív|Archiv Centrály|Archiv České Pirátsk" + r"é Strany|Archiv Pice|Archív Piráti|Archiv Pirátské Strany|Archiv Praha" + r"|Archiv, Sídlo Strany|Archiv, Sídlo Strany,|Archív, Sídlo Strany|Archi" + r"v, Sídlo Strany, Řehořova 19, Praha 3|Archiv, Sídlo Strany, Řehořova 1" + r"9, Praha 3, Zastupitelský Klub Pirátů, Mariánské Nám. 2, Praha 1 \/pří" + r"lohy|Archiv, Sídlo Strany, Řehořova 943\/19, Praha 3|Archív, Sídlo Str" + r"any, Řehořova 943\/19, Praha 3|Archiv, Sídlo Strany, Řehova 19, Praha " + r"3|Archiv Služeb|Archiv Smlouvy|Archiv Starny|Archiv Strany|Archív Stra" + r"ny|Archiv Strany, Řehořova 943\/19, Praha|Archiv Strany, Sídlo Strany|" + r"Archiv Strany \(zatím Koks, Do Konce 6\/2020 Doručím Do Archivu\)|Arch" + r"ív V Sídle Strany|Archvi Strany|Archyv Strany|Arhiv Strany|Ar Hiv Stra" + r"ny|Bude Zasláno Do Prahy|Mariánské Náměstí 2, Praha 1|Na Morání 360\/3" + r"|Na Moráni 360\/3, Praha|Na Moráni 360\/3. Praha|Na Moráni 360\/3; Pra" + r"ha|Na Morání 360\/3, Praha 120 00|Na Moráni 360\/3; Praha 12800|Na Mor" + r"áni 360\/3, Praha 2|Na Moráni Praha|Na Moráni, Praha|Pirátská Centrum " + r"Na Moráni 360\/3, Praha|Pirátská Centrum Praha, Na Moráni|Pirátské Cen" + r"trum, Na Moráni 360\/3, Praha|Pirátské Centrum, Na Moráni 360, Praha|P" + r"irátské Centrum Praha, Na Moráni|Pirátské Centrum, Řehořova 19|Pirátsk" + r"é Centrum, Řehořova 19 <!-- Donesl Štěpán Štrébl -->|Pirátské Centrum," + r" Řehořova 19, Praha 3|Praha, Kancelář Strany|Řehořova 943\/19 130 00 P" + r"raha 3|Řehořova 943\/19, 130 00 Praha 3|Řehořova 943\/19, 130 00, Prah" + r"a 3|Řehořova 943\/19, Praha 3|Sídlo Strany|Sídlo Strany, Řehořova 19, " + r"Praha 3|Smlouva Uložena Na Místě: Centrální Spisovna|Smlouva Uložena V" + r" Archivu Strany|Stranický Archív|V Archivu|V Archivukrajského Koordiná" + r"tora, Později V Archivu Smluv Strany|Zatím Archiv Krajského Koordináto" + r"ra, Bude Odeslána Do Registru|Zastupitelský Klub Pirátů, Sídlo Strany|" + r"Kancelář Srtrany|Koks Od 2019 V Archivu|Originál Uložen V Centrálním A" + r"rchivu Strany|Originál Včetně Osobních Údajů Uložen V Centrálním Archi" + r"vu Strany|Originál Včetně Podpisů A Osobních Údajů Je Uložen V Centrál" + r"ním Archivu Strany|Originál Vč. Osobních Údajů A Podpisů Uložen V Cent" + r"rálním Archivu Strany|Originál Vč. Osobních Údajů Uložen V Centrálním " + r"Archivu Strany|Pirátské Centrum Praha|Pice Praha|Podepsaný Orgininál V" + r"č. Osobních Údajů Uložen V Centrálním Archivu Strany)$", + "Centrální spisovna", + ), + ( + r"^(Archiv Pirátské Centrum V Plzni|Archiv Plzen Piratske Centrum|Archiv" + r" Plzeňské Pirátské Centrum|Archiv V Piratskem Centru Plzen|Archiv V Pl" + r"zenskem Piratskem Centru|Petr Vileta, Plzeň, Archiv, Sídlo Strany|Petr" + r" Vileta, Plzeň, Vedoucí Fo|Petr Vileta, Vedoucí Fo, Plzeň|Pice Plzeň|P" + r"iratske Centrum Plzen|Pirátské Centrum Plzeň|Pirátské Centrum V Plzni|" + r"Plzeň|Plzenske Piratske Centrum|Plzeňské Pirátské Centrum|V Archivu Pl" + r"zeňského Kraje|Vedoucí Fo, Plzeň)$", + "Plzeňská spisovna", + ), + ( + r"^(Archiv Klubu|Archiv Pk|Archiv Pk Piráti|Archiv Poslaneckého Klubu|Ar" + r"chiv Poslaneckého Klubu Piráti|Archiv, Poslanecký Klub Pirátů, Sněmovn" + r"í 4, 118 00, Praha 1|Kancelář Pirátského Klubu Č. 40, Mhmp, Mariánské " + r"Náměstí 2, Praha 1|Posklanecký Klub|Poslanecký Kllub|Poslanecký Klub|P" + r"oslanecký Klub Piráti|Poslanecký Klub Pirátů|Poslanecký Kub|Poslanekcý" + r" Klub|Poslaneský Klub Piráti)$", + "Spisovna poslaneckého klubu", + ), + ( + r"^(Pirátská Centrum Hradec Králové|Pirátské Centrum Hradec Králové|Koks" + r" - Khk Kraj - Krapice|Koks - Pirátské Centrum Krapice Hradec Králové|K" + r"rapice|Krapice Hk|Krapice Hradec Králové|Krapice - Masarykovo Nám. 368" + r"/1, Hradec Králové|Smlouva Uložena Na Místě: Koks - Khk Kraj - Krapice" + r")$", + "Spisovna Hradec Králové", + ), + ( + r"^(Archiv Psb|Archiv Psb Brno|Psb Brno|Psb Archiv)$", + "Brněnská spisovna", + ), + ( + r"^(Pice Olomouc|Archiv Olk \(dočasně\)|Pice Olomouc)$", + "Olomoucká spisovna", + ), + (r"^Archiv Ks Vysočina$", "Vysočincká spisovna"), + ) + + for pattern in patterns: + area_name = re.sub(pattern[0], pattern[1], area_name) + + return area_name + def parse_index( self, contract_root: str, @@ -61,13 +226,12 @@ class Command(BaseCommand): if len(split_contents) < 2: raise ValueError(f"{contract_root} index does not have valid metadata.") - # Use Jan 1, 1970 as the default, if any defined year is 0. - # Use Jan 1, 2100 as the default for infinitely valid contracts. + # Use Jan 1, 1970 as the default for invalid years. split_contents[1] = ( split_contents[1] .replace("0000-00-00", "1970-01-01") - .replace("2222-22-22", "2100-01-01") - .replace("2222-00-00", "2100-01-01") + .replace("2222-22-22", "1970-01-01") + .replace("2222-00-00", "1970-01-01") .replace("-32", "-31") .replace("\t", " ") ) @@ -106,7 +270,7 @@ class Command(BaseCommand): match key: case "datum účinnosti": if isinstance(value, date): - if date.year not in (1970, 2100): # Ignore placeholder years + if value.year not in (1970, 2100): # Ignore placeholder years contract.valid_start_date = value elif value is not None: observed_issues_count += 1 @@ -120,7 +284,7 @@ class Command(BaseCommand): ) case "datum ukončení": if isinstance(value, date): - if date.year not in (1970, 2100): # Ignore placeholder years + if value.year not in (1970, 2100): # Ignore placeholder years contract.valid_end_date = value elif value is not None and ( isinstance(value, str) and value.lower() != "na dobu neurčitou" @@ -148,7 +312,7 @@ class Command(BaseCommand): break case "použité smluvní typy": if isinstance(value, str): - value = string.capwords(value) + value = self.normalize_type(value) try: type_instance = ContractType.objects.get(name=value) @@ -185,7 +349,7 @@ class Command(BaseCommand): continue - type_name = string.capwords(type_name.strip()) + type_name = self.normalize_type(type_name) try: type_instance = ContractType.objects.get(name=type_name) @@ -248,8 +412,13 @@ class Command(BaseCommand): if value != 0: contract.cost_amount = int(value) contract.cost_unit = contract.CostUnits.TOTAL - elif isinstance(value, str) and value.endswith("Kč/h"): - split_value = value.split("Kč/h") + elif isinstance(value, str): + value = value.lower().replace(",-", "") + + if not value.endswith("kč/h"): + continue + + split_value = value.split("kč/h") if ( len(split_value) != 2 @@ -281,13 +450,17 @@ class Command(BaseCommand): ) case "místo uložení": if isinstance(value, str): - value = string.capwords(value) # Some normalization + value = self.normalize_filing_area(value) try: + contract.paper_form_state = contract.PaperFormStates.STORED filing_area = ContractFilingArea.objects.get(name=value) except ContractFilingArea.DoesNotExist: - if isinstance(value, str): - filing_area = ContractFilingArea(name=value) + if isinstance(value, str) and not value.startswith("Neznám"): + filing_area = ContractFilingArea( + name=value, + person_responsible="Doplň osobu!", + ) else: observed_issues_count += 1 contract.notes += f"Špatně zadaná spisovna: {value}\n" @@ -302,6 +475,15 @@ class Command(BaseCommand): continue if not is_already_imported: + if contract.name in (None, "") or ( + isinstance(contract.name, str) + and re.sub(r"/\s\s+/", "", contract.name) == "" + ): + contract.name = slug + + if contract.valid_start_date is None: + contract.valid_start_date = date(year=1970, month=1, day=1) + if observed_issues_count != 0: self.partial_import_count += 1 self.issue_count += observed_issues_count @@ -351,7 +533,7 @@ class Command(BaseCommand): self.fatal_error_count += 1 - continue + return self.assign_contract_metadata( contract, @@ -362,8 +544,6 @@ class Command(BaseCommand): # TODO continue - contract.save() - def import_all_contracts(self, git_dir) -> None: year_root = os.path.join( git_dir, @@ -474,6 +654,8 @@ class Command(BaseCommand): if options["purge"]: Contract.objects.filter().delete() + ContractType.objects.filter().delete() + ContractFilingArea.objects.filter().delete() if self.verbosity >= 1: self.stdout.write(self.style.SUCCESS("Deleted all previous records.")) diff --git a/contracts/views.py b/contracts/views.py index 43f09b4..693fca2 100644 --- a/contracts/views.py +++ b/contracts/views.py @@ -1,5 +1,4 @@ import os - from xml.etree import ElementTree import requests -- GitLab