diff --git a/print_config.py b/print_config.py index 64222f0596a45ea45aef034594c4273c7c9d2114..2859f3c560b7db4bf2beb436e20bea2b30ef027d 100644 --- a/print_config.py +++ b/print_config.py @@ -2,12 +2,14 @@ import hydra from omegaconf import OmegaConf import json + @hydra.main(config_path="config", config_name="config") def main(cfg): cfg_resolved = OmegaConf.to_container(cfg, resolve=True) cfg_resolved_json = json.dumps(cfg_resolved, indent=4) - + print(cfg_resolved_json) - + + if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/scripts/winer_input.py b/scripts/winer_input.py index 1527bdb5f65d719dfc2f38fa80518a4a235c2923..fda1cc70d5db5b5b593e8d579a3261e85ed688cc 100644 --- a/scripts/winer_input.py +++ b/scripts/winer_input.py @@ -1,10 +1,11 @@ from lpmn_client_biz import Connection, IOType, upload, download, Task, delete import json -lpmn = ["morphodita", - {"posconverter": - {"input_fromat": "ccl", "output_fromat": "json"} - }, - "winer"] + +lpmn = [ + "morphodita", + {"posconverter": {"input_fromat": "ccl", "output_fromat": "json"}}, + "winer", +] _connection = Connection() FILE = upload(_connection, "test.txt") @@ -13,5 +14,5 @@ task = Task(lpmn, _connection) output_file_id = task.run(FILE, IOType.FILE, verbose=True) downloaded = download(_connection, output_file_id, IOType.TEXT) result = json.loads(downloaded) -with open("test.jsonl","wt", encoding='utf8') as f: - json.dump(result,f, ensure_ascii=False) +with open("test.jsonl", "wt", encoding="utf8") as f: + json.dump(result, f, ensure_ascii=False) diff --git a/src/annotations/annotations.py b/src/annotations/annotations.py index 584f33fb208cd8112936e77f76f7854828903cca..008fa8913e82a52263b5a77f058fd453967f6d70 100644 --- a/src/annotations/annotations.py +++ b/src/annotations/annotations.py @@ -1,4 +1,6 @@ from dataclasses import dataclass + + @dataclass class Annotation: def __hash__(self) -> int: @@ -8,6 +10,8 @@ class Annotation: class MorphosyntacticAnnotation(Annotation): def __init__(self, morphosyntactic_tag) -> None: self.morphosyntactic_tag = morphosyntactic_tag + + @dataclass class NerAnnotation(Annotation): - ner_type: str \ No newline at end of file + ner_type: str diff --git a/src/detections/detection.py b/src/detections/detection.py index 58418f604fc94dcb04039db4cb07e5658bfa992c..a2df4be5638b26db9bcf8c585df0039232d06da0 100644 --- a/src/detections/detection.py +++ b/src/detections/detection.py @@ -1,6 +1,7 @@ from typing import Optional from dataclasses import dataclass + @dataclass class Detection: TYPE_NAME = "detection" @@ -32,36 +33,42 @@ class SurnameDetection(MorphosyntacticInfoMixin, Detection): def __init__(self, morpho_tag: Optional[str] = None) -> None: super().__init__(morpho_tag=morpho_tag) + class LocationDetection(MorphosyntacticInfoMixin, Detection): TYPE_NAME = "location" def __init__(self, morpho_tag: Optional[str] = None) -> None: super().__init__(morpho_tag=morpho_tag) + class OrganizationNameDetection(MorphosyntacticInfoMixin, Detection): TYPE_NAME = "organization_name" - + def __init__(self, morpho_tag: Optional[str] = None) -> None: super().__init__(morpho_tag=morpho_tag) - + + class ProperNameDetection(MorphosyntacticInfoMixin, Detection): TYPE_NAME = "proper_name" def __init__(self, morpho_tag: Optional[str] = None) -> None: super().__init__(morpho_tag=morpho_tag) + class TitleDetection(MorphosyntacticInfoMixin, Detection): TYPE_NAME = "title" def __init__(self, morpho_tag: Optional[str] = None) -> None: super().__init__(morpho_tag=morpho_tag) + class HydronymDetection(MorphosyntacticInfoMixin, Detection): TYPE_NAME = "hydronym" def __init__(self, morpho_tag: Optional[str] = None) -> None: super().__init__(morpho_tag=morpho_tag) + class StreetNameDetection(MorphosyntacticInfoMixin, Detection): TYPE_NAME = "street_name" @@ -76,7 +83,6 @@ class CityDetection(MorphosyntacticInfoMixin, Detection): super().__init__(morpho_tag=morpho_tag) - class CountryDetection(MorphosyntacticInfoMixin, Detection): TYPE_NAME = "country" @@ -132,12 +138,14 @@ class KRSDetection(Detection): # National Court Register def __init__(self) -> None: super().__init__() + class SerialNumberDetection(Detection): TYPE_NAME = "serial_number" def __init__(self) -> None: super().__init__() + class OtherDetection(Detection): # Non standard entity TYPE_NAME = "other" diff --git a/src/detectors/date/date.py b/src/detectors/date/date.py index 38adc0b4f2b5d185f0be904b07a9333a1d19f32d..43544d9cc96ccd933ef429799e4f879daf781174 100644 --- a/src/detectors/date/date.py +++ b/src/detectors/date/date.py @@ -32,5 +32,5 @@ def find_dates(text: str, language: str = "pl") -> List[Tuple[int, int, DateDete "pl": detect_dates_pl, "ru": detect_dates_ru, } - + return language_processors.get(language, detect_dates_en)(text) diff --git a/src/detectors/date/pl.py b/src/detectors/date/pl.py index f315d58ee4bb04057de4eba00ce908c39c7651e9..fc539858c13641b1d9c9b7e792a76e348206b081 100644 --- a/src/detectors/date/pl.py +++ b/src/detectors/date/pl.py @@ -27,9 +27,7 @@ PL_DATES_REGEX = re.compile( re.I, ) -PL_YEAR_REGEX = re.compile( - r"(?<year>\d+)\s*(?<addon>roku?)" -) +PL_YEAR_REGEX = re.compile(r"(?<year>\d+)\s*(?<addon>roku?)") def detect_dates_pl(text: str) -> List[Tuple[int, int, DateDetection]]: @@ -46,10 +44,10 @@ def detect_dates_pl(text: str) -> List[Tuple[int, int, DateDetection]]: for match in matches: format = parse_date_to_format(match.groupdict()) dates.append((match.start(), match.end(), DateDetection(format))) - + for match in PL_YEAR_REGEX.finditer(text): format = parse_date_to_format(match.groupdict()) - + dates.append((match.start("year"), match.end("year"), DateDetection(format))) - + return OrderBasedSuppressor().suppress(dates) diff --git a/src/detectors/date/utils.py b/src/detectors/date/utils.py index 64a9eb5f60805f31eaa4c521969ea720e071f818..b239d3d3381fc452e8f1437c9e971a8c820c9b39 100644 --- a/src/detectors/date/utils.py +++ b/src/detectors/date/utils.py @@ -165,9 +165,10 @@ def _parse_year_only(re_entry) -> List[Tuple[DateDetection.AnnotationPart, str]] result.append((DateDetection.AnnotationPart.TWO_DIGIT_YEAR, re_entry["year"])) else: result.append((DateDetection.AnnotationPart.FOUR_DIGIT_YEAR, re_entry["year"])) - + return result + def parse_date_to_format( re_entry, ) -> Optional[List[Tuple[DateDetection.AnnotationPart, str]]]: diff --git a/src/detectors/interface.py b/src/detectors/interface.py index fe8981eb98bf3215e84a3d51c0e81ab2fdcd9420..6978b027242636d69e2599dfafa6e3b2d7c85dd1 100644 --- a/src/detectors/interface.py +++ b/src/detectors/interface.py @@ -2,6 +2,7 @@ from typing import List, Dict, Any, Tuple from src.detections import Detection from abc import ABC, abstractmethod + class Detector(ABC): @abstractmethod def detect( @@ -16,4 +17,4 @@ class Detector(ABC): Returns: List[Tuple[int, int, Detection]]: List of detections. """ - pass \ No newline at end of file + pass diff --git a/src/detectors/number/number.py b/src/detectors/number/number.py index f8ba56781c18fd501da701984f8bc32599c15613..1939bc61481e50feb478a92fbbad56b9c3abc123 100644 --- a/src/detectors/number/number.py +++ b/src/detectors/number/number.py @@ -16,7 +16,7 @@ class NumberDetector(Detector): def detect( self, text: str, annotations: Dict[str, List[Tuple[int, int, Any]]] ) -> List[Tuple[int, int, NumberDetection]]: - + numbers = [] for number in NUMBER_REGEX.finditer(text): diff --git a/src/dictionaries/morphosyntactic/interface.py b/src/dictionaries/morphosyntactic/interface.py index 13d36e95ba7675d2b9162b4dc83509f770055ba4..3ec644b11e239947d430fda6e9fbfc3982d1af82 100644 --- a/src/dictionaries/morphosyntactic/interface.py +++ b/src/dictionaries/morphosyntactic/interface.py @@ -2,6 +2,7 @@ from src.detections import Detection from typing import Optional, List, Type from abc import ABC, abstractmethod + class MorphosyntacticDictionary(ABC): @abstractmethod def get_supported_detection_classes(self) -> List[Type[Detection]]: diff --git a/src/dictionaries/morphosyntactic/ner_file.py b/src/dictionaries/morphosyntactic/ner_file.py index aee9fe873f8d38b30dc942d8f5f65ffc2b61039d..e94cdb09991dd8bbf0e7eed04facfc223de92f71 100644 --- a/src/dictionaries/morphosyntactic/ner_file.py +++ b/src/dictionaries/morphosyntactic/ner_file.py @@ -42,21 +42,26 @@ class NERFileMorphosyntacticDictionary(MorphosyntacticDictionary): if issubclass(original_entry_type, MorphosyntacticInfoMixin): morpho_tag = original_entry.morpho_tag - if ( - original_entry_type_name in self._dictionary - ): + if original_entry_type_name in self._dictionary: if morpho_tag in self._dictionary[original_entry_type_name]: result = random.choice( list( - self._dictionary[original_entry_type_name][morpho_tag].values() + self._dictionary[original_entry_type_name][ + morpho_tag + ].values() ) ) else: - morpho_tag = result = random.choice(list(self._dictionary[original_entry_type_name].keys())) + morpho_tag = result = random.choice( + list(self._dictionary[original_entry_type_name].keys()) + ) result = random.choice( - list(self._dictionary[original_entry_type_name][morpho_tag].keys()) + list( + self._dictionary[original_entry_type_name][ + morpho_tag + ].keys() + ) ) - if result is None and self._always_replace: random_type = random.choice(list(self._dictionary.keys())) diff --git a/src/dictionaries/morphosyntactic/ner_file_nkjp.py b/src/dictionaries/morphosyntactic/ner_file_nkjp.py index bee31631e1d1b238cf418bed011a2521428736e1..6b8f49bdbd1731b479fa68ce5e06238eaaa90572 100644 --- a/src/dictionaries/morphosyntactic/ner_file_nkjp.py +++ b/src/dictionaries/morphosyntactic/ner_file_nkjp.py @@ -7,10 +7,10 @@ from src.dictionaries.morphosyntactic.ner_file import NERFileMorphosyntacticDict class NERFileNKJPMorphosyntacticDictionary(NERFileMorphosyntacticDictionary): def __init__( - self, - dictionary_path: Optional[str] = None, + self, + dictionary_path: Optional[str] = None, always_replace=True, - remove_first_morpho_subtag=True + remove_first_morpho_subtag=True, ) -> None: super().__init__(dictionary_path, always_replace) self._remove_first_morpho_subtag = remove_first_morpho_subtag diff --git a/src/pipeline/interface.py b/src/pipeline/interface.py index 3574d841324abf8b3c828cd82b217b47c29f2fd4..9a183eb7ed756c7ea3173412ac02a73bc1c09186 100644 --- a/src/pipeline/interface.py +++ b/src/pipeline/interface.py @@ -1,5 +1,6 @@ from abc import ABC, abstractmethod + class Pipeline(ABC): @abstractmethod def run(self, input_path) -> str: @@ -9,6 +10,6 @@ class Pipeline(ABC): input_path (_type_): Path to the input file. Returns: - str: Anonymized text. + str: Anonymized text. """ pass diff --git a/src/pipeline/sequential_jsonl.py b/src/pipeline/sequential_jsonl.py index a16eda7ee62c1ba5afea76d1f52ff4adf3f729dd..01effdb36a653011ebfc96dc79d597838e8e60ba 100644 --- a/src/pipeline/sequential_jsonl.py +++ b/src/pipeline/sequential_jsonl.py @@ -35,7 +35,7 @@ class SequentialJSONLPipeline(Pipeline): detected_entities += detector.detect( parsed_input[0], parsed_input[1] ) - + annotaitons_cleaned = self._suppressor.suppress(detected_entities) replaced_input = parsed_input[0] @@ -51,11 +51,15 @@ class SequentialJSONLPipeline(Pipeline): result_text = "" for item in result: text = item["text"] - if result_text != "" and result_text.rstrip() == result_text and text.lstrip() == text: + if ( + result_text != "" + and result_text.rstrip() == result_text + and text.lstrip() == text + ): result_text += " " + text else: result_text += text - + return result_text else: return "\n".join([json.dumps(item, ensure_ascii=False) for item in result]) diff --git a/src/replacers/tag_replacer.py b/src/replacers/tag_replacer.py index bf5c3057d1d4c134fbd981d44c0f412d1208d620..427a3d6946cb9c1f45007886470cd6e4106173d7 100644 --- a/src/replacers/tag_replacer.py +++ b/src/replacers/tag_replacer.py @@ -19,7 +19,7 @@ from src.detections import ( HydronymDetection, SerialNumberDetection, KRSDetection, - NumberDetection + NumberDetection, ) from src.string_replacements import replace from src.replacers.interface import ReplacerInterface @@ -46,7 +46,7 @@ class TagReplacer(ReplacerInterface): TINDetection: "[CYFRY]", KRSDetection: "[CYFRY]", SerialNumberDetection: "[NUMER IDENTYFIKACYJNY]", - NumberDetection: "[CYFRY]" + NumberDetection: "[CYFRY]", } def replace( diff --git a/tests/integration/wiktorner_jsonl_txt_output_configuration/test_wiktorner_jsonl_txt_output_configuration.py b/tests/integration/wiktorner_jsonl_txt_output_configuration/test_wiktorner_jsonl_txt_output_configuration.py index 3cb494fdca34d0a9073e92824b21d19b5644483d..6974f224483edae9ca902c477e1d9e6b24171ca2 100644 --- a/tests/integration/wiktorner_jsonl_txt_output_configuration/test_wiktorner_jsonl_txt_output_configuration.py +++ b/tests/integration/wiktorner_jsonl_txt_output_configuration/test_wiktorner_jsonl_txt_output_configuration.py @@ -6,7 +6,10 @@ def test_wiktorner_jsonl_txt_output_configuration(): with initialize(config_path="../../../config", version_base="1.1"): config = compose( config_name="config", - overrides=["paths.root_path=../../../", "configuration=wiktorner_jsonl_txt_output"], + overrides=[ + "paths.root_path=../../../", + "configuration=wiktorner_jsonl_txt_output", + ], ) pipeline = instantiate(config.pipeline) @@ -16,5 +19,5 @@ def test_wiktorner_jsonl_txt_output_configuration(): assert ( result - == 'ROZDZIAŠI. CO NIECO O SAMEJ PIPIDÓWCE Przede wszystkim muszę uprzedzić z góry czytelników, aby się daremnie nie trudzili nad szukaniem wyżej wyrażonego miasteczka na mapach [MIEJSCE] i [MIEJSCE], bo go tam nie znajdą. Nie dlatego, jakoby [MIEJSCE] nie istniała w rzeczywistości i była tylko wytworem fantazji autora, ale po prostu dlatego, że mieszkańcy owego sławnego grodu, urosłszy z czasem w ambicję, uważali tę nazwę jako ubliżającą ich powadze i podali do c. k. namiestnictwa pokorną prośbę o pozwolenie zamienienia jej na inną. Podobne zamiany nazwisk praktykują się dość często w [MIEJSCE], szczególnie u pojedynczych osób, które nie czując się na siłach uszlachetnienia sobą, swymi czynami własnego nazwiska, chcą nazwiskiem uszlachetnić siebie, i tak np. ROZDZIAŠI. CO NIECO O SAMEJ PIPIDÓWCE Przede wszystkim muszę uprzedzić z góry czytelników, aby się daremnie nie trudzili nad szukaniem wyżej wyrażonego miasteczka na mapach [MIEJSCE] i [MIEJSCE], bo go tam nie znajdą. Nie dlatego, jakoby [MIEJSCE] nie istniała w rzeczywistości i była tylko wytworem fantazji autora, ale po prostu dlatego, że mieszkańcy owego sławnego grodu, urosłszy z czasem w ambicję, uważali tę nazwę jako ubliżającą ich powadze i podali do c. k. namiestnictwa pokorną prośbę o pozwolenie zamienienia jej na inną. Podobne zamiany nazwisk praktykują się dość często w [MIEJSCE], szczególnie u pojedynczych osób, które nie czując się na siłach uszlachetnienia sobą, swymi czynami własnego nazwiska, chcą nazwiskiem uszlachetnić siebie, i tak np.' + == "ROZDZIAŠI. CO NIECO O SAMEJ PIPIDÓWCE Przede wszystkim muszę uprzedzić z góry czytelników, aby się daremnie nie trudzili nad szukaniem wyżej wyrażonego miasteczka na mapach [MIEJSCE] i [MIEJSCE], bo go tam nie znajdą. Nie dlatego, jakoby [MIEJSCE] nie istniała w rzeczywistości i była tylko wytworem fantazji autora, ale po prostu dlatego, że mieszkańcy owego sławnego grodu, urosłszy z czasem w ambicję, uważali tę nazwę jako ubliżającą ich powadze i podali do c. k. namiestnictwa pokorną prośbę o pozwolenie zamienienia jej na inną. Podobne zamiany nazwisk praktykują się dość często w [MIEJSCE], szczególnie u pojedynczych osób, które nie czując się na siłach uszlachetnienia sobą, swymi czynami własnego nazwiska, chcą nazwiskiem uszlachetnić siebie, i tak np. ROZDZIAŠI. CO NIECO O SAMEJ PIPIDÓWCE Przede wszystkim muszę uprzedzić z góry czytelników, aby się daremnie nie trudzili nad szukaniem wyżej wyrażonego miasteczka na mapach [MIEJSCE] i [MIEJSCE], bo go tam nie znajdą. Nie dlatego, jakoby [MIEJSCE] nie istniała w rzeczywistości i była tylko wytworem fantazji autora, ale po prostu dlatego, że mieszkańcy owego sławnego grodu, urosłszy z czasem w ambicję, uważali tę nazwę jako ubliżającą ich powadze i podali do c. k. namiestnictwa pokorną prośbę o pozwolenie zamienienia jej na inną. Podobne zamiany nazwisk praktykują się dość często w [MIEJSCE], szczególnie u pojedynczych osób, które nie czując się na siłach uszlachetnienia sobą, swymi czynami własnego nazwiska, chcą nazwiskiem uszlachetnić siebie, i tak np." ) diff --git a/tests/unit/detectors/date/test_pl.py b/tests/unit/detectors/date/test_pl.py index be0392fe04392aade3aebd4db1ce969bf1e20640..6281c8055980dd55d1a1d97e5fbf842b48c7a84a 100644 --- a/tests/unit/detectors/date/test_pl.py +++ b/tests/unit/detectors/date/test_pl.py @@ -35,11 +35,12 @@ def test_detect_dates_pl(): (34, 49, DateDetection(format_date2)), ] + def test_date_with_different_punctuations(): # There is discussion about this wheter we should even detect such cases # as a dates... However, for now we do and if we find cases where that is # problematic, this definitly could be changed. - + detector = DateDetector("pl") text = "1.01,2022" @@ -49,23 +50,24 @@ def test_date_with_different_punctuations(): ( DateDetection.AnnotationPart.TWO_DIGITS_DAY, "01", - ), + ), (DateDetection.AnnotationPart.OTHER, "."), (DateDetection.AnnotationPart.TWO_DIGIT_MONTH, "01"), (DateDetection.AnnotationPart.OTHER, ","), (DateDetection.AnnotationPart.FOUR_DIGIT_YEAR, "2022"), ] - + assert found_dates == [ (0, 9, DateDetection(format_date)), ] - + + def test_28_czerwca_1847(): detector = DateDetector("pl") - + text = "28 czerwca 1847 wraz z ojcem skazany na karę śmierci." found_dates = detector.detect(text, dict()) - + format_date = [ ( DateDetection.AnnotationPart.TWO_DIGITS_DAY, @@ -76,21 +78,22 @@ def test_28_czerwca_1847(): (DateDetection.AnnotationPart.OTHER, " "), (DateDetection.AnnotationPart.FOUR_DIGIT_YEAR, "1847"), ] - + assert found_dates == [ (0, 15, DateDetection(format_date)), ] - + + def test_year_only(): detector = DateDetector("pl") - + text = "W 2020 roku kupiłem psa." found_dates = detector.detect(text, dict()) - + format_date = [ (DateDetection.AnnotationPart.FOUR_DIGIT_YEAR, "2020"), ] - + assert found_dates == [ (2, 6, DateDetection(format_date)), - ] \ No newline at end of file + ]