diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3ec17705d1ae8ae2f2e7cf62cf5a3ac6812933a8..ee81085289d881c2cb9c4e33e3635cbc298d88b9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -55,13 +55,10 @@ pages: build_master: stage: builds - image: 'docker:18.09.7' + image: docker only: - master - services: - - 'docker:18.09.7-dind' script: - - docker build -t $CI_REGISTRY_IMAGE:latest -f DockerFile . + - docker build --load -t $CI_REGISTRY_IMAGE:latest -f DockerFile . - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY - docker push $CI_REGISTRY_IMAGE:latest - diff --git a/src/easymatcher_worker.py b/src/easymatcher_worker.py index b87c2ea874591d9a7cbaac07c06333e238e097d2..d8373a4d13e1204e68d6f6971cde60e03847346b 100644 --- a/src/easymatcher_worker.py +++ b/src/easymatcher_worker.py @@ -21,23 +21,45 @@ class EasymatcherWorker(nlp_ws.NLPWorker): https://gitlab.clarin-pl.eu/knowledge-extraction/tools/easymatcher """ + @staticmethod + def is_jsonl( + document_path: str | Path + ) -> bool: + """Validates whether text file has json/jsonl structure.""" + try: + with open(document_path, 'r', encoding="utf-8") as file: + for line in file: + json_obj = json.loads(line) + if "text" not in json_obj: + return False + return True + except (json.JSONDecodeError, FileNotFoundError): + return False + @staticmethod def prepare_and_append_document( - file_path: str | Path, document_path: str | Path + file_path: str | Path, document_path: str | Path ) -> None: """Formats and appends plain texts into jsonl file.""" document = {} - with open(document_path, "r", encoding="utf-8") as _df: - document["text"] = _df.read() + if EasymatcherWorker.is_jsonl(document_path): + with open(file_path, "a", encoding="utf-8") as _f: + with open(document_path, "r", encoding="utf-8") as _df: + for line in _df: + line_data = json.loads(line) + _f.write(json.dumps(line_data) + "\n") + else: + with open(document_path, "r", encoding="utf-8") as _df: + document["text"] = _df.read() - with open(file_path, "a", encoding="utf-8") as _f: - _f.write(json.dumps(document) + "\n") + with open(file_path, "a", encoding="utf-8") as _f: + _f.write(json.dumps(document) + "\n") def process( - self, - input_path: str, - task_options: dict[str, str | int | float], - output_path: str, + self, + input_path: str, + task_options: dict[str, str | int | float], + output_path: str, ) -> None: """Called for each request made to the worker. @@ -83,5 +105,8 @@ class EasymatcherWorker(nlp_ws.NLPWorker): os.unlink(tmpf.name) with open(output_path, "w", encoding="utf-8") as _f: - for out_document in out_documents: - _f.write(json.dumps(out_document) + "\n") + for out_document, document in zip(out_documents, documents): + # We want to keep content of the original labeled documents + document['label'] = out_document['label'] + document['text'] = out_document['text'] + _f.write(json.dumps(document) + "\n") diff --git a/tests/example_data/expected/document_with_concrete.jsonl b/tests/example_data/expected/document_with_concrete.jsonl new file mode 100644 index 0000000000000000000000000000000000000000..cb94387c27ce617bcb199950cba6e25f5e925c5b --- /dev/null +++ b/tests/example_data/expected/document_with_concrete.jsonl @@ -0,0 +1 @@ +{"text": "8703 Tarcza szlifierska Scanmaskin SC Elastic Metal 125 mm do betonu, lastryka i kamienia naturalnego (ziarnisto\u015b\u0107 30) Tarcza szlifierska SC Elastic Metal 125 mm marki Scanmaskin s\u0142u\u017cy do szlifowania betonu, lastryka i naturalnego kamienia. Powinna byc\u0301 stosowana przed na\u0142oz\u0307eniem cienkiej pow\u0142oki lub polerowaniem posadzki, aby usuna\u0328c\u0301 z powierzchni g\u0142e\u0328bokie zarysowania. Opis wariantu: Ziarnisto\u015b\u0107 30", "label": [[46, 51, "Metal"], [62, 68, "Beton"], [70, 78, "Lastryko"], [81, 89, "Kamie\u0144 naturalny"], [149, 154, "Metal"], [200, 206, "Beton"], [208, 216, "Lastryko"], [219, 239, "Kamie\u0144 naturalny"]]} diff --git a/tests/example_data/expected/documents.jsonl b/tests/example_data/expected/documents.jsonl index 1fce06743f2fcc88c4fc1170f14b4922bd487d24..b34ea4dc565aade51ce5e7e19fe5d25f0f35ecd0 100644 --- a/tests/example_data/expected/documents.jsonl +++ b/tests/example_data/expected/documents.jsonl @@ -1,2 +1,3 @@ {"text": "55calowy telewizor QLED Samsung QE55Q75TA Pozw\\u00f3l sobie na luksus wybieraj\\u0105c telewizor QLED Samsung QE55Q75TA kt\\u00f3ry zabierze Ci\\u0119 w \\u015bwiat wspania\\u0142ej rozrywki bez wychodzenia z domu Umozliwia Aktualizowanie oprogramowania przez USB oraz posiada Aktualizacje oprogramowania sprz\u0119towego przez Internet", "label": [[19, 23, "Typ TV"], [96, 100, "Typ TV"], [219, 258, "Aktualizowanie oprogramowania przez USB"], [272, 326, "Aktualizacja oprogramowania sprz\u0119towego przez Internet"]]} {"text": "55calowy telewizor QLED Samsung QE55Q75TA Pozw\\u00f3l sobie na luksus wybieraj\\u0105c telewizor QLED Samsung QE55Q75TA kt\\u00f3ry zabierze Ci\\u0119 w \\u015bwiat wspania\\u0142ej rozrywki bez wychodzenia z domu", "label": [[19, 23, "Typ TV"], [96, 100, "Typ TV"]]} +{"text": "8703 Tarcza szlifierska Scanmaskin SC Elastic Metal 125 mm do betonu, lastryka i kamienia naturalnego (ziarnisto\u015b\u0107 30) Tarcza szlifierska SC Elastic Metal 125 mm marki Scanmaskin s\u0142u\u017cy do szlifowania betonu, lastryka i naturalnego kamienia. Powinna byc\u0301 stosowana przed na\u0142oz\u0307eniem cienkiej pow\u0142oki lub polerowaniem posadzki, aby usuna\u0328c\u0301 z powierzchni g\u0142e\u0328bokie zarysowania. Opis wariantu: Ziarnisto\u015b\u0107 30", "label": []} diff --git a/tests/example_data/input/document_with_concrete.txt b/tests/example_data/input/document_with_concrete.txt new file mode 100644 index 0000000000000000000000000000000000000000..0ac7d3feb4e1cfa794f477f6a21e8f1d44e1572f --- /dev/null +++ b/tests/example_data/input/document_with_concrete.txt @@ -0,0 +1 @@ +{"text": "8703 Tarcza szlifierska Scanmaskin SC Elastic Metal 125 mm do betonu, lastryka i kamienia naturalnego (ziarnistość 30) Tarcza szlifierska SC Elastic Metal 125 mm marki Scanmaskin służy do szlifowania betonu, lastryka i naturalnego kamienia. Powinna być stosowana przed nałożeniem cienkiej powłoki lub polerowaniem posadzki, aby usunąć z powierzchni głębokie zarysowania. Opis wariantu: Ziarnistość 30","label":[]} diff --git a/tests/example_data/input/labels_with_concrete.json b/tests/example_data/input/labels_with_concrete.json new file mode 100644 index 0000000000000000000000000000000000000000..6b48c69994e1674a471263da5f603c5830bdbc3f --- /dev/null +++ b/tests/example_data/input/labels_with_concrete.json @@ -0,0 +1,332 @@ +{ + "labels": { + "Asfalt": [ + "asfalt", + "takich jak asfalt", + "Asfalt" + ], + "Beton": [ + "idealna do betonu", + "beton", + "do kostki betonowej", + "Beton", + "beton" + ], + "Beton - miękki": [ + "twardy", + "średni", + "miękki", + "ścierny", + "od miękkich do bardzo twardych kruszyw" + ], + "Beton - ścierny": [ + "ścierny", + "od miękkich do bardzo" + ], + "Beton - średni": [ + "średni", + "od miękkich do bardzo twardych" + ], + "Beton - świeży": [ + "beton świeży", + "świeży beton", + "dla cięcia betonu świeżego", + "betonu próżniowego" + ], + "Beton - twardy": [ + "z betonem utwardzonym", + "bardzo twardych kruszyw betonowych", + "twardego betonu", + "Twardy beton", + "Beton twardy" + ], + "Beton - zbrojony": [ + "betonie zbrojonym", + "idealna do silnie zbrojonego betonu", + "Beton zbrojony", + "także zbrojnego", + "betonu zbrojonego", + "Beton zbrojny", + "beton zbrojny", + "Beton zbrojony", + "beton zbrojony" + ], + "Bloki ścienne": [ + "bloki ścienne" + ], + "Bloki wapienne": [ + "do bloków wapiennych", + "Blok wapienny" + ], + "Cegła": [ + "cegł", + "cegły", + "cegła", + "Cegła", + "cegła", + "cegłą", + "cegły", + "Cegła wapienno-piaskowa" + ], + "Ceramika": [ + "ceramika", + "ceramiki", + "płytki ceramiczne", + "glazurowana ceramika", + "tarcza do płytek", + "twarde płytki ceramiczne", + "płytki ceramiczne", + "Ceramika", + "ceramika" + ], + "Chrommagnezyt": [ + "Chrommagnezyt" + ], + "Dachówka": [ + "Dachówka cementowa", + "dachówek betonowych", + "dachówka" + ], + "Drewno": [ + "Drewno", + "drewno" + ], + "Fuga murarska": [ + "Fuga murarska", + "Fugi murarskie" + ], + "Glazura": [ + "do cięcia glazury" + ], + "Gnejs": [ + "gnejsu", + "gnejs" + ], + "Grafit": [ + "grafit" + ], + "Granit": [ + "Cięcie takich materiałów jak granit", + "marmur", + "naturalny kamień jest dopuszczalne przy użyciu opisywanej tarczy", + "granit", + "granitu", + "rawężniki granitowe", + "granir", + "Granit" + ], + "Gres": [ + "Gres" + ], + "Jastrych": [ + "jastrych" + ], + "Kamień naturalny": [ + "kamienia", + "naturalnego kamienia", + "kamień naturalny", + "naturalny kamień jest dopuszczalne przy użyciu opisywanej tarczy", + "kamień naturalny", + "kamien naturalny", + "kamienia", + "Kamień", + "kamień", + "Kamień naturalny" + ], + "Kamionki": [ + "kamionki" + ], + "Klej": [ + "Klej" + ], + "Klinkier": [ + "Klinkier", + "klinkier", + "linkier" + ], + "Kostka brukowa": [ + "kostki brukowej", + "kostka brukowa" + ], + "Krzemionka": [ + "Krzemionka" + ], + "Kwarcyt": [ + "kwarcyt", + "Kwarcyt" + ], + "Lastryko": [ + "lastryka", + "lastryk" + ], + "Łupek": [ + "łupek" + ], + "Magnezyt": [ + "Magnezyt" + ], + "Marmur": [ + "Cięcie takich materiałów jak granit", + "marmur", + "marmur", + "marmuru", + "Marmur", + "marmur" + ], + "Materiały budowlane": [ + "materiały", + "Materiały budowlane", + "materiały budowlane", + "standardowych materiałów budowlanych" + ], + "Materiały ścierne": [ + "materiały ścierne" + ], + "Materiały twarde": [ + "Materiały twarde" + ], + "Metal": [ + "Metal" + ], + "Miękkie kruszywa": [ + "miękkich do twardych kruszyw", + "innych miękkich materiałów" + ], + "PCV": [ + "PCV" + ], + "Piaskowiec": [ + "iaskow", + "piaskowca", + "piaskowiec" + ], + "Plastik": [ + "Plastik" + ], + "Płyta chodnikowa": [ + "płyta chodnikowa" + ], + "Płytki": [ + "Płytki", + "płytki" + ], + "Płytki betonowe": [ + "płytki betonowe" + ], + "Płytki ceramiczne": [ + "Płytki ceramiczne", + "płytki ceramiczne" + ], + "Płytki marmurowe": [ + "płytek betonowych i marmurowych" + ], + "Polbruk": [ + "polbruk" + ], + "Porcelana": [ + "porcelan", + "Porcelana", + "porcelana" + ], + "Porfir": [ + "Porfir", + "porfir" + ], + "Poroterm": [ + "poroterm" + ], + "Powłoki posadzkowe": [ + "Powłoki posadzkowe" + ], + "Powłoki ścienne": [ + "Powłoki ścienne" + ], + "Rury z żeliwa ciągliwego": [ + "Rury z żeliwa ciągliwego" + ], + "Stal": [ + "Stal", + "stal" + ], + "Stal nierdzewna": [ + "stali nierdzewnej" + ], + "Strunobeton": [ + "Strunobeton" + ], + "Szamot": [ + "szamot", + "Szamot" + ], + "Szkło pancerne": [ + "Szkło pancerne" + ], + "Sztuczny kamień": [ + "sztucznego kamienia" + ], + "Ścierna płyta budowlana": [ + "ścierna płyta budowlana" + ], + "Ścierne": [ + "ścierne" + ], + "Terazzo": [ + "terazzo" + ], + "Tlenek cyrkonu": [ + "tlenek cyrkonu" + ], + "Tlenek glinu": [ + "tlenek glinu" + ], + "Trawertyn": [ + "trawert", + "trawertyn" + ], + "Twarda glazura": [ + "Twarda glazura", + "twarda glazura" + ], + "Twarde kruszywa": [ + "twardych kruszyw" + ], + "Twarde materiały": [ + "twardych materiałach", + "twarde", + "twardych materiałów", + "im twardszy materiał" + ], + "Tworzywo sztuczne": [ + "Tworzywo sztuczne", + "tworzywo sztuczne" + ], + "Tynk": [ + "tynk" + ], + "Wapień": [ + "wapień" + ], + "Zaprawa murarska": [ + "Zaprawa murarska", + "zaprawa murarska" + ], + "Żelazo": [ + "Żelazo" + ], + "Żeliwo": [ + "Żeliwo", + "żeliwo" + ], + "Żeliwo sferoidalne": [ + "żeliwo sferoidalne" + ] + }, + "blackList": { + "Metal": [ + "Elastic Metal" + ], + "Średnica otworu montażowego tarczy - uwaga": [ + "mm" + ] + } +} \ No newline at end of file diff --git a/tests/fixtures/example_data.py b/tests/fixtures/example_data.py index 05e6891a61fe5934bacf39810fcee46631eb159c..8a76ed08371da2b44b4f51a937362524e2cfc0ac 100644 --- a/tests/fixtures/example_data.py +++ b/tests/fixtures/example_data.py @@ -32,3 +32,12 @@ def example_labels_path(input_dir: Path) -> Path: def example_document_path(input_dir: Path, request) -> Path: document_number = request.param return input_dir / f"document{document_number}.txt" + + +@pytest.fixture +def example_document_path_jsonl(input_dir: Path, request) -> Path: + return input_dir / "document_with_concrete.txt" + +@pytest.fixture +def example_labels_path_jsonl(input_dir: Path) -> Path: + return input_dir / "labels_with_concrete.json" diff --git a/tests/utils.py b/tests/utils.py index 703c07fcd5df97496deeac4b54ca0a5eb9734262..4bdde7f21dec70eb7d425cfcf41de197e3bdfbb2 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,8 +1,15 @@ import os +import json from filecmp import cmp from pathlib import Path +def load_jsonl(file_path: Path): + with open(file_path) as f: + data = [json.loads(line) for line in f] + return data def check_and_cleanup(output_path: Path, expected_path: Path) -> Path: - assert cmp(output_path, expected_path) + output = load_jsonl(output_path) + expected = load_jsonl(expected_path) + assert sorted(output, key=lambda d : d['text']) == sorted(expected, key=lambda d : d['text']) os.remove(output_path) diff --git a/tests/worker/test_worker.py b/tests/worker/test_worker.py index 868416c1d1a01787f3d6a5ce0a2de5d919d017c2..1149147687d816082cb36080f48182ed60c05095 100644 --- a/tests/worker/test_worker.py +++ b/tests/worker/test_worker.py @@ -49,3 +49,17 @@ def test_easymatcher_process_folder( expected_path = expected_dir / "documents.jsonl" worker.process(input_dir, task_options, output_path) check_and_cleanup(output_path, expected_path) + + +def test_easymatcher_process_jsonl_document_structure( + worker: EasymatcherWorker, + example_document_path_jsonl: Path, + example_labels_path_jsonl: Path, + output_dir: Path, + expected_dir: Path, +): + task_options = {"labels_path": example_labels_path_jsonl} + output_path = output_dir / f"{example_document_path_jsonl.stem}.jsonl" + expected_path = expected_dir / f"{example_document_path_jsonl.stem}.jsonl" + worker.process(example_document_path_jsonl, task_options, output_path) + check_and_cleanup(output_path, expected_path) \ No newline at end of file