From 67d4b17f792d9db1a1789dda0dfdb4151dc3fcfa Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Wed, 10 Jan 2024 17:38:37 +0100 Subject: [PATCH 001/142] initial configs --- tests/network/__init__.py | 0 tests/network/conftest.py | 4 ++ tests/network/constants.py | 18 ++++++ tests/network/docker-compose.yml | 18 ++++++ tests/network/fixtures_mongodb.py | 46 ++++++++++++++ tests/network/fixtures_rabbitmq.py | 99 ++++++++++++++++++++++++++++++ tests/network/test_rabbitmq.py | 80 ++++++++++++++++++++++++ tests/network/utils.py | 13 ++++ 8 files changed, 278 insertions(+) create mode 100644 tests/network/__init__.py create mode 100644 tests/network/conftest.py create mode 100644 tests/network/constants.py create mode 100644 tests/network/docker-compose.yml create mode 100644 tests/network/fixtures_mongodb.py create mode 100644 tests/network/fixtures_rabbitmq.py create mode 100644 tests/network/test_rabbitmq.py create mode 100644 tests/network/utils.py diff --git a/tests/network/__init__.py b/tests/network/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/network/conftest.py b/tests/network/conftest.py new file mode 100644 index 0000000000..da47f3f8b5 --- /dev/null +++ b/tests/network/conftest.py @@ -0,0 +1,4 @@ +pytest_plugins = [ + "tests.network.fixtures_mongodb", + "tests.network.fixtures_rabbitmq" +] diff --git a/tests/network/constants.py b/tests/network/constants.py new file mode 100644 index 0000000000..d769ef51fb --- /dev/null +++ b/tests/network/constants.py @@ -0,0 +1,18 @@ +__all__ = [ + "DB_NAME", + "DB_URL", + "DEFAULT_EXCHANGER_NAME", + "DEFAULT_QUEUE", + "PROCESSING_SERVER_URL", + "RABBITMQ_URL" +] + +# mongodb://localhost:6701/ocrd_network_test +DB_NAME: str = "ocrd_network_test" +DB_URL: str = "mongodb://localhost:6701" + +DEFAULT_EXCHANGER_NAME: str = "ocrd-network-default" +DEFAULT_QUEUE: str = "ocrd-network-default" + +PROCESSING_SERVER_URL: str = "http://localhost:8000" +RABBITMQ_URL: str = "amqp://network_test:network_test@localhost:6672/" diff --git a/tests/network/docker-compose.yml b/tests/network/docker-compose.yml new file mode 100644 index 0000000000..2300dc6f09 --- /dev/null +++ b/tests/network/docker-compose.yml @@ -0,0 +1,18 @@ +services: + ocrd_network_mongo_db: + image: "mongo" + ports: + - "6701:27017" + environment: + - MONGO_INITDB_ROOT_USERNAME="network_test" + - MONGO_INITDB_ROOT_PASSWORD="network_test" + ocrd_network_rabbit_mq: + image: "rabbitmq:3.12-management" + ports: + - "6672:5672" + - "16672:15672" + - "26672:25672" + environment: + - RABBITMQ_DEFAULT_USER="network_test" + - RABBITMQ_DEFAULT_PASS="network_test" + - RABBITMQ_FEATURE_FLAGS=quorum_queue,implicit_default_bindings,classic_mirrored_queue_version diff --git a/tests/network/fixtures_mongodb.py b/tests/network/fixtures_mongodb.py new file mode 100644 index 0000000000..80d7b4c8ff --- /dev/null +++ b/tests/network/fixtures_mongodb.py @@ -0,0 +1,46 @@ +from pymongo import MongoClient, uri_parser as mongo_uri_parser +from pytest import fixture +from .constants import DB_NAME, DB_URL +from .utils import is_url_responsive + + +def verify_database_uri(mongodb_address: str) -> str: + try: + # perform validation check + mongo_uri_parser.parse_uri(uri=mongodb_address, validate=True) + except Exception as error: + raise ValueError(f"The MongoDB address '{mongodb_address}' is in wrong format, {error}") + return mongodb_address + + +@fixture(scope="package", name="mongo_db") +def fixture_mongo_db(docker_ip, docker_services): + port = docker_services.port_for("ocrd_network_mongo_db", 27017) + mongo_db_url = f"http://{docker_ip}:{port}" + docker_services.wait_until_responsive( + timeout=10.0, + pause=0.1, + check=lambda: is_url_responsive(mongo_db_url, retries=10) + ) + + +@fixture(scope="package", name="mongo_client") +def fixture_mongo_client(mongo_db): + mongo_client = MongoClient(DB_URL, serverSelectionTimeoutMS=3000) + yield mongo_client + + +@fixture(scope="package", name="mongo_processor_jobs") +def fixture_mongo_processor_jobs(mongo_client): + mydb = mongo_client[DB_NAME] + processor_jobs_collection = mydb["DBProcessorJob"] + yield processor_jobs_collection + processor_jobs_collection.drop() + + +@fixture(scope="package", name="mongo_workflow_jobs") +def fixture_mongo_workflow_jobs(mongo_client): + mydb = mongo_client[DB_NAME] + workflow_jobs_collection = mydb["DBWorkflowJob"] + yield workflow_jobs_collection + workflow_jobs_collection.drop() diff --git a/tests/network/fixtures_rabbitmq.py b/tests/network/fixtures_rabbitmq.py new file mode 100644 index 0000000000..28c3b7dc83 --- /dev/null +++ b/tests/network/fixtures_rabbitmq.py @@ -0,0 +1,99 @@ +from pika import URLParameters +from pika.credentials import PlainCredentials +from pytest import fixture +from re import match as re_match +from ocrd_network.rabbitmq_utils import RMQConnector, RMQConsumer, RMQPublisher +from .constants import RABBITMQ_URL, DEFAULT_EXCHANGER_NAME, DEFAULT_QUEUE +from .utils import is_url_responsive + + +def verify_and_parse_mq_uri(rabbitmq_address: str): + """ + Check the full list of available parameters in the docs here: + https://pika.readthedocs.io/en/stable/_modules/pika/connection.html#URLParameters + """ + + uri_pattern = r"^(?:([^:\/?#\s]+):\/{2})?(?:([^@\/?#\s]+)@)?([^\/?#\s]+)?(?:\/([^?#\s]*))?(?:[?]([^#\s]+))?\S*$" + match = re_match(pattern=uri_pattern, string=rabbitmq_address) + if not match: + raise ValueError(f"The RabbitMQ server address is in wrong format: '{rabbitmq_address}'") + url_params = URLParameters(rabbitmq_address) + + parsed_data = { + "username": url_params.credentials.username, + "password": url_params.credentials.password, + "host": url_params.host, + "port": url_params.port, + "vhost": url_params.virtual_host + } + return parsed_data + + +@fixture(scope="package", name="rabbit_mq") +def fixture_rabbit_mq(docker_ip, docker_services): + port = docker_services.port_for("ocrd_network_rabbit_mq", 15672) + rabbit_mq_management_url = f"http://{docker_ip}:{port}" + docker_services.wait_until_responsive( + timeout=10.0, + pause=0.1, + check=lambda: is_url_responsive(rabbit_mq_management_url, retries=10) + ) + + +@fixture(scope="package", name="rabbitmq_defaults") +def fixture_rabbitmq_defaults(): + rmq_data = verify_and_parse_mq_uri(RABBITMQ_URL) + rmq_username = rmq_data["username"] + rmq_password = rmq_data["password"] + rmq_host = rmq_data["host"] + rmq_port = rmq_data["port"] + rmq_vhost = rmq_data["vhost"] + + test_connection = RMQConnector.open_blocking_connection( + credentials=PlainCredentials(rmq_username, rmq_password), + host=rmq_host, + port=rmq_port, + vhost=rmq_vhost + ) + test_channel = RMQConnector.open_blocking_channel(test_connection) + RMQConnector.exchange_declare(channel=test_channel, exchange_name=DEFAULT_EXCHANGER_NAME, durable=False) + RMQConnector.queue_declare(channel=test_channel, queue_name=DEFAULT_QUEUE, durable=False) + RMQConnector.queue_bind( + channel=test_channel, + exchange_name=DEFAULT_EXCHANGER_NAME, + queue_name=DEFAULT_QUEUE, + routing_key=DEFAULT_QUEUE + ) + # Clean all messages inside if any from previous tests + RMQConnector.queue_purge(channel=test_channel, queue_name=DEFAULT_QUEUE) + + +@fixture(scope="package", name="rabbitmq_publisher") +def fixture_rabbitmq_publisher(rabbitmq_defaults): + rmq_data = verify_and_parse_mq_uri(RABBITMQ_URL) + rmq_publisher = RMQPublisher( + host=rmq_data["host"], + port=rmq_data["port"], + vhost=rmq_data["vhost"] + ) + rmq_publisher.authenticate_and_connect( + username=rmq_data["username"], + password=rmq_data["password"] + ) + rmq_publisher.enable_delivery_confirmations() + yield rmq_publisher + + +@fixture(scope="package", name="rabbitmq_consumer") +def fixture_rabbitmq_consumer(rabbitmq_defaults): + rmq_data = verify_and_parse_mq_uri(RABBITMQ_URL) + rmq_consumer = RMQConsumer( + host=rmq_data["host"], + port=rmq_data["port"], + vhost=rmq_data["vhost"] + ) + rmq_consumer.authenticate_and_connect( + username=rmq_data["username"], + password=rmq_data["password"] + ) + yield rmq_consumer diff --git a/tests/network/test_rabbitmq.py b/tests/network/test_rabbitmq.py new file mode 100644 index 0000000000..f9a2e89419 --- /dev/null +++ b/tests/network/test_rabbitmq.py @@ -0,0 +1,80 @@ +from pika import BasicProperties +from pickle import dumps, loads +from .constants import DEFAULT_EXCHANGER_NAME, DEFAULT_QUEUE + + +def test_publish_2_messages_to_rabbitmq(rabbitmq_publisher): + test_headers = {"Test Header": "Test Value"} + test_properties = BasicProperties( + app_id='webapi-processing-broker', + content_type='application/json', + headers=test_headers + ) + rabbitmq_publisher.publish_to_queue( + queue_name=DEFAULT_QUEUE, + message="RabbitMQ test 123", + exchange_name=DEFAULT_EXCHANGER_NAME, + properties=test_properties + ) + rabbitmq_publisher.publish_to_queue( + queue_name=DEFAULT_QUEUE, + message="RabbitMQ test 456", + exchange_name=DEFAULT_EXCHANGER_NAME, + properties=test_properties + ) + assert rabbitmq_publisher.message_counter == 2 + + +def test_consume_2_messages_from_rabbitmq(rabbitmq_consumer): + # Consume the 1st message + method_frame, header_frame, message = rabbitmq_consumer.get_one_message( + queue_name=DEFAULT_QUEUE, + auto_ack=True + ) + assert method_frame.delivery_tag == 1 # 1st delivered message to this queue + assert method_frame.message_count == 1 # messages left in the queue + assert method_frame.redelivered is False + assert method_frame.exchange == DEFAULT_EXCHANGER_NAME + assert method_frame.routing_key == DEFAULT_QUEUE + # It's possible to assert header_frame the same way + assert message.decode() == "RabbitMQ test 123" + + # Consume the 2nd message + method_frame, header_frame, message = rabbitmq_consumer.get_one_message( + queue_name=DEFAULT_QUEUE, + auto_ack=True + ) + assert method_frame.delivery_tag == 2 # 2nd delivered message to this queue + assert method_frame.message_count == 0 # messages left in the queue + assert method_frame.redelivered is False + assert method_frame.exchange == DEFAULT_EXCHANGER_NAME + assert method_frame.routing_key == DEFAULT_QUEUE + # It's possible to assert header_frame the same way + assert message.decode() == "RabbitMQ test 456" + + +def test_publish_ocrd_message_to_rabbitmq(rabbitmq_publisher): + ocrd_processing_message = { + "job_id": "Test_job_id", + "workflow_id": "Test_workflow_id", + "workspace_id": "Test_workspace_id" + } + message_bytes = dumps(ocrd_processing_message) + rabbitmq_publisher.publish_to_queue( + queue_name=DEFAULT_QUEUE, + message=message_bytes, + exchange_name=DEFAULT_EXCHANGER_NAME, + properties=None + ) + + +def test_consume_ocrd_message_from_rabbitmq(rabbitmq_consumer): + method_frame, header_frame, message = rabbitmq_consumer.get_one_message( + queue_name=DEFAULT_QUEUE, + auto_ack=True + ) + assert method_frame.message_count == 0 # messages left in the queue + decoded_message = loads(message) + assert decoded_message["job_id"] == "Test_job_id" + assert decoded_message["workflow_id"] == "Test_workflow_id" + assert decoded_message["workspace_id"] == "Test_workspace_id" diff --git a/tests/network/utils.py b/tests/network/utils.py new file mode 100644 index 0000000000..0d33fbc30d --- /dev/null +++ b/tests/network/utils.py @@ -0,0 +1,13 @@ +from requests import get + + +def is_url_responsive(url: str, retries: int = 0) -> bool: + while True: + try: + response = get(url) + if response.status_code == 200: + return True + except Exception: + if retries <= 0: + return False + retries -= 1 From ed13c4bc588ca07a0f43d8d24753ef9db5bb34e1 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Wed, 10 Jan 2024 18:29:08 +0100 Subject: [PATCH 002/142] fix rabbitmq tests --- tests/network/conftest.py | 8 ++++++++ tests/network/docker-compose.yml | 8 ++++---- tests/network/fixtures_rabbitmq.py | 9 +++++++-- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/tests/network/conftest.py b/tests/network/conftest.py index da47f3f8b5..67eb0b4399 100644 --- a/tests/network/conftest.py +++ b/tests/network/conftest.py @@ -1,4 +1,12 @@ +from pytest import fixture +from os.path import join + pytest_plugins = [ "tests.network.fixtures_mongodb", "tests.network.fixtures_rabbitmq" ] + + +@fixture(scope="session") +def docker_compose_file(pytestconfig): + return join(str(pytestconfig.rootdir), "docker-compose.yml") diff --git a/tests/network/docker-compose.yml b/tests/network/docker-compose.yml index 2300dc6f09..2805e2ebcd 100644 --- a/tests/network/docker-compose.yml +++ b/tests/network/docker-compose.yml @@ -4,8 +4,8 @@ services: ports: - "6701:27017" environment: - - MONGO_INITDB_ROOT_USERNAME="network_test" - - MONGO_INITDB_ROOT_PASSWORD="network_test" + - MONGO_INITDB_ROOT_USERNAME=network_test + - MONGO_INITDB_ROOT_PASSWORD=network_test ocrd_network_rabbit_mq: image: "rabbitmq:3.12-management" ports: @@ -13,6 +13,6 @@ services: - "16672:15672" - "26672:25672" environment: - - RABBITMQ_DEFAULT_USER="network_test" - - RABBITMQ_DEFAULT_PASS="network_test" + - RABBITMQ_DEFAULT_USER=network_test + - RABBITMQ_DEFAULT_PASS=network_test - RABBITMQ_FEATURE_FLAGS=quorum_queue,implicit_default_bindings,classic_mirrored_queue_version diff --git a/tests/network/fixtures_rabbitmq.py b/tests/network/fixtures_rabbitmq.py index 28c3b7dc83..6bf5ae9b3a 100644 --- a/tests/network/fixtures_rabbitmq.py +++ b/tests/network/fixtures_rabbitmq.py @@ -41,7 +41,7 @@ def fixture_rabbit_mq(docker_ip, docker_services): @fixture(scope="package", name="rabbitmq_defaults") -def fixture_rabbitmq_defaults(): +def fixture_rabbitmq_defaults(rabbit_mq): rmq_data = verify_and_parse_mq_uri(RABBITMQ_URL) rmq_username = rmq_data["username"] rmq_password = rmq_data["password"] @@ -56,7 +56,12 @@ def fixture_rabbitmq_defaults(): vhost=rmq_vhost ) test_channel = RMQConnector.open_blocking_channel(test_connection) - RMQConnector.exchange_declare(channel=test_channel, exchange_name=DEFAULT_EXCHANGER_NAME, durable=False) + RMQConnector.exchange_declare( + channel=test_channel, + exchange_name=DEFAULT_EXCHANGER_NAME, + exchange_type='direct', + durable=False + ) RMQConnector.queue_declare(channel=test_channel, queue_name=DEFAULT_QUEUE, durable=False) RMQConnector.queue_bind( channel=test_channel, From b8bab25dc78d49450891597e4326768234cbad81 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Wed, 10 Jan 2024 18:29:24 +0100 Subject: [PATCH 003/142] add pytest-docker to req --- requirements_test.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements_test.txt b/requirements_test.txt index 8b7997b1a0..254ccbd891 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -2,6 +2,7 @@ autopep8 pytest >= 4.0.0 generateDS == 2.35.20 pytest-benchmark >= 3.2.3 +pytest-docker>=1.0.0 coverage >= 4.5.2 sphinx sphinx_click From c18916e017d42b2a1b7320f856d10a8b765febed Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Thu, 11 Jan 2024 13:47:49 +0100 Subject: [PATCH 004/142] fix conftest - move to top level --- tests/{network => }/conftest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename tests/{network => }/conftest.py (63%) diff --git a/tests/network/conftest.py b/tests/conftest.py similarity index 63% rename from tests/network/conftest.py rename to tests/conftest.py index 67eb0b4399..f638928858 100644 --- a/tests/network/conftest.py +++ b/tests/conftest.py @@ -1,5 +1,5 @@ from pytest import fixture -from os.path import join +from pathlib import Path pytest_plugins = [ "tests.network.fixtures_mongodb", @@ -9,4 +9,4 @@ @fixture(scope="session") def docker_compose_file(pytestconfig): - return join(str(pytestconfig.rootdir), "docker-compose.yml") + return Path(str(pytestconfig.rootdir), "tests", "network", "docker-compose.yml") From f4c653b7dbab7b4f1be9da567f999406472fcb83 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Thu, 11 Jan 2024 14:02:15 +0100 Subject: [PATCH 005/142] fix docker file path --- tests/conftest.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index f638928858..49cdff50da 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,5 +1,6 @@ -from pytest import fixture from pathlib import Path +from pkg_resources import resource_filename +from pytest import fixture pytest_plugins = [ "tests.network.fixtures_mongodb", @@ -8,5 +9,5 @@ @fixture(scope="session") -def docker_compose_file(pytestconfig): - return Path(str(pytestconfig.rootdir), "tests", "network", "docker-compose.yml") +def docker_compose_file(): + return Path(resource_filename("tests", "network"), "docker-compose.yml") From 9e341ba674b27c6766727a096e057c14a22337d4 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Thu, 11 Jan 2024 14:48:19 +0100 Subject: [PATCH 006/142] experiment with circle ci --- .circleci/config.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 047780b810..c48fdc436f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -26,6 +26,8 @@ jobs: working_directory: ~/ocrd-core steps: - checkout + - setup_remote_docker: # https://circleci.com/docs/2.0/building-docker-images/ + docker_layer_caching: true - run: sudo apt-get -y update - run: sudo make deps-ubuntu - run: make install deps-test From 9f799e166e9e1ba3f76b64ac9e963246a433c17f Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Thu, 11 Jan 2024 15:41:59 +0100 Subject: [PATCH 007/142] increase timeout secs --- tests/network/fixtures_mongodb.py | 2 +- tests/network/fixtures_rabbitmq.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/network/fixtures_mongodb.py b/tests/network/fixtures_mongodb.py index 80d7b4c8ff..409fba9d93 100644 --- a/tests/network/fixtures_mongodb.py +++ b/tests/network/fixtures_mongodb.py @@ -20,7 +20,7 @@ def fixture_mongo_db(docker_ip, docker_services): docker_services.wait_until_responsive( timeout=10.0, pause=0.1, - check=lambda: is_url_responsive(mongo_db_url, retries=10) + check=lambda: is_url_responsive(mongo_db_url, retries=30) ) diff --git a/tests/network/fixtures_rabbitmq.py b/tests/network/fixtures_rabbitmq.py index 6bf5ae9b3a..073f8ddbba 100644 --- a/tests/network/fixtures_rabbitmq.py +++ b/tests/network/fixtures_rabbitmq.py @@ -36,7 +36,7 @@ def fixture_rabbit_mq(docker_ip, docker_services): docker_services.wait_until_responsive( timeout=10.0, pause=0.1, - check=lambda: is_url_responsive(rabbit_mq_management_url, retries=10) + check=lambda: is_url_responsive(rabbit_mq_management_url, retries=30) ) From 70b05d090605aa87f13c88174bec9c67ccc43206 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Thu, 11 Jan 2024 15:57:59 +0100 Subject: [PATCH 008/142] extend circle ci config --- .circleci/config.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index c48fdc436f..f87ef0c957 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -41,6 +41,8 @@ jobs: working_directory: ~/ocrd-core steps: - checkout + - setup_remote_docker: # https://circleci.com/docs/2.0/building-docker-images/ + docker_layer_caching: true - run: sudo apt-get -y update - run: sudo make deps-ubuntu - run: make install deps-test @@ -54,6 +56,8 @@ jobs: working_directory: ~/ocrd-core steps: - checkout + - setup_remote_docker: # https://circleci.com/docs/2.0/building-docker-images/ + docker_layer_caching: true - run: sudo apt-get -y update - run: sudo make deps-ubuntu - run: make install deps-test @@ -67,6 +71,8 @@ jobs: working_directory: ~/ocrd-core steps: - checkout + - setup_remote_docker: # https://circleci.com/docs/2.0/building-docker-images/ + docker_layer_caching: true - run: sudo apt-get -y update - run: sudo make deps-ubuntu - run: make install deps-test @@ -80,6 +86,8 @@ jobs: working_directory: ~/ocrd-core steps: - checkout + - setup_remote_docker: # https://circleci.com/docs/2.0/building-docker-images/ + docker_layer_caching: true - run: sudo apt-get -y update - run: sudo make deps-ubuntu - run: make install deps-test From 29a624741d9b3613f5e1eec19be371d0366ceba9 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Thu, 11 Jan 2024 16:16:32 +0100 Subject: [PATCH 009/142] try different host resolution --- tests/network/constants.py | 11 +---------- tests/network/fixtures_mongodb.py | 12 +++++++----- tests/network/fixtures_rabbitmq.py | 17 ++++++++++------- 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/tests/network/constants.py b/tests/network/constants.py index d769ef51fb..1c84a44236 100644 --- a/tests/network/constants.py +++ b/tests/network/constants.py @@ -1,18 +1,9 @@ __all__ = [ "DB_NAME", - "DB_URL", "DEFAULT_EXCHANGER_NAME", - "DEFAULT_QUEUE", - "PROCESSING_SERVER_URL", - "RABBITMQ_URL" + "DEFAULT_QUEUE" ] -# mongodb://localhost:6701/ocrd_network_test DB_NAME: str = "ocrd_network_test" -DB_URL: str = "mongodb://localhost:6701" - DEFAULT_EXCHANGER_NAME: str = "ocrd-network-default" DEFAULT_QUEUE: str = "ocrd-network-default" - -PROCESSING_SERVER_URL: str = "http://localhost:8000" -RABBITMQ_URL: str = "amqp://network_test:network_test@localhost:6672/" diff --git a/tests/network/fixtures_mongodb.py b/tests/network/fixtures_mongodb.py index 409fba9d93..770ddb5404 100644 --- a/tests/network/fixtures_mongodb.py +++ b/tests/network/fixtures_mongodb.py @@ -1,6 +1,6 @@ from pymongo import MongoClient, uri_parser as mongo_uri_parser from pytest import fixture -from .constants import DB_NAME, DB_URL +from .constants import DB_NAME from .utils import is_url_responsive @@ -16,17 +16,19 @@ def verify_database_uri(mongodb_address: str) -> str: @fixture(scope="package", name="mongo_db") def fixture_mongo_db(docker_ip, docker_services): port = docker_services.port_for("ocrd_network_mongo_db", 27017) - mongo_db_url = f"http://{docker_ip}:{port}" + test_url = f"http://{docker_ip}:{port}" docker_services.wait_until_responsive( timeout=10.0, pause=0.1, - check=lambda: is_url_responsive(mongo_db_url, retries=30) + check=lambda: is_url_responsive(test_url, retries=30) ) @fixture(scope="package", name="mongo_client") -def fixture_mongo_client(mongo_db): - mongo_client = MongoClient(DB_URL, serverSelectionTimeoutMS=3000) +def fixture_mongo_client(docker_ip, docker_services, mongo_db): + mongodb_port = docker_services.port_for("ocrd_network_mongo_db", 27017) + mongodb_url = f"mongodb://network_test:network_test@{docker_ip}:{mongodb_port}" + mongo_client = MongoClient(mongodb_url, serverSelectionTimeoutMS=3000) yield mongo_client diff --git a/tests/network/fixtures_rabbitmq.py b/tests/network/fixtures_rabbitmq.py index 073f8ddbba..72eddeaec3 100644 --- a/tests/network/fixtures_rabbitmq.py +++ b/tests/network/fixtures_rabbitmq.py @@ -3,7 +3,7 @@ from pytest import fixture from re import match as re_match from ocrd_network.rabbitmq_utils import RMQConnector, RMQConsumer, RMQPublisher -from .constants import RABBITMQ_URL, DEFAULT_EXCHANGER_NAME, DEFAULT_QUEUE +from .constants import DEFAULT_EXCHANGER_NAME, DEFAULT_QUEUE from .utils import is_url_responsive @@ -32,17 +32,19 @@ def verify_and_parse_mq_uri(rabbitmq_address: str): @fixture(scope="package", name="rabbit_mq") def fixture_rabbit_mq(docker_ip, docker_services): port = docker_services.port_for("ocrd_network_rabbit_mq", 15672) - rabbit_mq_management_url = f"http://{docker_ip}:{port}" + test_url = f"http://{docker_ip}:{port}" docker_services.wait_until_responsive( timeout=10.0, pause=0.1, - check=lambda: is_url_responsive(rabbit_mq_management_url, retries=30) + check=lambda: is_url_responsive(test_url, retries=30) ) @fixture(scope="package", name="rabbitmq_defaults") -def fixture_rabbitmq_defaults(rabbit_mq): - rmq_data = verify_and_parse_mq_uri(RABBITMQ_URL) +def fixture_rabbitmq_defaults(docker_ip, docker_services, rabbit_mq): + rabbitmq_port = docker_services.port_for("ocrd_network_rabbit_mq", 5672) + rabbitmq_url = f"amqp://network_test:network_test@{docker_ip}:{rabbitmq_port}/" + rmq_data = verify_and_parse_mq_uri(rabbitmq_url) rmq_username = rmq_data["username"] rmq_password = rmq_data["password"] rmq_host = rmq_data["host"] @@ -71,11 +73,12 @@ def fixture_rabbitmq_defaults(rabbit_mq): ) # Clean all messages inside if any from previous tests RMQConnector.queue_purge(channel=test_channel, queue_name=DEFAULT_QUEUE) + yield rabbitmq_url @fixture(scope="package", name="rabbitmq_publisher") def fixture_rabbitmq_publisher(rabbitmq_defaults): - rmq_data = verify_and_parse_mq_uri(RABBITMQ_URL) + rmq_data = verify_and_parse_mq_uri(rabbitmq_defaults) rmq_publisher = RMQPublisher( host=rmq_data["host"], port=rmq_data["port"], @@ -91,7 +94,7 @@ def fixture_rabbitmq_publisher(rabbitmq_defaults): @fixture(scope="package", name="rabbitmq_consumer") def fixture_rabbitmq_consumer(rabbitmq_defaults): - rmq_data = verify_and_parse_mq_uri(RABBITMQ_URL) + rmq_data = verify_and_parse_mq_uri(rabbitmq_defaults) rmq_consumer = RMQConsumer( host=rmq_data["host"], port=rmq_data["port"], From d4ce2b88217f735f3cd621d47f743867f3be9df1 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Thu, 11 Jan 2024 16:25:26 +0100 Subject: [PATCH 010/142] revert host resolution --- tests/network/constants.py | 11 ++++++++++- tests/network/fixtures_mongodb.py | 12 +++++------- tests/network/fixtures_rabbitmq.py | 17 +++++++---------- 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/tests/network/constants.py b/tests/network/constants.py index 1c84a44236..d769ef51fb 100644 --- a/tests/network/constants.py +++ b/tests/network/constants.py @@ -1,9 +1,18 @@ __all__ = [ "DB_NAME", + "DB_URL", "DEFAULT_EXCHANGER_NAME", - "DEFAULT_QUEUE" + "DEFAULT_QUEUE", + "PROCESSING_SERVER_URL", + "RABBITMQ_URL" ] +# mongodb://localhost:6701/ocrd_network_test DB_NAME: str = "ocrd_network_test" +DB_URL: str = "mongodb://localhost:6701" + DEFAULT_EXCHANGER_NAME: str = "ocrd-network-default" DEFAULT_QUEUE: str = "ocrd-network-default" + +PROCESSING_SERVER_URL: str = "http://localhost:8000" +RABBITMQ_URL: str = "amqp://network_test:network_test@localhost:6672/" diff --git a/tests/network/fixtures_mongodb.py b/tests/network/fixtures_mongodb.py index 770ddb5404..409fba9d93 100644 --- a/tests/network/fixtures_mongodb.py +++ b/tests/network/fixtures_mongodb.py @@ -1,6 +1,6 @@ from pymongo import MongoClient, uri_parser as mongo_uri_parser from pytest import fixture -from .constants import DB_NAME +from .constants import DB_NAME, DB_URL from .utils import is_url_responsive @@ -16,19 +16,17 @@ def verify_database_uri(mongodb_address: str) -> str: @fixture(scope="package", name="mongo_db") def fixture_mongo_db(docker_ip, docker_services): port = docker_services.port_for("ocrd_network_mongo_db", 27017) - test_url = f"http://{docker_ip}:{port}" + mongo_db_url = f"http://{docker_ip}:{port}" docker_services.wait_until_responsive( timeout=10.0, pause=0.1, - check=lambda: is_url_responsive(test_url, retries=30) + check=lambda: is_url_responsive(mongo_db_url, retries=30) ) @fixture(scope="package", name="mongo_client") -def fixture_mongo_client(docker_ip, docker_services, mongo_db): - mongodb_port = docker_services.port_for("ocrd_network_mongo_db", 27017) - mongodb_url = f"mongodb://network_test:network_test@{docker_ip}:{mongodb_port}" - mongo_client = MongoClient(mongodb_url, serverSelectionTimeoutMS=3000) +def fixture_mongo_client(mongo_db): + mongo_client = MongoClient(DB_URL, serverSelectionTimeoutMS=3000) yield mongo_client diff --git a/tests/network/fixtures_rabbitmq.py b/tests/network/fixtures_rabbitmq.py index 72eddeaec3..073f8ddbba 100644 --- a/tests/network/fixtures_rabbitmq.py +++ b/tests/network/fixtures_rabbitmq.py @@ -3,7 +3,7 @@ from pytest import fixture from re import match as re_match from ocrd_network.rabbitmq_utils import RMQConnector, RMQConsumer, RMQPublisher -from .constants import DEFAULT_EXCHANGER_NAME, DEFAULT_QUEUE +from .constants import RABBITMQ_URL, DEFAULT_EXCHANGER_NAME, DEFAULT_QUEUE from .utils import is_url_responsive @@ -32,19 +32,17 @@ def verify_and_parse_mq_uri(rabbitmq_address: str): @fixture(scope="package", name="rabbit_mq") def fixture_rabbit_mq(docker_ip, docker_services): port = docker_services.port_for("ocrd_network_rabbit_mq", 15672) - test_url = f"http://{docker_ip}:{port}" + rabbit_mq_management_url = f"http://{docker_ip}:{port}" docker_services.wait_until_responsive( timeout=10.0, pause=0.1, - check=lambda: is_url_responsive(test_url, retries=30) + check=lambda: is_url_responsive(rabbit_mq_management_url, retries=30) ) @fixture(scope="package", name="rabbitmq_defaults") -def fixture_rabbitmq_defaults(docker_ip, docker_services, rabbit_mq): - rabbitmq_port = docker_services.port_for("ocrd_network_rabbit_mq", 5672) - rabbitmq_url = f"amqp://network_test:network_test@{docker_ip}:{rabbitmq_port}/" - rmq_data = verify_and_parse_mq_uri(rabbitmq_url) +def fixture_rabbitmq_defaults(rabbit_mq): + rmq_data = verify_and_parse_mq_uri(RABBITMQ_URL) rmq_username = rmq_data["username"] rmq_password = rmq_data["password"] rmq_host = rmq_data["host"] @@ -73,12 +71,11 @@ def fixture_rabbitmq_defaults(docker_ip, docker_services, rabbit_mq): ) # Clean all messages inside if any from previous tests RMQConnector.queue_purge(channel=test_channel, queue_name=DEFAULT_QUEUE) - yield rabbitmq_url @fixture(scope="package", name="rabbitmq_publisher") def fixture_rabbitmq_publisher(rabbitmq_defaults): - rmq_data = verify_and_parse_mq_uri(rabbitmq_defaults) + rmq_data = verify_and_parse_mq_uri(RABBITMQ_URL) rmq_publisher = RMQPublisher( host=rmq_data["host"], port=rmq_data["port"], @@ -94,7 +91,7 @@ def fixture_rabbitmq_publisher(rabbitmq_defaults): @fixture(scope="package", name="rabbitmq_consumer") def fixture_rabbitmq_consumer(rabbitmq_defaults): - rmq_data = verify_and_parse_mq_uri(rabbitmq_defaults) + rmq_data = verify_and_parse_mq_uri(RABBITMQ_URL) rmq_consumer = RMQConsumer( host=rmq_data["host"], port=rmq_data["port"], From 55ff89a57f98e98a5b76dd3b20bc68b5bacf5b8f Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Thu, 11 Jan 2024 17:00:06 +0100 Subject: [PATCH 011/142] add docker network --- tests/network/docker-compose.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/network/docker-compose.yml b/tests/network/docker-compose.yml index 2805e2ebcd..7e1a2ca86e 100644 --- a/tests/network/docker-compose.yml +++ b/tests/network/docker-compose.yml @@ -1,6 +1,15 @@ +networks: + ocrd_network_test: + name: ocrd_network_test + driver: bridge + driver_opts: + com.docker.network.driver.mtu: 1450 services: ocrd_network_mongo_db: image: "mongo" + hostname: mongodb-docker-host + networks: + - ocrd_network_test ports: - "6701:27017" environment: @@ -8,6 +17,9 @@ services: - MONGO_INITDB_ROOT_PASSWORD=network_test ocrd_network_rabbit_mq: image: "rabbitmq:3.12-management" + hostname: rabbitmq-docker-host + networks: + - ocrd_network_test ports: - "6672:5672" - "16672:15672" From d383d2bb0ed364cf9d0e91f3ea0767c515d064df Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Fri, 12 Jan 2024 14:56:25 +0100 Subject: [PATCH 012/142] update python orb to 2.1.1 and config file to 2.1 --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f87ef0c957..94536bf491 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,7 +1,7 @@ -version: 2 +version: 2.1 orbs: - python: circleci/python@2.0.3 + python: circleci/python@2.1.1 jobs: From abe5d1ee494ab238104ffab3bfbf6950adc9a001 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Fri, 12 Jan 2024 15:05:58 +0100 Subject: [PATCH 013/142] increase timeouts in wait_until_responsive --- tests/network/fixtures_mongodb.py | 4 ++-- tests/network/fixtures_rabbitmq.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/network/fixtures_mongodb.py b/tests/network/fixtures_mongodb.py index 409fba9d93..8fdc6cdfcc 100644 --- a/tests/network/fixtures_mongodb.py +++ b/tests/network/fixtures_mongodb.py @@ -18,8 +18,8 @@ def fixture_mongo_db(docker_ip, docker_services): port = docker_services.port_for("ocrd_network_mongo_db", 27017) mongo_db_url = f"http://{docker_ip}:{port}" docker_services.wait_until_responsive( - timeout=10.0, - pause=0.1, + timeout=120.0, + pause=1, check=lambda: is_url_responsive(mongo_db_url, retries=30) ) diff --git a/tests/network/fixtures_rabbitmq.py b/tests/network/fixtures_rabbitmq.py index 073f8ddbba..a7968ba637 100644 --- a/tests/network/fixtures_rabbitmq.py +++ b/tests/network/fixtures_rabbitmq.py @@ -34,8 +34,8 @@ def fixture_rabbit_mq(docker_ip, docker_services): port = docker_services.port_for("ocrd_network_rabbit_mq", 15672) rabbit_mq_management_url = f"http://{docker_ip}:{port}" docker_services.wait_until_responsive( - timeout=10.0, - pause=0.1, + timeout=120.0, + pause=1, check=lambda: is_url_responsive(rabbit_mq_management_url, retries=30) ) From 1df014ae889a412f0509c404a2cf4839760a05d1 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Sun, 14 Jan 2024 17:52:54 +0100 Subject: [PATCH 014/142] implement make integration-test w/o pytest_docker --- Makefile | 7 +++++++ requirements_test.txt | 1 - tests/conftest.py | 9 --------- tests/network/fixtures_mongodb.py | 14 +------------- tests/network/fixtures_rabbitmq.py | 17 ++++------------- 5 files changed, 12 insertions(+), 36 deletions(-) diff --git a/Makefile b/Makefile index f2adfdca59..9147eb9c9a 100644 --- a/Makefile +++ b/Makefile @@ -209,9 +209,16 @@ test: assets $(PYTHON) \ -m pytest $(PYTEST_ARGS) --durations=10\ --ignore-glob="$(TESTDIR)/**/*bench*.py" \ + --ignore-glob="$(TESTDIR)/network/*.py" \ $(TESTDIR) cd ocrd_utils ; $(PYTHON) -m pytest --continue-on-collection-errors -k TestLogging -k TestDecorators $(TESTDIR) +integration-test: + docker compose -f tests/network/docker-compose.yml up -d + sleep 10 + pytest -k 'test_consume or test_publish' + docker compose -f tests/network/docker-compose.yml down + benchmark: $(PYTHON) -m pytest $(TESTDIR)/model/test_ocrd_mets_bench.py diff --git a/requirements_test.txt b/requirements_test.txt index 254ccbd891..8b7997b1a0 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -2,7 +2,6 @@ autopep8 pytest >= 4.0.0 generateDS == 2.35.20 pytest-benchmark >= 3.2.3 -pytest-docker>=1.0.0 coverage >= 4.5.2 sphinx sphinx_click diff --git a/tests/conftest.py b/tests/conftest.py index 49cdff50da..da47f3f8b5 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,13 +1,4 @@ -from pathlib import Path -from pkg_resources import resource_filename -from pytest import fixture - pytest_plugins = [ "tests.network.fixtures_mongodb", "tests.network.fixtures_rabbitmq" ] - - -@fixture(scope="session") -def docker_compose_file(): - return Path(resource_filename("tests", "network"), "docker-compose.yml") diff --git a/tests/network/fixtures_mongodb.py b/tests/network/fixtures_mongodb.py index 8fdc6cdfcc..1d8826aeaa 100644 --- a/tests/network/fixtures_mongodb.py +++ b/tests/network/fixtures_mongodb.py @@ -1,7 +1,6 @@ from pymongo import MongoClient, uri_parser as mongo_uri_parser from pytest import fixture from .constants import DB_NAME, DB_URL -from .utils import is_url_responsive def verify_database_uri(mongodb_address: str) -> str: @@ -13,19 +12,8 @@ def verify_database_uri(mongodb_address: str) -> str: return mongodb_address -@fixture(scope="package", name="mongo_db") -def fixture_mongo_db(docker_ip, docker_services): - port = docker_services.port_for("ocrd_network_mongo_db", 27017) - mongo_db_url = f"http://{docker_ip}:{port}" - docker_services.wait_until_responsive( - timeout=120.0, - pause=1, - check=lambda: is_url_responsive(mongo_db_url, retries=30) - ) - - @fixture(scope="package", name="mongo_client") -def fixture_mongo_client(mongo_db): +def fixture_mongo_client(): mongo_client = MongoClient(DB_URL, serverSelectionTimeoutMS=3000) yield mongo_client diff --git a/tests/network/fixtures_rabbitmq.py b/tests/network/fixtures_rabbitmq.py index a7968ba637..d2540b6418 100644 --- a/tests/network/fixtures_rabbitmq.py +++ b/tests/network/fixtures_rabbitmq.py @@ -4,7 +4,6 @@ from re import match as re_match from ocrd_network.rabbitmq_utils import RMQConnector, RMQConsumer, RMQPublisher from .constants import RABBITMQ_URL, DEFAULT_EXCHANGER_NAME, DEFAULT_QUEUE -from .utils import is_url_responsive def verify_and_parse_mq_uri(rabbitmq_address: str): @@ -29,19 +28,8 @@ def verify_and_parse_mq_uri(rabbitmq_address: str): return parsed_data -@fixture(scope="package", name="rabbit_mq") -def fixture_rabbit_mq(docker_ip, docker_services): - port = docker_services.port_for("ocrd_network_rabbit_mq", 15672) - rabbit_mq_management_url = f"http://{docker_ip}:{port}" - docker_services.wait_until_responsive( - timeout=120.0, - pause=1, - check=lambda: is_url_responsive(rabbit_mq_management_url, retries=30) - ) - - @fixture(scope="package", name="rabbitmq_defaults") -def fixture_rabbitmq_defaults(rabbit_mq): +def fixture_rabbitmq_defaults(): rmq_data = verify_and_parse_mq_uri(RABBITMQ_URL) rmq_username = rmq_data["username"] rmq_password = rmq_data["password"] @@ -56,6 +44,7 @@ def fixture_rabbitmq_defaults(rabbit_mq): vhost=rmq_vhost ) test_channel = RMQConnector.open_blocking_channel(test_connection) + assert(test_channel) RMQConnector.exchange_declare( channel=test_channel, exchange_name=DEFAULT_EXCHANGER_NAME, @@ -75,6 +64,7 @@ def fixture_rabbitmq_defaults(rabbit_mq): @fixture(scope="package", name="rabbitmq_publisher") def fixture_rabbitmq_publisher(rabbitmq_defaults): + assert rabbitmq_defaults rmq_data = verify_and_parse_mq_uri(RABBITMQ_URL) rmq_publisher = RMQPublisher( host=rmq_data["host"], @@ -91,6 +81,7 @@ def fixture_rabbitmq_publisher(rabbitmq_defaults): @fixture(scope="package", name="rabbitmq_consumer") def fixture_rabbitmq_consumer(rabbitmq_defaults): + assert rabbitmq_defaults rmq_data = verify_and_parse_mq_uri(RABBITMQ_URL) rmq_consumer = RMQConsumer( host=rmq_data["host"], From fd13145f3742a58ab2f0ed892ef5cc069465ddde Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Sun, 14 Jan 2024 18:00:12 +0100 Subject: [PATCH 015/142] ci: make integration-test --- .circleci/config.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 94536bf491..74276b0a34 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -18,7 +18,7 @@ jobs: - checkout - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install imagemagick geos bash - run: make install - - run: PATH="/Users/distiller/Library/Python/3.9/bin:$PATH" make deps-test test benchmark + - run: PATH="/Users/distiller/Library/Python/3.9/bin:$PATH" make deps-test test benchmark integration-test test-python37: docker: @@ -31,7 +31,7 @@ jobs: - run: sudo apt-get -y update - run: sudo make deps-ubuntu - run: make install deps-test - - run: make test benchmark + - run: make test benchmark integration-test # smoke test to ensure that --editable install works - run: make install-dev; ocrd --version @@ -46,7 +46,7 @@ jobs: - run: sudo apt-get -y update - run: sudo make deps-ubuntu - run: make install deps-test - - run: make test benchmark + - run: make test benchmark integration-test # smoke test to ensure that --editable install works - run: make install-dev; ocrd --version @@ -61,7 +61,7 @@ jobs: - run: sudo apt-get -y update - run: sudo make deps-ubuntu - run: make install deps-test - - run: make test benchmark + - run: make test benchmark integration-test # smoke test to ensure that --editable install works - run: make install-dev; ocrd --version @@ -76,7 +76,7 @@ jobs: - run: sudo apt-get -y update - run: sudo make deps-ubuntu - run: make install deps-test - - run: make test benchmark + - run: make test benchmark integration-test # smoke test to ensure that --editable install works - run: make install-dev; ocrd --version @@ -91,7 +91,7 @@ jobs: - run: sudo apt-get -y update - run: sudo make deps-ubuntu - run: make install deps-test - - run: make test benchmark + - run: make test benchmark integration-test # smoke test to ensure that --editable install works - run: make install-dev; ocrd --version From 55c3e3b66630788f7b19ef9ac9cb72afb3e8153e Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Sun, 14 Jan 2024 18:10:42 +0100 Subject: [PATCH 016/142] network: try 0.0.0.0 over localhost --- tests/network/constants.py | 6 +++--- tests/network/fixtures_rabbitmq.py | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/network/constants.py b/tests/network/constants.py index d769ef51fb..f7ef5a75bf 100644 --- a/tests/network/constants.py +++ b/tests/network/constants.py @@ -9,10 +9,10 @@ # mongodb://localhost:6701/ocrd_network_test DB_NAME: str = "ocrd_network_test" -DB_URL: str = "mongodb://localhost:6701" +DB_URL: str = "mongodb://0.0.0.0:6701" DEFAULT_EXCHANGER_NAME: str = "ocrd-network-default" DEFAULT_QUEUE: str = "ocrd-network-default" -PROCESSING_SERVER_URL: str = "http://localhost:8000" -RABBITMQ_URL: str = "amqp://network_test:network_test@localhost:6672/" +PROCESSING_SERVER_URL: str = "http://0.0.0.0:8000" +RABBITMQ_URL: str = "amqp://network_test:network_test@0.0.0.0:6672/" diff --git a/tests/network/fixtures_rabbitmq.py b/tests/network/fixtures_rabbitmq.py index d2540b6418..a99e759a2a 100644 --- a/tests/network/fixtures_rabbitmq.py +++ b/tests/network/fixtures_rabbitmq.py @@ -64,7 +64,6 @@ def fixture_rabbitmq_defaults(): @fixture(scope="package", name="rabbitmq_publisher") def fixture_rabbitmq_publisher(rabbitmq_defaults): - assert rabbitmq_defaults rmq_data = verify_and_parse_mq_uri(RABBITMQ_URL) rmq_publisher = RMQPublisher( host=rmq_data["host"], @@ -81,7 +80,6 @@ def fixture_rabbitmq_publisher(rabbitmq_defaults): @fixture(scope="package", name="rabbitmq_consumer") def fixture_rabbitmq_consumer(rabbitmq_defaults): - assert rabbitmq_defaults rmq_data = verify_and_parse_mq_uri(RABBITMQ_URL) rmq_consumer = RMQConsumer( host=rmq_data["host"], From b73879b2f8f89e6656c4e9a7b95713b6874b06e8 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Mon, 15 Jan 2024 11:24:41 +0100 Subject: [PATCH 017/142] improve local exec of integration tests --- Makefile | 2 +- tests/network/docker-compose.yml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9147eb9c9a..257710461e 100644 --- a/Makefile +++ b/Makefile @@ -217,7 +217,7 @@ integration-test: docker compose -f tests/network/docker-compose.yml up -d sleep 10 pytest -k 'test_consume or test_publish' - docker compose -f tests/network/docker-compose.yml down + docker compose -f tests/network/docker-compose.yml down --remove-orphans benchmark: $(PYTHON) -m pytest $(TESTDIR)/model/test_ocrd_mets_bench.py diff --git a/tests/network/docker-compose.yml b/tests/network/docker-compose.yml index 7e1a2ca86e..f2ecd197df 100644 --- a/tests/network/docker-compose.yml +++ b/tests/network/docker-compose.yml @@ -4,6 +4,7 @@ networks: driver: bridge driver_opts: com.docker.network.driver.mtu: 1450 + external: true services: ocrd_network_mongo_db: image: "mongo" From 87e21e4aa714130f24bcc0c27757245c9169e972 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Mon, 15 Jan 2024 11:34:06 +0100 Subject: [PATCH 018/142] remove external expectation --- tests/network/docker-compose.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/network/docker-compose.yml b/tests/network/docker-compose.yml index f2ecd197df..7e1a2ca86e 100644 --- a/tests/network/docker-compose.yml +++ b/tests/network/docker-compose.yml @@ -4,7 +4,6 @@ networks: driver: bridge driver_opts: com.docker.network.driver.mtu: 1450 - external: true services: ocrd_network_mongo_db: image: "mongo" From 0c296108e303c5fa7dfab709981af63ae9b7c8da Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Mon, 15 Jan 2024 12:13:40 +0100 Subject: [PATCH 019/142] try machine executor for pyt37 --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 74276b0a34..17f5f34f33 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -26,7 +26,7 @@ jobs: working_directory: ~/ocrd-core steps: - checkout - - setup_remote_docker: # https://circleci.com/docs/2.0/building-docker-images/ + - machine: # https://circleci.com/docs/2.0/building-docker-images/ docker_layer_caching: true - run: sudo apt-get -y update - run: sudo make deps-ubuntu From 68a5c6ff16d4683ca292aa61de8ab07566118ded Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Mon, 15 Jan 2024 12:20:17 +0100 Subject: [PATCH 020/142] revert to remote docker --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 17f5f34f33..74276b0a34 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -26,7 +26,7 @@ jobs: working_directory: ~/ocrd-core steps: - checkout - - machine: # https://circleci.com/docs/2.0/building-docker-images/ + - setup_remote_docker: # https://circleci.com/docs/2.0/building-docker-images/ docker_layer_caching: true - run: sudo apt-get -y update - run: sudo make deps-ubuntu From 12836a83a59323955a6247fd280b8aaf5a133c3d Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Mon, 15 Jan 2024 12:24:08 +0100 Subject: [PATCH 021/142] proof-of-concept of in-docker integration tests --- .dockerignore | 3 +++ Dockerfile | 20 +++++++++++++++----- Makefile | 7 ++++--- tests/conftest.py | 12 ++++++++++++ tests/network/docker-compose.yml | 19 +++++++++++++++++++ tests/network/fixtures_mongodb.py | 4 +++- tests/network/fixtures_rabbitmq.py | 6 +++++- tests/network/test_rabbitmq.py | 5 ++++- 8 files changed, 65 insertions(+), 11 deletions(-) diff --git a/.dockerignore b/.dockerignore index aefc504c60..705eb6dd36 100644 --- a/.dockerignore +++ b/.dockerignore @@ -4,3 +4,6 @@ !LICENSE !README.md !.git +!tests +!requirements_test.txt +!.gitmodules diff --git a/Dockerfile b/Dockerfile index 00bd8bb30a..f1cf5cb009 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ ARG BASE_IMAGE -FROM $BASE_IMAGE +FROM $BASE_IMAGE as ocrd_core_base ARG FIXUP=echo MAINTAINER OCR-D ENV DEBIAN_FRONTEND noninteractive @@ -33,14 +33,24 @@ RUN apt-get update && apt-get -y install software-properties-common \ curl \ sudo \ git \ - && make deps-ubuntu \ - && python3 -m venv /usr/local \ + && make deps-ubuntu +RUN python3 -m venv /usr/local \ && hash -r \ && pip install --upgrade pip setuptools wheel \ && make install \ - && eval $FIXUP \ - && rm -rf /build-ocrd + && eval $FIXUP WORKDIR /data CMD ["/usr/local/bin/ocrd", "--help"] + +FROM ocrd_core_base as ocrd_core_test +WORKDIR /build-ocrd +COPY tests ./tests +COPY .gitmodules . +COPY Makefile . +COPY requirements_test.txt . +RUN pip install -r requirements_test.txt + +CMD ["yes"] +# CMD ["make", "test", "integration-test"] diff --git a/Makefile b/Makefile index 9147eb9c9a..9997f968ec 100644 --- a/Makefile +++ b/Makefile @@ -213,10 +213,11 @@ test: assets $(TESTDIR) cd ocrd_utils ; $(PYTHON) -m pytest --continue-on-collection-errors -k TestLogging -k TestDecorators $(TESTDIR) +INTEGRATION_TEST_IN_DOCKER = docker exec core_test integration-test: docker compose -f tests/network/docker-compose.yml up -d - sleep 10 - pytest -k 'test_consume or test_publish' + sleep 5 + $(INTEGRATION_TEST_IN_DOCKER) pytest -k 'test_consume or test_publish' docker compose -f tests/network/docker-compose.yml down benchmark: @@ -295,7 +296,7 @@ docker-cuda: DOCKER_FILE = Dockerfile.cuda docker-cuda: docker docker docker-cuda: - docker build --progress=plain -f $(DOCKER_FILE) -t $(DOCKER_TAG) --build-arg BASE_IMAGE=$(DOCKER_BASE_IMAGE) $(DOCKER_ARGS) . + docker build --progress=plain -f $(DOCKER_FILE) -t $(DOCKER_TAG) --target ocrd_core_base --build-arg BASE_IMAGE=$(DOCKER_BASE_IMAGE) $(DOCKER_ARGS) . # Build wheels and source dist and twine upload them pypi: build diff --git a/tests/conftest.py b/tests/conftest.py index da47f3f8b5..5bf13863aa 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,16 @@ +from ocrd_utils.config import config + pytest_plugins = [ "tests.network.fixtures_mongodb", "tests.network.fixtures_rabbitmq" ] + +config.add("DB_NAME", description="...", default=(True, 'ocrd_network_test')) +# mongodb://localhost:6701/ocrd_network_test +config.add("DB_URL", description="...", default=(True, 'mongodb://0.0.0.0:6701')) + +config.add('DEFAULT_EXCHANGER_NAME', description="...", default=(True, 'ocrd-network-default')) +config.add('DEFAULT_QUEUE', description="...", default=(True, 'ocrd-network-default')) + +config.add('PROCESSING_SERVER_URL', description="...", default=(True, "http://0.0.0.0:8000")) +config.add('RABBITMQ_URL', description="...", default=(True, "amqp://network_test:network_test@0.0.0.0:6672/")) diff --git a/tests/network/docker-compose.yml b/tests/network/docker-compose.yml index 7e1a2ca86e..7019629530 100644 --- a/tests/network/docker-compose.yml +++ b/tests/network/docker-compose.yml @@ -4,7 +4,9 @@ networks: driver: bridge driver_opts: com.docker.network.driver.mtu: 1450 + services: + ocrd_network_mongo_db: image: "mongo" hostname: mongodb-docker-host @@ -15,6 +17,7 @@ services: environment: - MONGO_INITDB_ROOT_USERNAME=network_test - MONGO_INITDB_ROOT_PASSWORD=network_test + ocrd_network_rabbit_mq: image: "rabbitmq:3.12-management" hostname: rabbitmq-docker-host @@ -28,3 +31,19 @@ services: - RABBITMQ_DEFAULT_USER=network_test - RABBITMQ_DEFAULT_PASS=network_test - RABBITMQ_FEATURE_FLAGS=quorum_queue,implicit_default_bindings,classic_mirrored_queue_version + + ocrd_network_core_test: + build: + context: ../../ + dockerfile: Dockerfile + args: + BASE_IMAGE: 'ubuntu:20.04' + target: ocrd_core_test + container_name: core_test + networks: + - ocrd_network_test + environment: + DB_NAME: ocrd_network_test + DB_URL: mongodb://mongodb-docker-host:27017 + # PROCESSING_SERVER_URL : http://... + RABBITMQ_URL: amqp://network_test:network_test@rabbitmq-docker-host.ocrd_network_test:5672 diff --git a/tests/network/fixtures_mongodb.py b/tests/network/fixtures_mongodb.py index 1d8826aeaa..deecf61da8 100644 --- a/tests/network/fixtures_mongodb.py +++ b/tests/network/fixtures_mongodb.py @@ -1,6 +1,8 @@ from pymongo import MongoClient, uri_parser as mongo_uri_parser from pytest import fixture -from .constants import DB_NAME, DB_URL +from ocrd_utils.config import config +DB_NAME = config.DB_NAME +DB_URL = config.DB_URL def verify_database_uri(mongodb_address: str) -> str: diff --git a/tests/network/fixtures_rabbitmq.py b/tests/network/fixtures_rabbitmq.py index a99e759a2a..06e99a98c1 100644 --- a/tests/network/fixtures_rabbitmq.py +++ b/tests/network/fixtures_rabbitmq.py @@ -3,7 +3,11 @@ from pytest import fixture from re import match as re_match from ocrd_network.rabbitmq_utils import RMQConnector, RMQConsumer, RMQPublisher -from .constants import RABBITMQ_URL, DEFAULT_EXCHANGER_NAME, DEFAULT_QUEUE +from ocrd_utils.config import config + +RABBITMQ_URL = config.RABBITMQ_URL +DEFAULT_EXCHANGER_NAME = config.DEFAULT_EXCHANGER_NAME +DEFAULT_QUEUE = config.DEFAULT_QUEUE def verify_and_parse_mq_uri(rabbitmq_address: str): diff --git a/tests/network/test_rabbitmq.py b/tests/network/test_rabbitmq.py index f9a2e89419..644f86adca 100644 --- a/tests/network/test_rabbitmq.py +++ b/tests/network/test_rabbitmq.py @@ -1,6 +1,9 @@ from pika import BasicProperties from pickle import dumps, loads -from .constants import DEFAULT_EXCHANGER_NAME, DEFAULT_QUEUE +from ocrd_utils.config import config + +DEFAULT_EXCHANGER_NAME = config.DEFAULT_EXCHANGER_NAME +DEFAULT_QUEUE = config.DEFAULT_QUEUE def test_publish_2_messages_to_rabbitmq(rabbitmq_publisher): From bea14b7bd56c6d6598b06567fe93c1f61f3879f1 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Mon, 15 Jan 2024 12:35:03 +0100 Subject: [PATCH 022/142] increase sleep time again X( --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9997f968ec..67b87cc71e 100644 --- a/Makefile +++ b/Makefile @@ -216,7 +216,7 @@ test: assets INTEGRATION_TEST_IN_DOCKER = docker exec core_test integration-test: docker compose -f tests/network/docker-compose.yml up -d - sleep 5 + sleep 10 $(INTEGRATION_TEST_IN_DOCKER) pytest -k 'test_consume or test_publish' docker compose -f tests/network/docker-compose.yml down From 52064a57349c477acc45d637f5492dccc522194a Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Mon, 15 Jan 2024 13:06:10 +0100 Subject: [PATCH 023/142] change core test envs --- tests/network/docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/network/docker-compose.yml b/tests/network/docker-compose.yml index 7019629530..3c2ef56852 100644 --- a/tests/network/docker-compose.yml +++ b/tests/network/docker-compose.yml @@ -44,6 +44,6 @@ services: - ocrd_network_test environment: DB_NAME: ocrd_network_test - DB_URL: mongodb://mongodb-docker-host:27017 + DB_URL: mongodb://network_test:network_test@mongodb-docker-host:27017 # PROCESSING_SERVER_URL : http://... - RABBITMQ_URL: amqp://network_test:network_test@rabbitmq-docker-host.ocrd_network_test:5672 + RABBITMQ_URL: amqp://network_test:network_test@rabbitmq-docker-host:5672 From c96fd2ed5cb24ac83ad2ce97eccae3e204a48a97 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Mon, 15 Jan 2024 13:08:08 +0100 Subject: [PATCH 024/142] remove constants --- tests/network/constants.py | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 tests/network/constants.py diff --git a/tests/network/constants.py b/tests/network/constants.py deleted file mode 100644 index f7ef5a75bf..0000000000 --- a/tests/network/constants.py +++ /dev/null @@ -1,18 +0,0 @@ -__all__ = [ - "DB_NAME", - "DB_URL", - "DEFAULT_EXCHANGER_NAME", - "DEFAULT_QUEUE", - "PROCESSING_SERVER_URL", - "RABBITMQ_URL" -] - -# mongodb://localhost:6701/ocrd_network_test -DB_NAME: str = "ocrd_network_test" -DB_URL: str = "mongodb://0.0.0.0:6701" - -DEFAULT_EXCHANGER_NAME: str = "ocrd-network-default" -DEFAULT_QUEUE: str = "ocrd-network-default" - -PROCESSING_SERVER_URL: str = "http://0.0.0.0:8000" -RABBITMQ_URL: str = "amqp://network_test:network_test@0.0.0.0:6672/" From 0e5cf7e1b3831decf2065b02f6d6cb8b213f4315 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Mon, 15 Jan 2024 14:46:26 +0100 Subject: [PATCH 025/142] add 2 base db tests --- ocrd_network/ocrd_network/database.py | 8 +++---- tests/conftest.py | 9 +++++--- tests/network/test_db.py | 33 +++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 tests/network/test_db.py diff --git a/ocrd_network/ocrd_network/database.py b/ocrd_network/ocrd_network/database.py index 4945c13353..7844df991f 100644 --- a/ocrd_network/ocrd_network/database.py +++ b/ocrd_network/ocrd_network/database.py @@ -28,17 +28,17 @@ from .utils import call_sync -async def initiate_database(db_url: str): +async def initiate_database(db_url: str, db_name: str = 'ocrd'): client = AsyncIOMotorClient(db_url) await init_beanie( - database=client.get_default_database(default='ocrd'), + database=client.get_default_database(default=db_name), document_models=[DBProcessorJob, DBWorkflowJob, DBWorkspace, DBWorkflowScript] ) @call_sync -async def sync_initiate_database(db_url: str): - await initiate_database(db_url) +async def sync_initiate_database(db_url: str, db_name: str = 'ocrd'): + await initiate_database(db_url, db_name) async def db_create_workspace(mets_path: str) -> DBWorkspace: diff --git a/tests/conftest.py b/tests/conftest.py index 5bf13863aa..d6c0850c2d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,16 +1,19 @@ +from ocrd_network.database import sync_initiate_database from ocrd_utils.config import config + pytest_plugins = [ "tests.network.fixtures_mongodb", "tests.network.fixtures_rabbitmq" ] -config.add("DB_NAME", description="...", default=(True, 'ocrd_network_test')) -# mongodb://localhost:6701/ocrd_network_test -config.add("DB_URL", description="...", default=(True, 'mongodb://0.0.0.0:6701')) +config.add("DB_NAME", description="...", default=(True, 'ocrd')) +config.add("DB_URL", description="...", default=(True, 'mongodb://network_test:network_test@0.0.0.0:6701')) config.add('DEFAULT_EXCHANGER_NAME', description="...", default=(True, 'ocrd-network-default')) config.add('DEFAULT_QUEUE', description="...", default=(True, 'ocrd-network-default')) config.add('PROCESSING_SERVER_URL', description="...", default=(True, "http://0.0.0.0:8000")) config.add('RABBITMQ_URL', description="...", default=(True, "amqp://network_test:network_test@0.0.0.0:6672/")) + +sync_initiate_database(config.DB_URL, config.DB_NAME) diff --git a/tests/network/test_db.py b/tests/network/test_db.py new file mode 100644 index 0000000000..41b02a4cbd --- /dev/null +++ b/tests/network/test_db.py @@ -0,0 +1,33 @@ +from ocrd_network.models import DBProcessorJob, StateEnum +from ocrd_network.database import sync_db_get_processing_job + + +def test_db_write_processing_job(mongo_processor_jobs): + job_id = 'test_id_1234' + db_processing_job = DBProcessorJob( + job_id=job_id, + processor_name='ocrd-dummy', + state=StateEnum.cached, + path_to_mets='/ocrd/dummy/path', + input_file_grps=['DEFAULT'], + output_file_grps=['OCR-D-DUMMY'] + ) + inserted_job = db_processing_job.insert() + assert inserted_job + found_job = mongo_processor_jobs.find_one({"job_id": job_id}) + assert found_job + + +def test_db_read_processing_job(): + job_id = 'test_id_1234' + db_processing_job = sync_db_get_processing_job(job_id=job_id) + assert db_processing_job.job_id == job_id + assert db_processing_job.processor_name == 'ocrd-dummy' + assert db_processing_job.state == StateEnum.cached + assert db_processing_job.path_to_mets == '/ocrd/dummy/path' + assert db_processing_job.input_file_grps == ['DEFAULT'] + assert db_processing_job.output_file_grps == ['OCR-D-DUMMY'] + + +def test_db_update_processing_job(): + pass From 780621159ebe42669b4a6af1a0ca5fe2047732a0 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Mon, 15 Jan 2024 14:46:43 +0100 Subject: [PATCH 026/142] refactor name rabbitmq tests --- Makefile | 2 +- tests/network/test_rabbitmq.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index f4a7cd4855..5432df523a 100644 --- a/Makefile +++ b/Makefile @@ -217,7 +217,7 @@ INTEGRATION_TEST_IN_DOCKER = docker exec core_test integration-test: docker compose -f tests/network/docker-compose.yml up -d sleep 10 - $(INTEGRATION_TEST_IN_DOCKER) pytest -k 'test_consume or test_publish' + $(INTEGRATION_TEST_IN_DOCKER) pytest -k 'test_rmq or test_db' docker compose -f tests/network/docker-compose.yml down --remove-orphans benchmark: diff --git a/tests/network/test_rabbitmq.py b/tests/network/test_rabbitmq.py index 644f86adca..0b208718f3 100644 --- a/tests/network/test_rabbitmq.py +++ b/tests/network/test_rabbitmq.py @@ -6,7 +6,7 @@ DEFAULT_QUEUE = config.DEFAULT_QUEUE -def test_publish_2_messages_to_rabbitmq(rabbitmq_publisher): +def test_rmq_publish_2_messages(rabbitmq_publisher): test_headers = {"Test Header": "Test Value"} test_properties = BasicProperties( app_id='webapi-processing-broker', @@ -28,7 +28,7 @@ def test_publish_2_messages_to_rabbitmq(rabbitmq_publisher): assert rabbitmq_publisher.message_counter == 2 -def test_consume_2_messages_from_rabbitmq(rabbitmq_consumer): +def test_rmq_consume_2_messages(rabbitmq_consumer): # Consume the 1st message method_frame, header_frame, message = rabbitmq_consumer.get_one_message( queue_name=DEFAULT_QUEUE, @@ -56,7 +56,7 @@ def test_consume_2_messages_from_rabbitmq(rabbitmq_consumer): assert message.decode() == "RabbitMQ test 456" -def test_publish_ocrd_message_to_rabbitmq(rabbitmq_publisher): +def test_rmq_publish_ocrd_message(rabbitmq_publisher): ocrd_processing_message = { "job_id": "Test_job_id", "workflow_id": "Test_workflow_id", @@ -71,7 +71,7 @@ def test_publish_ocrd_message_to_rabbitmq(rabbitmq_publisher): ) -def test_consume_ocrd_message_from_rabbitmq(rabbitmq_consumer): +def test_rmq_consume_ocrd_message(rabbitmq_consumer): method_frame, header_frame, message = rabbitmq_consumer.get_one_message( queue_name=DEFAULT_QUEUE, auto_ack=True From 18d7c7ef644916b8f86bf0108fd7f7e22b448e10 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Mon, 15 Jan 2024 15:51:01 +0100 Subject: [PATCH 027/142] fix DB tests --- tests/conftest.py | 5 +--- tests/network/fixtures_mongodb.py | 4 +++ tests/network/test_db.py | 50 ++++++++++++++++++------------- 3 files changed, 35 insertions(+), 24 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index d6c0850c2d..de39d7874a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,3 @@ -from ocrd_network.database import sync_initiate_database from ocrd_utils.config import config @@ -7,7 +6,7 @@ "tests.network.fixtures_rabbitmq" ] -config.add("DB_NAME", description="...", default=(True, 'ocrd')) +config.add("DB_NAME", description="...", default=(True, 'ocrd_network_test')) config.add("DB_URL", description="...", default=(True, 'mongodb://network_test:network_test@0.0.0.0:6701')) config.add('DEFAULT_EXCHANGER_NAME', description="...", default=(True, 'ocrd-network-default')) @@ -15,5 +14,3 @@ config.add('PROCESSING_SERVER_URL', description="...", default=(True, "http://0.0.0.0:8000")) config.add('RABBITMQ_URL', description="...", default=(True, "amqp://network_test:network_test@0.0.0.0:6672/")) - -sync_initiate_database(config.DB_URL, config.DB_NAME) diff --git a/tests/network/fixtures_mongodb.py b/tests/network/fixtures_mongodb.py index deecf61da8..6930962194 100644 --- a/tests/network/fixtures_mongodb.py +++ b/tests/network/fixtures_mongodb.py @@ -1,9 +1,13 @@ from pymongo import MongoClient, uri_parser as mongo_uri_parser from pytest import fixture from ocrd_utils.config import config +from ocrd_network.database import sync_initiate_database + DB_NAME = config.DB_NAME DB_URL = config.DB_URL +sync_initiate_database(DB_URL, DB_NAME) + def verify_database_uri(mongodb_address: str) -> str: try: diff --git a/tests/network/test_db.py b/tests/network/test_db.py index 41b02a4cbd..5c0944ff89 100644 --- a/tests/network/test_db.py +++ b/tests/network/test_db.py @@ -1,33 +1,43 @@ -from ocrd_network.models import DBProcessorJob, StateEnum -from ocrd_network.database import sync_db_get_processing_job +from ocrd_network.models import StateEnum def test_db_write_processing_job(mongo_processor_jobs): job_id = 'test_id_1234' - db_processing_job = DBProcessorJob( - job_id=job_id, - processor_name='ocrd-dummy', - state=StateEnum.cached, - path_to_mets='/ocrd/dummy/path', - input_file_grps=['DEFAULT'], - output_file_grps=['OCR-D-DUMMY'] + inserted_job = mongo_processor_jobs.insert_one( + document={ + "job_id": job_id, + "processor_name": 'ocrd-dummy', + "state": StateEnum.cached, + "path_to_mets": '/ocrd/dummy/path', + "input_file_grps": ['DEFAULT'], + "output_file_grps": ['OCR-D-DUMMY'] + } ) - inserted_job = db_processing_job.insert() assert inserted_job found_job = mongo_processor_jobs.find_one({"job_id": job_id}) assert found_job -def test_db_read_processing_job(): +def test_db_read_processing_job(mongo_processor_jobs): job_id = 'test_id_1234' - db_processing_job = sync_db_get_processing_job(job_id=job_id) - assert db_processing_job.job_id == job_id - assert db_processing_job.processor_name == 'ocrd-dummy' - assert db_processing_job.state == StateEnum.cached - assert db_processing_job.path_to_mets == '/ocrd/dummy/path' - assert db_processing_job.input_file_grps == ['DEFAULT'] - assert db_processing_job.output_file_grps == ['OCR-D-DUMMY'] + found_job = mongo_processor_jobs.find_one({"job_id": job_id}) + assert found_job + assert found_job['job_id'] == job_id + assert found_job['processor_name'] == 'ocrd-dummy' + assert found_job['state'] == StateEnum.cached + assert found_job['path_to_mets'] == '/ocrd/dummy/path' + assert found_job['input_file_grps'] == ['DEFAULT'] + assert found_job['output_file_grps'] == ['OCR-D-DUMMY'] -def test_db_update_processing_job(): - pass +def test_db_update_processing_job(mongo_processor_jobs): + job_id = 'test_id_1234' + found_job = mongo_processor_jobs.find_one({"job_id": job_id}) + assert found_job + found_job['state'] = StateEnum.running + assert found_job['job_id'] == job_id + assert found_job['processor_name'] == 'ocrd-dummy' + assert found_job['state'] == StateEnum.running + assert found_job['path_to_mets'] == '/ocrd/dummy/path' + assert found_job['input_file_grps'] == ['DEFAULT'] + assert found_job['output_file_grps'] == ['OCR-D-DUMMY'] From 28debcf732b7d6c469d5f4baac60c64553945a36 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Mon, 15 Jan 2024 16:01:40 +0100 Subject: [PATCH 028/142] do a proper update --- tests/network/test_db.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/tests/network/test_db.py b/tests/network/test_db.py index 5c0944ff89..a128aa1856 100644 --- a/tests/network/test_db.py +++ b/tests/network/test_db.py @@ -32,12 +32,11 @@ def test_db_read_processing_job(mongo_processor_jobs): def test_db_update_processing_job(mongo_processor_jobs): job_id = 'test_id_1234' - found_job = mongo_processor_jobs.find_one({"job_id": job_id}) - assert found_job - found_job['state'] = StateEnum.running - assert found_job['job_id'] == job_id - assert found_job['processor_name'] == 'ocrd-dummy' - assert found_job['state'] == StateEnum.running - assert found_job['path_to_mets'] == '/ocrd/dummy/path' - assert found_job['input_file_grps'] == ['DEFAULT'] - assert found_job['output_file_grps'] == ['OCR-D-DUMMY'] + updated_job = mongo_processor_jobs.update_one(filter={"job_id": job_id}, update={"state": StateEnum.running}) + assert updated_job + assert updated_job['job_id'] == job_id + assert updated_job['processor_name'] == 'ocrd-dummy' + assert updated_job['state'] == StateEnum.running + assert updated_job['path_to_mets'] == '/ocrd/dummy/path' + assert updated_job['input_file_grps'] == ['DEFAULT'] + assert updated_job['output_file_grps'] == ['OCR-D-DUMMY'] From 425c4c24e8baef28e0468dbf22868f57ae00aee9 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Mon, 15 Jan 2024 16:02:06 +0100 Subject: [PATCH 029/142] fix host for circle ci --- tests/network/docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/network/docker-compose.yml b/tests/network/docker-compose.yml index 3c2ef56852..5c20d29b94 100644 --- a/tests/network/docker-compose.yml +++ b/tests/network/docker-compose.yml @@ -44,6 +44,6 @@ services: - ocrd_network_test environment: DB_NAME: ocrd_network_test - DB_URL: mongodb://network_test:network_test@mongodb-docker-host:27017 + DB_URL: mongodb://network_test:network_test@mongodb-docker-host.ocrd_network_test:27017 # PROCESSING_SERVER_URL : http://... - RABBITMQ_URL: amqp://network_test:network_test@rabbitmq-docker-host:5672 + RABBITMQ_URL: amqp://network_test:network_test@rabbitmq-docker-host.ocrd_network_test:5672 From 38a073779ca73a16d5296d58a6d8ec9ea74e0884 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Mon, 15 Jan 2024 16:32:17 +0100 Subject: [PATCH 030/142] fix db update test --- tests/network/test_db.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/tests/network/test_db.py b/tests/network/test_db.py index a128aa1856..435afbdf5a 100644 --- a/tests/network/test_db.py +++ b/tests/network/test_db.py @@ -14,13 +14,13 @@ def test_db_write_processing_job(mongo_processor_jobs): } ) assert inserted_job - found_job = mongo_processor_jobs.find_one({"job_id": job_id}) + found_job = mongo_processor_jobs.find_one(filter={"job_id": job_id}) assert found_job def test_db_read_processing_job(mongo_processor_jobs): job_id = 'test_id_1234' - found_job = mongo_processor_jobs.find_one({"job_id": job_id}) + found_job = mongo_processor_jobs.find_one(filter={"job_id": job_id}) assert found_job assert found_job['job_id'] == job_id assert found_job['processor_name'] == 'ocrd-dummy' @@ -32,11 +32,15 @@ def test_db_read_processing_job(mongo_processor_jobs): def test_db_update_processing_job(mongo_processor_jobs): job_id = 'test_id_1234' - updated_job = mongo_processor_jobs.update_one(filter={"job_id": job_id}, update={"state": StateEnum.running}) - assert updated_job - assert updated_job['job_id'] == job_id - assert updated_job['processor_name'] == 'ocrd-dummy' - assert updated_job['state'] == StateEnum.running - assert updated_job['path_to_mets'] == '/ocrd/dummy/path' - assert updated_job['input_file_grps'] == ['DEFAULT'] - assert updated_job['output_file_grps'] == ['OCR-D-DUMMY'] + mongo_processor_jobs.update_one( + filter={"job_id": job_id}, + update={"$set": {"state": StateEnum.running}} + ) + found_job = mongo_processor_jobs.find_one(filter={"job_id": job_id}) + assert found_job + assert found_job['job_id'] == job_id + assert found_job['processor_name'] == 'ocrd-dummy' + assert found_job['state'] == StateEnum.running + assert found_job['path_to_mets'] == '/ocrd/dummy/path' + assert found_job['input_file_grps'] == ['DEFAULT'] + assert found_job['output_file_grps'] == ['OCR-D-DUMMY'] From 84656ef7dc9ca60cd706499e4648b68bd43fe137 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Mon, 15 Jan 2024 16:42:46 +0100 Subject: [PATCH 031/142] wait longer time --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5432df523a..81b97ae800 100644 --- a/Makefile +++ b/Makefile @@ -216,7 +216,7 @@ test: assets INTEGRATION_TEST_IN_DOCKER = docker exec core_test integration-test: docker compose -f tests/network/docker-compose.yml up -d - sleep 10 + sleep 20 $(INTEGRATION_TEST_IN_DOCKER) pytest -k 'test_rmq or test_db' docker compose -f tests/network/docker-compose.yml down --remove-orphans From 9afea111610df9434175388c9393b497c0debb6c Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Mon, 15 Jan 2024 16:51:26 +0100 Subject: [PATCH 032/142] move init db inside fixture --- tests/network/fixtures_mongodb.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/network/fixtures_mongodb.py b/tests/network/fixtures_mongodb.py index 6930962194..4c02897e8d 100644 --- a/tests/network/fixtures_mongodb.py +++ b/tests/network/fixtures_mongodb.py @@ -6,8 +6,6 @@ DB_NAME = config.DB_NAME DB_URL = config.DB_URL -sync_initiate_database(DB_URL, DB_NAME) - def verify_database_uri(mongodb_address: str) -> str: try: @@ -26,6 +24,7 @@ def fixture_mongo_client(): @fixture(scope="package", name="mongo_processor_jobs") def fixture_mongo_processor_jobs(mongo_client): + sync_initiate_database(DB_URL, DB_NAME) mydb = mongo_client[DB_NAME] processor_jobs_collection = mydb["DBProcessorJob"] yield processor_jobs_collection From c9a8cd37d0c227eb2fb66d1407c710b816abc643 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Mon, 15 Jan 2024 16:57:47 +0100 Subject: [PATCH 033/142] put init_db inside the fixture --- tests/network/fixtures_mongodb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/network/fixtures_mongodb.py b/tests/network/fixtures_mongodb.py index 4c02897e8d..ec4fb23211 100644 --- a/tests/network/fixtures_mongodb.py +++ b/tests/network/fixtures_mongodb.py @@ -18,13 +18,13 @@ def verify_database_uri(mongodb_address: str) -> str: @fixture(scope="package", name="mongo_client") def fixture_mongo_client(): + sync_initiate_database(DB_URL, DB_NAME) mongo_client = MongoClient(DB_URL, serverSelectionTimeoutMS=3000) yield mongo_client @fixture(scope="package", name="mongo_processor_jobs") def fixture_mongo_processor_jobs(mongo_client): - sync_initiate_database(DB_URL, DB_NAME) mydb = mongo_client[DB_NAME] processor_jobs_collection = mydb["DBProcessorJob"] yield processor_jobs_collection From be359f8244dade73654e2c943ef09d9fe49a7666 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Mon, 15 Jan 2024 17:10:48 +0100 Subject: [PATCH 034/142] comment out init db --- tests/network/fixtures_mongodb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/network/fixtures_mongodb.py b/tests/network/fixtures_mongodb.py index ec4fb23211..2fd7e3f680 100644 --- a/tests/network/fixtures_mongodb.py +++ b/tests/network/fixtures_mongodb.py @@ -18,7 +18,7 @@ def verify_database_uri(mongodb_address: str) -> str: @fixture(scope="package", name="mongo_client") def fixture_mongo_client(): - sync_initiate_database(DB_URL, DB_NAME) + # sync_initiate_database(DB_URL, DB_NAME) mongo_client = MongoClient(DB_URL, serverSelectionTimeoutMS=3000) yield mongo_client From cdf7c4afc0ab189e494dcd06d8064fa8be8fc29b Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Mon, 15 Jan 2024 18:34:50 +0100 Subject: [PATCH 035/142] ci: disable layer caching until we fix the outdated core_test image problem --- .circleci/config.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 74276b0a34..7da320a83d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -27,7 +27,7 @@ jobs: steps: - checkout - setup_remote_docker: # https://circleci.com/docs/2.0/building-docker-images/ - docker_layer_caching: true + docker_layer_caching: false - run: sudo apt-get -y update - run: sudo make deps-ubuntu - run: make install deps-test @@ -42,7 +42,7 @@ jobs: steps: - checkout - setup_remote_docker: # https://circleci.com/docs/2.0/building-docker-images/ - docker_layer_caching: true + docker_layer_caching: false - run: sudo apt-get -y update - run: sudo make deps-ubuntu - run: make install deps-test @@ -57,7 +57,7 @@ jobs: steps: - checkout - setup_remote_docker: # https://circleci.com/docs/2.0/building-docker-images/ - docker_layer_caching: true + docker_layer_caching: false - run: sudo apt-get -y update - run: sudo make deps-ubuntu - run: make install deps-test @@ -72,7 +72,7 @@ jobs: steps: - checkout - setup_remote_docker: # https://circleci.com/docs/2.0/building-docker-images/ - docker_layer_caching: true + docker_layer_caching: false - run: sudo apt-get -y update - run: sudo make deps-ubuntu - run: make install deps-test @@ -87,7 +87,7 @@ jobs: steps: - checkout - setup_remote_docker: # https://circleci.com/docs/2.0/building-docker-images/ - docker_layer_caching: true + docker_layer_caching: false - run: sudo apt-get -y update - run: sudo make deps-ubuntu - run: make install deps-test @@ -101,7 +101,7 @@ jobs: steps: - checkout - setup_remote_docker: # https://circleci.com/docs/2.0/building-docker-images/ - docker_layer_caching: true + docker_layer_caching: false - run: make docker - run: make docker-cuda - run: From 6182a004fa45564ae70c94403d07c9965b25515e Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Tue, 16 Jan 2024 11:32:45 +0100 Subject: [PATCH 036/142] add sync version of db_create_workspace --- ocrd_network/ocrd_network/database.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ocrd_network/ocrd_network/database.py b/ocrd_network/ocrd_network/database.py index 7844df991f..73e2699f23 100644 --- a/ocrd_network/ocrd_network/database.py +++ b/ocrd_network/ocrd_network/database.py @@ -60,6 +60,11 @@ async def db_create_workspace(mets_path: str) -> DBWorkspace: return workspace_db +@call_sync +async def sync_db_create_workspace(mets_path: str) -> DBWorkspace: + return await db_create_workspace(mets_path=mets_path) + + async def db_get_workspace(workspace_id: str = None, workspace_mets_path: str = None) -> DBWorkspace: workspace = None if not workspace_id and not workspace_mets_path: From a23fe9ef06b31ec9837ef5ebc2ace0342576c3ea Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Tue, 16 Jan 2024 11:34:42 +0100 Subject: [PATCH 037/142] add db workspace tests --- tests/network/fixtures_mongodb.py | 11 ++++++++- tests/network/test_db.py | 39 +++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/tests/network/fixtures_mongodb.py b/tests/network/fixtures_mongodb.py index 2fd7e3f680..b3f8b8c2b8 100644 --- a/tests/network/fixtures_mongodb.py +++ b/tests/network/fixtures_mongodb.py @@ -18,7 +18,8 @@ def verify_database_uri(mongodb_address: str) -> str: @fixture(scope="package", name="mongo_client") def fixture_mongo_client(): - # sync_initiate_database(DB_URL, DB_NAME) + # This extra initialization is needed to test the db wrapper methods + sync_initiate_database(DB_URL, DB_NAME) mongo_client = MongoClient(DB_URL, serverSelectionTimeoutMS=3000) yield mongo_client @@ -37,3 +38,11 @@ def fixture_mongo_workflow_jobs(mongo_client): workflow_jobs_collection = mydb["DBWorkflowJob"] yield workflow_jobs_collection workflow_jobs_collection.drop() + + +@fixture(scope="package", name="mongo_workspaces") +def fixture_mongo_workspaces(mongo_client): + mydb = mongo_client[DB_NAME] + workspaces_collection = mydb["DBWorkspace"] + yield workspaces_collection + workspaces_collection.drop() diff --git a/tests/network/test_db.py b/tests/network/test_db.py index 435afbdf5a..a3fb87317a 100644 --- a/tests/network/test_db.py +++ b/tests/network/test_db.py @@ -1,4 +1,10 @@ +from tests.base import assets from ocrd_network.models import StateEnum +from ocrd_network.database import ( + sync_db_create_workspace, + sync_db_get_workspace, + sync_db_update_workspace +) def test_db_write_processing_job(mongo_processor_jobs): @@ -44,3 +50,36 @@ def test_db_update_processing_job(mongo_processor_jobs): assert found_job['path_to_mets'] == '/ocrd/dummy/path' assert found_job['input_file_grps'] == ['DEFAULT'] assert found_job['output_file_grps'] == ['OCR-D-DUMMY'] + + +def test_db_create_workspace(mongo_workspaces): + mets_path = assets.path_to('kant_aufklaerung_1784/data/mets.xml') + db_created_workspace = sync_db_create_workspace(mets_path=mets_path) + assert db_created_workspace + assert db_created_workspace.workspace_mets_path == mets_path + db_found_workspace = sync_db_get_workspace(workspace_mets_path=mets_path) + assert db_found_workspace + assert db_found_workspace.workspace_mets_path == mets_path + + +def test_db_update_workspace(mongo_workspaces): + mets_path = assets.path_to('kant_aufklaerung_1784-binarized/data/mets.xml') + dummy_mets_server_url = '/tmp/dummy.sock' + + db_created_workspace = sync_db_create_workspace(mets_path=mets_path) + assert db_created_workspace + db_found_workspace = sync_db_get_workspace(workspace_mets_path=mets_path) + assert db_found_workspace + assert db_created_workspace == db_found_workspace + + db_updated_workspace = sync_db_update_workspace( + workspace_mets_path=mets_path, + mets_server_url=dummy_mets_server_url + ) + assert db_updated_workspace + assert db_updated_workspace != db_created_workspace + + db_found_updated_workspace = sync_db_get_workspace(workspace_mets_path=mets_path) + assert db_found_updated_workspace + assert db_found_updated_workspace.workspace_mets_path == mets_path + assert db_found_updated_workspace.mets_server_url == dummy_mets_server_url From 39c7e05e06b9c137fecf7bf80965196d1aeaec88 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Tue, 16 Jan 2024 12:28:22 +0100 Subject: [PATCH 038/142] refactor db processing job tests --- tests/network/test_db.py | 75 +++++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 32 deletions(-) diff --git a/tests/network/test_db.py b/tests/network/test_db.py index a3fb87317a..371b0dbe10 100644 --- a/tests/network/test_db.py +++ b/tests/network/test_db.py @@ -1,15 +1,19 @@ from tests.base import assets from ocrd_network.models import StateEnum from ocrd_network.database import ( + sync_db_get_processing_job, + sync_db_update_processing_job, sync_db_create_workspace, sync_db_get_workspace, sync_db_update_workspace ) -def test_db_write_processing_job(mongo_processor_jobs): +def test_db_processing_job_create(mongo_processor_jobs): job_id = 'test_id_1234' - inserted_job = mongo_processor_jobs.insert_one( + # TODO: There is no db wrapper to create processing job + # Hence, for now, use a low level method to insert a job + db_created_processing_job = mongo_processor_jobs.insert_one( document={ "job_id": job_id, "processor_name": 'ocrd-dummy', @@ -19,50 +23,56 @@ def test_db_write_processing_job(mongo_processor_jobs): "output_file_grps": ['OCR-D-DUMMY'] } ) - assert inserted_job - found_job = mongo_processor_jobs.find_one(filter={"job_id": job_id}) - assert found_job + assert db_created_processing_job + db_found_processing_job = sync_db_get_processing_job(job_id=job_id) + assert db_found_processing_job + assert db_found_processing_job.job_id == job_id + assert db_found_processing_job.processor_name == 'ocrd-dummy' + assert db_found_processing_job.state == StateEnum.cached + assert db_found_processing_job.path_to_mets == '/ocrd/dummy/path' + assert db_found_processing_job.input_file_grps == ['DEFAULT'] + assert db_found_processing_job.output_file_grps == ['OCR-D-DUMMY'] -def test_db_read_processing_job(mongo_processor_jobs): - job_id = 'test_id_1234' - found_job = mongo_processor_jobs.find_one(filter={"job_id": job_id}) - assert found_job - assert found_job['job_id'] == job_id - assert found_job['processor_name'] == 'ocrd-dummy' - assert found_job['state'] == StateEnum.cached - assert found_job['path_to_mets'] == '/ocrd/dummy/path' - assert found_job['input_file_grps'] == ['DEFAULT'] - assert found_job['output_file_grps'] == ['OCR-D-DUMMY'] +def test_db_processing_job_update(mongo_processor_jobs): + job_id = 'test_id_12345' + # TODO: There is no db wrapper to create processing job + # Hence, for now, use a low level method to insert a job + db_created_processing_job = mongo_processor_jobs.insert_one( + document={ + "job_id": job_id, + "processor_name": 'ocrd-dummy', + "state": StateEnum.cached, + "path_to_mets": '/ocrd/dummy/path', + "input_file_grps": ['DEFAULT'], + "output_file_grps": ['OCR-D-DUMMY'] + } + ) + assert db_created_processing_job + db_found_processing_job = sync_db_get_processing_job(job_id=job_id) + assert db_found_processing_job -def test_db_update_processing_job(mongo_processor_jobs): - job_id = 'test_id_1234' - mongo_processor_jobs.update_one( - filter={"job_id": job_id}, - update={"$set": {"state": StateEnum.running}} - ) - found_job = mongo_processor_jobs.find_one(filter={"job_id": job_id}) - assert found_job - assert found_job['job_id'] == job_id - assert found_job['processor_name'] == 'ocrd-dummy' - assert found_job['state'] == StateEnum.running - assert found_job['path_to_mets'] == '/ocrd/dummy/path' - assert found_job['input_file_grps'] == ['DEFAULT'] - assert found_job['output_file_grps'] == ['OCR-D-DUMMY'] + db_updated_processing_job = sync_db_update_processing_job(job_id=job_id, state=StateEnum.running) + assert db_found_processing_job != db_updated_processing_job + + db_found_updated_processing_job = sync_db_get_processing_job(job_id=job_id) + assert db_found_updated_processing_job + assert db_found_updated_processing_job == db_updated_processing_job + assert db_found_updated_processing_job.state == StateEnum.running -def test_db_create_workspace(mongo_workspaces): +def test_db_workspace_create(mongo_workspaces): mets_path = assets.path_to('kant_aufklaerung_1784/data/mets.xml') db_created_workspace = sync_db_create_workspace(mets_path=mets_path) assert db_created_workspace assert db_created_workspace.workspace_mets_path == mets_path db_found_workspace = sync_db_get_workspace(workspace_mets_path=mets_path) assert db_found_workspace - assert db_found_workspace.workspace_mets_path == mets_path + assert db_found_workspace == db_created_workspace -def test_db_update_workspace(mongo_workspaces): +def test_db_workspace_update(mongo_workspaces): mets_path = assets.path_to('kant_aufklaerung_1784-binarized/data/mets.xml') dummy_mets_server_url = '/tmp/dummy.sock' @@ -83,3 +93,4 @@ def test_db_update_workspace(mongo_workspaces): assert db_found_updated_workspace assert db_found_updated_workspace.workspace_mets_path == mets_path assert db_found_updated_workspace.mets_server_url == dummy_mets_server_url + assert db_found_updated_workspace == db_updated_workspace From 9ee09f9788094212030770809c9c4f44e8b84a83 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Tue, 16 Jan 2024 13:01:41 +0100 Subject: [PATCH 039/142] refine tests, remove unnecessary --- tests/network/fixtures_mongodb.py | 16 ---------------- tests/network/test_db.py | 23 ++++++++++++++++++----- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/tests/network/fixtures_mongodb.py b/tests/network/fixtures_mongodb.py index b3f8b8c2b8..647492986b 100644 --- a/tests/network/fixtures_mongodb.py +++ b/tests/network/fixtures_mongodb.py @@ -30,19 +30,3 @@ def fixture_mongo_processor_jobs(mongo_client): processor_jobs_collection = mydb["DBProcessorJob"] yield processor_jobs_collection processor_jobs_collection.drop() - - -@fixture(scope="package", name="mongo_workflow_jobs") -def fixture_mongo_workflow_jobs(mongo_client): - mydb = mongo_client[DB_NAME] - workflow_jobs_collection = mydb["DBWorkflowJob"] - yield workflow_jobs_collection - workflow_jobs_collection.drop() - - -@fixture(scope="package", name="mongo_workspaces") -def fixture_mongo_workspaces(mongo_client): - mydb = mongo_client[DB_NAME] - workspaces_collection = mydb["DBWorkspace"] - yield workspaces_collection - workspaces_collection.drop() diff --git a/tests/network/test_db.py b/tests/network/test_db.py index 371b0dbe10..6ab7d9816b 100644 --- a/tests/network/test_db.py +++ b/tests/network/test_db.py @@ -1,3 +1,4 @@ +from pytest import raises from tests.base import assets from ocrd_network.models import StateEnum from ocrd_network.database import ( @@ -33,6 +34,9 @@ def test_db_processing_job_create(mongo_processor_jobs): assert db_found_processing_job.input_file_grps == ['DEFAULT'] assert db_found_processing_job.output_file_grps == ['OCR-D-DUMMY'] + with raises(ValueError) as value_error: + sync_db_get_processing_job(job_id='non-existing-id') + def test_db_processing_job_update(mongo_processor_jobs): job_id = 'test_id_12345' @@ -49,20 +53,22 @@ def test_db_processing_job_update(mongo_processor_jobs): } ) assert db_created_processing_job - db_found_processing_job = sync_db_get_processing_job(job_id=job_id) assert db_found_processing_job - db_updated_processing_job = sync_db_update_processing_job(job_id=job_id, state=StateEnum.running) assert db_found_processing_job != db_updated_processing_job - db_found_updated_processing_job = sync_db_get_processing_job(job_id=job_id) assert db_found_updated_processing_job assert db_found_updated_processing_job == db_updated_processing_job assert db_found_updated_processing_job.state == StateEnum.running + with raises(ValueError) as value_error: + sync_db_update_processing_job(job_id='non-existing', state=StateEnum.running) + sync_db_update_processing_job(job_id=job_id, non_existing_field='dummy_value') + sync_db_update_processing_job(job_id=job_id, processor_name='non-updatable-field') -def test_db_workspace_create(mongo_workspaces): + +def test_db_workspace_create(): mets_path = assets.path_to('kant_aufklaerung_1784/data/mets.xml') db_created_workspace = sync_db_create_workspace(mets_path=mets_path) assert db_created_workspace @@ -71,8 +77,15 @@ def test_db_workspace_create(mongo_workspaces): assert db_found_workspace assert db_found_workspace == db_created_workspace + with raises(ValueError) as value_error: + sync_db_get_workspace(workspace_id='non-existing-id') + sync_db_get_workspace(workspace_mets_path='non-existing-mets') + + with raises(FileNotFoundError) as io_error: + sync_db_create_workspace(mets_path='non-existing-mets') + -def test_db_workspace_update(mongo_workspaces): +def test_db_workspace_update(): mets_path = assets.path_to('kant_aufklaerung_1784-binarized/data/mets.xml') dummy_mets_server_url = '/tmp/dummy.sock' From 720e0ccaef18893a7af69e20c1ae1a17859b1107 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Tue, 16 Jan 2024 13:27:58 +0100 Subject: [PATCH 040/142] add db create for jobs --- ocrd_network/ocrd_network/database.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ocrd_network/ocrd_network/database.py b/ocrd_network/ocrd_network/database.py index 73e2699f23..72324a132d 100644 --- a/ocrd_network/ocrd_network/database.py +++ b/ocrd_network/ocrd_network/database.py @@ -139,6 +139,15 @@ async def sync_db_update_workspace(workspace_id: str = None, workspace_mets_path return await db_update_workspace(workspace_id=workspace_id, workspace_mets_path=workspace_mets_path, **kwargs) +async def db_create_processing_job(db_processing_job: DBProcessorJob) -> DBProcessorJob: + return await db_processing_job.insert() + + +@call_sync +async def sync_db_create_processing_job(db_processing_job: DBProcessorJob) -> DBProcessorJob: + return await db_create_processing_job(db_processing_job=db_processing_job) + + async def db_get_processing_job(job_id: str) -> DBProcessorJob: job = await DBProcessorJob.find_one( DBProcessorJob.job_id == job_id) From 0bf4bc9bc48cc85f8831ed4fa64530893b839176 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Tue, 16 Jan 2024 13:28:19 +0100 Subject: [PATCH 041/142] simplify fixtures --- tests/network/fixtures_mongodb.py | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/tests/network/fixtures_mongodb.py b/tests/network/fixtures_mongodb.py index 647492986b..d146f4f0f9 100644 --- a/tests/network/fixtures_mongodb.py +++ b/tests/network/fixtures_mongodb.py @@ -1,11 +1,8 @@ -from pymongo import MongoClient, uri_parser as mongo_uri_parser +from pymongo import uri_parser as mongo_uri_parser from pytest import fixture from ocrd_utils.config import config from ocrd_network.database import sync_initiate_database -DB_NAME = config.DB_NAME -DB_URL = config.DB_URL - def verify_database_uri(mongodb_address: str) -> str: try: @@ -18,15 +15,4 @@ def verify_database_uri(mongodb_address: str) -> str: @fixture(scope="package", name="mongo_client") def fixture_mongo_client(): - # This extra initialization is needed to test the db wrapper methods - sync_initiate_database(DB_URL, DB_NAME) - mongo_client = MongoClient(DB_URL, serverSelectionTimeoutMS=3000) - yield mongo_client - - -@fixture(scope="package", name="mongo_processor_jobs") -def fixture_mongo_processor_jobs(mongo_client): - mydb = mongo_client[DB_NAME] - processor_jobs_collection = mydb["DBProcessorJob"] - yield processor_jobs_collection - processor_jobs_collection.drop() + sync_initiate_database(config.DB_URL, config.DB_NAME) From afcbd3fadaf5e814aa60dd10877c73584c742fe1 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Tue, 16 Jan 2024 13:28:32 +0100 Subject: [PATCH 042/142] simplify db tests --- tests/network/test_db.py | 51 +++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/tests/network/test_db.py b/tests/network/test_db.py index 6ab7d9816b..d138ad1c1b 100644 --- a/tests/network/test_db.py +++ b/tests/network/test_db.py @@ -1,7 +1,8 @@ from pytest import raises from tests.base import assets -from ocrd_network.models import StateEnum +from ocrd_network.models import DBProcessorJob, StateEnum from ocrd_network.database import ( + sync_db_create_processing_job, sync_db_get_processing_job, sync_db_update_processing_job, sync_db_create_workspace, @@ -10,19 +11,17 @@ ) -def test_db_processing_job_create(mongo_processor_jobs): +def test_db_processing_job_create(mongo_client): job_id = 'test_id_1234' - # TODO: There is no db wrapper to create processing job - # Hence, for now, use a low level method to insert a job - db_created_processing_job = mongo_processor_jobs.insert_one( - document={ - "job_id": job_id, - "processor_name": 'ocrd-dummy', - "state": StateEnum.cached, - "path_to_mets": '/ocrd/dummy/path', - "input_file_grps": ['DEFAULT'], - "output_file_grps": ['OCR-D-DUMMY'] - } + db_created_processing_job = sync_db_create_processing_job( + db_processing_job=DBProcessorJob( + job_id=job_id, + processor_name='ocrd-dummy', + state=StateEnum.cached, + path_to_mets='/ocrd/dummy/path', + input_file_grps=['DEFAULT'], + output_file_grps=['OCR-D-DUMMY'] + ) ) assert db_created_processing_job db_found_processing_job = sync_db_get_processing_job(job_id=job_id) @@ -38,19 +37,17 @@ def test_db_processing_job_create(mongo_processor_jobs): sync_db_get_processing_job(job_id='non-existing-id') -def test_db_processing_job_update(mongo_processor_jobs): +def test_db_processing_job_update(mongo_client): job_id = 'test_id_12345' - # TODO: There is no db wrapper to create processing job - # Hence, for now, use a low level method to insert a job - db_created_processing_job = mongo_processor_jobs.insert_one( - document={ - "job_id": job_id, - "processor_name": 'ocrd-dummy', - "state": StateEnum.cached, - "path_to_mets": '/ocrd/dummy/path', - "input_file_grps": ['DEFAULT'], - "output_file_grps": ['OCR-D-DUMMY'] - } + db_created_processing_job = sync_db_create_processing_job( + db_processing_job=DBProcessorJob( + job_id=job_id, + processor_name='ocrd-dummy', + state=StateEnum.cached, + path_to_mets='/ocrd/dummy/path', + input_file_grps=['DEFAULT'], + output_file_grps=['OCR-D-DUMMY'] + ) ) assert db_created_processing_job db_found_processing_job = sync_db_get_processing_job(job_id=job_id) @@ -68,7 +65,7 @@ def test_db_processing_job_update(mongo_processor_jobs): sync_db_update_processing_job(job_id=job_id, processor_name='non-updatable-field') -def test_db_workspace_create(): +def test_db_workspace_create(mongo_client): mets_path = assets.path_to('kant_aufklaerung_1784/data/mets.xml') db_created_workspace = sync_db_create_workspace(mets_path=mets_path) assert db_created_workspace @@ -85,7 +82,7 @@ def test_db_workspace_create(): sync_db_create_workspace(mets_path='non-existing-mets') -def test_db_workspace_update(): +def test_db_workspace_update(mongo_client): mets_path = assets.path_to('kant_aufklaerung_1784-binarized/data/mets.xml') dummy_mets_server_url = '/tmp/dummy.sock' From 1115e3d5782e0f8a3dd1dfa1b1b34c5206bec820 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Tue, 16 Jan 2024 15:12:36 +0100 Subject: [PATCH 043/142] extend db tests --- ocrd_network/ocrd_network/database.py | 19 ++++++++ tests/network/dummy-workflow.txt | 1 + tests/network/fixtures_mongodb.py | 1 + tests/network/test_db.py | 67 +++++++++++++++++++++++++-- 4 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 tests/network/dummy-workflow.txt diff --git a/ocrd_network/ocrd_network/database.py b/ocrd_network/ocrd_network/database.py index 72324a132d..946ed95a5b 100644 --- a/ocrd_network/ocrd_network/database.py +++ b/ocrd_network/ocrd_network/database.py @@ -194,6 +194,15 @@ async def sync_db_update_processing_job(job_id: str, **kwargs) -> DBProcessorJob return await db_update_processing_job(job_id=job_id, **kwargs) +async def db_create_workflow_job(db_workflow_job: DBWorkflowJob) -> DBWorkflowJob: + return await db_workflow_job.insert() + + +@call_sync +async def sync_db_create_workflow_job(db_workflow_job: DBWorkflowJob) -> DBWorkflowJob: + return await db_create_workflow_job(db_workflow_job=db_workflow_job) + + async def db_get_workflow_job(job_id: str) -> DBWorkflowJob: job = await DBWorkflowJob.find_one(DBWorkflowJob.job_id == job_id) if not job: @@ -216,6 +225,15 @@ async def sync_db_get_processing_jobs(job_ids: List[str]) -> [DBProcessorJob]: return await db_get_processing_jobs(job_ids) +async def db_create_workflow_script(db_workflow_script: DBWorkflowScript) -> DBWorkflowScript: + return await db_workflow_script.insert() + + +@call_sync +async def sync_db_create_workflow_script(db_workflow_script: DBWorkflowScript) -> DBWorkflowScript: + return await db_create_workflow_script(db_workflow_script=db_workflow_script) + + async def db_get_workflow_script(workflow_id: str) -> DBWorkflowScript: workflow = await DBWorkflowScript.find_one(DBWorkflowScript.workflow_id == workflow_id) if not workflow: @@ -235,6 +253,7 @@ async def db_find_first_workflow_script_by_content(content_hash: str) -> DBWorkf return workflow +# TODO: Resolve the inconsistency between the async and sync versions of the same method @call_sync async def sync_db_find_first_workflow_script_by_content(workflow_id: str) -> DBWorkflowScript: return await db_get_workflow_script(workflow_id) diff --git a/tests/network/dummy-workflow.txt b/tests/network/dummy-workflow.txt new file mode 100644 index 0000000000..223c217678 --- /dev/null +++ b/tests/network/dummy-workflow.txt @@ -0,0 +1 @@ +dummy -I DEFAULT -O OCR-D-DUMMY \ No newline at end of file diff --git a/tests/network/fixtures_mongodb.py b/tests/network/fixtures_mongodb.py index d146f4f0f9..0365560dd2 100644 --- a/tests/network/fixtures_mongodb.py +++ b/tests/network/fixtures_mongodb.py @@ -15,4 +15,5 @@ def verify_database_uri(mongodb_address: str) -> str: @fixture(scope="package", name="mongo_client") def fixture_mongo_client(): + verify_database_uri(config.DB_URL) sync_initiate_database(config.DB_URL, config.DB_NAME) diff --git a/tests/network/test_db.py b/tests/network/test_db.py index d138ad1c1b..bc044812a2 100644 --- a/tests/network/test_db.py +++ b/tests/network/test_db.py @@ -1,18 +1,23 @@ +from hashlib import md5 +from pathlib import Path from pytest import raises from tests.base import assets -from ocrd_network.models import DBProcessorJob, StateEnum +from ocrd_network.models import DBProcessorJob, DBWorkflowScript, StateEnum from ocrd_network.database import ( sync_db_create_processing_job, sync_db_get_processing_job, sync_db_update_processing_job, sync_db_create_workspace, sync_db_get_workspace, - sync_db_update_workspace + sync_db_update_workspace, + sync_db_create_workflow_script, + sync_db_get_workflow_script, + sync_db_find_first_workflow_script_by_content ) def test_db_processing_job_create(mongo_client): - job_id = 'test_id_1234' + job_id = 'test_job_id_1' db_created_processing_job = sync_db_create_processing_job( db_processing_job=DBProcessorJob( job_id=job_id, @@ -38,7 +43,7 @@ def test_db_processing_job_create(mongo_client): def test_db_processing_job_update(mongo_client): - job_id = 'test_id_12345' + job_id = 'test_job_id_2' db_created_processing_job = sync_db_create_processing_job( db_processing_job=DBProcessorJob( job_id=job_id, @@ -104,3 +109,57 @@ def test_db_workspace_update(mongo_client): assert db_found_updated_workspace.workspace_mets_path == mets_path assert db_found_updated_workspace.mets_server_url == dummy_mets_server_url assert db_found_updated_workspace == db_updated_workspace + + +# TODO: There is no db wrapper implemented due to direct access in the processing server... +# TODO2: Should be refactored with proper asset access +def create_db_model_workflow_script( + workflow_id: str, + script_path: Path = Path(Path(__file__).parent, "dummy-workflow.txt") +) -> DBWorkflowScript: + workflow_id = workflow_id + with open(script_path, 'rb') as fp: + content = (fp.read()).decode("utf-8") + content_hash = md5(content.encode("utf-8")).hexdigest() + return DBWorkflowScript(workflow_id=workflow_id, content=content, content_hash=content_hash) + + +def test_db_workflow_script_create(mongo_client): + workflow_id = 'test_workflow_1' + db_model_workflow_script = create_db_model_workflow_script(workflow_id=workflow_id) + db_created_workflow_script = sync_db_create_workflow_script( + db_workflow_script=db_model_workflow_script + ) + assert db_created_workflow_script + db_found_workflow_script = sync_db_get_workflow_script(workflow_id=workflow_id) + assert db_found_workflow_script + assert db_found_workflow_script == db_created_workflow_script + + with raises(ValueError) as value_error: + sync_db_get_workflow_script(workflow_id='non-existing-id') + + +def test_db_find_workflow_script_by_content(mongo_client): + workflow_id = 'test_workflow_2' + db_model_workflow_script = create_db_model_workflow_script(workflow_id=workflow_id) + db_created_workflow_script = sync_db_create_workflow_script( + db_workflow_script=db_model_workflow_script + ) + assert db_created_workflow_script + db_found_workflow_script = sync_db_find_first_workflow_script_by_content( + workflow_id=db_model_workflow_script.workflow_id + ) + assert db_found_workflow_script + assert db_found_workflow_script == db_created_workflow_script + + +# TODO: hard to implement without some refactoring in the ocrd_network +# and providing proper db wrappers +def test_db_workflow_job_create(): + pass + + +# TODO: hard to implement without some refactoring in the ocrd_network +# and providing proper db wrappers +def test_db_workflow_job_update(): + pass From 2d4809ac0155b8fdc3aaff9b84fc522f65b04d8d Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Thu, 18 Jan 2024 14:22:00 +0100 Subject: [PATCH 044/142] add processing server test --- Makefile | 2 +- .../ocrd_network/processing_server.py | 17 +++++++ tests/network/docker-compose.yml | 47 ++++++++++++++++++- tests/network/ps_config.yml | 28 +++++++++++ tests/network/test_processing_server.py | 18 +++++++ 5 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 tests/network/ps_config.yml create mode 100644 tests/network/test_processing_server.py diff --git a/Makefile b/Makefile index 81b97ae800..14427a5a65 100644 --- a/Makefile +++ b/Makefile @@ -217,7 +217,7 @@ INTEGRATION_TEST_IN_DOCKER = docker exec core_test integration-test: docker compose -f tests/network/docker-compose.yml up -d sleep 20 - $(INTEGRATION_TEST_IN_DOCKER) pytest -k 'test_rmq or test_db' + $(INTEGRATION_TEST_IN_DOCKER) pytest -k 'test_rmq or test_db or test_processing_server' docker compose -f tests/network/docker-compose.yml down --remove-orphans benchmark: diff --git a/ocrd_network/ocrd_network/processing_server.py b/ocrd_network/ocrd_network/processing_server.py index e6606f90f0..77c2dd9025 100644 --- a/ocrd_network/ocrd_network/processing_server.py +++ b/ocrd_network/ocrd_network/processing_server.py @@ -1,3 +1,4 @@ +from datetime import datetime from hashlib import md5 import httpx import json @@ -127,6 +128,14 @@ def __init__(self, config_path: str, host: str, port: int) -> None: else: self.internal_job_callback_url = f'http://{host}:{port}/result_callback' + self.router.add_api_route( + path='/', + endpoint=self.home_page, + methods=['GET'], + status_code=status.HTTP_200_OK, + summary='Get information about the processing server' + ) + # Create routes self.router.add_api_route( path='/stop', @@ -288,6 +297,14 @@ async def on_shutdown(self) -> None: """ await self.stop_deployed_agents() + async def home_page(self): + message = f"The home page of the {self.title}" + json_message = { + "message": message, + "time": datetime.now().strftime("%Y-%m-%d %H:%M") + } + return json_message + async def stop_deployed_agents(self) -> None: self.deployer.kill_all() diff --git a/tests/network/docker-compose.yml b/tests/network/docker-compose.yml index 5c20d29b94..a17f306e38 100644 --- a/tests/network/docker-compose.yml +++ b/tests/network/docker-compose.yml @@ -10,6 +10,7 @@ services: ocrd_network_mongo_db: image: "mongo" hostname: mongodb-docker-host + container_name: ocrd_network_mongo_db networks: - ocrd_network_test ports: @@ -17,10 +18,16 @@ services: environment: - MONGO_INITDB_ROOT_USERNAME=network_test - MONGO_INITDB_ROOT_PASSWORD=network_test + healthcheck: + test: echo 'db.runCommand("ping").ok' | mongosh localhost:27017/test --quiet + interval: 1s + timeout: 3s + retries: 30 ocrd_network_rabbit_mq: image: "rabbitmq:3.12-management" hostname: rabbitmq-docker-host + container_name: ocrd_network_rabbit_mq networks: - ocrd_network_test ports: @@ -31,6 +38,39 @@ services: - RABBITMQ_DEFAULT_USER=network_test - RABBITMQ_DEFAULT_PASS=network_test - RABBITMQ_FEATURE_FLAGS=quorum_queue,implicit_default_bindings,classic_mirrored_queue_version + healthcheck: + test: rabbitmq-diagnostics check_port_connectivity + interval: 1s + timeout: 3s + retries: 30 + + ocrd_network_processing_server: + build: + context: ../../ + dockerfile: Dockerfile + args: + BASE_IMAGE: 'ubuntu:20.04' + hostname: processing-server-host + container_name: ocrd_network_processing_server + depends_on: + ocrd_network_mongo_db: + condition: service_healthy + ocrd_network_rabbit_mq: + condition: service_healthy + networks: + - ocrd_network_test + ports: + - "8000:8000" + environment: + DB_NAME: ocrd_network_test + DB_URL: mongodb://network_test:network_test@mongodb-docker-host.ocrd_network_test:27017 + RABBITMQ_URL: amqp://network_test:network_test@rabbitmq-docker-host.ocrd_network_test:5672 + volumes: + - "./ps_config.yml:/ps_config.yml" + - "/home/mm/.ssh/cloud.key:/ssh_key" + - "/tmp/ocrd_network_logs:/tmp/ocrd_network_logs" + - "/tmp/ocrd_network_sockets:/tmp/ocrd_network_sockets" + command: ocrd network processing-server /ps_config.yml --address 0.0.0.0:8000 ocrd_network_core_test: build: @@ -40,10 +80,15 @@ services: BASE_IMAGE: 'ubuntu:20.04' target: ocrd_core_test container_name: core_test + depends_on: + ocrd_network_mongo_db: + condition: service_healthy + ocrd_network_rabbit_mq: + condition: service_healthy networks: - ocrd_network_test environment: DB_NAME: ocrd_network_test DB_URL: mongodb://network_test:network_test@mongodb-docker-host.ocrd_network_test:27017 - # PROCESSING_SERVER_URL : http://... + PROCESSING_SERVER_URL: http://processing-server-host.ocrd_network_test:8000 RABBITMQ_URL: amqp://network_test:network_test@rabbitmq-docker-host.ocrd_network_test:5672 diff --git a/tests/network/ps_config.yml b/tests/network/ps_config.yml new file mode 100644 index 0000000000..63c21d0175 --- /dev/null +++ b/tests/network/ps_config.yml @@ -0,0 +1,28 @@ +process_queue: + address: rabbitmq-docker-host.ocrd_network_test + port: 5672 + credentials: + username: network_test + password: network_test + ssh: + username: root + path_to_privkey: /ssh_key + skip_deployment: true +database: + address: mongodb-docker-host.ocrd_network_test + port: 27017 + credentials: + username: network_test + password: network_test + ssh: + username: root + path_to_privkey: /ssh_key + skip_deployment: true +hosts: + - address: processing-server-host.ocrd_network_test + username: root + path_to_privkey: /ssh_key + workers: + - name: ocrd-dummy + number_of_instance: 1 + deploy_type: native diff --git a/tests/network/test_processing_server.py b/tests/network/test_processing_server.py new file mode 100644 index 0000000000..f758012aa1 --- /dev/null +++ b/tests/network/test_processing_server.py @@ -0,0 +1,18 @@ +from requests import get +from ocrd_utils.config import config + +PROCESSING_SERVER_URL = config.PROCESSING_SERVER_URL + + +def test_processing_server_connectivity(): + test_url = f'{PROCESSING_SERVER_URL}/' + response = get(test_url) + assert response.status_code == 200, \ + f'Processing server is not reachable on: {test_url}, {response.status_code}' + + +def _test_processing_server_deployed_processors(): + test_url = f'{PROCESSING_SERVER_URL}/processor' + response = get(test_url) + assert response.status_code == 200, \ + f'Processing server is not reachable on: {test_url}, {response.status_code}' From f5183a758a1effd859d6d722c820a23674a3ab25 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Fri, 19 Jan 2024 13:12:14 +0100 Subject: [PATCH 045/142] convert multi-dist to src-package layout --- .gitignore | 3 +-- ocrd/ocrd/.doctrees/environment.pickle | Bin 779431 -> 0 bytes ocrd_modelfactory/ocrd_modelfactory | 1 + .../.doctrees/environment.pickle | Bin 779457 -> 0 bytes .../ocrd_models/.doctrees/environment.pickle | Bin 779445 -> 0 bytes ocrd_network/ocrd_network | 1 + ocrd_utils/ocrd_utils | 1 + ocrd_validators/ocrd_validators | 1 + .../.doctrees/environment.pickle | Bin 779453 -> 0 bytes {ocrd => src}/ocrd/__init__.py | 0 {ocrd => src}/ocrd/cli/__init__.py | 0 {ocrd => src}/ocrd/cli/bashlib.py | 0 {ocrd => src}/ocrd/cli/log.py | 0 {ocrd => src}/ocrd/cli/network.py | 0 {ocrd => src}/ocrd/cli/ocrd_tool.py | 0 {ocrd => src}/ocrd/cli/process.py | 0 {ocrd => src}/ocrd/cli/resmgr.py | 0 {ocrd => src}/ocrd/cli/validate.py | 0 {ocrd => src}/ocrd/cli/workspace.py | 0 {ocrd => src}/ocrd/cli/zip.py | 0 {ocrd => src}/ocrd/constants.py | 0 {ocrd => src}/ocrd/decorators/__init__.py | 0 .../ocrd/decorators/loglevel_option.py | 0 .../ocrd/decorators/mets_find_options.py | 0 .../ocrd/decorators/ocrd_cli_options.py | 0 .../ocrd/decorators/parameter_option.py | 0 {ocrd => src}/ocrd/lib.bash | 0 {ocrd => src}/ocrd/mets_server.py | 0 {ocrd => src}/ocrd/processor/__init__.py | 0 {ocrd => src}/ocrd/processor/base.py | 0 .../ocrd/processor/builtin/__init__.py | 0 .../ocrd/processor/builtin/dummy/__init__.py | 0 .../processor/builtin/dummy/ocrd-tool.json | 0 .../ocrd/processor/builtin/dummy_processor.py | 0 {ocrd => src}/ocrd/processor/helpers.py | 0 {ocrd => src}/ocrd/resolver.py | 0 {ocrd => src}/ocrd/resource_list.yml | 0 {ocrd => src}/ocrd/resource_manager.py | 0 {ocrd => src}/ocrd/task_sequence.py | 0 {ocrd => src}/ocrd/workspace.py | 0 {ocrd => src}/ocrd/workspace_backup.py | 0 {ocrd => src}/ocrd/workspace_bagger.py | 0 .../ocrd_modelfactory/__init__.py | 0 {ocrd_models => src}/ocrd_models/__init__.py | 0 {ocrd_models => src}/ocrd_models/constants.py | 0 .../ocrd_models/mets-empty.xml | 0 {ocrd_models => src}/ocrd_models/ocrd_agent.py | 0 {ocrd_models => src}/ocrd_models/ocrd_exif.py | 0 {ocrd_models => src}/ocrd_models/ocrd_file.py | 0 {ocrd_models => src}/ocrd_models/ocrd_mets.py | 0 {ocrd_models => src}/ocrd_models/ocrd_page.py | 0 .../ocrd_models/ocrd_page_generateds.py | 0 .../ocrd_models/ocrd_xml_base.py | 0 {ocrd_models => src}/ocrd_models/report.py | 0 {ocrd_models => src}/ocrd_models/utils.py | 0 {ocrd_network => src}/ocrd_network/__init__.py | 0 .../ocrd_network/cli/__init__.py | 0 .../ocrd_network/cli/client.py | 0 .../ocrd_network/cli/processing_server.py | 0 .../ocrd_network/cli/processing_worker.py | 0 .../ocrd_network/cli/processor_server.py | 0 {ocrd_network => src}/ocrd_network/client.py | 0 .../ocrd_network/constants.py | 0 {ocrd_network => src}/ocrd_network/database.py | 0 {ocrd_network => src}/ocrd_network/deployer.py | 0 .../ocrd_network/deployment_utils.py | 0 {ocrd_network => src}/ocrd_network/logging.py | 0 .../ocrd_network/models/__init__.py | 0 .../ocrd_network/models/job.py | 0 .../ocrd_network/models/messages.py | 0 .../ocrd_network/models/ocrd_tool.py | 0 .../ocrd_network/models/workflow.py | 0 .../ocrd_network/models/workspace.py | 0 .../ocrd_network/param_validators.py | 0 .../ocrd_network/process_helpers.py | 0 .../ocrd_network/processing_server.py | 0 .../ocrd_network/processing_worker.py | 0 .../ocrd_network/processor_server.py | 0 .../ocrd_network/rabbitmq_utils/__init__.py | 0 .../ocrd_network/rabbitmq_utils/connector.py | 0 .../ocrd_network/rabbitmq_utils/constants.py | 0 .../ocrd_network/rabbitmq_utils/consumer.py | 0 .../rabbitmq_utils/ocrd_messages.py | 0 .../ocrd_network/rabbitmq_utils/publisher.py | 0 .../ocrd_network/runtime_data.py | 0 .../ocrd_network/server_cache.py | 0 .../ocrd_network/server_utils.py | 0 {ocrd_network => src}/ocrd_network/utils.py | 0 {ocrd_utils => src}/ocrd_utils/__init__.py | 0 {ocrd_utils => src}/ocrd_utils/config.py | 0 {ocrd_utils => src}/ocrd_utils/constants.py | 2 +- {ocrd_utils => src}/ocrd_utils/deprecate.py | 0 {ocrd_utils => src}/ocrd_utils/image.py | 0 {ocrd_utils => src}/ocrd_utils/introspect.py | 0 {ocrd_utils => src}/ocrd_utils/logging.py | 0 {ocrd_utils => src}/ocrd_utils/os.py | 0 {ocrd_utils => src}/ocrd_utils/str.py | 0 .../ocrd_validators/__init__.py | 0 .../ocrd_validators/bagit-profile.yml | 0 .../ocrd_validators/constants.py | 0 .../ocrd_validators/json_validator.py | 0 .../message_processing.schema.yml | 0 .../ocrd_validators/message_result.schema.yml | 0 .../ocrd_validators/mets.xsd | 0 .../ocrd_network_message_validator.py | 0 .../ocrd_validators/ocrd_tool.schema.yml | 0 .../ocrd_validators/ocrd_tool_validator.py | 0 .../ocrd_validators/ocrd_zip_validator.py | 0 .../ocrd_validators/page.xsd | 0 .../ocrd_validators/page_validator.py | 0 .../ocrd_validators/parameter_validator.py | 0 .../processing_server_config.schema.yml | 0 .../processing_server_config_validator.py | 0 .../ocrd_validators/resource_list_validator.py | 0 .../ocrd_validators/workspace_validator.py | 0 .../ocrd_validators/xlink.xsd | 0 .../ocrd_validators/xsd_mets_validator.py | 0 .../ocrd_validators/xsd_page_validator.py | 0 .../ocrd_validators/xsd_validator.py | 0 tests/cli/test_bashlib.py | 4 ++-- 120 files changed, 8 insertions(+), 5 deletions(-) delete mode 100644 ocrd/ocrd/.doctrees/environment.pickle create mode 120000 ocrd_modelfactory/ocrd_modelfactory delete mode 100644 ocrd_modelfactory/ocrd_modelfactory/.doctrees/environment.pickle delete mode 100644 ocrd_models/ocrd_models/.doctrees/environment.pickle create mode 120000 ocrd_network/ocrd_network create mode 120000 ocrd_utils/ocrd_utils create mode 120000 ocrd_validators/ocrd_validators delete mode 100644 ocrd_validators/ocrd_validators/.doctrees/environment.pickle rename {ocrd => src}/ocrd/__init__.py (100%) rename {ocrd => src}/ocrd/cli/__init__.py (100%) rename {ocrd => src}/ocrd/cli/bashlib.py (100%) rename {ocrd => src}/ocrd/cli/log.py (100%) rename {ocrd => src}/ocrd/cli/network.py (100%) rename {ocrd => src}/ocrd/cli/ocrd_tool.py (100%) rename {ocrd => src}/ocrd/cli/process.py (100%) rename {ocrd => src}/ocrd/cli/resmgr.py (100%) rename {ocrd => src}/ocrd/cli/validate.py (100%) rename {ocrd => src}/ocrd/cli/workspace.py (100%) rename {ocrd => src}/ocrd/cli/zip.py (100%) rename {ocrd => src}/ocrd/constants.py (100%) rename {ocrd => src}/ocrd/decorators/__init__.py (100%) rename {ocrd => src}/ocrd/decorators/loglevel_option.py (100%) rename {ocrd => src}/ocrd/decorators/mets_find_options.py (100%) rename {ocrd => src}/ocrd/decorators/ocrd_cli_options.py (100%) rename {ocrd => src}/ocrd/decorators/parameter_option.py (100%) rename {ocrd => src}/ocrd/lib.bash (100%) rename {ocrd => src}/ocrd/mets_server.py (100%) rename {ocrd => src}/ocrd/processor/__init__.py (100%) rename {ocrd => src}/ocrd/processor/base.py (100%) rename {ocrd => src}/ocrd/processor/builtin/__init__.py (100%) rename {ocrd => src}/ocrd/processor/builtin/dummy/__init__.py (100%) rename {ocrd => src}/ocrd/processor/builtin/dummy/ocrd-tool.json (100%) rename {ocrd => src}/ocrd/processor/builtin/dummy_processor.py (100%) rename {ocrd => src}/ocrd/processor/helpers.py (100%) rename {ocrd => src}/ocrd/resolver.py (100%) rename {ocrd => src}/ocrd/resource_list.yml (100%) rename {ocrd => src}/ocrd/resource_manager.py (100%) rename {ocrd => src}/ocrd/task_sequence.py (100%) rename {ocrd => src}/ocrd/workspace.py (100%) rename {ocrd => src}/ocrd/workspace_backup.py (100%) rename {ocrd => src}/ocrd/workspace_bagger.py (100%) rename {ocrd_modelfactory => src}/ocrd_modelfactory/__init__.py (100%) rename {ocrd_models => src}/ocrd_models/__init__.py (100%) rename {ocrd_models => src}/ocrd_models/constants.py (100%) rename {ocrd_models => src}/ocrd_models/mets-empty.xml (100%) rename {ocrd_models => src}/ocrd_models/ocrd_agent.py (100%) rename {ocrd_models => src}/ocrd_models/ocrd_exif.py (100%) rename {ocrd_models => src}/ocrd_models/ocrd_file.py (100%) rename {ocrd_models => src}/ocrd_models/ocrd_mets.py (100%) rename {ocrd_models => src}/ocrd_models/ocrd_page.py (100%) rename {ocrd_models => src}/ocrd_models/ocrd_page_generateds.py (100%) rename {ocrd_models => src}/ocrd_models/ocrd_xml_base.py (100%) rename {ocrd_models => src}/ocrd_models/report.py (100%) rename {ocrd_models => src}/ocrd_models/utils.py (100%) rename {ocrd_network => src}/ocrd_network/__init__.py (100%) rename {ocrd_network => src}/ocrd_network/cli/__init__.py (100%) rename {ocrd_network => src}/ocrd_network/cli/client.py (100%) rename {ocrd_network => src}/ocrd_network/cli/processing_server.py (100%) rename {ocrd_network => src}/ocrd_network/cli/processing_worker.py (100%) rename {ocrd_network => src}/ocrd_network/cli/processor_server.py (100%) rename {ocrd_network => src}/ocrd_network/client.py (100%) rename {ocrd_network => src}/ocrd_network/constants.py (100%) rename {ocrd_network => src}/ocrd_network/database.py (100%) rename {ocrd_network => src}/ocrd_network/deployer.py (100%) rename {ocrd_network => src}/ocrd_network/deployment_utils.py (100%) rename {ocrd_network => src}/ocrd_network/logging.py (100%) rename {ocrd_network => src}/ocrd_network/models/__init__.py (100%) rename {ocrd_network => src}/ocrd_network/models/job.py (100%) rename {ocrd_network => src}/ocrd_network/models/messages.py (100%) rename {ocrd_network => src}/ocrd_network/models/ocrd_tool.py (100%) rename {ocrd_network => src}/ocrd_network/models/workflow.py (100%) rename {ocrd_network => src}/ocrd_network/models/workspace.py (100%) rename {ocrd_network => src}/ocrd_network/param_validators.py (100%) rename {ocrd_network => src}/ocrd_network/process_helpers.py (100%) rename {ocrd_network => src}/ocrd_network/processing_server.py (100%) rename {ocrd_network => src}/ocrd_network/processing_worker.py (100%) rename {ocrd_network => src}/ocrd_network/processor_server.py (100%) rename {ocrd_network => src}/ocrd_network/rabbitmq_utils/__init__.py (100%) rename {ocrd_network => src}/ocrd_network/rabbitmq_utils/connector.py (100%) rename {ocrd_network => src}/ocrd_network/rabbitmq_utils/constants.py (100%) rename {ocrd_network => src}/ocrd_network/rabbitmq_utils/consumer.py (100%) rename {ocrd_network => src}/ocrd_network/rabbitmq_utils/ocrd_messages.py (100%) rename {ocrd_network => src}/ocrd_network/rabbitmq_utils/publisher.py (100%) rename {ocrd_network => src}/ocrd_network/runtime_data.py (100%) rename {ocrd_network => src}/ocrd_network/server_cache.py (100%) rename {ocrd_network => src}/ocrd_network/server_utils.py (100%) rename {ocrd_network => src}/ocrd_network/utils.py (100%) rename {ocrd_utils => src}/ocrd_utils/__init__.py (100%) rename {ocrd_utils => src}/ocrd_utils/config.py (100%) rename {ocrd_utils => src}/ocrd_utils/constants.py (98%) rename {ocrd_utils => src}/ocrd_utils/deprecate.py (100%) rename {ocrd_utils => src}/ocrd_utils/image.py (100%) rename {ocrd_utils => src}/ocrd_utils/introspect.py (100%) rename {ocrd_utils => src}/ocrd_utils/logging.py (100%) rename {ocrd_utils => src}/ocrd_utils/os.py (100%) rename {ocrd_utils => src}/ocrd_utils/str.py (100%) rename {ocrd_validators => src}/ocrd_validators/__init__.py (100%) rename {ocrd_validators => src}/ocrd_validators/bagit-profile.yml (100%) rename {ocrd_validators => src}/ocrd_validators/constants.py (100%) rename {ocrd_validators => src}/ocrd_validators/json_validator.py (100%) rename {ocrd_validators => src}/ocrd_validators/message_processing.schema.yml (100%) rename {ocrd_validators => src}/ocrd_validators/message_result.schema.yml (100%) rename {ocrd_validators => src}/ocrd_validators/mets.xsd (100%) rename {ocrd_validators => src}/ocrd_validators/ocrd_network_message_validator.py (100%) rename {ocrd_validators => src}/ocrd_validators/ocrd_tool.schema.yml (100%) rename {ocrd_validators => src}/ocrd_validators/ocrd_tool_validator.py (100%) rename {ocrd_validators => src}/ocrd_validators/ocrd_zip_validator.py (100%) rename {ocrd_validators => src}/ocrd_validators/page.xsd (100%) rename {ocrd_validators => src}/ocrd_validators/page_validator.py (100%) rename {ocrd_validators => src}/ocrd_validators/parameter_validator.py (100%) rename {ocrd_validators => src}/ocrd_validators/processing_server_config.schema.yml (100%) rename {ocrd_validators => src}/ocrd_validators/processing_server_config_validator.py (100%) rename {ocrd_validators => src}/ocrd_validators/resource_list_validator.py (100%) rename {ocrd_validators => src}/ocrd_validators/workspace_validator.py (100%) rename {ocrd_validators => src}/ocrd_validators/xlink.xsd (100%) rename {ocrd_validators => src}/ocrd_validators/xsd_mets_validator.py (100%) rename {ocrd_validators => src}/ocrd_validators/xsd_page_validator.py (100%) rename {ocrd_validators => src}/ocrd_validators/xsd_validator.py (100%) diff --git a/.gitignore b/.gitignore index 21aa725bf8..aff9382067 100644 --- a/.gitignore +++ b/.gitignore @@ -109,7 +109,6 @@ env2/ ocrd.egg-info .pytest_cache -/src /profile .~lock* .pynative @@ -125,5 +124,5 @@ tests/assets/test.ocrd.zip /foo sanders* ws1 -*.doctree +*.doctrees .vscode diff --git a/ocrd/ocrd/.doctrees/environment.pickle b/ocrd/ocrd/.doctrees/environment.pickle deleted file mode 100644 index d941ba88ab7da437f1be87214ee773cc480da70f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 779431 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

-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

X#j|d^-6 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

%@FK!VK(Z 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 diff --git a/ocrd_modelfactory/ocrd_modelfactory b/ocrd_modelfactory/ocrd_modelfactory new file mode 120000 index 0000000000..f2c294cc70 --- /dev/null +++ b/ocrd_modelfactory/ocrd_modelfactory @@ -0,0 +1 @@ +../src/ocrd_modelfactory \ No newline at end of file diff --git a/ocrd_modelfactory/ocrd_modelfactory/.doctrees/environment.pickle b/ocrd_modelfactory/ocrd_modelfactory/.doctrees/environment.pickle deleted file mode 100644 index b7405edc62c199d1626ad796c8f63189a0005caf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 779457 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 z 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

  • &(tc=^^eN}v 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

    wLjQpFl(~H}gco*7s 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 diff --git a/ocrd_network/ocrd_network b/ocrd_network/ocrd_network new file mode 120000 index 0000000000..92db94bfe4 --- /dev/null +++ b/ocrd_network/ocrd_network @@ -0,0 +1 @@ +../src/ocrd_network \ No newline at end of file diff --git a/ocrd_utils/ocrd_utils b/ocrd_utils/ocrd_utils new file mode 120000 index 0000000000..4c8b474a93 --- /dev/null +++ b/ocrd_utils/ocrd_utils @@ -0,0 +1 @@ +../src/ocrd_utils \ No newline at end of file diff --git a/ocrd_validators/ocrd_validators b/ocrd_validators/ocrd_validators new file mode 120000 index 0000000000..59d7e21559 --- /dev/null +++ b/ocrd_validators/ocrd_validators @@ -0,0 +1 @@ +../src/ocrd_validators \ No newline at end of file diff --git a/ocrd_validators/ocrd_validators/.doctrees/environment.pickle b/ocrd_validators/ocrd_validators/.doctrees/environment.pickle deleted file mode 100644 index cb19bf22c11572ce9d750d4ec8bdb8fe99f27550..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 779453 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

    |a5dVlH{@e;rQ;hZTrpay-(aRPd5frt)dfRNq* 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 EVwFOMkT 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 0 From c063a67f138424d3625547116b0f2027370bd805 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Fri, 19 Jan 2024 13:13:04 +0100 Subject: [PATCH 046/142] fix processing server tests --- tests/network/docker-compose.yml | 42 +++++++++++++++++++++++-- tests/network/test_processing_server.py | 9 +++++- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/tests/network/docker-compose.yml b/tests/network/docker-compose.yml index a17f306e38..10c95594ba 100644 --- a/tests/network/docker-compose.yml +++ b/tests/network/docker-compose.yml @@ -50,6 +50,7 @@ services: dockerfile: Dockerfile args: BASE_IMAGE: 'ubuntu:20.04' + target: ocrd_core_test hostname: processing-server-host container_name: ocrd_network_processing_server depends_on: @@ -66,11 +67,46 @@ services: DB_URL: mongodb://network_test:network_test@mongodb-docker-host.ocrd_network_test:27017 RABBITMQ_URL: amqp://network_test:network_test@rabbitmq-docker-host.ocrd_network_test:5672 volumes: - - "./ps_config.yml:/ps_config.yml" - - "/home/mm/.ssh/cloud.key:/ssh_key" - "/tmp/ocrd_network_logs:/tmp/ocrd_network_logs" - "/tmp/ocrd_network_sockets:/tmp/ocrd_network_sockets" - command: ocrd network processing-server /ps_config.yml --address 0.0.0.0:8000 + command: | + /bin/bash -c "echo -e \" + internal_callback_url: http://processing-server-host.ocrd_network_test:8000 + process_queue: + address: rabbitmq-docker-host.ocrd_network_test + port: 5672 + skip_deployment: true + credentials: + username: network_test + password: network_test + database: + address: mongodb-docker-host.ocrd_network_test + port: 27017 + skip_deployment: true + credentials: + username: network_test + password: network_test + hosts: []\" > ./ps_config.yaml && \ + ocrd network processing-server -a 0.0.0.0:8000 ./ps_config.yaml" + + ocrd_dummy_processing_worker: + build: + context: ../../ + dockerfile: Dockerfile + args: + BASE_IMAGE: 'ubuntu:20.04' + target: ocrd_core_test + depends_on: + ocrd_network_mongo_db: + condition: service_healthy + ocrd_network_rabbit_mq: + condition: service_healthy + networks: + - ocrd_network_test + volumes: + - "/tmp/ocrd_network_logs:/tmp/ocrd_network_logs" + - "/tmp/ocrd_network_sockets:/tmp/ocrd_network_sockets" + command: ocrd-dummy worker --database mongodb://network_test:network_test@mongodb-docker-host.ocrd_network_test:27017 --queue amqp://network_test:network_test@rabbitmq-docker-host.ocrd_network_test:5672 ocrd_network_core_test: build: diff --git a/tests/network/test_processing_server.py b/tests/network/test_processing_server.py index f758012aa1..12057dadb9 100644 --- a/tests/network/test_processing_server.py +++ b/tests/network/test_processing_server.py @@ -9,10 +9,17 @@ def test_processing_server_connectivity(): response = get(test_url) assert response.status_code == 200, \ f'Processing server is not reachable on: {test_url}, {response.status_code}' + message = response.json()['message'] + assert message.startsWith('The home page of'), \ + f'Processing server home page message is corrupted' -def _test_processing_server_deployed_processors(): +# TODO: The processing workers are still not registered when deployed separately. +# Fix that by extending the processing server. +def test_processing_server_deployed_processors(): test_url = f'{PROCESSING_SERVER_URL}/processor' response = get(test_url) + processors = response.json() assert response.status_code == 200, \ f'Processing server is not reachable on: {test_url}, {response.status_code}' + assert processors == [], f'Mismatch in deployed processors' From f0654bf212e151d4023534f0dc67a8fc9ef3a236 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Fri, 19 Jan 2024 13:24:49 +0100 Subject: [PATCH 047/142] fix test typo --- tests/network/test_processing_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/network/test_processing_server.py b/tests/network/test_processing_server.py index 12057dadb9..d1c58b87d2 100644 --- a/tests/network/test_processing_server.py +++ b/tests/network/test_processing_server.py @@ -10,7 +10,7 @@ def test_processing_server_connectivity(): assert response.status_code == 200, \ f'Processing server is not reachable on: {test_url}, {response.status_code}' message = response.json()['message'] - assert message.startsWith('The home page of'), \ + assert message.startswith('The home page of'), \ f'Processing server home page message is corrupted' From 13407bb6f3e9de11e1b6af49b890787a568dc78a Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Fri, 19 Jan 2024 13:39:33 +0100 Subject: [PATCH 048/142] adapt makefile --- Makefile | 9 +++---- ocrd/MANIFEST.in | 3 --- ocrd/pyproject.toml | 42 ------------------------------ ocrd/requirements.txt | 23 ---------------- ocrd/setup.py | 16 ------------ ocrd_modelfactory/MANIFEST.in | 2 -- ocrd_modelfactory/pyproject.toml | 40 ---------------------------- ocrd_modelfactory/requirements.txt | 3 --- ocrd_modelfactory/setup.py | 16 ------------ ocrd_models/MANIFEST.in | 2 -- ocrd_models/pyproject.toml | 40 ---------------------------- ocrd_models/requirements.txt | 2 -- ocrd_models/setup.py | 16 ------------ ocrd_network/MANIFEST.in | 2 -- ocrd_network/pyproject.toml | 37 -------------------------- ocrd_network/requirements.txt | 11 -------- ocrd_network/setup.py | 16 ------------ ocrd_utils/MANIFEST.in | 3 --- ocrd_utils/pyproject.toml | 40 ---------------------------- ocrd_utils/requirements.txt | 7 ----- ocrd_utils/setup.py | 4 --- ocrd_validators/MANIFEST.in | 2 -- ocrd_validators/pyproject.toml | 40 ---------------------------- ocrd_validators/requirements.txt | 9 ------- ocrd_validators/setup.py | 16 ------------ 25 files changed, 4 insertions(+), 397 deletions(-) delete mode 100644 ocrd/MANIFEST.in delete mode 100644 ocrd/pyproject.toml delete mode 100644 ocrd/requirements.txt delete mode 100644 ocrd/setup.py delete mode 100644 ocrd_modelfactory/MANIFEST.in delete mode 100644 ocrd_modelfactory/pyproject.toml delete mode 100644 ocrd_modelfactory/requirements.txt delete mode 100644 ocrd_modelfactory/setup.py delete mode 100644 ocrd_models/MANIFEST.in delete mode 100644 ocrd_models/pyproject.toml delete mode 100644 ocrd_models/requirements.txt delete mode 100644 ocrd_models/setup.py delete mode 100644 ocrd_network/MANIFEST.in delete mode 100644 ocrd_network/pyproject.toml delete mode 100644 ocrd_network/requirements.txt delete mode 100644 ocrd_network/setup.py delete mode 100644 ocrd_utils/MANIFEST.in delete mode 100644 ocrd_utils/pyproject.toml delete mode 100644 ocrd_utils/requirements.txt delete mode 100644 ocrd_utils/setup.py delete mode 100644 ocrd_validators/MANIFEST.in delete mode 100644 ocrd_validators/pyproject.toml delete mode 100644 ocrd_validators/requirements.txt delete mode 100644 ocrd_validators/setup.py diff --git a/Makefile b/Makefile index f2adfdca59..86601682cb 100644 --- a/Makefile +++ b/Makefile @@ -119,14 +119,13 @@ deps-test: .PHONY: build install install-dev uninstall build: - $(PIP) install build setuptools_scm - $(foreach MODULE,$(BUILD_ORDER),$(PYTHON) -m build ./$(MODULE) &&) echo done + $(PIP) install build + $(PYTHON) -m build . # or use -n ? # (Re)install the tool install: #build -# $(PIP_INSTALL) $(BUILD_ORDER:%=./%/dist/ocrd*`$(PYTHON) -m setuptools_scm 2>/dev/null`*.whl) - $(foreach MODULE,$(BUILD_ORDER),$(PIP_INSTALL) ./$(MODULE) $(PIP_INSTALL_CONFIG_OPTION) &&) echo done + $(PIP_INSTALL) . $(PIP_INSTALL_CONFIG_OPTION) @# workaround for shapely#1598 $(PIP) config set global.no-binary shapely @@ -138,7 +137,7 @@ install-dev: uninstall # Uninstall the tool uninstall: - $(PIP) uninstall -y $(call reverse,$(BUILD_ORDER)) + $(PIP) uninstall . # Regenerate python code from PAGE XSD generate-page: GDS_PAGE = ocrd_models/ocrd_models/ocrd_page_generateds.py diff --git a/ocrd/MANIFEST.in b/ocrd/MANIFEST.in deleted file mode 100644 index af6a7f6bad..0000000000 --- a/ocrd/MANIFEST.in +++ /dev/null @@ -1,3 +0,0 @@ -recursive-include ocrd *.json *.yml *.yaml *.bash *.xml -include requirements.txt -include README.md diff --git a/ocrd/pyproject.toml b/ocrd/pyproject.toml deleted file mode 100644 index fb8dd79ecc..0000000000 --- a/ocrd/pyproject.toml +++ /dev/null @@ -1,42 +0,0 @@ -[build-system] -requires = [ - "setuptools>=61", - "setuptools_scm[toml]", - "wheel", -] # PEP 508 specifications. -build-backend = "setuptools.build_meta" - -[project] -name = "ocrd" -authors = [{name = "Konstantin Baierer", email = "unixprog@gmail.com"}] -license = {text = "Apache License 2.0"} -description = "OCR-D framework" -requires-python = ">=3.7" -dynamic = ["version", "dependencies"] - -[project.readme] -file = "README.md" -content-type = "text/markdown" - -[tool.setuptools.dynamic.dependencies] -file = ["requirements.txt"] - -[project.urls] -Homepage = "https://ocr-d.de" -Documentation = "https://ocr-d.de/core" -Repository = "https://github.com/OCR-D/core" -Issues = "https://github.com/OCR-D/core/issues" - -[project.scripts] -ocrd = "ocrd.cli:cli" -ocrd-dummy = "ocrd.processor.builtin.dummy_processor:cli" - -[tool.setuptools] -include-package-data = true - -[tool.setuptools.packages.find] -exclude = ["tests"] # docs -namespaces = false - -[tool.setuptools_scm] -root = ".." diff --git a/ocrd/requirements.txt b/ocrd/requirements.txt deleted file mode 100644 index 9c057532a2..0000000000 --- a/ocrd/requirements.txt +++ /dev/null @@ -1,23 +0,0 @@ -bagit >= 1.7.0 -bagit_profile >= 1.3.0 -click >=7 -requests < 2.30 -lxml -opencv-python-headless -Flask -jsonschema -pyyaml -Deprecated == 1.2.0 -memory-profiler >= 0.58.0 -sparklines >= 0.4.2 -python-magic -uvicorn -fastapi -python-multipart -requests_unixsocket -gdown -ocrd_utils -ocrd_models -ocrd_modelfactory -ocrd_validators -ocrd_network diff --git a/ocrd/setup.py b/ocrd/setup.py deleted file mode 100644 index 929849c9f5..0000000000 --- a/ocrd/setup.py +++ /dev/null @@ -1,16 +0,0 @@ -# -*- coding: utf-8 -*- -from setuptools import setup, Command -from setuptools.command.build import build as orig_build - -class build(orig_build): - def finalize_options(self): - vers = ' == ' + self.distribution.metadata.version - self.distribution.install_requires = [ - req + vers if req.startswith('ocrd') and '==' not in req else req - for req in self.distribution.install_requires - ] - orig_build.finalize_options(self) - -setup( - cmdclass={"build": build} -) diff --git a/ocrd_modelfactory/MANIFEST.in b/ocrd_modelfactory/MANIFEST.in deleted file mode 100644 index 4fae91089a..0000000000 --- a/ocrd_modelfactory/MANIFEST.in +++ /dev/null @@ -1,2 +0,0 @@ -include requirements.txt -include README.md diff --git a/ocrd_modelfactory/pyproject.toml b/ocrd_modelfactory/pyproject.toml deleted file mode 100644 index 34798daf29..0000000000 --- a/ocrd_modelfactory/pyproject.toml +++ /dev/null @@ -1,40 +0,0 @@ -[build-system] -requires = [ - "setuptools>=61", - "setuptools_scm[toml]", - "wheel", -] # PEP 508 specifications. -build-backend = "setuptools.build_meta" - -[project] -name = "ocrd_modelfactory" -authors = [{name = "Konstantin Baierer", email = "unixprog@gmail.com"}] -license = {text = "Apache License 2.0"} -description = "OCR-D framework - wrappers to create ocrd_model instances" -requires-python = ">=3.7" -dynamic = ["version", "dependencies"] - -[project.readme] -file = "README.md" -content-type = "text/markdown" - -[tool.setuptools.dynamic.dependencies] -file = ["requirements.txt"] - -[project.urls] -Homepage = "https://ocr-d.de" -Documentation = "https://ocr-d.de/core" -Repository = "https://github.com/OCR-D/core" -Issues = "https://github.com/OCR-D/core/issues" - -[tool.setuptools] -include-package-data = true - -[tool.setuptools.package-data] -"*" = ["*.json"] # *.yml; *.xml - -[tool.setuptools.packages.find] -namespaces = false - -[tool.setuptools_scm] -root = ".." diff --git a/ocrd_modelfactory/requirements.txt b/ocrd_modelfactory/requirements.txt deleted file mode 100644 index c577acff93..0000000000 --- a/ocrd_modelfactory/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -lxml -ocrd_utils -ocrd_models diff --git a/ocrd_modelfactory/setup.py b/ocrd_modelfactory/setup.py deleted file mode 100644 index 929849c9f5..0000000000 --- a/ocrd_modelfactory/setup.py +++ /dev/null @@ -1,16 +0,0 @@ -# -*- coding: utf-8 -*- -from setuptools import setup, Command -from setuptools.command.build import build as orig_build - -class build(orig_build): - def finalize_options(self): - vers = ' == ' + self.distribution.metadata.version - self.distribution.install_requires = [ - req + vers if req.startswith('ocrd') and '==' not in req else req - for req in self.distribution.install_requires - ] - orig_build.finalize_options(self) - -setup( - cmdclass={"build": build} -) diff --git a/ocrd_models/MANIFEST.in b/ocrd_models/MANIFEST.in deleted file mode 100644 index 4fae91089a..0000000000 --- a/ocrd_models/MANIFEST.in +++ /dev/null @@ -1,2 +0,0 @@ -include requirements.txt -include README.md diff --git a/ocrd_models/pyproject.toml b/ocrd_models/pyproject.toml deleted file mode 100644 index 5f8e3ebb1a..0000000000 --- a/ocrd_models/pyproject.toml +++ /dev/null @@ -1,40 +0,0 @@ -[build-system] -requires = [ - "setuptools>=61", - "setuptools_scm[toml]", - "wheel", -] # PEP 508 specifications. -build-backend = "setuptools.build_meta" - -[project] -name = "ocrd_models" -authors = [{name = "Konstantin Baierer", email = "unixprog@gmail.com"}] -license = {text = "Apache License 2.0"} -description = "OCR-D framework - file format APIs and schemas" -requires-python = ">=3.7" -dynamic = ["version", "dependencies"] - -[project.readme] -file = "README.md" -content-type = "text/markdown" - -[tool.setuptools.dynamic.dependencies] -file = ["requirements.txt"] - -[project.urls] -Homepage = "https://ocr-d.de" -Documentation = "https://ocr-d.de/core" -Repository = "https://github.com/OCR-D/core" -Issues = "https://github.com/OCR-D/core/issues" - -[tool.setuptools] -include-package-data = true - -[tool.setuptools.package-data] -"*" = ["*.json"] # *.yml; *.xml - -[tool.setuptools.packages.find] -namespaces = false - -[tool.setuptools_scm] -root = ".." diff --git a/ocrd_models/requirements.txt b/ocrd_models/requirements.txt deleted file mode 100644 index 299120a695..0000000000 --- a/ocrd_models/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -lxml -ocrd_utils diff --git a/ocrd_models/setup.py b/ocrd_models/setup.py deleted file mode 100644 index 929849c9f5..0000000000 --- a/ocrd_models/setup.py +++ /dev/null @@ -1,16 +0,0 @@ -# -*- coding: utf-8 -*- -from setuptools import setup, Command -from setuptools.command.build import build as orig_build - -class build(orig_build): - def finalize_options(self): - vers = ' == ' + self.distribution.metadata.version - self.distribution.install_requires = [ - req + vers if req.startswith('ocrd') and '==' not in req else req - for req in self.distribution.install_requires - ] - orig_build.finalize_options(self) - -setup( - cmdclass={"build": build} -) diff --git a/ocrd_network/MANIFEST.in b/ocrd_network/MANIFEST.in deleted file mode 100644 index 4fae91089a..0000000000 --- a/ocrd_network/MANIFEST.in +++ /dev/null @@ -1,2 +0,0 @@ -include requirements.txt -include README.md diff --git a/ocrd_network/pyproject.toml b/ocrd_network/pyproject.toml deleted file mode 100644 index 778038ab8d..0000000000 --- a/ocrd_network/pyproject.toml +++ /dev/null @@ -1,37 +0,0 @@ -[build-system] -requires = [ - "setuptools>=61", - "setuptools_scm[toml]", - "wheel", -] # PEP 508 specifications. -build-backend = "setuptools.build_meta" - -[project] -name = "ocrd_network" -authors = [{name = "Mehmed Mustafa", email = "unixprog@gmail.com"}, {name = "Jonas Schrewe"}, {name = "Triet Doan"}] -license = {text = "Apache License 2.0"} -description = "OCR-D framework - network" -requires-python = ">=3.7" -dynamic = ["version", "dependencies"] - -[project.readme] -file = "README.md" -content-type = "text/markdown" - -[tool.setuptools.dynamic.dependencies] -file = ["requirements.txt"] - -[project.urls] -Homepage = "https://ocr-d.de" -Documentation = "https://ocr-d.de/core" -Repository = "https://github.com/OCR-D/core" -Issues = "https://github.com/OCR-D/core/issues" - -[tool.setuptools] -include-package-data = false - -[tool.setuptools.packages.find] -namespaces = false - -[tool.setuptools_scm] -root = ".." diff --git a/ocrd_network/requirements.txt b/ocrd_network/requirements.txt deleted file mode 100644 index 60b66e5383..0000000000 --- a/ocrd_network/requirements.txt +++ /dev/null @@ -1,11 +0,0 @@ -uvicorn>=0.17.6 -fastapi>=0.78.0 -pydantic==1.* -docker -paramiko -pika>=1.2.0 -beanie~=1.7 -httpx>=0.22.0 -ocrd_validators -ocrd_utils -# ocrd # commented to avoid circular dependency diff --git a/ocrd_network/setup.py b/ocrd_network/setup.py deleted file mode 100644 index 929849c9f5..0000000000 --- a/ocrd_network/setup.py +++ /dev/null @@ -1,16 +0,0 @@ -# -*- coding: utf-8 -*- -from setuptools import setup, Command -from setuptools.command.build import build as orig_build - -class build(orig_build): - def finalize_options(self): - vers = ' == ' + self.distribution.metadata.version - self.distribution.install_requires = [ - req + vers if req.startswith('ocrd') and '==' not in req else req - for req in self.distribution.install_requires - ] - orig_build.finalize_options(self) - -setup( - cmdclass={"build": build} -) diff --git a/ocrd_utils/MANIFEST.in b/ocrd_utils/MANIFEST.in deleted file mode 100644 index b6ae33acad..0000000000 --- a/ocrd_utils/MANIFEST.in +++ /dev/null @@ -1,3 +0,0 @@ -include README.md -include ocrd_logging.conf -include requirements.txt diff --git a/ocrd_utils/pyproject.toml b/ocrd_utils/pyproject.toml deleted file mode 100644 index dec419a9bf..0000000000 --- a/ocrd_utils/pyproject.toml +++ /dev/null @@ -1,40 +0,0 @@ -[build-system] -requires = [ - "setuptools>=61", - "setuptools_scm[toml]", - "wheel", -] # PEP 508 specifications. -build-backend = "setuptools.build_meta" - -[project] -name = "ocrd_utils" -authors = [{name = "Konstantin Baierer", email = "unixprog@gmail.com"}] -license = {text = "Apache License 2.0"} -description = "OCR-D framework - shared code, helpers, constants" -requires-python = ">=3.7" -dynamic = ["version", "dependencies"] - -[project.readme] -file = "README.md" -content-type = "text/markdown" - -[tool.setuptools.dynamic.dependencies] -file = ["requirements.txt"] - -[project.urls] -Homepage = "https://ocr-d.de" -Documentation = "https://ocr-d.de/core" -Repository = "https://github.com/OCR-D/core" -Issues = "https://github.com/OCR-D/core/issues" - -[tool.setuptools] -include-package-data = true - -[tool.setuptools.package-data] -"*" = ["*.json"] # *.yml; *.xml - -[tool.setuptools.packages.find] -namespaces = false - -[tool.setuptools_scm] -root = ".." diff --git a/ocrd_utils/requirements.txt b/ocrd_utils/requirements.txt deleted file mode 100644 index 328783adda..0000000000 --- a/ocrd_utils/requirements.txt +++ /dev/null @@ -1,7 +0,0 @@ -Pillow >= 7.2.0 -# XXX explicitly do not restrict the numpy version because different -# tensorflow versions might require different versions -numpy -atomicwrites >= 1.3.0 -frozendict>=2.3.4 -filetype diff --git a/ocrd_utils/setup.py b/ocrd_utils/setup.py deleted file mode 100644 index 53d3ffceba..0000000000 --- a/ocrd_utils/setup.py +++ /dev/null @@ -1,4 +0,0 @@ -# -*- coding: utf-8 -*- -from setuptools import setup - -setup() diff --git a/ocrd_validators/MANIFEST.in b/ocrd_validators/MANIFEST.in deleted file mode 100644 index 4fae91089a..0000000000 --- a/ocrd_validators/MANIFEST.in +++ /dev/null @@ -1,2 +0,0 @@ -include requirements.txt -include README.md diff --git a/ocrd_validators/pyproject.toml b/ocrd_validators/pyproject.toml deleted file mode 100644 index e039baeff4..0000000000 --- a/ocrd_validators/pyproject.toml +++ /dev/null @@ -1,40 +0,0 @@ -[build-system] -requires = [ - "setuptools>=61", - "setuptools_scm[toml]", - "wheel", -] # PEP 508 specifications. -build-backend = "setuptools.build_meta" - -[project] -name = "ocrd_validators" -authors = [{name = "Konstantin Baierer", email = "unixprog@gmail.com"}] -license = {text = "Apache License 2.0"} -description = "OCR-D framework - data validators" -requires-python = ">=3.7" -dynamic = ["version", "dependencies"] - -[project.readme] -file = "README.md" -content-type = "text/markdown" - -[tool.setuptools.dynamic.dependencies] -file = ["requirements.txt"] - -[project.urls] -Homepage = "https://ocr-d.de" -Documentation = "https://ocr-d.de/core" -Repository = "https://github.com/OCR-D/core" -Issues = "https://github.com/OCR-D/core/issues" - -[tool.setuptools] -include-package-data = true - -[tool.setuptools.package-data] -"*" = ["*.yml"] # *.xsd - -[tool.setuptools.packages.find] -namespaces = false - -[tool.setuptools_scm] -root = ".." diff --git a/ocrd_validators/requirements.txt b/ocrd_validators/requirements.txt deleted file mode 100644 index 2d5bff4ad5..0000000000 --- a/ocrd_validators/requirements.txt +++ /dev/null @@ -1,9 +0,0 @@ -bagit >= 1.7.0 -bagit_profile >= 1.3.0 -click >=7 -jsonschema -pyyaml -shapely -ocrd_utils -ocrd_models -ocrd_modelfactory diff --git a/ocrd_validators/setup.py b/ocrd_validators/setup.py deleted file mode 100644 index 929849c9f5..0000000000 --- a/ocrd_validators/setup.py +++ /dev/null @@ -1,16 +0,0 @@ -# -*- coding: utf-8 -*- -from setuptools import setup, Command -from setuptools.command.build import build as orig_build - -class build(orig_build): - def finalize_options(self): - vers = ' == ' + self.distribution.metadata.version - self.distribution.install_requires = [ - req + vers if req.startswith('ocrd') and '==' not in req else req - for req in self.distribution.install_requires - ] - orig_build.finalize_options(self) - -setup( - cmdclass={"build": build} -) From d19c7e05a15f025b5d4b387bc4441531047429f7 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Fri, 19 Jan 2024 13:56:53 +0100 Subject: [PATCH 049/142] forgot pyproject and requirements --- pyproject.toml | 42 ++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 pyproject.toml create mode 100644 requirements.txt diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000..689e73ac8f --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,42 @@ +[build-system] +requires = [ + "setuptools>=61", + "wheel", +] # PEP 508 specifications. +build-backend = "setuptools.build_meta" + +[project] +name = "ocrd" +version = '2.61.0' +authors = [{name = "Konstantin Baierer", email = "unixprog@gmail.com"}] +license = {text = "Apache License 2.0"} +description = "OCR-D framework" +requires-python = ">=3.7" +dynamic = ["dependencies"] + +[project.readme] +file = "README.md" +content-type = "text/markdown" + +[tool.setuptools.dynamic.dependencies] +file = ["requirements.txt"] + +[project.urls] +Homepage = "https://ocr-d.de" +Documentation = "https://ocr-d.de/core" +Repository = "https://github.com/OCR-D/core" +Issues = "https://github.com/OCR-D/core/issues" + +[project.scripts] +ocrd = "ocrd.cli:cli" +ocrd-dummy = "ocrd.processor.builtin.dummy_processor:cli" + +[tool.setuptools] +include-package-data = true + +[tool.setuptools.package-data] +"*" = ["*.json", '*.yml', '*.xml', '*.conf', '*.bash', '*.xsd'] + +[tool.setuptools.packages.find] +where = ["src"] +namespaces = false diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000..9a9b217218 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,34 @@ +atomicwrites >= 1.3.0 +bagit >= 1.7.0 +bagit_profile >= 1.3.0 +beanie~=1.7 +click >=7 +Deprecated == 1.2.0 +docker +fastapi +fastapi>=0.78.0 +filetype +Flask +frozendict>=2.3.4 +gdown +httpx>=0.22.0 +jsonschema +lxml +memory-profiler >= 0.58.0 +numpy +opencv-python-headless +paramiko +pika>=1.2.0 +Pillow >= 7.2.0 +pydantic==1.* +python-magic +python-multipart +pyyaml +requests < 2.30 +requests_unixsocket +shapely +sparklines >= 0.4.2 +# tensorflow versions might require different versions +uvicorn +uvicorn>=0.17.6 +# XXX explicitly do not restrict the numpy version because different From 14a163b494075cdca654081e3291a2ff8c30ff97 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Fri, 19 Jan 2024 17:19:50 +0100 Subject: [PATCH 050/142] add processing request test - failing --- .../ocrd_network/processing_server.py | 12 ++ tests/network/docker-compose.yml | 42 ++--- tests/network/dummy-workflow.txt | 4 +- tests/network/ocrd_logging.conf | 150 ++++++++++++++++++ tests/network/ps_config.yml | 28 ---- tests/network/test_processing_server.py | 19 ++- 6 files changed, 206 insertions(+), 49 deletions(-) create mode 100644 tests/network/ocrd_logging.conf delete mode 100644 tests/network/ps_config.yml diff --git a/ocrd_network/ocrd_network/processing_server.py b/ocrd_network/ocrd_network/processing_server.py index 77c2dd9025..884ae8783d 100644 --- a/ocrd_network/ocrd_network/processing_server.py +++ b/ocrd_network/ocrd_network/processing_server.py @@ -339,6 +339,12 @@ def create_message_queues(self) -> None: unique_only=True ) + # TODO: Reconsider and refactor this. + # Added ocrd-dummy by default if not available for the integration tests. + # A proper Processing Worker / Processor Server registration endpoint is needed on the Processing Server side + if 'dummy' not in queue_names: + queue_names.append('dummy') + for queue_name in queue_names: # The existence/validity of the worker.name is not tested. # Even if an ocr-d processor does not exist, the queue is created @@ -395,6 +401,12 @@ def processing_agent_exists(self, processor_name: str, agent_type: str) -> bool: if agent_type not in [NETWORK_AGENT_SERVER, NETWORK_AGENT_WORKER]: return False if agent_type == NETWORK_AGENT_WORKER: + # TODO: Reconsider and refactor this. + # Added ocrd-dummy by default if not available for the integration tests. + # A proper Processing Worker / Processor Server registration endpoint + # is needed on the Processing Server side + if processor_name == 'dummy': + return True if not self.check_if_queue_exists(processor_name): return False if agent_type == NETWORK_AGENT_SERVER: diff --git a/tests/network/docker-compose.yml b/tests/network/docker-compose.yml index 10c95594ba..02a984bce1 100644 --- a/tests/network/docker-compose.yml +++ b/tests/network/docker-compose.yml @@ -45,6 +45,7 @@ services: retries: 30 ocrd_network_processing_server: + image: "ocrd_core_test" build: context: ../../ dockerfile: Dockerfile @@ -66,9 +67,16 @@ services: DB_NAME: ocrd_network_test DB_URL: mongodb://network_test:network_test@mongodb-docker-host.ocrd_network_test:27017 RABBITMQ_URL: amqp://network_test:network_test@rabbitmq-docker-host.ocrd_network_test:5672 + healthcheck: + test: curl -f http://processing-server-host.ocrd_network_test:8000/ + interval: 1s + timeout: 3s + retries: 30 volumes: - "/tmp/ocrd_network_logs:/tmp/ocrd_network_logs" - "/tmp/ocrd_network_sockets:/tmp/ocrd_network_sockets" + - "../assets:/tmp/assets" + - "./ocrd_logging.conf:/etc/ocrd_logging.conf" command: | /bin/bash -c "echo -e \" internal_callback_url: http://processing-server-host.ocrd_network_test:8000 @@ -90,39 +98,35 @@ services: ocrd network processing-server -a 0.0.0.0:8000 ./ps_config.yaml" ocrd_dummy_processing_worker: - build: - context: ../../ - dockerfile: Dockerfile - args: - BASE_IMAGE: 'ubuntu:20.04' - target: ocrd_core_test + image: "ocrd_core_test" depends_on: - ocrd_network_mongo_db: - condition: service_healthy - ocrd_network_rabbit_mq: + ocrd_network_processing_server: condition: service_healthy networks: - ocrd_network_test volumes: - "/tmp/ocrd_network_logs:/tmp/ocrd_network_logs" - "/tmp/ocrd_network_sockets:/tmp/ocrd_network_sockets" - command: ocrd-dummy worker --database mongodb://network_test:network_test@mongodb-docker-host.ocrd_network_test:27017 --queue amqp://network_test:network_test@rabbitmq-docker-host.ocrd_network_test:5672 + - "../assets:/tmp/assets" + - "./ocrd_logging.conf:/etc/ocrd_logging.conf" + command: > + ocrd-dummy worker + --database mongodb://network_test:network_test@mongodb-docker-host.ocrd_network_test:27017 + --queue amqp://network_test:network_test@rabbitmq-docker-host.ocrd_network_test:5672 ocrd_network_core_test: - build: - context: ../../ - dockerfile: Dockerfile - args: - BASE_IMAGE: 'ubuntu:20.04' - target: ocrd_core_test + image: "ocrd_core_test" container_name: core_test depends_on: - ocrd_network_mongo_db: - condition: service_healthy - ocrd_network_rabbit_mq: + ocrd_network_processing_server: condition: service_healthy networks: - ocrd_network_test + volumes: + - "/tmp/ocrd_network_logs:/tmp/ocrd_network_logs" + - "/tmp/ocrd_network_sockets:/tmp/ocrd_network_sockets" + - "../assets:/tmp/assets" + - "./ocrd_logging.conf:/etc/ocrd_logging.conf" environment: DB_NAME: ocrd_network_test DB_URL: mongodb://network_test:network_test@mongodb-docker-host.ocrd_network_test:27017 diff --git a/tests/network/dummy-workflow.txt b/tests/network/dummy-workflow.txt index 223c217678..2de01dc67b 100644 --- a/tests/network/dummy-workflow.txt +++ b/tests/network/dummy-workflow.txt @@ -1 +1,3 @@ -dummy -I DEFAULT -O OCR-D-DUMMY \ No newline at end of file +dummy -I DEFAULT -O OCR-D-DUMMY1 +dummy -I OCR-D-DUMMY1 -O OCR-D-DUMMY2 +dummy -I OCR-D-DUMMY2 -O OCR-D-DUMMY3 diff --git a/tests/network/ocrd_logging.conf b/tests/network/ocrd_logging.conf new file mode 100644 index 0000000000..5ee5e015d8 --- /dev/null +++ b/tests/network/ocrd_logging.conf @@ -0,0 +1,150 @@ +# This is a template configuration file which allows customizing +# format and destination of log messages with OCR-D. +# It is meant as an example, and should be customized. +# To get into effect, you must put a copy (under the same name) +# into your CWD, HOME or /etc. These directories are searched +# in said order, and the first find wins. When no config file +# is found, the default logging configuration applies (cf. ocrd.logging.py). +# +# mandatory loggers section +# configure loggers with corresponding keys "root", "" +# each logger requires a corresponding configuration section below +# +[loggers] +keys=root,ocrd,ocrd_network,ocrd_tensorflow,ocrd_shapely_geos,ocrd_PIL,uvicorn,uvicorn_access,uvicorn_error,multipart + +# +# mandatory handlers section +# handle output for each logging "channel" +# i.e. console, file, smtp, syslog, http, ... +# each handler requires a corresponding configuration section below +# +[handlers] +keys=consoleHandler,fileHandler,processingServerHandler + +# +# optional custom formatters section +# format message fields, to be used differently by logging handlers +# each formatter requires a corresponding formatter section below +# +[formatters] +keys=defaultFormatter,detailedFormatter + +# +# default logger "root" using consoleHandler +# +[logger_root] +level=DEBUG +handlers=consoleHandler,fileHandler + + +# +# additional logger configurations can be added +# as separate configuration sections like below +# +# example logger "ocrd_workspace" uses fileHandler and overrides +# default log level "INFO" with custom level "DEBUG" +# "qualname" must match the logger label used in the corresponding +# ocrd module +# see in the module-of-interest (moi) +# +#[logger_ocrd_workspace] +#level=DEBUG +#handlers=fileHandler +#qualname=ocrd.workspace + +# ocrd loggers +[logger_ocrd] +level=DEBUG +handlers=consoleHandler,fileHandler +qualname=ocrd +propagate=0 + +[logger_ocrd_network] +level=DEBUG +handlers=consoleHandler,processingServerHandler +qualname=ocrd_network +propagate=0 + +# +# logger tensorflow +# +[logger_ocrd_tensorflow] +level=DEBUG +handlers=consoleHandler +qualname=tensorflow + +# +# logger shapely.geos +# +[logger_ocrd_shapely_geos] +level=DEBUG +handlers=consoleHandler +qualname=shapely.geos + + +# +# logger PIL +# +[logger_ocrd_PIL] +level=DEBUG +handlers=consoleHandler +qualname=PIL + +# +# uvicorn loggers +# +[logger_uvicorn] +level=INFO +handlers=consoleHandler +qualname=uvicorn +[logger_uvicorn_access] +level=DEBUG +handlers=consoleHandler +qualname=uvicorn.access +[logger_uvicorn_error] +level=DEBUG +handlers=consoleHandler +qualname=uvicorn.error +[logger_multipart] +level=DEBUG +handlers=consoleHandler +qualname=multipart + + + +# +# handle stderr output +# +[handler_consoleHandler] +class=StreamHandler +formatter=defaultFormatter +args=(sys.stderr,) + +# +# example logfile handler +# handle output with logfile +# +[handler_fileHandler] +class=FileHandler +formatter=defaultFormatter +args=('ocrd.log','a+') + +[handler_processingServerHandler] +class=FileHandler +formatter=defaultFormatter +args=('/tmp/ocrd_processing_server.log','a+') + +# +# default log format conforming to OCR-D (https://ocr-d.de/en/spec/cli#logging) +# +[formatter_defaultFormatter] +format=%(asctime)s.%(msecs)03d %(levelname)s %(name)s - %(message)s +datefmt=%H:%M:%S + +# +# store more logging context information +# +[formatter_detailedFormatter] +format=%(asctime)s.%(msecs)03d %(levelname)-8s (%(name)s)[%(filename)s:%(lineno)d] - %(message)s +datefmt=%H:%M:%S diff --git a/tests/network/ps_config.yml b/tests/network/ps_config.yml deleted file mode 100644 index 63c21d0175..0000000000 --- a/tests/network/ps_config.yml +++ /dev/null @@ -1,28 +0,0 @@ -process_queue: - address: rabbitmq-docker-host.ocrd_network_test - port: 5672 - credentials: - username: network_test - password: network_test - ssh: - username: root - path_to_privkey: /ssh_key - skip_deployment: true -database: - address: mongodb-docker-host.ocrd_network_test - port: 27017 - credentials: - username: network_test - password: network_test - ssh: - username: root - path_to_privkey: /ssh_key - skip_deployment: true -hosts: - - address: processing-server-host.ocrd_network_test - username: root - path_to_privkey: /ssh_key - workers: - - name: ocrd-dummy - number_of_instance: 1 - deploy_type: native diff --git a/tests/network/test_processing_server.py b/tests/network/test_processing_server.py index d1c58b87d2..33cbbe6a69 100644 --- a/tests/network/test_processing_server.py +++ b/tests/network/test_processing_server.py @@ -1,5 +1,6 @@ -from requests import get +from requests import get, post from ocrd_utils.config import config +from ocrd_network import NETWORK_AGENT_WORKER PROCESSING_SERVER_URL = config.PROCESSING_SERVER_URL @@ -23,3 +24,19 @@ def test_processing_server_deployed_processors(): assert response.status_code == 200, \ f'Processing server is not reachable on: {test_url}, {response.status_code}' assert processors == [], f'Mismatch in deployed processors' + + +def test_processing_server_processing_request(): + # Note: the path_to_mets is volume mapped + test_processing_job_input = { + "path_to_mets": "/tmp/assets/kant_aufklaerung_1784/data/mets.xml", + "input_file_grps": ['OCR-D-IMG'], + "output_file_grps": ['OCR-D-DUMMY'], + "agent_type": NETWORK_AGENT_WORKER, + "parameters": {} + } + test_processor = 'ocrd-dummy' + test_url = f'{PROCESSING_SERVER_URL}/processor/run/{test_processor}' + response = post(url=test_url, data=test_processing_job_input) + assert response.status_code == 200, \ + f'Processing server is not reachable on: {test_url}, {response.status_code}' From cdb71eea7e61b35b8dc7ccde6138233b7e142346 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Fri, 19 Jan 2024 17:33:47 +0100 Subject: [PATCH 051/142] fix ocrd-dummy queue and processor name --- ocrd_network/ocrd_network/processing_server.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ocrd_network/ocrd_network/processing_server.py b/ocrd_network/ocrd_network/processing_server.py index 884ae8783d..f60201311e 100644 --- a/ocrd_network/ocrd_network/processing_server.py +++ b/ocrd_network/ocrd_network/processing_server.py @@ -342,8 +342,8 @@ def create_message_queues(self) -> None: # TODO: Reconsider and refactor this. # Added ocrd-dummy by default if not available for the integration tests. # A proper Processing Worker / Processor Server registration endpoint is needed on the Processing Server side - if 'dummy' not in queue_names: - queue_names.append('dummy') + if 'ocrd-dummy' not in queue_names: + queue_names.append('ocrd-dummy') for queue_name in queue_names: # The existence/validity of the worker.name is not tested. @@ -405,7 +405,7 @@ def processing_agent_exists(self, processor_name: str, agent_type: str) -> bool: # Added ocrd-dummy by default if not available for the integration tests. # A proper Processing Worker / Processor Server registration endpoint # is needed on the Processing Server side - if processor_name == 'dummy': + if processor_name == 'ocrd-dummy': return True if not self.check_if_queue_exists(processor_name): return False From de11f8f656162281b934e5aa01619ef6da231e4a Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Fri, 19 Jan 2024 17:42:08 +0100 Subject: [PATCH 052/142] expose ocrd-tool.json for ocrd-dummy in root like processors --- ocrd-tool.json | 1 + .../processor/builtin/dummy/ocrd-tool.json | 26 +++++++++++-------- .../ocrd/processor/builtin/dummy_processor.py | 2 +- tests/cli/test_bashlib.py | 5 ++-- 4 files changed, 19 insertions(+), 15 deletions(-) create mode 120000 ocrd-tool.json diff --git a/ocrd-tool.json b/ocrd-tool.json new file mode 120000 index 0000000000..9ba9c7b311 --- /dev/null +++ b/ocrd-tool.json @@ -0,0 +1 @@ +./ocrd/ocrd/processor/builtin/dummy/ocrd-tool.json \ No newline at end of file diff --git a/ocrd/ocrd/processor/builtin/dummy/ocrd-tool.json b/ocrd/ocrd/processor/builtin/dummy/ocrd-tool.json index 3ad29b5d77..30a6d99fd9 100644 --- a/ocrd/ocrd/processor/builtin/dummy/ocrd-tool.json +++ b/ocrd/ocrd/processor/builtin/dummy/ocrd-tool.json @@ -1,15 +1,19 @@ { - "executable": "ocrd-dummy", - "description": "Bare-bones processor creates PAGE-XML and optionally copies file from input group to output group", - "steps": ["preprocessing/optimization"], - "categories": ["Image preprocessing"], - "input_file_grp": "DUMMY_INPUT", - "output_file_grp": "DUMMY_OUTPUT", - "parameters": { - "copy_files": { - "type": "boolean", - "default": false, - "description": "Whether to actually copy files (true) or just create PAGE-XML as a side effect (false)" + "tools": { + "ocrd-dummy": { + "executable": "ocrd-dummy", + "description": "Bare-bones processor creates PAGE-XML and optionally copies file from input group to output group", + "steps": ["preprocessing/optimization"], + "categories": ["Image preprocessing"], + "input_file_grp": "DUMMY_INPUT", + "output_file_grp": "DUMMY_OUTPUT", + "parameters": { + "copy_files": { + "type": "boolean", + "default": false, + "description": "Whether to actually copy files (true) or just create PAGE-XML as a side effect (false)" + } + } } } } diff --git a/ocrd/ocrd/processor/builtin/dummy_processor.py b/ocrd/ocrd/processor/builtin/dummy_processor.py index c0371e2d0e..74b2dafb86 100644 --- a/ocrd/ocrd/processor/builtin/dummy_processor.py +++ b/ocrd/ocrd/processor/builtin/dummy_processor.py @@ -79,7 +79,7 @@ def process(self): def __init__(self, *args, **kwargs): - kwargs['ocrd_tool'] = OCRD_TOOL + kwargs['ocrd_tool'] = OCRD_TOOL['tools']['ocrd-dummy'] kwargs['version'] = '0.0.3' super(DummyProcessor, self).__init__(*args, **kwargs) diff --git a/tests/cli/test_bashlib.py b/tests/cli/test_bashlib.py index fc043b5fd7..c0e8854f3b 100644 --- a/tests/cli/test_bashlib.py +++ b/tests/cli/test_bashlib.py @@ -1,6 +1,5 @@ from tests.base import CapturingTestCase as TestCase, main, assets, copy_of_directory -from pkg_resources import parse_version import os, sys import traceback import subprocess @@ -103,8 +102,8 @@ def test_bashlib_minversion(self): exit_code, out, err = self.invoke_bash( "source $(ocrd bashlib filename) && ocrd__minversion 2.29.0") assert exit_code == 0 - version = parse_version(VERSION) - version = "%d.%d.%d" % (version.major, version.minor+1, 0) + major, minor = map(int, VERSION.split('.')[0:2]) + version = "%d.%d.%d" % (major, minor + 1, 0) exit_code, out, err = self.invoke_bash( "source $(ocrd bashlib filename) && ocrd__minversion " + version) assert exit_code > 0 From f58d5c8e699773c43bff67425b8186b59c409413 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Fri, 19 Jan 2024 17:49:33 +0100 Subject: [PATCH 053/142] make uninstall: ocrd not . --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 86601682cb..09712a777f 100644 --- a/Makefile +++ b/Makefile @@ -137,7 +137,7 @@ install-dev: uninstall # Uninstall the tool uninstall: - $(PIP) uninstall . + $(PIP) uninstall ocrd # Regenerate python code from PAGE XSD generate-page: GDS_PAGE = ocrd_models/ocrd_models/ocrd_page_generateds.py From 5bb027df227e09740e76cd95545e0667feec8602 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Fri, 19 Jan 2024 18:06:11 +0100 Subject: [PATCH 054/142] uninstall --yes --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 09712a777f..bcf1a66637 100644 --- a/Makefile +++ b/Makefile @@ -137,7 +137,7 @@ install-dev: uninstall # Uninstall the tool uninstall: - $(PIP) uninstall ocrd + $(PIP) uninstall --yes ocrd # Regenerate python code from PAGE XSD generate-page: GDS_PAGE = ocrd_models/ocrd_models/ocrd_page_generateds.py From 804c0b38ad7e56aff349aaa7a44c894f0abb49bd Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Fri, 19 Jan 2024 18:22:53 +0100 Subject: [PATCH 055/142] remove the old directories, adapt make generate-page, save READMEs --- Makefile | 6 +++--- ocrd/README.md => README_ocrd.md | 0 ocrd_modelfactory/README.md => README_ocrd_modelfactory.md | 0 ocrd_models/README.md => README_ocrd_models.md | 0 ocrd_network/README.md => README_ocrd_network.md | 0 ocrd_utils/README.md => README_ocrd_utils.md | 0 ocrd_validators/README.md => README_ocrd_validators.md | 0 ocrd_modelfactory/ocrd_modelfactory | 1 - ocrd_network/ocrd_network | 1 - ocrd_utils/ocrd_utils | 1 - ocrd_validators/ocrd_validators | 1 - {ocrd_models => src}/ocrd_page_user_methods.py | 0 {ocrd_models => src}/ocrd_page_user_methods/__hash__.py | 0 .../ocrd_page_user_methods/clear_AllIndexed.py | 0 .../ocrd_page_user_methods/exportChildren_GroupType.py | 0 .../ocrd_page_user_methods/extend_AllIndexed.py | 0 .../ocrd_page_user_methods/get_AllAlternativeImagePaths.py | 0 .../ocrd_page_user_methods/get_AllAlternativeImages.py | 0 .../ocrd_page_user_methods/get_AllIndexed.py | 0 .../ocrd_page_user_methods/get_AllRegions.py | 0 .../ocrd_page_user_methods/get_AllTextLines.py | 0 .../ocrd_page_user_methods/get_UnorderedGroupChildren.py | 0 {ocrd_models => src}/ocrd_page_user_methods/id.py | 0 .../ocrd_page_user_methods/invalidate_AlternativeImage.py | 0 .../ocrd_page_user_methods/prune_ReadingOrder.py | 0 {ocrd_models => src}/ocrd_page_user_methods/set_Border.py | 0 {ocrd_models => src}/ocrd_page_user_methods/set_Coords.py | 0 .../ocrd_page_user_methods/set_orientation.py | 0 {ocrd_models => src}/ocrd_page_user_methods/set_points.py | 0 .../ocrd_page_user_methods/sort_AllIndexed.py | 0 {ocrd_utils => src/ocrd_utils}/ocrd_logging.conf | 0 tests/test_logging_conf.py | 2 +- 32 files changed, 4 insertions(+), 8 deletions(-) rename ocrd/README.md => README_ocrd.md (100%) rename ocrd_modelfactory/README.md => README_ocrd_modelfactory.md (100%) rename ocrd_models/README.md => README_ocrd_models.md (100%) rename ocrd_network/README.md => README_ocrd_network.md (100%) rename ocrd_utils/README.md => README_ocrd_utils.md (100%) rename ocrd_validators/README.md => README_ocrd_validators.md (100%) delete mode 120000 ocrd_modelfactory/ocrd_modelfactory delete mode 120000 ocrd_network/ocrd_network delete mode 120000 ocrd_utils/ocrd_utils delete mode 120000 ocrd_validators/ocrd_validators rename {ocrd_models => src}/ocrd_page_user_methods.py (100%) rename {ocrd_models => src}/ocrd_page_user_methods/__hash__.py (100%) rename {ocrd_models => src}/ocrd_page_user_methods/clear_AllIndexed.py (100%) rename {ocrd_models => src}/ocrd_page_user_methods/exportChildren_GroupType.py (100%) rename {ocrd_models => src}/ocrd_page_user_methods/extend_AllIndexed.py (100%) rename {ocrd_models => src}/ocrd_page_user_methods/get_AllAlternativeImagePaths.py (100%) rename {ocrd_models => src}/ocrd_page_user_methods/get_AllAlternativeImages.py (100%) rename {ocrd_models => src}/ocrd_page_user_methods/get_AllIndexed.py (100%) rename {ocrd_models => src}/ocrd_page_user_methods/get_AllRegions.py (100%) rename {ocrd_models => src}/ocrd_page_user_methods/get_AllTextLines.py (100%) rename {ocrd_models => src}/ocrd_page_user_methods/get_UnorderedGroupChildren.py (100%) rename {ocrd_models => src}/ocrd_page_user_methods/id.py (100%) rename {ocrd_models => src}/ocrd_page_user_methods/invalidate_AlternativeImage.py (100%) rename {ocrd_models => src}/ocrd_page_user_methods/prune_ReadingOrder.py (100%) rename {ocrd_models => src}/ocrd_page_user_methods/set_Border.py (100%) rename {ocrd_models => src}/ocrd_page_user_methods/set_Coords.py (100%) rename {ocrd_models => src}/ocrd_page_user_methods/set_orientation.py (100%) rename {ocrd_models => src}/ocrd_page_user_methods/set_points.py (100%) rename {ocrd_models => src}/ocrd_page_user_methods/sort_AllIndexed.py (100%) rename {ocrd_utils => src/ocrd_utils}/ocrd_logging.conf (100%) diff --git a/Makefile b/Makefile index bcf1a66637..dde9d578a7 100644 --- a/Makefile +++ b/Makefile @@ -140,8 +140,8 @@ uninstall: $(PIP) uninstall --yes ocrd # Regenerate python code from PAGE XSD -generate-page: GDS_PAGE = ocrd_models/ocrd_models/ocrd_page_generateds.py -generate-page: GDS_PAGE_USER = ocrd_models/ocrd_page_user_methods.py +generate-page: GDS_PAGE = src/ocrd_models/ocrd_page_generateds.py +generate-page: GDS_PAGE_USER = src/ocrd_page_user_methods.py generate-page: repo/assets generateDS \ -f \ @@ -151,7 +151,7 @@ generate-page: repo/assets --export "write etree" \ --disable-generatedssuper-lookup \ --user-methods=$(GDS_PAGE_USER) \ - ocrd_validators/ocrd_validators/page.xsd + src/ocrd_validators/page.xsd # hack to prevent #451: enum keys will be strings sed -i 's/(Enum):$$/(str, Enum):/' $(GDS_PAGE) # hack to ensure output has pc: prefix diff --git a/ocrd/README.md b/README_ocrd.md similarity index 100% rename from ocrd/README.md rename to README_ocrd.md diff --git a/ocrd_modelfactory/README.md b/README_ocrd_modelfactory.md similarity index 100% rename from ocrd_modelfactory/README.md rename to README_ocrd_modelfactory.md diff --git a/ocrd_models/README.md b/README_ocrd_models.md similarity index 100% rename from ocrd_models/README.md rename to README_ocrd_models.md diff --git a/ocrd_network/README.md b/README_ocrd_network.md similarity index 100% rename from ocrd_network/README.md rename to README_ocrd_network.md diff --git a/ocrd_utils/README.md b/README_ocrd_utils.md similarity index 100% rename from ocrd_utils/README.md rename to README_ocrd_utils.md diff --git a/ocrd_validators/README.md b/README_ocrd_validators.md similarity index 100% rename from ocrd_validators/README.md rename to README_ocrd_validators.md diff --git a/ocrd_modelfactory/ocrd_modelfactory b/ocrd_modelfactory/ocrd_modelfactory deleted file mode 120000 index f2c294cc70..0000000000 --- a/ocrd_modelfactory/ocrd_modelfactory +++ /dev/null @@ -1 +0,0 @@ -../src/ocrd_modelfactory \ No newline at end of file diff --git a/ocrd_network/ocrd_network b/ocrd_network/ocrd_network deleted file mode 120000 index 92db94bfe4..0000000000 --- a/ocrd_network/ocrd_network +++ /dev/null @@ -1 +0,0 @@ -../src/ocrd_network \ No newline at end of file diff --git a/ocrd_utils/ocrd_utils b/ocrd_utils/ocrd_utils deleted file mode 120000 index 4c8b474a93..0000000000 --- a/ocrd_utils/ocrd_utils +++ /dev/null @@ -1 +0,0 @@ -../src/ocrd_utils \ No newline at end of file diff --git a/ocrd_validators/ocrd_validators b/ocrd_validators/ocrd_validators deleted file mode 120000 index 59d7e21559..0000000000 --- a/ocrd_validators/ocrd_validators +++ /dev/null @@ -1 +0,0 @@ -../src/ocrd_validators \ No newline at end of file diff --git a/ocrd_models/ocrd_page_user_methods.py b/src/ocrd_page_user_methods.py similarity index 100% rename from ocrd_models/ocrd_page_user_methods.py rename to src/ocrd_page_user_methods.py diff --git a/ocrd_models/ocrd_page_user_methods/__hash__.py b/src/ocrd_page_user_methods/__hash__.py similarity index 100% rename from ocrd_models/ocrd_page_user_methods/__hash__.py rename to src/ocrd_page_user_methods/__hash__.py diff --git a/ocrd_models/ocrd_page_user_methods/clear_AllIndexed.py b/src/ocrd_page_user_methods/clear_AllIndexed.py similarity index 100% rename from ocrd_models/ocrd_page_user_methods/clear_AllIndexed.py rename to src/ocrd_page_user_methods/clear_AllIndexed.py diff --git a/ocrd_models/ocrd_page_user_methods/exportChildren_GroupType.py b/src/ocrd_page_user_methods/exportChildren_GroupType.py similarity index 100% rename from ocrd_models/ocrd_page_user_methods/exportChildren_GroupType.py rename to src/ocrd_page_user_methods/exportChildren_GroupType.py diff --git a/ocrd_models/ocrd_page_user_methods/extend_AllIndexed.py b/src/ocrd_page_user_methods/extend_AllIndexed.py similarity index 100% rename from ocrd_models/ocrd_page_user_methods/extend_AllIndexed.py rename to src/ocrd_page_user_methods/extend_AllIndexed.py diff --git a/ocrd_models/ocrd_page_user_methods/get_AllAlternativeImagePaths.py b/src/ocrd_page_user_methods/get_AllAlternativeImagePaths.py similarity index 100% rename from ocrd_models/ocrd_page_user_methods/get_AllAlternativeImagePaths.py rename to src/ocrd_page_user_methods/get_AllAlternativeImagePaths.py diff --git a/ocrd_models/ocrd_page_user_methods/get_AllAlternativeImages.py b/src/ocrd_page_user_methods/get_AllAlternativeImages.py similarity index 100% rename from ocrd_models/ocrd_page_user_methods/get_AllAlternativeImages.py rename to src/ocrd_page_user_methods/get_AllAlternativeImages.py diff --git a/ocrd_models/ocrd_page_user_methods/get_AllIndexed.py b/src/ocrd_page_user_methods/get_AllIndexed.py similarity index 100% rename from ocrd_models/ocrd_page_user_methods/get_AllIndexed.py rename to src/ocrd_page_user_methods/get_AllIndexed.py diff --git a/ocrd_models/ocrd_page_user_methods/get_AllRegions.py b/src/ocrd_page_user_methods/get_AllRegions.py similarity index 100% rename from ocrd_models/ocrd_page_user_methods/get_AllRegions.py rename to src/ocrd_page_user_methods/get_AllRegions.py diff --git a/ocrd_models/ocrd_page_user_methods/get_AllTextLines.py b/src/ocrd_page_user_methods/get_AllTextLines.py similarity index 100% rename from ocrd_models/ocrd_page_user_methods/get_AllTextLines.py rename to src/ocrd_page_user_methods/get_AllTextLines.py diff --git a/ocrd_models/ocrd_page_user_methods/get_UnorderedGroupChildren.py b/src/ocrd_page_user_methods/get_UnorderedGroupChildren.py similarity index 100% rename from ocrd_models/ocrd_page_user_methods/get_UnorderedGroupChildren.py rename to src/ocrd_page_user_methods/get_UnorderedGroupChildren.py diff --git a/ocrd_models/ocrd_page_user_methods/id.py b/src/ocrd_page_user_methods/id.py similarity index 100% rename from ocrd_models/ocrd_page_user_methods/id.py rename to src/ocrd_page_user_methods/id.py diff --git a/ocrd_models/ocrd_page_user_methods/invalidate_AlternativeImage.py b/src/ocrd_page_user_methods/invalidate_AlternativeImage.py similarity index 100% rename from ocrd_models/ocrd_page_user_methods/invalidate_AlternativeImage.py rename to src/ocrd_page_user_methods/invalidate_AlternativeImage.py diff --git a/ocrd_models/ocrd_page_user_methods/prune_ReadingOrder.py b/src/ocrd_page_user_methods/prune_ReadingOrder.py similarity index 100% rename from ocrd_models/ocrd_page_user_methods/prune_ReadingOrder.py rename to src/ocrd_page_user_methods/prune_ReadingOrder.py diff --git a/ocrd_models/ocrd_page_user_methods/set_Border.py b/src/ocrd_page_user_methods/set_Border.py similarity index 100% rename from ocrd_models/ocrd_page_user_methods/set_Border.py rename to src/ocrd_page_user_methods/set_Border.py diff --git a/ocrd_models/ocrd_page_user_methods/set_Coords.py b/src/ocrd_page_user_methods/set_Coords.py similarity index 100% rename from ocrd_models/ocrd_page_user_methods/set_Coords.py rename to src/ocrd_page_user_methods/set_Coords.py diff --git a/ocrd_models/ocrd_page_user_methods/set_orientation.py b/src/ocrd_page_user_methods/set_orientation.py similarity index 100% rename from ocrd_models/ocrd_page_user_methods/set_orientation.py rename to src/ocrd_page_user_methods/set_orientation.py diff --git a/ocrd_models/ocrd_page_user_methods/set_points.py b/src/ocrd_page_user_methods/set_points.py similarity index 100% rename from ocrd_models/ocrd_page_user_methods/set_points.py rename to src/ocrd_page_user_methods/set_points.py diff --git a/ocrd_models/ocrd_page_user_methods/sort_AllIndexed.py b/src/ocrd_page_user_methods/sort_AllIndexed.py similarity index 100% rename from ocrd_models/ocrd_page_user_methods/sort_AllIndexed.py rename to src/ocrd_page_user_methods/sort_AllIndexed.py diff --git a/ocrd_utils/ocrd_logging.conf b/src/ocrd_utils/ocrd_logging.conf similarity index 100% rename from ocrd_utils/ocrd_logging.conf rename to src/ocrd_utils/ocrd_logging.conf diff --git a/tests/test_logging_conf.py b/tests/test_logging_conf.py index 1bfe742b76..898722a413 100644 --- a/tests/test_logging_conf.py +++ b/tests/test_logging_conf.py @@ -30,7 +30,7 @@ def resetLogging(): def _fixture_loggin_conf(tmpdir): path_logging_conf_orig = os.path.join( - str(TEST_ROOT), 'ocrd_utils', 'ocrd_logging.conf') + str(TEST_ROOT), 'src', 'ocrd_utils', 'ocrd_logging.conf') path_logging_conf_dest = os.path.join(str(tmpdir), 'ocrd_logging.conf') shutil.copy(path_logging_conf_orig, path_logging_conf_dest) return str(tmpdir) From 7ca2b38e10d0f6ce2670104204b493ae553b3bb5 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Fri, 19 Jan 2024 18:38:53 +0100 Subject: [PATCH 056/142] fix make docs, add VERSION file --- Makefile | 4 ++-- VERSION | 1 + docs/api/ocrd_network/ocrd_network.constants.rst | 7 +++++++ docs/api/ocrd_network/ocrd_network.models.rst | 1 + .../ocrd_network.models.workflow.rst | 7 +++++++ docs/api/ocrd_network/ocrd_network.rst | 1 + docs/conf.py | 16 +++++++--------- pyproject.toml | 6 ++++-- 8 files changed, 30 insertions(+), 13 deletions(-) create mode 100644 VERSION create mode 100644 docs/api/ocrd_network/ocrd_network.constants.rst create mode 100644 docs/api/ocrd_network/ocrd_network.models.workflow.rst diff --git a/Makefile b/Makefile index dde9d578a7..0d3d47869c 100644 --- a/Makefile +++ b/Makefile @@ -235,8 +235,8 @@ coverage: assets # Build documentation docs: for mod in $(BUILD_ORDER);do sphinx-apidoc -f -M -e \ - -o docs/api/$$mod $$mod/$$mod \ - 'ocrd_models/ocrd_models/ocrd_page_generateds.py' \ + -o docs/api/$$mod src/$$mod \ + 'src/ocrd_models/ocrd_page_generateds.py' \ ;done cd docs ; $(MAKE) html diff --git a/VERSION b/VERSION new file mode 100644 index 0000000000..26c014497f --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +2.61.0 diff --git a/docs/api/ocrd_network/ocrd_network.constants.rst b/docs/api/ocrd_network/ocrd_network.constants.rst new file mode 100644 index 0000000000..041c77f22d --- /dev/null +++ b/docs/api/ocrd_network/ocrd_network.constants.rst @@ -0,0 +1,7 @@ +ocrd\_network.constants module +============================== + +.. automodule:: ocrd_network.constants + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/api/ocrd_network/ocrd_network.models.rst b/docs/api/ocrd_network/ocrd_network.models.rst index dba609ccb5..27bb84ad38 100644 --- a/docs/api/ocrd_network/ocrd_network.models.rst +++ b/docs/api/ocrd_network/ocrd_network.models.rst @@ -15,4 +15,5 @@ Submodules ocrd_network.models.job ocrd_network.models.messages ocrd_network.models.ocrd_tool + ocrd_network.models.workflow ocrd_network.models.workspace diff --git a/docs/api/ocrd_network/ocrd_network.models.workflow.rst b/docs/api/ocrd_network/ocrd_network.models.workflow.rst new file mode 100644 index 0000000000..f9046cd8e7 --- /dev/null +++ b/docs/api/ocrd_network/ocrd_network.models.workflow.rst @@ -0,0 +1,7 @@ +ocrd\_network.models.workflow module +==================================== + +.. automodule:: ocrd_network.models.workflow + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/api/ocrd_network/ocrd_network.rst b/docs/api/ocrd_network/ocrd_network.rst index bcecb065c8..ae12ae1f5d 100644 --- a/docs/api/ocrd_network/ocrd_network.rst +++ b/docs/api/ocrd_network/ocrd_network.rst @@ -23,6 +23,7 @@ Submodules :maxdepth: 4 ocrd_network.client + ocrd_network.constants ocrd_network.database ocrd_network.deployer ocrd_network.deployment_utils diff --git a/docs/conf.py b/docs/conf.py index cd2ef233f1..3ab2e1826f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -12,23 +12,21 @@ # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # -import os -import sys -sys.path.insert(0, os.path.abspath('..')) -import json -import re -with open(os.path.join(os.path.abspath('..'), 'ocrd_utils', 'setup.py'), 'r', encoding='utf-8') as f: - VERSION = re.findall(r'\bversion=\'([^\']*)\'', f.read())[0] +# import os +# import sys +# # sys.path.insert(0, os.path.abspath('..')) +with open('VERSION', encoding='utf-8') as f: + VERSION = f.read() # -- Project information ----------------------------------------------------- project = u'ocrd' -copyright = u'2018-2021, OCR-D' +copyright = u'2018-2024, OCR-D' author = u'OCR-D' # The short X.Y version -version = VERSION +version = VERSION.rsplit('.', 1)[0] # The full version, including alpha/beta/rc tags release = VERSION diff --git a/pyproject.toml b/pyproject.toml index 689e73ac8f..34ca89df37 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,12 +7,11 @@ build-backend = "setuptools.build_meta" [project] name = "ocrd" -version = '2.61.0' authors = [{name = "Konstantin Baierer", email = "unixprog@gmail.com"}] license = {text = "Apache License 2.0"} description = "OCR-D framework" requires-python = ">=3.7" -dynamic = ["dependencies"] +dynamic = ['version', 'dependencies'] [project.readme] file = "README.md" @@ -21,6 +20,9 @@ content-type = "text/markdown" [tool.setuptools.dynamic.dependencies] file = ["requirements.txt"] +[tool.setuptools.dynamic.version] +file = ["VERSION"] + [project.urls] Homepage = "https://ocr-d.de" Documentation = "https://ocr-d.de/core" From 066739209c04d0f713b1cb2ff0cbbb207bc183d5 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Fri, 19 Jan 2024 18:41:42 +0100 Subject: [PATCH 057/142] ci: osx --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 047780b810..571fb9b765 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -17,6 +17,7 @@ jobs: steps: - checkout - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install imagemagick geos bash + - run: $PIP_INSTALL -U pip setuptools - run: make install - run: PATH="/Users/distiller/Library/Python/3.9/bin:$PATH" make deps-test test benchmark From b0c1d90d345bfd1b3f508d5d514f292a25960a74 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Fri, 19 Jan 2024 18:52:04 +0100 Subject: [PATCH 058/142] ci: osx: brew install opencv --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 571fb9b765..1280fb29c3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -16,10 +16,10 @@ jobs: PIP_INSTALL: pip3 install --user steps: - checkout - - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install imagemagick geos bash + - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install imagemagick geos bash opencv - run: $PIP_INSTALL -U pip setuptools - run: make install - - run: PATH="/Users/distiller/Library/Python/3.9/bin:$PATH" make deps-test test benchmark + - run: export PATH="/Users/distiller/Library/Python/3.9/bin:$PATH"; make deps-test test benchmark test-python37: docker: From 8f8ce4f82620d7053763a8a337b45e7e602c4da6 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Sun, 21 Jan 2024 23:18:12 +0100 Subject: [PATCH 059/142] deps-cuda: add workaround for tf-keras#62 --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index f2adfdca59..52ce50f673 100644 --- a/Makefile +++ b/Makefile @@ -61,6 +61,8 @@ deps-cuda: mv bin/micromamba $(CONDA_EXE) # Install Conda system-wide (for interactive / login shells) echo 'export MAMBA_EXE=$(CONDA_EXE) MAMBA_ROOT_PREFIX=$(CONDA_PREFIX) CONDA_PREFIX=$(CONDA_PREFIX) PATH=$(CONDA_PREFIX)/bin:$$PATH' >> /etc/profile.d/98-conda.sh +# workaround for tf-keras#62 + echo 'export XLA_FLAGS=--xla_gpu_cuda_data_dir=$(CONDA_PREFIX)/' >> /etc/profile.d/98-conda.sh mkdir -p $(CONDA_PREFIX)/lib $(CONDA_PREFIX)/include echo $(CONDA_PREFIX)/lib >> /etc/ld.so.conf.d/conda.conf # Get CUDA toolkit, including compiler and libraries with dev, From 4d4aaf4f801ec4ae4d1d6830ec1afd508b4cbdd6 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Mon, 22 Jan 2024 10:35:23 +0100 Subject: [PATCH 060/142] ci: disable macosx testing until time to fix CI issue --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1280fb29c3..8d3667af6f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -122,4 +122,4 @@ workflows: - test-python39 - test-python310 - test-python311 - - test-macos + # - test-macos From fcba83d4f74783035b1c68fc68fce968c822e2a6 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Mon, 22 Jan 2024 11:08:14 +0100 Subject: [PATCH 061/142] GH Action to do what .circleci/config.yml currently does --- .github/workflows/unit-test.yml | 54 +++++++++++++++++++++++++++++++++ tests/test_resource_manager.py | 2 ++ 2 files changed, 56 insertions(+) create mode 100644 .github/workflows/unit-test.yml diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml new file mode 100644 index 0000000000..1b47b89910 --- /dev/null +++ b/.github/workflows/unit-test.yml @@ -0,0 +1,54 @@ +name: Test core installation and run tests + +# This workflow will install Python dependencies, run tests and lint with a variety of Python versions +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + build: + + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + python-version: + - '3.7' + - '3.8' + - '3.9' + - '3.10' + - '3.11' + os: + - ubuntu-latest + # - macos-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + sudo apt-get -y update + if test "${{ matrix.os }}" = "ubuntu-latest";then + sudo make deps-ubuntu + fi + make install deps-test + - name: Test with pytest + run: | + make test benchmark + - name: test to ensure that --editable install works + run: | + make install-dev; ocrd --version + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics diff --git a/tests/test_resource_manager.py b/tests/test_resource_manager.py index 785b568833..427882cc25 100644 --- a/tests/test_resource_manager.py +++ b/tests/test_resource_manager.py @@ -23,10 +23,12 @@ def test_resources_manager_config_default(monkeypatch, tmp_path): # act mgr = OcrdResourceManager() + mgr._xdg_config_home = None # assert default_config_dir = os.path.join(os.environ['HOME'], '.config', 'ocrd') f = Path(default_config_dir) / CONST_RESOURCE_YML + assert os.environ['HOME'] == str(tmp_path) assert f.exists() assert f == mgr.user_list assert mgr.add_to_user_database('ocrd-foo', f) From b22cf06e2b2cefb473a34a4573d008d5596fab55 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Mon, 22 Jan 2024 12:16:19 +0100 Subject: [PATCH 062/142] debug resmgr test failure --- src/ocrd/resource_manager.py | 15 ++++++++++++--- src/ocrd_utils/config.py | 2 ++ tests/test_resource_manager.py | 7 +++++-- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/ocrd/resource_manager.py b/src/ocrd/resource_manager.py index 9d1e6ac596..61f93fadc1 100644 --- a/src/ocrd/resource_manager.py +++ b/src/ocrd/resource_manager.py @@ -36,7 +36,16 @@ def __init__(self, userdir=None, xdg_config_home=None, xdg_data_home=None, skip_ self._xdg_data_home = xdg_data_home self._xdg_config_home = xdg_config_home self._userdir = userdir + import os + # import traceback + # traceback.print_stack() + print('os.environ.HOME', os.environ['HOME']) + print('config.HOME', config.HOME) + print('config.XDG_CONFIG_HOME', config.XDG_CONFIG_HOME) + print('xdg_config_home', xdg_config_home) + print('self.xdg_config_home', self.xdg_config_home) self.user_list = Path(self.xdg_config_home, 'ocrd', 'resources.yml') + # print(self.user_list) if not skip_init: self.load_resource_list(Path(RESOURCE_LIST_FILENAME)) @@ -60,9 +69,9 @@ def xdg_data_home(self): @property def xdg_config_home(self): - if not self._xdg_config_home: - self._xdg_config_home = config.XDG_CONFIG_HOME - return self._xdg_config_home + if self._xdg_config_home: + return self._xdg_config_home + return config.XDG_CONFIG_HOME def save_user_list(self, database=None): if not database: diff --git a/src/ocrd_utils/config.py b/src/ocrd_utils/config.py index 7b4538223b..cc12a31152 100644 --- a/src/ocrd_utils/config.py +++ b/src/ocrd_utils/config.py @@ -179,10 +179,12 @@ def _ocrd_download_timeout_parser(val): config.add("XDG_DATA_HOME", description="Directory to look for `./ocrd/resources.yml` (i.e. `ocrd resmgr` user database)", + parser=lambda val: Path(val), default=(True, lambda: Path(config.HOME, '.local/share'))) config.add("XDG_CONFIG_HOME", description="Directory to look for `./ocrd-resources/*` (i.e. `ocrd resmgr` data location)", + parser=lambda val: Path(val), default=(True, lambda: Path(config.HOME, '.config'))) config.add("OCRD_LOGGING_DEBUG", diff --git a/tests/test_resource_manager.py b/tests/test_resource_manager.py index 427882cc25..9a0e7dc602 100644 --- a/tests/test_resource_manager.py +++ b/tests/test_resource_manager.py @@ -3,6 +3,7 @@ import pdb from ocrd.resource_manager import OcrdResourceManager +from ocrd_utils import config from ocrd_utils.os import get_ocrd_tool_json from pytest import raises, fixture @@ -20,17 +21,19 @@ def test_resources_manager_config_default(monkeypatch, tmp_path): # arrange monkeypatch.setenv('HOME', str(tmp_path)) + monkeypatch.delenv('XDG_CONFIG_HOME') # act mgr = OcrdResourceManager() - mgr._xdg_config_home = None # assert default_config_dir = os.path.join(os.environ['HOME'], '.config', 'ocrd') f = Path(default_config_dir) / CONST_RESOURCE_YML assert os.environ['HOME'] == str(tmp_path) - assert f.exists() + assert config.HOME == tmp_path + assert Path.home() == tmp_path assert f == mgr.user_list + assert f.exists() assert mgr.add_to_user_database('ocrd-foo', f) # pdb.set_trace() From 00eb1b20de161772a63f05961134b702176c5a78 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Mon, 22 Jan 2024 13:39:14 +0100 Subject: [PATCH 063/142] ci/gh: fix the issue w/ monkeypatch.delenv --- .github/workflows/unit-test.yml | 5 +++-- src/ocrd/resource_manager.py | 9 --------- tests/test_resource_manager.py | 2 +- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 1b47b89910..bd274de669 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -48,7 +48,8 @@ jobs: make install-dev; ocrd --version - name: Lint with flake8 run: | + python -m pip install flake8 # stop the build if there are Python syntax errors or undefined names - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + flake8 src --count --exit-zero --select=E9,F63,F7,F82 --show-source --statistics # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + flake8 src --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics diff --git a/src/ocrd/resource_manager.py b/src/ocrd/resource_manager.py index 61f93fadc1..c668028e9c 100644 --- a/src/ocrd/resource_manager.py +++ b/src/ocrd/resource_manager.py @@ -36,16 +36,7 @@ def __init__(self, userdir=None, xdg_config_home=None, xdg_data_home=None, skip_ self._xdg_data_home = xdg_data_home self._xdg_config_home = xdg_config_home self._userdir = userdir - import os - # import traceback - # traceback.print_stack() - print('os.environ.HOME', os.environ['HOME']) - print('config.HOME', config.HOME) - print('config.XDG_CONFIG_HOME', config.XDG_CONFIG_HOME) - print('xdg_config_home', xdg_config_home) - print('self.xdg_config_home', self.xdg_config_home) self.user_list = Path(self.xdg_config_home, 'ocrd', 'resources.yml') - # print(self.user_list) if not skip_init: self.load_resource_list(Path(RESOURCE_LIST_FILENAME)) diff --git a/tests/test_resource_manager.py b/tests/test_resource_manager.py index 9a0e7dc602..5ae5de221d 100644 --- a/tests/test_resource_manager.py +++ b/tests/test_resource_manager.py @@ -1,6 +1,6 @@ import os from pathlib import Path -import pdb +# import pdb from ocrd.resource_manager import OcrdResourceManager from ocrd_utils import config From 1aa1b6340bc5d3b7155078770e6b3f2671264347 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Mon, 22 Jan 2024 13:47:09 +0100 Subject: [PATCH 064/142] ci/gha: enable macos --- .github/workflows/unit-test.yml | 16 ++++++++++++---- tests/test_resource_manager.py | 2 +- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index bd274de669..cfe282cd54 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -24,20 +24,28 @@ jobs: - '3.10' - '3.11' os: - - ubuntu-latest - # - macos-latest + - ubuntu-22.04 + - ubuntu-20.04 + - macos-latest steps: - uses: actions/checkout@v3 + - name: Set up Homebrew + id: set-up-homebrew + uses: Homebrew/actions/setup-homebrew@master - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | - sudo apt-get -y update - if test "${{ matrix.os }}" = "ubuntu-latest";then + if [[ "${{ matrix.os }}" == "ubuntu"* ]];then + sudo apt-get -y update sudo make deps-ubuntu + else + HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 \ + HOMEBREW_NO_AUTO_UPDATE=1 \ + brew install imagemagick geos bash # opencv fi make install deps-test - name: Test with pytest diff --git a/tests/test_resource_manager.py b/tests/test_resource_manager.py index 5ae5de221d..fb6db3adc4 100644 --- a/tests/test_resource_manager.py +++ b/tests/test_resource_manager.py @@ -21,7 +21,7 @@ def test_resources_manager_config_default(monkeypatch, tmp_path): # arrange monkeypatch.setenv('HOME', str(tmp_path)) - monkeypatch.delenv('XDG_CONFIG_HOME') + monkeypatch.delenv('XDG_CONFIG_HOME', raising=False) # act mgr = OcrdResourceManager() From 3c84bcddc34f230885eecd05501bd6aec1bb3dce Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Mon, 22 Jan 2024 14:42:54 +0100 Subject: [PATCH 065/142] readme: add badge for GHA unit-test workflow --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 23fa8ab6b2..ff19645401 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ [![image](https://img.shields.io/pypi/v/ocrd.svg)](https://pypi.org/project/ocrd/) [![image](https://circleci.com/gh/OCR-D/core.svg?style=svg)](https://circleci.com/gh/OCR-D/core) [![Docker Image CI](https://github.com/OCR-D/core/actions/workflows/docker-image.yml/badge.svg)](https://github.com/OCR-D/core/actions/workflows/docker-image.yml) +[![Unit Test CI](https://github.com/OCR-D/core/actions/workflows/unit-test.yml/badge.svg)](https://github.com/OCR-D/core/actions/workflows/unit-test.yml) [![image](https://scrutinizer-ci.com/g/OCR-D/core/badges/build.png?b=master)](https://scrutinizer-ci.com/g/OCR-D/core) [![image](https://codecov.io/gh/OCR-D/core/branch/master/graph/badge.svg)](https://codecov.io/gh/OCR-D/core) [![image](https://scrutinizer-ci.com/g/OCR-D/core/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/OCR-D/core) From 7a0dde2d2731e6db92c18bfc1945f5ee33a56200 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Mon, 22 Jan 2024 17:47:21 +0100 Subject: [PATCH 066/142] make buil-workaround to continue publishing package-specific dists aliases --- Makefile | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 0d3d47869c..c93e697abf 100644 --- a/Makefile +++ b/Makefile @@ -262,8 +262,12 @@ gh-pages: # pyclean: + rm -rf ./build + rm -rf ./dist + rm -rf htmlcov + rm -rf .benchmarks rm -f **/*.pyc - find . -name '__pycache__' -exec rm -rf '{}' \; + -find . -name '__pycache__' -exec rm -rf '{}' \; rm -rf .pytest_cache # @@ -292,3 +296,34 @@ docker docker-cuda: # Build wheels and source dist and twine upload them pypi: build twine upload ocrd*/dist/ocrd*`$(PYTHON) -m setuptools_scm 2>/dev/null`*{tar.gz,whl} + +# Only in place until v3 so we don't break existing installations +build-workaround: + cp pyproject.toml pyproject.toml.BAK + cp src/ocrd_utils/constants.py src/ocrd_utils/constants.py.BAK + cp src/ocrd/cli/__init__.py src/ocrd/cli/__init__.py.BAK + for dist in $(BUILD_ORDER);do \ + cat pyproject.toml.BAK | sed "s,^name =.*,name = \"$$dist\"," > pyproject.toml; \ + cat src/ocrd_utils/constants.py.BAK | sed "s,get_distribution('ocrd'),get_distribution('$$dist')," > src/ocrd_utils/constants.py; \ + cat src/ocrd/cli/__init__.py.BAK | sed "s,package_name='ocrd',package_name='$$dist'," > src/ocrd/cli/__init__.py; \ + $(MAKE) build; \ + done + rm pyproject.toml.BAK + rm src/ocrd_utils/constants.py.BAK + rm src/ocrd/cli/__init__.py.BAK + +# test that the aliased packages work in isolation and combined +test-workaround: build-workaround + for dist in $(BUILD_ORDER);do pip uninstall --yes $$dist;done + for dist in $(BUILD_ORDER);do \ + pip install dist/$$dist-*.whl ;\ + ocrd --version ;\ + make test ;\ + pip uninstall --yes $$dist ;\ + done + for dist in $(BUILD_ORDER);do \ + pip install dist/$$dist-*.whl ;\ + done + ocrd --version ;\ + make test ;\ + for dist in $(BUILD_ORDER);do pip uninstall --yes $$dist;done From 14e5fb3c66471e46bb48e5093b65ababe2880c02 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Mon, 22 Jan 2024 18:01:02 +0100 Subject: [PATCH 067/142] reset version to last release --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 26c014497f..69038bae7e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.61.0 +2.60.3 From 6f3a6569398ec3faee79687be95f0e17569b72df Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Mon, 22 Jan 2024 18:13:35 +0100 Subject: [PATCH 068/142] make pypi-workaround --- Makefile | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index c93e697abf..de0968fac1 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,7 @@ LOG_LEVEL = INFO PYTHONIOENCODING=utf8 TESTDIR = $(CURDIR)/tests PYTEST_ARGS = --continue-on-collection-errors +VERSION = $(shell cat VERSION) SPHINX_APIDOC = @@ -295,10 +296,13 @@ docker docker-cuda: # Build wheels and source dist and twine upload them pypi: build - twine upload ocrd*/dist/ocrd*`$(PYTHON) -m setuptools_scm 2>/dev/null`*{tar.gz,whl} + twine upload dist/ocrd-$(VERSION)*{tar.gz,whl} + +pypi-workaround: build-workaround + for dist in $(BUILD_ORDER);do twine upload dist/$$dist-$(VERSION)*{tar.gz,whl};done # Only in place until v3 so we don't break existing installations -build-workaround: +build-workaround: pyclean cp pyproject.toml pyproject.toml.BAK cp src/ocrd_utils/constants.py src/ocrd_utils/constants.py.BAK cp src/ocrd/cli/__init__.py src/ocrd/cli/__init__.py.BAK From da861871a28fe556008512ea0104df02626bb037 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Mon, 22 Jan 2024 18:20:13 +0100 Subject: [PATCH 069/142] downgrade setuptools min version to 44 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 34ca89df37..4e6d12cd89 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [build-system] requires = [ - "setuptools>=61", + "setuptools>=44", "wheel", ] # PEP 508 specifications. build-backend = "setuptools.build_meta" From 11848811b74413e2351c5568689ad59d81d9cfb5 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Mon, 22 Jan 2024 18:30:54 +0100 Subject: [PATCH 070/142] forgot MANIFEST.in for the READMEs --- MANIFEST.in | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 MANIFEST.in diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000000..3b1e6958b4 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,2 @@ +include README*.md +include requirements.txt From 0c785f254099185727e0aa2ef518eba2ddd4bf51 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky <38561704+bertsky@users.noreply.github.com> Date: Tue, 23 Jan 2024 12:16:07 +0100 Subject: [PATCH 071/142] README: fix links to sub-readmes --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ff19645401..6417e6c66c 100644 --- a/README.md +++ b/README.md @@ -122,19 +122,19 @@ Some parts of the software are configured via environement variables: Contains utilities and constants, e.g. for logging, path normalization, coordinate calculation etc. -See [README for `ocrd_utils`](./ocrd_utils/README.md) for further information. +See [README for `ocrd_utils`](./README_ocrd_utils.md) for further information. ### ocrd_models Contains file format wrappers for PAGE-XML, METS, EXIF metadata etc. -See [README for `ocrd_models`](./ocrd_models/README.md) for further information. +See [README for `ocrd_models`](./README_ocrd_models.md) for further information. ### ocrd_modelfactory Code to instantiate [models](#ocrd-models) from existing data. -See [README for `ocrd_modelfactory`](./ocrd_modelfactory/README.md) for further information. +See [README for `ocrd_modelfactory`](./README_ocrd_modelfactory.md) for further information. ### ocrd_validators @@ -146,7 +146,7 @@ See [README for `ocrd_validators`](./ocrd_validators/README.md) for further info Components related to OCR-D Web API -See [README for `ocrd_network`](./ocrd_network/README.md) for further information. +See [README for `ocrd_network`](./README_ocrd_network.md) for further information. ### ocrd @@ -154,7 +154,7 @@ Depends on all of the above, also contains decorators and classes for creating O Also contains the command line tool `ocrd`. -See [README for `ocrd`](./ocrd/README.md) for further information. +See [README for `ocrd`](./README_ocrd.md) for further information. ## bash library From a9e0db7fa5583955fa2671e0f2ee6d891f0fe9db Mon Sep 17 00:00:00 2001 From: Robert Sachunsky <38561704+bertsky@users.noreply.github.com> Date: Tue, 23 Jan 2024 12:17:24 +0100 Subject: [PATCH 072/142] MANIFEST.in: include VERSION --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index 3b1e6958b4..f002905188 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,3 @@ include README*.md include requirements.txt +include VERSION From e3069e6b316e47d8c29de689f876cb17e656ff06 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky <38561704+bertsky@users.noreply.github.com> Date: Tue, 23 Jan 2024 12:19:50 +0100 Subject: [PATCH 073/142] Create README_bashlib.md --- README_bashlib.md | 158 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 README_bashlib.md diff --git a/README_bashlib.md b/README_bashlib.md new file mode 100644 index 0000000000..dca5bdf486 --- /dev/null +++ b/README_bashlib.md @@ -0,0 +1,158 @@ +# bashlib + +> Builds a bash script that can be sourced by other bash scripts to create OCRD-compliant CLI. + +For example: + + source `ocrd bashlib filename` + declare -A NAMESPACES MIMETYPES + eval NAMESPACES=( `ocrd bashlib constants NAMESPACES` ) + echo ${NAMESPACES[page]} + eval MIMETYPE_PAGE=( `ocrd bashlib constants MIMETYPE_PAGE` ) + echo $MIMETYPE_PAGE + eval MIMETYPES=( `ocrd bashlib constants EXT_TO_MIME` ) + echo ${MIMETYPES[.jpg]} + + +## bashlib CLI + +See [CLI usage](https://ocr-d.de/core/api/ocrd/ocrd.cli.bashlib.html) + +## bashlib API + +### `ocrd__raise` + +Raise an error and exit. + +### `ocrd__log` + +Delegate logging to [`ocrd log`](#ocrd-cli) + +### `ocrd__minversion` + +Ensure minimum version + +### `ocrd__dumpjson` + +Output ocrd-tool.json content verbatim. + +Requires `$OCRD_TOOL_JSON` and `$OCRD_TOOL_NAME` to be set: + +```sh +export OCRD_TOOL_JSON=/path/to/ocrd-tool.json +export OCRD_TOOL_NAME=ocrd-foo-bar +``` + +(Which you automatically get from [`ocrd__wrap`](#ocrd__wrap).) + +### `ocrd__show_resource` + +Output given resource file's content. + +### `ocrd__list_resources` + +Output all resource files' names. + +### `ocrd__usage` + +Print help on CLI usage. + +### `ocrd__parse_argv` + +Parses arguments according to [OCR-D CLI](https://ocr-d.de/en/spec/cli). +In doing so, depending on the values passed to it, may delegate to … +- [`ocrd__raise`](#ocrd__raise) and exit (if something went wrong) +- [`ocrd__usage`](#ocrd__usage) and exit +- [`ocrd__dumpjson`](#ocrd__dumpjson) and exit +- [`ocrd__show_resource`](#ocrd__show_resource) and exit +- [`ocrd__list_resources`](#ocrd__list_resources) and exit +- [`ocrd validate tasks`](#ocrd-cli) and return + +Expects an associative array ("hash"/"dict") **`ocrd__argv`** to be predefined: + + declare -A ocrd__argv=() + +This will be filled by the parser along the following keys: +- `overwrite`: whether `--overwrite` is enabled +- `profile`: whether `--profile` is enabled +- `profile_file`: the argument of `--profile-file` +- `log_level`: the argument of `--log-level` +- `mets_file`: absolute path of the `--mets` argument +- `working_dir`: absolute path of the `--working-dir` argument or the parent of `mets_file` +- `page_id`: the argument of `--page-id` +- `input_file_grp`: the argument of `--input-file-grp` +- `output_file_grp`: the argument of `--output-file-grp` + +Moreover, there will be an associative array **`params`** +with the fully expanded runtime values of the ocrd-tool.json parameters. + +### `ocrd__wrap` + +Parses an [ocrd-tool.json](https://ocr-d.de/en/spec/ocrd_tool) for a specific `tool` (i.e. processor `executable`). + +Delegates to … +- [`ocrd__parse_argv`](#ocrd__parse_argv), creating the `ocrd__argv` associative array +- [`ocrd bashlib input-files`](#ocrd-cli), creating the data structures used by [`ocrd__input_file`](#ocrd__input_file) + +Usage: `ocrd__wrap PATH/TO/OCRD-TOOL.JSON EXECUTABLE ARGS` + +For example: + + ocrd__wrap $SHAREDIR/ocrd-tool.json ocrd-olena-binarize "$@" + ... + +### `ocrd__input_file` + +(Requires [`ocrd__wrap`](#ocrd__wrap) to have been run first.) + +Access information on the input files according to the parsed CLI arguments: +- their file `url` (or local file path) +- their file `ID` +- their `mimetype` +- their `pageId` +- their proposed corresponding `outputFileId` (generated from `${ocrd__argv[output__file_grp]}` and input file `ID`) + +Usage: `ocrd__input_file NR KEY` + +For example: + + pageId=`ocrd__input_file 3 pageId` + +To be used in a **loop over all selected pages**: + + for ((n=0; n<${#ocrd__files[*]}; n++)); do + local in_fpath=($(ocrd__input_file $n url)) + local in_id=($(ocrd__input_file $n ID)) + local in_mimetype=($(ocrd__input_file $n mimetype)) + local in_pageId=($(ocrd__input_file $n pageId)) + local out_id=$(ocrd__input_file $n outputFileId) + local out_fpath="${ocrd__argv[output_file_grp]}/${out_id}.xml + + # process $in_fpath to $out_fpath ... + + declare -a options + if [ -n "$in_pageId" ]; then + options=( -g $in_pageId ) + else + options=() + fi + if [[ "${ocrd__argv[overwrite]}" == true ]];then + options+=( --force ) + fi + options+=( -G ${ocrd__argv[output_file_grp]} + -m $MIMETYPE_PAGE -i "$out_id" + "$out_fpath" ) + ocrd -l ${ocrd__argv[log_level]} workspace -d ${ocrd__argv[working_dir]} add "${options[@]}" + +> **Note**: If the `--input-file-grp` is **multi-valued** (N fileGrps separated by commas), +> then usage is similar: +> * The function `ocrd__input_file` can be used, but +> its results will be **lists** (delimited by whitespace and surrounded by single quotes), +> e.g. `[url]='file1.xml file2.xml' [ID]='id_file1 id_file2' [mimetype]='application/vnd.prima.page+xml image/tiff' ...`. +> * Therefore its results should be encapsulated in a (non-associative) **array variable** +> and without extra quotes, e.g. `in_file=($(ocrd__input_file 3 url))`, or as shown above. +> * This will yield the first fileGrp's results on index 0, +> which in bash will always be the same as if you referenced the array without index +> (so code does not need to be changed much), e.g. `test -f $in_file` which equals `test -f ${in_file[0]}`. +> * Additional fileGrps will have to be fetched from higher indexes, e.g. `test -f ${in_file[1]}`. + From 814c26259d445b93fa3b6aed84082775a7b2cc34 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky <38561704+bertsky@users.noreply.github.com> Date: Tue, 23 Jan 2024 12:22:23 +0100 Subject: [PATCH 074/142] README_bashlib.md: add TOC --- README_bashlib.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README_bashlib.md b/README_bashlib.md index dca5bdf486..09199468cc 100644 --- a/README_bashlib.md +++ b/README_bashlib.md @@ -14,6 +14,17 @@ For example: echo ${MIMETYPES[.jpg]} + +* [bashlib CLI](#bashlib-cli) +* [bashlib API](#bashlib-api) +* [`ocrd__raise`](#ocrd__raise) +* [`ocrd__log`](#ocrd__log) +* [`ocrd__minversion`](#ocrd__minversion) +* [`ocrd__dumpjson`](#ocrd__dumpjson) +* [`ocrd__usage`](#ocrd__usage) +* [`ocrd__parse_argv`](#ocrd__parse_argv) + + ## bashlib CLI See [CLI usage](https://ocr-d.de/core/api/ocrd/ocrd.cli.bashlib.html) From 06f471d35e223c4e6e9e6c921ec2320339694f23 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky <38561704+bertsky@users.noreply.github.com> Date: Tue, 23 Jan 2024 12:24:10 +0100 Subject: [PATCH 075/142] README.md: remove/link bashlib readme --- README.md | 162 +----------------------------------------------------- 1 file changed, 1 insertion(+), 161 deletions(-) diff --git a/README.md b/README.md index 6417e6c66c..e56a76d933 100644 --- a/README.md +++ b/README.md @@ -28,13 +28,6 @@ * [ocrd_network](#ocrd_network) * [ocrd](#ocrd) * [bash library](#bash-library) - * [bashlib API](#bashlib-api) - * [`ocrd__raise`](#ocrd__raise) - * [`ocrd__log`](#ocrd__log) - * [`ocrd__minversion`](#ocrd__minversion) - * [`ocrd__dumpjson`](#ocrd__dumpjson) - * [`ocrd__usage`](#ocrd__usage) - * [`ocrd__parse_argv`](#ocrd__parse_argv) * [Testing](#testing) * [See Also](#see-also) @@ -160,160 +153,7 @@ See [README for `ocrd`](./README_ocrd.md) for further information. Builds a bash script that can be sourced by other bash scripts to create OCRD-compliant CLI. -For example: - - source `ocrd bashlib filename` - declare -A NAMESPACES MIMETYPES - eval NAMESPACES=( `ocrd bashlib constants NAMESPACES` ) - echo ${NAMESPACES[page]} - eval MIMETYPE_PAGE=( `ocrd bashlib constants MIMETYPE_PAGE` ) - echo $MIMETYPE_PAGE - eval MIMETYPES=( `ocrd bashlib constants EXT_TO_MIME` ) - echo ${MIMETYPES[.jpg]} - - -### bashlib CLI - -See [CLI usage](https://ocr-d.de/core/api/ocrd/ocrd.cli.bashlib.html) - -### bashlib API - -### `ocrd__raise` - -Raise an error and exit. - -### `ocrd__log` - -Delegate logging to [`ocrd log`](#ocrd-cli) - -### `ocrd__minversion` - -Ensure minimum version - -### `ocrd__dumpjson` - -Output ocrd-tool.json content verbatim. - -Requires `$OCRD_TOOL_JSON` and `$OCRD_TOOL_NAME` to be set: - -```sh -export OCRD_TOOL_JSON=/path/to/ocrd-tool.json -export OCRD_TOOL_NAME=ocrd-foo-bar -``` - -(Which you automatically get from [`ocrd__wrap`](#ocrd__wrap).) - -### `ocrd__show_resource` - -Output given resource file's content. - -### `ocrd__list_resources` - -Output all resource files' names. - -### `ocrd__usage` - -Print help on CLI usage. - -### `ocrd__parse_argv` - -Parses arguments according to [OCR-D CLI](https://ocr-d.de/en/spec/cli). -In doing so, depending on the values passed to it, may delegate to … -- [`ocrd__raise`](#ocrd__raise) and exit (if something went wrong) -- [`ocrd__usage`](#ocrd__usage) and exit -- [`ocrd__dumpjson`](#ocrd__dumpjson) and exit -- [`ocrd__show_resource`](#ocrd__show_resource) and exit -- [`ocrd__list_resources`](#ocrd__list_resources) and exit -- [`ocrd validate tasks`](#ocrd-cli) and return - -Expects an associative array ("hash"/"dict") **`ocrd__argv`** to be predefined: - - declare -A ocrd__argv=() - -This will be filled by the parser along the following keys: -- `overwrite`: whether `--overwrite` is enabled -- `profile`: whether `--profile` is enabled -- `profile_file`: the argument of `--profile-file` -- `log_level`: the argument of `--log-level` -- `mets_file`: absolute path of the `--mets` argument -- `working_dir`: absolute path of the `--working-dir` argument or the parent of `mets_file` -- `page_id`: the argument of `--page-id` -- `input_file_grp`: the argument of `--input-file-grp` -- `output_file_grp`: the argument of `--output-file-grp` - -Moreover, there will be an associative array **`params`** -with the fully expanded runtime values of the ocrd-tool.json parameters. - -### `ocrd__wrap` - -Parses an [ocrd-tool.json](https://ocr-d.de/en/spec/ocrd_tool) for a specific `tool` (i.e. processor `executable`). - -Delegates to … -- [`ocrd__parse_argv`](#ocrd__parse_argv), creating the `ocrd__argv` associative array -- [`ocrd bashlib input-files`](#ocrd-cli), creating the data structures used by [`ocrd__input_file`](#ocrd__input_file) - -Usage: `ocrd__wrap PATH/TO/OCRD-TOOL.JSON EXECUTABLE ARGS` - -For example: - - ocrd__wrap $SHAREDIR/ocrd-tool.json ocrd-olena-binarize "$@" - ... - -### `ocrd__input_file` - -(Requires [`ocrd__wrap`](#ocrd__wrap) to have been run first.) - -Access information on the input files according to the parsed CLI arguments: -- their file `url` (or local file path) -- their file `ID` -- their `mimetype` -- their `pageId` -- their proposed corresponding `outputFileId` (generated from `${ocrd__argv[output__file_grp]}` and input file `ID`) - -Usage: `ocrd__input_file NR KEY` - -For example: - - pageId=`ocrd__input_file 3 pageId` - -To be used in a **loop over all selected pages**: - - for ((n=0; n<${#ocrd__files[*]}; n++)); do - local in_fpath=($(ocrd__input_file $n url)) - local in_id=($(ocrd__input_file $n ID)) - local in_mimetype=($(ocrd__input_file $n mimetype)) - local in_pageId=($(ocrd__input_file $n pageId)) - local out_id=$(ocrd__input_file $n outputFileId) - local out_fpath="${ocrd__argv[output_file_grp]}/${out_id}.xml - - # process $in_fpath to $out_fpath ... - - declare -a options - if [ -n "$in_pageId" ]; then - options=( -g $in_pageId ) - else - options=() - fi - if [[ "${ocrd__argv[overwrite]}" == true ]];then - options+=( --force ) - fi - options+=( -G ${ocrd__argv[output_file_grp]} - -m $MIMETYPE_PAGE -i "$out_id" - "$out_fpath" ) - ocrd -l ${ocrd__argv[log_level]} workspace -d ${ocrd__argv[working_dir]} add "${options[@]}" - -> **Note**: If the `--input-file-grp` is **multi-valued** (N fileGrps separated by commas), -> then usage is similar: -> * The function `ocrd__input_file` can be used, but -> its results will be **lists** (delimited by whitespace and surrounded by single quotes), -> e.g. `[url]='file1.xml file2.xml' [ID]='id_file1 id_file2' [mimetype]='application/vnd.prima.page+xml image/tiff' ...`. -> * Therefore its results should be encapsulated in a (non-associative) **array variable** -> and without extra quotes, e.g. `in_file=($(ocrd__input_file 3 url))`, or as shown above. -> * This will yield the first fileGrp's results on index 0, -> which in bash will always be the same as if you referenced the array without index -> (so code does not need to be changed much), e.g. `test -f $in_file` which equals `test -f ${in_file[0]}`. -> * Additional fileGrps will have to be fetched from higher indexes, e.g. `test -f ${in_file[1]}`. - +See [README for `bashlib`](./README_bashlib.md) for further information. ## Testing From ecc0dbccdcdd67ed2465983a5edcdcd2d877862f Mon Sep 17 00:00:00 2001 From: Robert Sachunsky <38561704+bertsky@users.noreply.github.com> Date: Tue, 23 Jan 2024 12:25:41 +0100 Subject: [PATCH 076/142] README.md: relink ocrd_valitors readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e56a76d933..f2ad546316 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,7 @@ See [README for `ocrd_modelfactory`](./README_ocrd_modelfactory.md) for further Schemas and routines for validating BagIt, `ocrd-tool.json`, workspaces, METS, page, CLI parameters etc. -See [README for `ocrd_validators`](./ocrd_validators/README.md) for further information. +See [README for `ocrd_validators`](./README_ocrd_validators.md) for further information. ### ocrd_network From eae826e0496c36e1923b6065ef3377abc557c3a4 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Tue, 23 Jan 2024 13:58:22 +0100 Subject: [PATCH 077/142] make install: update/install pip and wheel before --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index de0968fac1..b9a94cb4c7 100644 --- a/Makefile +++ b/Makefile @@ -126,6 +126,8 @@ build: # (Re)install the tool install: #build + # not stricttly necessary but a precaution against outdated python build tools, https://github.com/OCR-D/core/pull/1166 + $(PIP) install -U pip wheel $(PIP_INSTALL) . $(PIP_INSTALL_CONFIG_OPTION) @# workaround for shapely#1598 $(PIP) config set global.no-binary shapely From 6f667f72e08a7a634947679c1d8e9d96bdc6748e Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Tue, 23 Jan 2024 14:37:55 +0100 Subject: [PATCH 078/142] make install: install setuptools for pkg_resources --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b9a94cb4c7..4a2cffe3c3 100644 --- a/Makefile +++ b/Makefile @@ -127,7 +127,7 @@ build: # (Re)install the tool install: #build # not stricttly necessary but a precaution against outdated python build tools, https://github.com/OCR-D/core/pull/1166 - $(PIP) install -U pip wheel + $(PIP) install -U pip wheel setuptools $(PIP_INSTALL) . $(PIP_INSTALL_CONFIG_OPTION) @# workaround for shapely#1598 $(PIP) config set global.no-binary shapely From dd721d2ae6ec14b8ec87a9189db357c8622de96f Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Tue, 23 Jan 2024 18:25:38 +0100 Subject: [PATCH 079/142] :memo: changelog --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9c7119f3f..324c3103b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ Versioned according to [Semantic Versioning](http://semver.org/). ## Unreleased +Changed: + + * :fire: simplify the project layout and distribution policy, #1166 + * In the future there will be only one distribution `ocrd` + * The previous separate distributions of the `ocrd_utils`, `ocrd_models`, `ocrd_modelfactory`, `ocrd_validators` and `ocrd_network` are all part of `ocrd` now + * Nothing needs to be changed in code using OCR-D/core, the package structure and API is the same as before + * Until the next major release, we will continue to provide distributions for `ocrd_utils` etc. that contain the same code as `ocrd` + * Using `ocrd_utils` etc. as distributions in `requirements.txt` or `install_requires` is now deprecated + * Once we release v3.0.0, these distributions will be depublished + ## [2.60.3] - 2024-01-10 Fixed: From e92cac87016859b3c1705cbdc4c3aaf2dac2eef9 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Tue, 23 Jan 2024 18:26:37 +0100 Subject: [PATCH 080/142] :package: v2.61.0 --- CHANGELOG.md | 3 +++ VERSION | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 324c3103b2..46eda4410c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ Versioned according to [Semantic Versioning](http://semver.org/). ## Unreleased +## [2.61.0] - 2024-01-23 + Changed: * :fire: simplify the project layout and distribution policy, #1166 @@ -1967,6 +1969,7 @@ Fixed Initial Release +[2.61.0]: ../../compare/v2.61.0..v2.60.3 [2.60.3]: ../../compare/v2.60.3..v2.60.2 [2.60.2]: ../../compare/v2.60.2..v2.60.1 [2.60.1]: ../../compare/v2.60.1..v2.60.0 diff --git a/VERSION b/VERSION index 69038bae7e..26c014497f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.60.3 +2.61.0 From 2389bc309a782882d52218ff00ce779b7f2b5623 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Tue, 23 Jan 2024 18:52:22 +0100 Subject: [PATCH 081/142] :memo: changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46eda4410c..38280e6f82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ Versioned according to [Semantic Versioning](http://semver.org/). ## Unreleased +Fixed: + + * deps-cuda: add workaround for tf-keras#62, #1169 + + ## [2.61.0] - 2024-01-23 Changed: From ac1d248575fb3437b0f09254d4aa2482dd78214f Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Tue, 23 Jan 2024 19:06:31 +0100 Subject: [PATCH 082/142] fix docker builds --- .dockerignore | 3 ++- Dockerfile | 13 +++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/.dockerignore b/.dockerignore index aefc504c60..ec30b48a25 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,5 +1,6 @@ * -!ocrd* +!src* +!pyproject.toml !Makefile !LICENSE !README.md diff --git a/Dockerfile b/Dockerfile index 00bd8bb30a..e752d69722 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,17 +9,15 @@ ENV LANG=C.UTF-8 ENV PIP=pip WORKDIR /build-ocrd -COPY ocrd ./ocrd -COPY ocrd_modelfactory ./ocrd_modelfactory/ -COPY ocrd_models ./ocrd_models -COPY ocrd_utils ./ocrd_utils -RUN mv ./ocrd_utils/ocrd_logging.conf /etc -COPY ocrd_validators/ ./ocrd_validators -COPY ocrd_network/ ./ocrd_network + +COPY src ./src +COPY pyproject.toml . COPY Makefile . COPY README.md . COPY LICENSE . COPY .git ./.git + +RUN mv ./src/ocrd_utils/ocrd_logging.conf /etc RUN echo 'APT::Install-Recommends "0"; APT::Install-Suggests "0";' >/etc/apt/apt.conf.d/ocr-d.conf RUN apt-get update && apt-get -y install software-properties-common \ && apt-get update && apt-get -y install \ @@ -36,7 +34,6 @@ RUN apt-get update && apt-get -y install software-properties-common \ && make deps-ubuntu \ && python3 -m venv /usr/local \ && hash -r \ - && pip install --upgrade pip setuptools wheel \ && make install \ && eval $FIXUP \ && rm -rf /build-ocrd From dd4c52912c392655bdf1257439de2f50924239c1 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Tue, 23 Jan 2024 19:09:07 +0100 Subject: [PATCH 083/142] we need VERSION during build --- .dockerignore | 1 + Dockerfile | 1 + 2 files changed, 2 insertions(+) diff --git a/.dockerignore b/.dockerignore index aefc504c60..00e7fac95b 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,6 +1,7 @@ * !ocrd* !Makefile +!VERSION !LICENSE !README.md !.git diff --git a/Dockerfile b/Dockerfile index 00bd8bb30a..470e78b327 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,6 +13,7 @@ COPY ocrd ./ocrd COPY ocrd_modelfactory ./ocrd_modelfactory/ COPY ocrd_models ./ocrd_models COPY ocrd_utils ./ocrd_utils +COPY VERSION ./VERSION RUN mv ./ocrd_utils/ocrd_logging.conf /etc COPY ocrd_validators/ ./ocrd_validators COPY ocrd_network/ ./ocrd_network From bea51d2f444c91a6997b726b8a3a368cd36bbf81 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Tue, 23 Jan 2024 20:19:12 +0100 Subject: [PATCH 084/142] :memo: changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38280e6f82..30c0dce25c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,8 @@ Versioned according to [Semantic Versioning](http://semver.org/). Fixed: * deps-cuda: add workaround for tf-keras#62, #1169 - + * fix regression docker deployment, #1172 + ## [2.61.0] - 2024-01-23 From a0bbfae2b9077b3f435972fdf9c7fc7368607c5b Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Tue, 23 Jan 2024 20:19:49 +0100 Subject: [PATCH 085/142] :package: v2.61.1 --- CHANGELOG.md | 3 +++ VERSION | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 30c0dce25c..2a06fa895f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ Versioned according to [Semantic Versioning](http://semver.org/). ## Unreleased +## [2.61.1] - 2024-01-23 + Fixed: * deps-cuda: add workaround for tf-keras#62, #1169 @@ -1975,6 +1977,7 @@ Fixed Initial Release +[2.61.1]: ../../compare/v2.61.1..v2.61.1 [2.61.0]: ../../compare/v2.61.0..v2.60.3 [2.60.3]: ../../compare/v2.60.3..v2.60.2 [2.60.2]: ../../compare/v2.60.2..v2.60.1 diff --git a/VERSION b/VERSION index 26c014497f..6679a60439 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.61.0 +2.61.1 From 6178082a5da2e418a28cc271631f836141c1ab45 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Tue, 23 Jan 2024 20:23:33 +0100 Subject: [PATCH 086/142] typo in changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a06fa895f..af7d07bcd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ Versioned according to [Semantic Versioning](http://semver.org/). Fixed: - * deps-cuda: add workaround for tf-keras#62, #1169 + * deps-cuda: add workaround for keras-team/tf-keras#62, #1169 * fix regression docker deployment, #1172 From e7261f4cd0cbacadf24c521d37332b716bd87a55 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Wed, 24 Jan 2024 12:12:14 +0100 Subject: [PATCH 087/142] rm integ tests from CCI --- .circleci/config.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a8c1ab1e60..086810af4b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -32,7 +32,7 @@ jobs: - run: sudo apt-get -y update - run: sudo make deps-ubuntu - run: make install deps-test - - run: make test benchmark integration-test + - run: make test benchmark # smoke test to ensure that --editable install works - run: make install-dev; ocrd --version @@ -47,7 +47,7 @@ jobs: - run: sudo apt-get -y update - run: sudo make deps-ubuntu - run: make install deps-test - - run: make test benchmark integration-test + - run: make test benchmark # smoke test to ensure that --editable install works - run: make install-dev; ocrd --version @@ -62,7 +62,7 @@ jobs: - run: sudo apt-get -y update - run: sudo make deps-ubuntu - run: make install deps-test - - run: make test benchmark integration-test + - run: make test benchmark # smoke test to ensure that --editable install works - run: make install-dev; ocrd --version @@ -77,7 +77,7 @@ jobs: - run: sudo apt-get -y update - run: sudo make deps-ubuntu - run: make install deps-test - - run: make test benchmark integration-test + - run: make test benchmark # smoke test to ensure that --editable install works - run: make install-dev; ocrd --version @@ -92,7 +92,7 @@ jobs: - run: sudo apt-get -y update - run: sudo make deps-ubuntu - run: make install deps-test - - run: make test benchmark integration-test + - run: make test benchmark # smoke test to ensure that --editable install works - run: make install-dev; ocrd --version From 9640edb86c4e01a1c82af579f316b984e52bec6d Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Wed, 24 Jan 2024 12:18:34 +0100 Subject: [PATCH 088/142] add integ test yml workflow --- .github/workflows/integration-test.yml | 60 ++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 .github/workflows/integration-test.yml diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml new file mode 100644 index 0000000000..b58f8322dc --- /dev/null +++ b/.github/workflows/integration-test.yml @@ -0,0 +1,60 @@ +name: Test core installation and run ocrd network integration tests + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + build: + + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + python-version: + - '3.7' + - '3.8' + - '3.9' + - '3.10' + - '3.11' + os: + - ubuntu-22.04 + - ubuntu-20.04 + - macos-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Homebrew + id: set-up-homebrew + uses: Homebrew/actions/setup-homebrew@master + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + if [[ "${{ matrix.os }}" == "ubuntu"* ]];then + sudo apt-get -y update + sudo make deps-ubuntu + else + HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 \ + HOMEBREW_NO_AUTO_UPDATE=1 \ + brew install imagemagick geos bash # opencv + fi + make install deps-test + - name: Test with pytest + run: | + make test integration-test + - name: test to ensure that --editable install works + run: | + make install-dev; ocrd --version + - name: Lint with flake8 + run: | + python -m pip install flake8 + # stop the build if there are Python syntax errors or undefined names + flake8 src --count --exit-zero --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 src --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics From 6ba0b4ca3c84f1b3f9fe410e93e66a2421fff416 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Wed, 24 Jan 2024 12:20:02 +0100 Subject: [PATCH 089/142] remove editable install check --- .github/workflows/integration-test.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index b58f8322dc..fed553dd74 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -48,9 +48,6 @@ jobs: - name: Test with pytest run: | make test integration-test - - name: test to ensure that --editable install works - run: | - make install-dev; ocrd --version - name: Lint with flake8 run: | python -m pip install flake8 From 4bb3a54f58a9b797d24889b48d367fbfa5749ae9 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Wed, 24 Jan 2024 12:29:17 +0100 Subject: [PATCH 090/142] refine integration test yml --- .github/workflows/integration-test.yml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index fed553dd74..adba4879f2 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -22,7 +22,6 @@ jobs: - '3.11' os: - ubuntu-22.04 - - ubuntu-20.04 - macos-latest steps: @@ -45,13 +44,6 @@ jobs: brew install imagemagick geos bash # opencv fi make install deps-test - - name: Test with pytest + - name: Test network integration with pytest run: | make test integration-test - - name: Lint with flake8 - run: | - python -m pip install flake8 - # stop the build if there are Python syntax errors or undefined names - flake8 src --count --exit-zero --select=E9,F63,F7,F82 --show-source --statistics - # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - flake8 src --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics From 4bbd9dc25ac1aadb91d7d1706c1bb0e465742880 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Wed, 24 Jan 2024 12:34:30 +0100 Subject: [PATCH 091/142] run only integration-test --- .github/workflows/integration-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index adba4879f2..bba46dfcdd 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -46,4 +46,4 @@ jobs: make install deps-test - name: Test network integration with pytest run: | - make test integration-test + make integration-test From ea1e1357d9704dc6c87c608f2e05e2299026025e Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Wed, 24 Jan 2024 13:20:53 +0100 Subject: [PATCH 092/142] requirements.txt missing from Dockerfile --- .dockerignore | 1 + .github/workflows/docker-image.yml | 4 ++++ Dockerfile | 1 + 3 files changed, 6 insertions(+) diff --git a/.dockerignore b/.dockerignore index 5768541c89..fc93d25fe7 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,6 +1,7 @@ * !src* !pyproject.toml +!requirements.txt !Makefile !VERSION !LICENSE diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 25999a199e..5d26dd3f0e 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -28,6 +28,10 @@ jobs: - name: Build the Docker image with GPU support # default tag uses docker.io, so override on command-line run: make docker-cuda DOCKER_TAG=${{ env.DOCKER_TAG }}-cuda DOCKER_BASE_IMAGE=${{ env.DOCKER_TAG }} + - name: Smoke Test that ocrd --help works + run: | + docker run --rm -it {{ env.DOCKER_TAG }} ocrd --version + docker run --rm -it {{ env.DOCKER_TAG }}-cuda ocrd --version - name: Login to GitHub Container Registry uses: docker/login-action@v2 with: diff --git a/Dockerfile b/Dockerfile index 5fbb4a9a94..9cdb648372 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,6 +13,7 @@ WORKDIR /build-ocrd COPY src ./src COPY pyproject.toml . COPY VERSION ./VERSION +COPY requirements.txt ./requirements.txt RUN mv ./src/ocrd_utils/ocrd_logging.conf /etc COPY Makefile . COPY README.md . From dd5bacd57d7e5540422668efc088f22f474c93f0 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Wed, 24 Jan 2024 13:24:47 +0100 Subject: [PATCH 093/142] revert unnecessary changes --- .circleci/config.yml | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 086810af4b..858d03901a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -27,8 +27,6 @@ jobs: working_directory: ~/ocrd-core steps: - checkout - - setup_remote_docker: # https://circleci.com/docs/2.0/building-docker-images/ - docker_layer_caching: false - run: sudo apt-get -y update - run: sudo make deps-ubuntu - run: make install deps-test @@ -42,8 +40,6 @@ jobs: working_directory: ~/ocrd-core steps: - checkout - - setup_remote_docker: # https://circleci.com/docs/2.0/building-docker-images/ - docker_layer_caching: false - run: sudo apt-get -y update - run: sudo make deps-ubuntu - run: make install deps-test @@ -57,8 +53,6 @@ jobs: working_directory: ~/ocrd-core steps: - checkout - - setup_remote_docker: # https://circleci.com/docs/2.0/building-docker-images/ - docker_layer_caching: false - run: sudo apt-get -y update - run: sudo make deps-ubuntu - run: make install deps-test @@ -72,8 +66,6 @@ jobs: working_directory: ~/ocrd-core steps: - checkout - - setup_remote_docker: # https://circleci.com/docs/2.0/building-docker-images/ - docker_layer_caching: false - run: sudo apt-get -y update - run: sudo make deps-ubuntu - run: make install deps-test @@ -87,8 +79,6 @@ jobs: working_directory: ~/ocrd-core steps: - checkout - - setup_remote_docker: # https://circleci.com/docs/2.0/building-docker-images/ - docker_layer_caching: false - run: sudo apt-get -y update - run: sudo make deps-ubuntu - run: make install deps-test @@ -102,7 +92,7 @@ jobs: steps: - checkout - setup_remote_docker: # https://circleci.com/docs/2.0/building-docker-images/ - docker_layer_caching: false + docker_layer_caching: true - run: make docker - run: make docker-cuda - run: From f88f4e0b0145d9f74f336b484e627c3d3fb6ecbf Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Wed, 24 Jan 2024 13:48:39 +0100 Subject: [PATCH 094/142] :memo: changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index af7d07bcd3..67a0eaef1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ Versioned according to [Semantic Versioning](http://semver.org/). ## Unreleased +Fixed: + + * another regression to docker deployment (requirements.txt missing), #1173 + ## [2.61.1] - 2024-01-23 Fixed: From 1c7a9107d1a6c07d3cdaeac2beb16118220edb19 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Wed, 24 Jan 2024 13:49:16 +0100 Subject: [PATCH 095/142] :package: v2.61.2 --- CHANGELOG.md | 3 +++ VERSION | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 67a0eaef1f..1888ac1a62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ Versioned according to [Semantic Versioning](http://semver.org/). ## Unreleased +## [2.61.2] - 2024-01-24 + Fixed: * another regression to docker deployment (requirements.txt missing), #1173 @@ -1981,6 +1983,7 @@ Fixed Initial Release +[2.61.2]: ../../compare/v2.61.2..v2.61.1 [2.61.1]: ../../compare/v2.61.1..v2.61.1 [2.61.0]: ../../compare/v2.61.0..v2.60.3 [2.60.3]: ../../compare/v2.60.3..v2.60.2 diff --git a/VERSION b/VERSION index 6679a60439..fb82db5291 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.61.1 +2.61.2 From 721f0af6919fc0b470d42fe6c2612e8eef32ea84 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Wed, 24 Jan 2024 13:50:58 +0100 Subject: [PATCH 096/142] shorten the GAct wf name --- .github/workflows/integration-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index bba46dfcdd..1915a4f3d9 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -1,4 +1,4 @@ -name: Test core installation and run ocrd network integration tests +name: Run ocrd network integration tests on: push: From a8baddbc179627ae4e94b573562a18c3ade1a2e4 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Wed, 24 Jan 2024 14:02:29 +0100 Subject: [PATCH 097/142] ci/gha/docker-image: run smoke tests non-interactive --- .github/workflows/docker-image.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 5d26dd3f0e..eb3b60cc4b 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -30,8 +30,8 @@ jobs: run: make docker-cuda DOCKER_TAG=${{ env.DOCKER_TAG }}-cuda DOCKER_BASE_IMAGE=${{ env.DOCKER_TAG }} - name: Smoke Test that ocrd --help works run: | - docker run --rm -it {{ env.DOCKER_TAG }} ocrd --version - docker run --rm -it {{ env.DOCKER_TAG }}-cuda ocrd --version + docker run --rm {{ env.DOCKER_TAG }} ocrd --version + docker run --rm {{ env.DOCKER_TAG }}-cuda ocrd --version - name: Login to GitHub Container Registry uses: docker/login-action@v2 with: From e9c20e092aa01351ddb67f0f6bb7eb8e6f439cfb Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Wed, 24 Jan 2024 14:15:52 +0100 Subject: [PATCH 098/142] ci/gha/docker-image: another typo --- .github/workflows/docker-image.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index eb3b60cc4b..85a602a1b2 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -30,8 +30,8 @@ jobs: run: make docker-cuda DOCKER_TAG=${{ env.DOCKER_TAG }}-cuda DOCKER_BASE_IMAGE=${{ env.DOCKER_TAG }} - name: Smoke Test that ocrd --help works run: | - docker run --rm {{ env.DOCKER_TAG }} ocrd --version - docker run --rm {{ env.DOCKER_TAG }}-cuda ocrd --version + docker run --rm ${{ env.DOCKER_TAG }} ocrd --version + docker run --rm ${{ env.DOCKER_TAG }}-cuda ocrd --version - name: Login to GitHub Container Registry uses: docker/login-action@v2 with: From eefcd1ec5cdd3adad1326d37cd3cbf0a79fe9e07 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Wed, 24 Jan 2024 15:46:57 +0100 Subject: [PATCH 099/142] replace pkg_resources with importlib --- Makefile | 3 +- requirements.txt | 12 ++++---- src/ocrd/cli/workspace.py | 4 +-- src/ocrd/constants.py | 6 ++-- src/ocrd/processor/base.py | 7 ++--- src/ocrd/processor/builtin/dummy_processor.py | 6 ++-- src/ocrd/workspace_bagger.py | 6 ++-- src/ocrd_models/constants.py | 5 ++-- src/ocrd_models/ocrd_mets.py | 2 +- src/ocrd_utils/__init__.py | 5 +++- src/ocrd_utils/constants.py | 4 +-- src/ocrd_utils/introspect.py | 28 +++++++++++++++++++ src/ocrd_validators/constants.py | 16 +++++------ tests/cli/test_bashlib.py | 1 - tests/test_resource_manager.py | 9 +++--- tests/validator/test_xsd_validator.py | 2 +- 16 files changed, 74 insertions(+), 42 deletions(-) diff --git a/Makefile b/Makefile index f863684d0d..cefcec2461 100644 --- a/Makefile +++ b/Makefile @@ -129,7 +129,7 @@ build: # (Re)install the tool install: #build # not stricttly necessary but a precaution against outdated python build tools, https://github.com/OCR-D/core/pull/1166 - $(PIP) install -U pip wheel setuptools + $(PIP) install -U pip wheel $(PIP_INSTALL) . $(PIP_INSTALL_CONFIG_OPTION) @# workaround for shapely#1598 $(PIP) config set global.no-binary shapely @@ -271,6 +271,7 @@ pyclean: rm -rf ./dist rm -rf htmlcov rm -rf .benchmarks + rm -rf **/*.egg-info rm -f **/*.pyc -find . -name '__pycache__' -exec rm -rf '{}' \; rm -rf .pytest_cache diff --git a/requirements.txt b/requirements.txt index 9a9b217218..a8ce91ba45 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,21 +1,24 @@ atomicwrites >= 1.3.0 -bagit >= 1.7.0 -bagit_profile >= 1.3.0 beanie~=1.7 click >=7 Deprecated == 1.2.0 docker -fastapi fastapi>=0.78.0 filetype Flask frozendict>=2.3.4 gdown httpx>=0.22.0 +importlib_metadata ; python_version < '3.8' +importlib_resources ; python_version < '3.9' jsonschema lxml memory-profiler >= 0.58.0 +# XXX explicitly do not restrict the numpy version because different +# tensorflow versions might require different versions numpy +ocrd-fork-bagit >= 1.8.1.post2 +ocrd-fork-bagit_profile >= 1.3.0.post1 opencv-python-headless paramiko pika>=1.2.0 @@ -28,7 +31,6 @@ requests < 2.30 requests_unixsocket shapely sparklines >= 0.4.2 -# tensorflow versions might require different versions uvicorn uvicorn>=0.17.6 -# XXX explicitly do not restrict the numpy version because different + diff --git a/src/ocrd/cli/workspace.py b/src/ocrd/cli/workspace.py index 318c1e971d..d77e981dd4 100644 --- a/src/ocrd/cli/workspace.py +++ b/src/ocrd/cli/workspace.py @@ -286,14 +286,14 @@ def workspace_cli_bulk_add(ctx, regex, mimetype, page_id, file_id, url, local_fi \b Examples: ocrd workspace bulk-add \\ - --regex '(?P[^/]+)/page_(?P.*)\.[^.]+' \\ + --regex '(?P[^/]+)/page_(?P.*)\\.[^.]+' \\ --page-id 'PHYS_{{ pageid }}' \\ --file-grp "{{ fileGrp }}" \\ path/to/files/*/*.* \b echo "path/to/src/file.xml SEG/page_p0001.xml" \\ | ocrd workspace bulk-add \\ - --regex '(?P.*?) (?P.+?)/page_(?P.*)\.(?P[^\.]*)' \\ + --regex '(?P.*?) (?P.+?)/page_(?P.*)\\.(?P[^\\.]*)' \\ --file-id 'FILE_{{ fileGrp }}_{{ pageid }}' \\ --page-id 'PHYS_{{ pageid }}' \\ --file-grp "{{ fileGrp }}" \\ diff --git a/src/ocrd/constants.py b/src/ocrd/constants.py index 1d436a7fa9..796aff1895 100644 --- a/src/ocrd/constants.py +++ b/src/ocrd/constants.py @@ -1,7 +1,7 @@ """ Constants for ocrd. """ -from pkg_resources import resource_filename +from ocrd_utils import resource_filename __all__ = [ 'TMP_PREFIX', @@ -18,7 +18,7 @@ DEFAULT_UPLOAD_FOLDER = '/tmp/uploads-ocrd-core' DOWNLOAD_DIR = '/tmp/ocrd-core-downloads' DEFAULT_REPOSITORY_URL = 'http://localhost:5000/' -BASHLIB_FILENAME = resource_filename(__name__, 'lib.bash') -RESOURCE_LIST_FILENAME = resource_filename(__name__, 'resource_list.yml') +BASHLIB_FILENAME = resource_filename(__package__, 'lib.bash') +RESOURCE_LIST_FILENAME = resource_filename(__package__, 'resource_list.yml') RESOURCE_USER_LIST_COMMENT = "# OCR-D private resource list (consider sending a PR with your own resources to OCR-D/core)" BACKUP_DIR = '.backup' diff --git a/src/ocrd/processor/base.py b/src/ocrd/processor/base.py index 38b7848a03..6107688bc2 100644 --- a/src/ocrd/processor/base.py +++ b/src/ocrd/processor/base.py @@ -9,8 +9,6 @@ 'run_processor' ] -from warnings import warn -from pkg_resources import resource_filename from os.path import exists from shutil import copyfileobj import json @@ -30,7 +28,8 @@ list_resource_candidates, pushd_popd, list_all_resources, - get_processor_resource_types + get_processor_resource_types, + resource_filename, ) from ocrd_validators import ParameterValidator from ocrd_models.ocrd_page import MetadataItemType, LabelType, LabelsType @@ -266,7 +265,7 @@ def moduledir(self): """ The filesystem path of the module directory. """ - return resource_filename(self.module, '') + return resource_filename(self.module, '.') @property def input_files(self): diff --git a/src/ocrd/processor/builtin/dummy_processor.py b/src/ocrd/processor/builtin/dummy_processor.py index c0371e2d0e..fcb24af6ec 100644 --- a/src/ocrd/processor/builtin/dummy_processor.py +++ b/src/ocrd/processor/builtin/dummy_processor.py @@ -1,6 +1,5 @@ # pylint: disable=missing-module-docstring,invalid-name from os.path import join, basename -from pkg_resources import resource_string import click @@ -13,11 +12,12 @@ make_file_id, MIME_TO_EXT, MIMETYPE_PAGE, - parse_json_string_with_comments + parse_json_string_with_comments, + resource_string ) from ocrd_modelfactory import page_from_file -OCRD_TOOL = parse_json_string_with_comments(resource_string(__name__, 'dummy/ocrd-tool.json').decode('utf8')) +OCRD_TOOL = parse_json_string_with_comments(resource_string(__package__ + '.dummy', 'ocrd-tool.json')) class DummyProcessor(Processor): """ diff --git a/src/ocrd/workspace_bagger.py b/src/ocrd/workspace_bagger.py index f838a65894..5c10103ce5 100644 --- a/src/ocrd/workspace_bagger.py +++ b/src/ocrd/workspace_bagger.py @@ -9,7 +9,6 @@ import sys from bagit import Bag, make_manifests, _load_tag_file, _make_tag_file, _make_tagmanifest_file # pylint: disable=no-name-in-module from distutils.dir_util import copy_tree -from pkg_resources import get_distribution from ocrd_utils import ( pushd_popd, @@ -19,6 +18,7 @@ DEFAULT_METS_BASENAME, MIMETYPE_PAGE, VERSION, + dist_version, ) from ocrd_validators.constants import BAGIT_TXT, TMP_BAGIT_PREFIX, OCRD_BAGIT_PROFILE_URL from ocrd_modelfactory import page_from_file @@ -117,8 +117,8 @@ def _set_bag_info(self, bag, total_bytes, total_files, ocrd_identifier, ocrd_bas bag.info['BagIt-Profile-Identifier'] = OCRD_BAGIT_PROFILE_URL bag.info['Bag-Software-Agent'] = 'ocrd/core %s (bagit.py %s, bagit_profile %s) [cmdline: "%s"]' % ( VERSION, # TODO - get_distribution('bagit').version, - get_distribution('bagit_profile').version, + dist_version('ocrd-fork-bagit'), + dist_version('ocrd-fork-bagit_profile'), ' '.join(sys.argv)) bag.info['Ocrd-Identifier'] = ocrd_identifier diff --git a/src/ocrd_models/constants.py b/src/ocrd_models/constants.py index 6c8b0e1017..01068b7f34 100644 --- a/src/ocrd_models/constants.py +++ b/src/ocrd_models/constants.py @@ -1,8 +1,7 @@ """ Constants for ocrd_models. """ -from pkg_resources import resource_string -import re +from ocrd_utils import resource_string __all__ = [ 'IDENTIFIER_PRIORITY', @@ -32,7 +31,7 @@ IDENTIFIER_PRIORITY = ['purl', 'urn', 'doi', 'url'] -METS_XML_EMPTY = resource_string(__name__, 'mets-empty.xml') +METS_XML_EMPTY = resource_string(__package__, 'mets-empty.xml') NAMESPACES = { 'mets': "http://www.loc.gov/METS/", diff --git a/src/ocrd_models/ocrd_mets.py b/src/ocrd_models/ocrd_mets.py index 3319f8f6ff..ae4d75e290 100644 --- a/src/ocrd_models/ocrd_mets.py +++ b/src/ocrd_models/ocrd_mets.py @@ -52,7 +52,7 @@ def empty_mets(now=None, cache_flag=False): """ if not now: now = datetime.now().isoformat() - tpl = METS_XML_EMPTY.decode('utf-8') + tpl = METS_XML_EMPTY tpl = tpl.replace('{{ VERSION }}', VERSION) tpl = tpl.replace('{{ NOW }}', '%s' % now) return OcrdMets(content=tpl.encode('utf-8'), cache_flag=cache_flag) diff --git a/src/ocrd_utils/__init__.py b/src/ocrd_utils/__init__.py index 90cd554779..2348265c90 100644 --- a/src/ocrd_utils/__init__.py +++ b/src/ocrd_utils/__init__.py @@ -158,7 +158,10 @@ from .introspect import ( freeze_args, set_json_key_value_overrides, - membername + membername, + resource_filename, + resource_string, + dist_version ) from .logging import ( diff --git a/src/ocrd_utils/constants.py b/src/ocrd_utils/constants.py index 0b9f0ae02b..ca2d65d481 100644 --- a/src/ocrd_utils/constants.py +++ b/src/ocrd_utils/constants.py @@ -1,7 +1,7 @@ """ Constants for ocrd_utils. """ -from pkg_resources import get_distribution +from .introspect import dist_version from re import compile as regex_compile __all__ = [ @@ -18,7 +18,7 @@ 'VERSION', ] -VERSION = get_distribution('ocrd').version +VERSION = dist_version('ocrd') MIMETYPE_PAGE = 'application/vnd.prima.page+xml' diff --git a/src/ocrd_utils/introspect.py b/src/ocrd_utils/introspect.py index cfd3d32b52..a86b9b8984 100644 --- a/src/ocrd_utils/introspect.py +++ b/src/ocrd_utils/introspect.py @@ -3,8 +3,25 @@ """ import json from functools import wraps +from pathlib import Path from frozendict import frozendict +import atexit +from contextlib import ExitStack +# cannot use importlib.resources until we move to 3.9+ forimportlib.resources.files +import sys +if sys.version_info < (3, 9): + import importlib_resources +else: + import importlib.resources as importlib_resources + +if sys.version_info < (3, 8): + import importlib_metadata +else: + import importlib.metadata as importlib_metadata + +file_manager = ExitStack() +atexit.register(file_manager.close) # Taken from https://github.com/OCR-D/core/pull/884 def freeze_args(func): @@ -32,3 +49,14 @@ def set_json_key_value_overrides(obj, *kvpairs): except json.decoder.JSONDecodeError: obj[k] = v return obj + +def resource_filename(pkg : str, fname : str) -> Path: + ref = importlib_resources.files(pkg) / fname + return file_manager.enter_context(importlib_resources.as_file(ref)) + +def resource_string(pkg : str, fname : str) -> str: + with open(resource_filename(pkg, fname), 'r', encoding='utf-8') as f: + return f.read() + +def dist_version(module : str) -> str: + return importlib_metadata.version(module) diff --git a/src/ocrd_validators/constants.py b/src/ocrd_validators/constants.py index fc1ff445ae..b8d145bbba 100644 --- a/src/ocrd_validators/constants.py +++ b/src/ocrd_validators/constants.py @@ -2,7 +2,7 @@ Constants for ocrd_validators. """ import yaml -from pkg_resources import resource_string, resource_filename +from ocrd_utils import resource_string, resource_filename __all__ = [ 'PROCESSING_SERVER_CONFIG_SCHEMA', @@ -21,10 +21,10 @@ 'XSD_PATHS', ] -PROCESSING_SERVER_CONFIG_SCHEMA = yaml.safe_load(resource_string(__name__, 'processing_server_config.schema.yml')) -MESSAGE_SCHEMA_PROCESSING = yaml.safe_load(resource_string(__name__, 'message_processing.schema.yml')) -MESSAGE_SCHEMA_RESULT = yaml.safe_load(resource_string(__name__, 'message_result.schema.yml')) -OCRD_TOOL_SCHEMA = yaml.safe_load(resource_string(__name__, 'ocrd_tool.schema.yml')) +PROCESSING_SERVER_CONFIG_SCHEMA = yaml.safe_load(resource_string(__package__, 'processing_server_config.schema.yml')) +MESSAGE_SCHEMA_PROCESSING = yaml.safe_load(resource_string(__package__, 'message_processing.schema.yml')) +MESSAGE_SCHEMA_RESULT = yaml.safe_load(resource_string(__package__, 'message_result.schema.yml')) +OCRD_TOOL_SCHEMA = yaml.safe_load(resource_string(__package__, 'ocrd_tool.schema.yml')) RESOURCE_LIST_SCHEMA = { 'type': 'object', 'additionalProperties': False, @@ -32,7 +32,7 @@ '^ocrd-.*': OCRD_TOOL_SCHEMA['properties']['tools']['patternProperties']['ocrd-.*']['properties']['resources'] } } -OCRD_BAGIT_PROFILE = yaml.safe_load(resource_string(__name__, 'bagit-profile.yml')) +OCRD_BAGIT_PROFILE = yaml.safe_load(resource_string(__package__, 'bagit-profile.yml')) BAGIT_TXT = 'BagIt-Version: 1.0\nTag-File-Character-Encoding: UTF-8' FILE_GROUP_PREFIX = 'OCR-D-' @@ -42,5 +42,5 @@ XSD_METS_URL = 'https://www.loc.gov/standards/mets/mets.xsd' XSD_PAGE_URL = 'http://www.primaresearch.org/schema/PAGE/gts/pagecontent/2019-07-15/pagecontent.xsd' XSD_PATHS = {} -XSD_PATHS[XSD_METS_URL] = resource_filename(__name__, 'mets.xsd') -XSD_PATHS[XSD_PAGE_URL] = resource_filename(__name__, 'page.xsd') +XSD_PATHS[XSD_METS_URL] = resource_filename(__package__, 'mets.xsd') +XSD_PATHS[XSD_PAGE_URL] = resource_filename(__package__, 'page.xsd') diff --git a/tests/cli/test_bashlib.py b/tests/cli/test_bashlib.py index 8a6e40b029..74a623d1b6 100644 --- a/tests/cli/test_bashlib.py +++ b/tests/cli/test_bashlib.py @@ -1,6 +1,5 @@ from tests.base import CapturingTestCase as TestCase, main, assets, copy_of_directory -from pkg_resources import parse_version import os, sys import traceback import subprocess diff --git a/tests/test_resource_manager.py b/tests/test_resource_manager.py index fb6db3adc4..221b0a3af1 100644 --- a/tests/test_resource_manager.py +++ b/tests/test_resource_manager.py @@ -21,7 +21,8 @@ def test_resources_manager_config_default(monkeypatch, tmp_path): # arrange monkeypatch.setenv('HOME', str(tmp_path)) - monkeypatch.delenv('XDG_CONFIG_HOME', raising=False) + if 'XDG_CONFIG_HOME' in os.environ: + monkeypatch.delenv('XDG_CONFIG_HOME', raising=False) # act mgr = OcrdResourceManager() @@ -48,9 +49,9 @@ def test_resources_manager_config_default(monkeypatch, tmp_path): def test_resources_manager_from_environment(tmp_path, monkeypatch): # arrange - monkeypatch.setenv('XDG_CONFIG_HOME', tmp_path) - monkeypatch.setenv('XDG_DATA_HOME', tmp_path) - monkeypatch.setenv('HOME', tmp_path) + monkeypatch.setenv('XDG_CONFIG_HOME', str(tmp_path)) + monkeypatch.setenv('XDG_DATA_HOME', str(tmp_path)) + monkeypatch.setenv('HOME', str(tmp_path)) # act mgr = OcrdResourceManager() diff --git a/tests/validator/test_xsd_validator.py b/tests/validator/test_xsd_validator.py index 4d4deadc15..d0150338dd 100644 --- a/tests/validator/test_xsd_validator.py +++ b/tests/validator/test_xsd_validator.py @@ -25,7 +25,7 @@ def test_constructor(self): def test_mets_empty(self): with TemporaryDirectory() as tempdir: mets_path = Path(tempdir, 'mets.xml') - mets_path.write_bytes(METS_XML_EMPTY) + mets_path.write_text(METS_XML_EMPTY) report = XsdMetsValidator.validate(mets_path) self.assertEqual(len(report.errors), 2) self.assertEqual(report.errors[0], From c69b334dba3f1ff8e5a0f639107fc3a1f81be784 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Wed, 24 Jan 2024 16:12:05 +0100 Subject: [PATCH 100/142] add PS run workflow test --- tests/network/docker-compose.yml | 11 +++++---- tests/network/test_processing_server.py | 31 +++++++++++++++++++++---- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/tests/network/docker-compose.yml b/tests/network/docker-compose.yml index 02a984bce1..a732026221 100644 --- a/tests/network/docker-compose.yml +++ b/tests/network/docker-compose.yml @@ -50,7 +50,7 @@ services: context: ../../ dockerfile: Dockerfile args: - BASE_IMAGE: 'ubuntu:20.04' + BASE_IMAGE: 'ubuntu:22.04' target: ocrd_core_test hostname: processing-server-host container_name: ocrd_network_processing_server @@ -75,7 +75,8 @@ services: volumes: - "/tmp/ocrd_network_logs:/tmp/ocrd_network_logs" - "/tmp/ocrd_network_sockets:/tmp/ocrd_network_sockets" - - "../assets:/tmp/assets" + - "../assets/kant_aufklaerung_1784:/tmp/assets/kant_aufklaerung_1784" + - "./dummy-workflow.txt:/tmp/assets/dummy-workflow.txt" - "./ocrd_logging.conf:/etc/ocrd_logging.conf" command: | /bin/bash -c "echo -e \" @@ -107,7 +108,8 @@ services: volumes: - "/tmp/ocrd_network_logs:/tmp/ocrd_network_logs" - "/tmp/ocrd_network_sockets:/tmp/ocrd_network_sockets" - - "../assets:/tmp/assets" + - "../assets/kant_aufklaerung_1784:/tmp/assets/kant_aufklaerung_1784" + - "./dummy-workflow.txt:/tmp/assets/dummy-workflow.txt" - "./ocrd_logging.conf:/etc/ocrd_logging.conf" command: > ocrd-dummy worker @@ -125,7 +127,8 @@ services: volumes: - "/tmp/ocrd_network_logs:/tmp/ocrd_network_logs" - "/tmp/ocrd_network_sockets:/tmp/ocrd_network_sockets" - - "../assets:/tmp/assets" + - "../assets/kant_aufklaerung_1784:/tmp/assets/kant_aufklaerung_1784" + - "./dummy-workflow.txt:/tmp/assets/dummy-workflow.txt" - "./ocrd_logging.conf:/etc/ocrd_logging.conf" environment: DB_NAME: ocrd_network_test diff --git a/tests/network/test_processing_server.py b/tests/network/test_processing_server.py index 33cbbe6a69..35e4d9aa45 100644 --- a/tests/network/test_processing_server.py +++ b/tests/network/test_processing_server.py @@ -22,12 +22,13 @@ def test_processing_server_deployed_processors(): response = get(test_url) processors = response.json() assert response.status_code == 200, \ - f'Processing server is not reachable on: {test_url}, {response.status_code}' + f'Processing server: {test_url}, {response.status_code}' assert processors == [], f'Mismatch in deployed processors' -def test_processing_server_processing_request(): - # Note: the path_to_mets is volume mapped +# TODO: Still failing test with internal error 500 +def _test_processing_server_processing_request(): + # Note: the used path is volume mapped test_processing_job_input = { "path_to_mets": "/tmp/assets/kant_aufklaerung_1784/data/mets.xml", "input_file_grps": ['OCR-D-IMG'], @@ -37,6 +38,26 @@ def test_processing_server_processing_request(): } test_processor = 'ocrd-dummy' test_url = f'{PROCESSING_SERVER_URL}/processor/run/{test_processor}' - response = post(url=test_url, data=test_processing_job_input) + response = post( + url=test_url, + headers={"accept": "application/json"}, + json=test_processing_job_input + ) assert response.status_code == 200, \ - f'Processing server is not reachable on: {test_url}, {response.status_code}' + f'Processing server: {test_url}, {response.status_code}' + + +def test_processing_server_workflow_request(): + # Note: the used paths are volume mapped + path_to_mets = "/tmp/assets/kant_aufklaerung_1784/data/mets.xml" + path_to_dummy_wf = "/tmp/assets/dummy-workflow.txt" + + test_url = f"{PROCESSING_SERVER_URL}/workflow?mets_path={path_to_mets}&page_wise=False" + response = post( + url=test_url, + files={"workflow": open(path_to_dummy_wf, 'rb')} + ) + assert response.status_code == 201, \ + f'Processing server: {test_url}, {response.status_code}' + + # TODO: Check workflow status here From 6b52c21671c7c0028bea18ea33316ad96cdf1e79 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Thu, 25 Jan 2024 12:18:24 +0100 Subject: [PATCH 101/142] include assets in ocrd_core_test --- Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 9178e65c66..e87800bf73 100644 --- a/Dockerfile +++ b/Dockerfile @@ -45,9 +45,10 @@ CMD ["/usr/local/bin/ocrd", "--help"] FROM ocrd_core_base as ocrd_core_test WORKDIR /build-ocrd +COPY Makefile . +RUN make assets COPY tests ./tests COPY .gitmodules . -COPY Makefile . COPY requirements_test.txt . RUN pip install -r requirements_test.txt From cc2fca71b9575c54560154e4c41b9c391b03ce3b Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Thu, 25 Jan 2024 12:32:16 +0100 Subject: [PATCH 102/142] revert and add make test again --- .github/workflows/integration-test.yml | 2 +- Dockerfile | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index 1915a4f3d9..8b0cf6e89e 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -46,4 +46,4 @@ jobs: make install deps-test - name: Test network integration with pytest run: | - make integration-test + make test integration-test diff --git a/Dockerfile b/Dockerfile index e87800bf73..9178e65c66 100644 --- a/Dockerfile +++ b/Dockerfile @@ -45,10 +45,9 @@ CMD ["/usr/local/bin/ocrd", "--help"] FROM ocrd_core_base as ocrd_core_test WORKDIR /build-ocrd -COPY Makefile . -RUN make assets COPY tests ./tests COPY .gitmodules . +COPY Makefile . COPY requirements_test.txt . RUN pip install -r requirements_test.txt From da59c5f7eaf84f33e967629f44acf5cffedd5710 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Thu, 25 Jan 2024 12:38:27 +0100 Subject: [PATCH 103/142] revert previous commit --- .github/workflows/integration-test.yml | 2 +- Dockerfile | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index 8b0cf6e89e..1915a4f3d9 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -46,4 +46,4 @@ jobs: make install deps-test - name: Test network integration with pytest run: | - make test integration-test + make integration-test diff --git a/Dockerfile b/Dockerfile index 9178e65c66..e87800bf73 100644 --- a/Dockerfile +++ b/Dockerfile @@ -45,9 +45,10 @@ CMD ["/usr/local/bin/ocrd", "--help"] FROM ocrd_core_base as ocrd_core_test WORKDIR /build-ocrd +COPY Makefile . +RUN make assets COPY tests ./tests COPY .gitmodules . -COPY Makefile . COPY requirements_test.txt . RUN pip install -r requirements_test.txt From 1fe515555fb41a0de1abd91eb70277c1cc3810d9 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Thu, 25 Jan 2024 12:44:52 +0100 Subject: [PATCH 104/142] temp disable mac-os --- .github/workflows/integration-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index 1915a4f3d9..6b4791a37b 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -22,7 +22,7 @@ jobs: - '3.11' os: - ubuntu-22.04 - - macos-latest + # - macos-latest steps: - uses: actions/checkout@v3 From a57b8ead1558641192fdeb75298db526e415b653 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Thu, 25 Jan 2024 13:41:37 +0100 Subject: [PATCH 105/142] add simple wf status check --- src/ocrd_network/processing_server.py | 44 +++++++++++++++++++++++++ tests/network/test_processing_server.py | 26 ++++++++++++--- 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/src/ocrd_network/processing_server.py b/src/ocrd_network/processing_server.py index f60201311e..d4a28e58ec 100644 --- a/src/ocrd_network/processing_server.py +++ b/src/ocrd_network/processing_server.py @@ -228,6 +228,15 @@ def __init__(self, config_path: str, host: str, port: int) -> None: summary='Get information about a workflow run', ) + self.router.add_api_route( + path='/workflow/job-simple/{workflow_job_id}', + endpoint=self.get_workflow_info_simple, + methods=['GET'], + tags=['workflow', 'processing'], + status_code=status.HTTP_200_OK, + summary='Get simplified overall job status', + ) + self.router.add_api_route( path='/workflow', endpoint=self.upload_workflow, @@ -911,6 +920,41 @@ async def get_workflow_info(self, workflow_job_id) -> Dict: }) return res + """ + Simplified version of the `get_workflow_info` that returns a single state for the entire workflow. + - If a single processing job fails, the entire workflow job status is set to FAILED. + - If there are any processing jobs running, regardless of other states, such as QUEUED and CACHED, + the entire workflow job status is set to RUNNING. + - If all processing jobs has finished successfully, only then the workflow job status is set to SUCCESS + """ + async def get_workflow_info_simple(self, workflow_job_id) -> Dict[str, StateEnum]: + """ Return list of a workflow's processor jobs + """ + try: + workflow_job = await db_get_workflow_job(workflow_job_id) + except ValueError: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, + detail=f"Workflow-Job with id: {workflow_job_id} not found") + job_ids: List[str] = [job_id for lst in workflow_job.processing_job_ids.values() for job_id in lst] + jobs = await db_get_processing_jobs(job_ids) + + workflow_job_state = "UNSET" + success_jobs = 0 + for job in jobs: + if job.state == StateEnum.cached or job.state == StateEnum.queued: + continue + if job.state == StateEnum.failed or job.state == StateEnum.cancelled: + workflow_job_state = StateEnum.failed + break + if job.state == StateEnum.running: + workflow_job_state = StateEnum.running + if job.state == StateEnum.success: + success_jobs += 1 + # if all jobs succeeded + if len(job_ids) == success_jobs: + workflow_job_state = StateEnum.success + return {"wf_job_state": workflow_job_state} + async def upload_workflow(self, workflow: UploadFile) -> Dict: """ Store a script for a workflow in the database """ diff --git a/tests/network/test_processing_server.py b/tests/network/test_processing_server.py index 35e4d9aa45..6716ddf503 100644 --- a/tests/network/test_processing_server.py +++ b/tests/network/test_processing_server.py @@ -1,6 +1,8 @@ +from time import sleep from requests import get, post from ocrd_utils.config import config from ocrd_network import NETWORK_AGENT_WORKER +from ocrd_network.models import StateEnum PROCESSING_SERVER_URL = config.PROCESSING_SERVER_URL @@ -52,12 +54,28 @@ def test_processing_server_workflow_request(): path_to_mets = "/tmp/assets/kant_aufklaerung_1784/data/mets.xml" path_to_dummy_wf = "/tmp/assets/dummy-workflow.txt" - test_url = f"{PROCESSING_SERVER_URL}/workflow?mets_path={path_to_mets}&page_wise=False" + # submit the workflow job + test_url = f"{PROCESSING_SERVER_URL}/workflow/run?mets_path={path_to_mets}&page_wise=False" response = post( url=test_url, files={"workflow": open(path_to_dummy_wf, 'rb')} ) - assert response.status_code == 201, \ - f'Processing server: {test_url}, {response.status_code}' + assert response.status_code == 200, f"Processing server: {test_url}, {response.status_code}" + + wf_job_id = response.json()["job_id"] + assert wf_job_id - # TODO: Check workflow status here + # check simplified workflow status till timeout + tries = 50 + wait_between_tries = 30 + wf_job_state = None + test_url = f"{PROCESSING_SERVER_URL}/workflow/job-simple/{wf_job_id}" + while tries > 0: + sleep(wait_between_tries) + response = post(url=test_url) + assert response.status_code == 200, f"Processing server: {test_url}, {response.status_code}" + wf_job_state = response.json()["wf_job_state"] + if wf_job_state == StateEnum.success or wf_job_state == StateEnum.failed: + break + tries -= 1 + assert wf_job_state == "SUCCESS" From ff5e41e4f8d708f0a8a998e4bd287bccd7c94001 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Thu, 25 Jan 2024 14:05:55 +0100 Subject: [PATCH 106/142] fix post -> get --- tests/network/test_processing_server.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/network/test_processing_server.py b/tests/network/test_processing_server.py index 6716ddf503..f91aa91caa 100644 --- a/tests/network/test_processing_server.py +++ b/tests/network/test_processing_server.py @@ -55,7 +55,7 @@ def test_processing_server_workflow_request(): path_to_dummy_wf = "/tmp/assets/dummy-workflow.txt" # submit the workflow job - test_url = f"{PROCESSING_SERVER_URL}/workflow/run?mets_path={path_to_mets}&page_wise=False" + test_url = f"{PROCESSING_SERVER_URL}/workflow/run?mets_path={path_to_mets}&page_wise=True" response = post( url=test_url, files={"workflow": open(path_to_dummy_wf, 'rb')} @@ -72,7 +72,7 @@ def test_processing_server_workflow_request(): test_url = f"{PROCESSING_SERVER_URL}/workflow/job-simple/{wf_job_id}" while tries > 0: sleep(wait_between_tries) - response = post(url=test_url) + response = get(url=test_url) assert response.status_code == 200, f"Processing server: {test_url}, {response.status_code}" wf_job_state = response.json()["wf_job_state"] if wf_job_state == StateEnum.success or wf_job_state == StateEnum.failed: From 2c7bea135063678fdbae86a6939ad53fa44e4250 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Thu, 25 Jan 2024 14:36:26 +0100 Subject: [PATCH 107/142] add header to request --- tests/network/test_processing_server.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/network/test_processing_server.py b/tests/network/test_processing_server.py index f91aa91caa..4a18f6042f 100644 --- a/tests/network/test_processing_server.py +++ b/tests/network/test_processing_server.py @@ -58,6 +58,7 @@ def test_processing_server_workflow_request(): test_url = f"{PROCESSING_SERVER_URL}/workflow/run?mets_path={path_to_mets}&page_wise=True" response = post( url=test_url, + headers={"accept": "application/json"}, files={"workflow": open(path_to_dummy_wf, 'rb')} ) assert response.status_code == 200, f"Processing server: {test_url}, {response.status_code}" From 6ed08e7c7ca79101cde5476c2b7490b3fb57f1fe Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Fri, 26 Jan 2024 12:26:06 +0100 Subject: [PATCH 108/142] fix workflow run test --- Dockerfile | 1 + Makefile | 3 ++- src/ocrd_utils/ocrd_logging.conf | 2 +- tests/network/docker-compose.yml | 34 +++++++++++++++---------- tests/network/dummy-workflow.txt | 2 +- tests/network/test_processing_server.py | 13 +++++++--- 6 files changed, 34 insertions(+), 21 deletions(-) diff --git a/Dockerfile b/Dockerfile index e87800bf73..95130fdd4a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -51,6 +51,7 @@ COPY tests ./tests COPY .gitmodules . COPY requirements_test.txt . RUN pip install -r requirements_test.txt +RUN mkdir /ocrd-data && chmod 777 /ocrd-data CMD ["yes"] # CMD ["make", "test", "integration-test"] diff --git a/Makefile b/Makefile index 8f58c614a6..158415d424 100644 --- a/Makefile +++ b/Makefile @@ -221,7 +221,8 @@ INTEGRATION_TEST_IN_DOCKER = docker exec core_test integration-test: docker compose -f tests/network/docker-compose.yml up -d sleep 20 - $(INTEGRATION_TEST_IN_DOCKER) pytest -k 'test_rmq or test_db or test_processing_server' + # TODO: Remove the `-s` flag before finalizing the PR + $(INTEGRATION_TEST_IN_DOCKER) pytest -k 'test_rmq or test_db or test_processing_server' -v -s docker compose -f tests/network/docker-compose.yml down --remove-orphans benchmark: diff --git a/src/ocrd_utils/ocrd_logging.conf b/src/ocrd_utils/ocrd_logging.conf index 6f1a28ace5..8650c7d473 100644 --- a/src/ocrd_utils/ocrd_logging.conf +++ b/src/ocrd_utils/ocrd_logging.conf @@ -133,7 +133,7 @@ args=('ocrd.log','a+') [handler_processingServerHandler] class=FileHandler formatter=defaultFormatter -args=('/tmp/ocrd_processing_server.log','a+') +args=('/ocrd-data/ocrd_processing_server.log','a+') # # default log format conforming to OCR-D (https://ocr-d.de/en/spec/cli#logging) diff --git a/tests/network/docker-compose.yml b/tests/network/docker-compose.yml index a732026221..fcdd1e5f53 100644 --- a/tests/network/docker-compose.yml +++ b/tests/network/docker-compose.yml @@ -67,16 +67,18 @@ services: DB_NAME: ocrd_network_test DB_URL: mongodb://network_test:network_test@mongodb-docker-host.ocrd_network_test:27017 RABBITMQ_URL: amqp://network_test:network_test@rabbitmq-docker-host.ocrd_network_test:5672 + OCRD_NETWORK_LOGS_ROOT_DIR: /ocrd-data/ocrd_network_logs + OCRD_NETWORK_SOCKETS_ROOT_DIR: /ocrd-data/ocrd_network_sockets healthcheck: test: curl -f http://processing-server-host.ocrd_network_test:8000/ interval: 1s timeout: 3s retries: 30 volumes: - - "/tmp/ocrd_network_logs:/tmp/ocrd_network_logs" - - "/tmp/ocrd_network_sockets:/tmp/ocrd_network_sockets" - - "../assets/kant_aufklaerung_1784:/tmp/assets/kant_aufklaerung_1784" - - "./dummy-workflow.txt:/tmp/assets/dummy-workflow.txt" + - "/tmp/ocrd_network_logs:/ocrd-data/ocrd_network_logs" + - "/tmp/ocrd_network_sockets:/ocrd-data/ocrd_network_sockets" + - "../assets/kant_aufklaerung_1784:/ocrd-data/assets/kant_aufklaerung_1784" + - "./dummy-workflow.txt:/ocrd-data/assets/dummy-workflow.txt" - "./ocrd_logging.conf:/etc/ocrd_logging.conf" command: | /bin/bash -c "echo -e \" @@ -105,11 +107,13 @@ services: condition: service_healthy networks: - ocrd_network_test + environment: + OCRD_NETWORK_LOGS_ROOT_DIR: /ocrd-data/ocrd_network_logs volumes: - - "/tmp/ocrd_network_logs:/tmp/ocrd_network_logs" - - "/tmp/ocrd_network_sockets:/tmp/ocrd_network_sockets" - - "../assets/kant_aufklaerung_1784:/tmp/assets/kant_aufklaerung_1784" - - "./dummy-workflow.txt:/tmp/assets/dummy-workflow.txt" + - "/tmp/ocrd_network_logs:/ocrd-data/ocrd_network_logs" + - "/tmp/ocrd_network_sockets:/ocrd-data/ocrd_network_sockets" + - "../assets/kant_aufklaerung_1784:/ocrd-data/assets/kant_aufklaerung_1784" + - "./dummy-workflow.txt:/ocrd-data/assets/dummy-workflow.txt" - "./ocrd_logging.conf:/etc/ocrd_logging.conf" command: > ocrd-dummy worker @@ -124,14 +128,16 @@ services: condition: service_healthy networks: - ocrd_network_test - volumes: - - "/tmp/ocrd_network_logs:/tmp/ocrd_network_logs" - - "/tmp/ocrd_network_sockets:/tmp/ocrd_network_sockets" - - "../assets/kant_aufklaerung_1784:/tmp/assets/kant_aufklaerung_1784" - - "./dummy-workflow.txt:/tmp/assets/dummy-workflow.txt" - - "./ocrd_logging.conf:/etc/ocrd_logging.conf" environment: DB_NAME: ocrd_network_test DB_URL: mongodb://network_test:network_test@mongodb-docker-host.ocrd_network_test:27017 PROCESSING_SERVER_URL: http://processing-server-host.ocrd_network_test:8000 RABBITMQ_URL: amqp://network_test:network_test@rabbitmq-docker-host.ocrd_network_test:5672 + OCRD_NETWORK_LOGS_ROOT_DIR: /ocrd-data/ocrd_network_logs + OCRD_NETWORK_SOCKETS_ROOT_DIR: /ocrd-data/ocrd_network_sockets + volumes: + - "/tmp/ocrd_network_logs:/ocrd-data/ocrd_network_logs" + - "/tmp/ocrd_network_sockets:/ocrd-data/ocrd_network_sockets" + - "../assets/kant_aufklaerung_1784:/ocrd-data/assets/kant_aufklaerung_1784" + - "./dummy-workflow.txt:/ocrd-data/assets/dummy-workflow.txt" + - "./ocrd_logging.conf:/etc/ocrd_logging.conf" diff --git a/tests/network/dummy-workflow.txt b/tests/network/dummy-workflow.txt index 2de01dc67b..8d4e04d651 100644 --- a/tests/network/dummy-workflow.txt +++ b/tests/network/dummy-workflow.txt @@ -1,3 +1,3 @@ -dummy -I DEFAULT -O OCR-D-DUMMY1 +dummy -I OCR-D-IMG -O OCR-D-DUMMY1 dummy -I OCR-D-DUMMY1 -O OCR-D-DUMMY2 dummy -I OCR-D-DUMMY2 -O OCR-D-DUMMY3 diff --git a/tests/network/test_processing_server.py b/tests/network/test_processing_server.py index 4a18f6042f..cd05ae6823 100644 --- a/tests/network/test_processing_server.py +++ b/tests/network/test_processing_server.py @@ -32,7 +32,7 @@ def test_processing_server_deployed_processors(): def _test_processing_server_processing_request(): # Note: the used path is volume mapped test_processing_job_input = { - "path_to_mets": "/tmp/assets/kant_aufklaerung_1784/data/mets.xml", + "path_to_mets": "/ocrd-data/assets/kant_aufklaerung_1784/data/mets.xml", "input_file_grps": ['OCR-D-IMG'], "output_file_grps": ['OCR-D-DUMMY'], "agent_type": NETWORK_AGENT_WORKER, @@ -45,14 +45,16 @@ def _test_processing_server_processing_request(): headers={"accept": "application/json"}, json=test_processing_job_input ) + # TODO: Remove print before finalizing the PR + print(response.json()) assert response.status_code == 200, \ f'Processing server: {test_url}, {response.status_code}' -def test_processing_server_workflow_request(): +def _test_processing_server_workflow_request(): # Note: the used paths are volume mapped - path_to_mets = "/tmp/assets/kant_aufklaerung_1784/data/mets.xml" - path_to_dummy_wf = "/tmp/assets/dummy-workflow.txt" + path_to_mets = "/ocrd-data/assets/kant_aufklaerung_1784/data/mets.xml" + path_to_dummy_wf = "/ocrd-data/assets/dummy-workflow.txt" # submit the workflow job test_url = f"{PROCESSING_SERVER_URL}/workflow/run?mets_path={path_to_mets}&page_wise=True" @@ -61,6 +63,9 @@ def test_processing_server_workflow_request(): headers={"accept": "application/json"}, files={"workflow": open(path_to_dummy_wf, 'rb')} ) + + # TODO: Remove print before finalizing the PR + print(response.json()) assert response.status_code == 200, f"Processing server: {test_url}, {response.status_code}" wf_job_id = response.json()["job_id"] From 0dcd789c01dc45a4cb3ce565a249747c09d7cb27 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Fri, 26 Jan 2024 12:26:34 +0100 Subject: [PATCH 109/142] enable the test --- tests/network/test_processing_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/network/test_processing_server.py b/tests/network/test_processing_server.py index cd05ae6823..d2442e2244 100644 --- a/tests/network/test_processing_server.py +++ b/tests/network/test_processing_server.py @@ -51,7 +51,7 @@ def _test_processing_server_processing_request(): f'Processing server: {test_url}, {response.status_code}' -def _test_processing_server_workflow_request(): +def test_processing_server_workflow_request(): # Note: the used paths are volume mapped path_to_mets = "/ocrd-data/assets/kant_aufklaerung_1784/data/mets.xml" path_to_dummy_wf = "/ocrd-data/assets/dummy-workflow.txt" From bc160a6ffc5cd7089021983f7aaea6ed0c3f3a0c Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Fri, 26 Jan 2024 12:39:48 +0100 Subject: [PATCH 110/142] fix the ocrd_logging.conf --- src/ocrd_utils/ocrd_logging.conf | 2 +- tests/network/ocrd_logging.conf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ocrd_utils/ocrd_logging.conf b/src/ocrd_utils/ocrd_logging.conf index 8650c7d473..6f1a28ace5 100644 --- a/src/ocrd_utils/ocrd_logging.conf +++ b/src/ocrd_utils/ocrd_logging.conf @@ -133,7 +133,7 @@ args=('ocrd.log','a+') [handler_processingServerHandler] class=FileHandler formatter=defaultFormatter -args=('/ocrd-data/ocrd_processing_server.log','a+') +args=('/tmp/ocrd_processing_server.log','a+') # # default log format conforming to OCR-D (https://ocr-d.de/en/spec/cli#logging) diff --git a/tests/network/ocrd_logging.conf b/tests/network/ocrd_logging.conf index 5ee5e015d8..49391334e4 100644 --- a/tests/network/ocrd_logging.conf +++ b/tests/network/ocrd_logging.conf @@ -133,7 +133,7 @@ args=('ocrd.log','a+') [handler_processingServerHandler] class=FileHandler formatter=defaultFormatter -args=('/tmp/ocrd_processing_server.log','a+') +args=('/ocrd-data/ocrd_processing_server.log','a+') # # default log format conforming to OCR-D (https://ocr-d.de/en/spec/cli#logging) From 00b983be882305e04f6f1d1ecce2cfc0a4c7b202 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Fri, 26 Jan 2024 13:23:12 +0100 Subject: [PATCH 111/142] use internal assets for testing --- tests/network/docker-compose.yml | 3 --- tests/network/ocrd_logging.conf | 2 +- tests/network/test_processing_server.py | 9 +++++---- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/tests/network/docker-compose.yml b/tests/network/docker-compose.yml index fcdd1e5f53..0d65cf6a45 100644 --- a/tests/network/docker-compose.yml +++ b/tests/network/docker-compose.yml @@ -77,7 +77,6 @@ services: volumes: - "/tmp/ocrd_network_logs:/ocrd-data/ocrd_network_logs" - "/tmp/ocrd_network_sockets:/ocrd-data/ocrd_network_sockets" - - "../assets/kant_aufklaerung_1784:/ocrd-data/assets/kant_aufklaerung_1784" - "./dummy-workflow.txt:/ocrd-data/assets/dummy-workflow.txt" - "./ocrd_logging.conf:/etc/ocrd_logging.conf" command: | @@ -112,7 +111,6 @@ services: volumes: - "/tmp/ocrd_network_logs:/ocrd-data/ocrd_network_logs" - "/tmp/ocrd_network_sockets:/ocrd-data/ocrd_network_sockets" - - "../assets/kant_aufklaerung_1784:/ocrd-data/assets/kant_aufklaerung_1784" - "./dummy-workflow.txt:/ocrd-data/assets/dummy-workflow.txt" - "./ocrd_logging.conf:/etc/ocrd_logging.conf" command: > @@ -138,6 +136,5 @@ services: volumes: - "/tmp/ocrd_network_logs:/ocrd-data/ocrd_network_logs" - "/tmp/ocrd_network_sockets:/ocrd-data/ocrd_network_sockets" - - "../assets/kant_aufklaerung_1784:/ocrd-data/assets/kant_aufklaerung_1784" - "./dummy-workflow.txt:/ocrd-data/assets/dummy-workflow.txt" - "./ocrd_logging.conf:/etc/ocrd_logging.conf" diff --git a/tests/network/ocrd_logging.conf b/tests/network/ocrd_logging.conf index 49391334e4..ee081b7b56 100644 --- a/tests/network/ocrd_logging.conf +++ b/tests/network/ocrd_logging.conf @@ -133,7 +133,7 @@ args=('ocrd.log','a+') [handler_processingServerHandler] class=FileHandler formatter=defaultFormatter -args=('/ocrd-data/ocrd_processing_server.log','a+') +args=('/ocrd-data/ocrd_processing_server_conf.log','a+') # # default log format conforming to OCR-D (https://ocr-d.de/en/spec/cli#logging) diff --git a/tests/network/test_processing_server.py b/tests/network/test_processing_server.py index d2442e2244..c24e537ec6 100644 --- a/tests/network/test_processing_server.py +++ b/tests/network/test_processing_server.py @@ -3,6 +3,7 @@ from ocrd_utils.config import config from ocrd_network import NETWORK_AGENT_WORKER from ocrd_network.models import StateEnum +from tests.base import assets PROCESSING_SERVER_URL = config.PROCESSING_SERVER_URL @@ -30,9 +31,9 @@ def test_processing_server_deployed_processors(): # TODO: Still failing test with internal error 500 def _test_processing_server_processing_request(): - # Note: the used path is volume mapped + path_to_mets = assets.path_to('kant_aufklaerung_1784/data/mets.xml') test_processing_job_input = { - "path_to_mets": "/ocrd-data/assets/kant_aufklaerung_1784/data/mets.xml", + "path_to_mets": path_to_mets, "input_file_grps": ['OCR-D-IMG'], "output_file_grps": ['OCR-D-DUMMY'], "agent_type": NETWORK_AGENT_WORKER, @@ -52,9 +53,9 @@ def _test_processing_server_processing_request(): def test_processing_server_workflow_request(): - # Note: the used paths are volume mapped - path_to_mets = "/ocrd-data/assets/kant_aufklaerung_1784/data/mets.xml" + # Note: the used workflow path is volume mapped path_to_dummy_wf = "/ocrd-data/assets/dummy-workflow.txt" + path_to_mets = assets.path_to('kant_aufklaerung_1784/data/mets.xml') # submit the workflow job test_url = f"{PROCESSING_SERVER_URL}/workflow/run?mets_path={path_to_mets}&page_wise=True" From d4a46f658489d017c6ae93065c4f9d950c138ce8 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Fri, 26 Jan 2024 13:47:26 +0100 Subject: [PATCH 112/142] temp disable macos and u20 wfs --- .github/workflows/integration-test.yml | 2 +- .github/workflows/unit-test.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index 6b4791a37b..d0834500f8 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -22,7 +22,7 @@ jobs: - '3.11' os: - ubuntu-22.04 - # - macos-latest + # - macos-latest TODO: Activate back before finalizing the PR steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index cfe282cd54..6aac7aa262 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -25,8 +25,8 @@ jobs: - '3.11' os: - ubuntu-22.04 - - ubuntu-20.04 - - macos-latest + # - ubuntu-20.04 TODO: Activate back before finalizing the PR + # - macos-latest TODO: Activate back before finalizing the PR steps: - uses: actions/checkout@v3 From ca6e7ebf6bac9fd569a4329f67f96c21a63dec85 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Fri, 26 Jan 2024 14:51:37 +0100 Subject: [PATCH 113/142] fix and refactor processing test --- src/ocrd_network/processing_server.py | 30 +++++++++----- tests/network/test_processing_server.py | 54 ++++++++++++++++++++++--- 2 files changed, 69 insertions(+), 15 deletions(-) diff --git a/src/ocrd_network/processing_server.py b/src/ocrd_network/processing_server.py index d4a28e58ec..a23423d326 100644 --- a/src/ocrd_network/processing_server.py +++ b/src/ocrd_network/processing_server.py @@ -472,15 +472,25 @@ async def push_processor_job(self, processor_name: str, data: PYJobInput) -> PYJ validate_job_input(self.log, data.processor_name, ocrd_tool, data) - db_workspace = await db_get_workspace( - workspace_id=data.workspace_id, - workspace_mets_path=data.path_to_mets - ) - if not db_workspace: - raise HTTPException( - status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, - detail=f"Workspace with id: {data.workspace_id} or path: {data.path_to_mets} not found" - ) + if data.workspace_id: + try: + db_workspace = await db_get_workspace(workspace_id=data.workspace_id) + except ValueError as error: + self.log.exception(error) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"Workspace with id `{data.workspace_id}` not found in the DB." + ) + else: # data.path_to_mets provided instead + try: + # TODO: Reconsider and refactor this. Core cannot create workspaces by api, but processing-server needs + # the workspace in the database. Here the workspace is created if the path is available locally and + # not existing in the DB - since it has not been uploaded through the Workspace Server. + await db_create_workspace(data.path_to_mets) + except FileNotFoundError: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, + detail=f"Mets file not existing: {data.path_to_mets}") + workspace_key = data.path_to_mets if data.path_to_mets else data.workspace_id # initialize the request counter for the workspace_key self.cache_processing_requests.update_request_counter(workspace_key=workspace_key, by_value=0) @@ -953,7 +963,7 @@ async def get_workflow_info_simple(self, workflow_job_id) -> Dict[str, StateEnum # if all jobs succeeded if len(job_ids) == success_jobs: workflow_job_state = StateEnum.success - return {"wf_job_state": workflow_job_state} + return {"state": workflow_job_state} async def upload_workflow(self, workflow: UploadFile) -> Dict: """ Store a script for a workflow in the database diff --git a/tests/network/test_processing_server.py b/tests/network/test_processing_server.py index c24e537ec6..722a120adf 100644 --- a/tests/network/test_processing_server.py +++ b/tests/network/test_processing_server.py @@ -8,6 +8,18 @@ PROCESSING_SERVER_URL = config.PROCESSING_SERVER_URL +def poll_till_timeout_fail_or_success(test_url: str, tries: int, wait: int) -> StateEnum: + while tries > 0: + sleep(wait) + response = get(url=test_url) + assert response.status_code == 200, f"Processing server: {test_url}, {response.status_code}" + job_state = response.json()["state"] + if job_state == StateEnum.success or job_state == StateEnum.failed: + break + tries -= 1 + return job_state + + def test_processing_server_connectivity(): test_url = f'{PROCESSING_SERVER_URL}/' response = get(test_url) @@ -29,8 +41,7 @@ def test_processing_server_deployed_processors(): assert processors == [], f'Mismatch in deployed processors' -# TODO: Still failing test with internal error 500 -def _test_processing_server_processing_request(): +def test_processing_server_processing_request(): path_to_mets = assets.path_to('kant_aufklaerung_1784/data/mets.xml') test_processing_job_input = { "path_to_mets": path_to_mets, @@ -50,6 +61,32 @@ def _test_processing_server_processing_request(): print(response.json()) assert response.status_code == 200, \ f'Processing server: {test_url}, {response.status_code}' + processing_job_id = response.json()["job_id"] + assert processing_job_id + + job_state = poll_till_timeout_fail_or_success( + test_url=f"{PROCESSING_SERVER_URL}/processor/job/{processing_job_id}", + tries=10, + wait=10 + ) + assert job_state == StateEnum.success + + """ + # check processing job status till timeout + tries = 50 + wait_between_tries = 30 + processing_job_state = None + test_url = f"{PROCESSING_SERVER_URL}/processor/job/{processing_job_id}" + while tries > 0: + sleep(wait_between_tries) + response = get(url=test_url) + assert response.status_code == 200, f"Processing server: {test_url}, {response.status_code}" + processing_job_state = response.json()["state"] + if processing_job_state == StateEnum.success or processing_job_state == StateEnum.failed: + break + tries -= 1 + assert processing_job_state == StateEnum.success + """ def test_processing_server_workflow_request(): @@ -64,14 +101,20 @@ def test_processing_server_workflow_request(): headers={"accept": "application/json"}, files={"workflow": open(path_to_dummy_wf, 'rb')} ) - # TODO: Remove print before finalizing the PR print(response.json()) assert response.status_code == 200, f"Processing server: {test_url}, {response.status_code}" - wf_job_id = response.json()["job_id"] assert wf_job_id + job_state = poll_till_timeout_fail_or_success( + test_url=f"{PROCESSING_SERVER_URL}/workflow/job-simple/{wf_job_id}", + tries=30, + wait=10 + ) + assert job_state == StateEnum.success + + """ # check simplified workflow status till timeout tries = 50 wait_between_tries = 30 @@ -85,4 +128,5 @@ def test_processing_server_workflow_request(): if wf_job_state == StateEnum.success or wf_job_state == StateEnum.failed: break tries -= 1 - assert wf_job_state == "SUCCESS" + assert wf_job_state == StateEnum.success + """ From 68dc06b2a0e0f1a73a4b3ed9d95c7aa705aadcb4 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Fri, 26 Jan 2024 15:10:36 +0100 Subject: [PATCH 114/142] new job state - unset, refactor --- src/ocrd_network/models/job.py | 2 ++ src/ocrd_network/processing_server.py | 2 +- tests/network/test_processing_server.py | 35 +------------------------ 3 files changed, 4 insertions(+), 35 deletions(-) diff --git a/src/ocrd_network/models/job.py b/src/ocrd_network/models/job.py index e5230aa5fd..6cb31bfb92 100644 --- a/src/ocrd_network/models/job.py +++ b/src/ocrd_network/models/job.py @@ -20,6 +20,8 @@ class StateEnum(str, Enum): success = 'SUCCESS' # Processing job failed failed = 'FAILED' + # Processing job has not been assigned yet + unset = 'UNSET' class PYJobInput(BaseModel): diff --git a/src/ocrd_network/processing_server.py b/src/ocrd_network/processing_server.py index a23423d326..3c09e8a107 100644 --- a/src/ocrd_network/processing_server.py +++ b/src/ocrd_network/processing_server.py @@ -948,7 +948,7 @@ async def get_workflow_info_simple(self, workflow_job_id) -> Dict[str, StateEnum job_ids: List[str] = [job_id for lst in workflow_job.processing_job_ids.values() for job_id in lst] jobs = await db_get_processing_jobs(job_ids) - workflow_job_state = "UNSET" + workflow_job_state = StateEnum.unset success_jobs = 0 for job in jobs: if job.state == StateEnum.cached or job.state == StateEnum.queued: diff --git a/tests/network/test_processing_server.py b/tests/network/test_processing_server.py index 722a120adf..749122700f 100644 --- a/tests/network/test_processing_server.py +++ b/tests/network/test_processing_server.py @@ -9,6 +9,7 @@ def poll_till_timeout_fail_or_success(test_url: str, tries: int, wait: int) -> StateEnum: + job_state = StateEnum.unset while tries > 0: sleep(wait) response = get(url=test_url) @@ -71,23 +72,6 @@ def test_processing_server_processing_request(): ) assert job_state == StateEnum.success - """ - # check processing job status till timeout - tries = 50 - wait_between_tries = 30 - processing_job_state = None - test_url = f"{PROCESSING_SERVER_URL}/processor/job/{processing_job_id}" - while tries > 0: - sleep(wait_between_tries) - response = get(url=test_url) - assert response.status_code == 200, f"Processing server: {test_url}, {response.status_code}" - processing_job_state = response.json()["state"] - if processing_job_state == StateEnum.success or processing_job_state == StateEnum.failed: - break - tries -= 1 - assert processing_job_state == StateEnum.success - """ - def test_processing_server_workflow_request(): # Note: the used workflow path is volume mapped @@ -113,20 +97,3 @@ def test_processing_server_workflow_request(): wait=10 ) assert job_state == StateEnum.success - - """ - # check simplified workflow status till timeout - tries = 50 - wait_between_tries = 30 - wf_job_state = None - test_url = f"{PROCESSING_SERVER_URL}/workflow/job-simple/{wf_job_id}" - while tries > 0: - sleep(wait_between_tries) - response = get(url=test_url) - assert response.status_code == 200, f"Processing server: {test_url}, {response.status_code}" - wf_job_state = response.json()["wf_job_state"] - if wf_job_state == StateEnum.success or wf_job_state == StateEnum.failed: - break - tries -= 1 - assert wf_job_state == StateEnum.success - """ From a34bf19eeac8151fd48b45ff70639bb3db3dee10 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Fri, 26 Jan 2024 15:34:27 +0100 Subject: [PATCH 115/142] randomize ids --- tests/network/test_db.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/network/test_db.py b/tests/network/test_db.py index bc044812a2..4dd8f26999 100644 --- a/tests/network/test_db.py +++ b/tests/network/test_db.py @@ -1,3 +1,4 @@ +from datetime import datetime from hashlib import md5 from pathlib import Path from pytest import raises @@ -17,7 +18,7 @@ def test_db_processing_job_create(mongo_client): - job_id = 'test_job_id_1' + job_id = f'test_job_id_{datetime.now()}' db_created_processing_job = sync_db_create_processing_job( db_processing_job=DBProcessorJob( job_id=job_id, @@ -43,7 +44,7 @@ def test_db_processing_job_create(mongo_client): def test_db_processing_job_update(mongo_client): - job_id = 'test_job_id_2' + job_id = f'test_job_id_{datetime.now()}' db_created_processing_job = sync_db_create_processing_job( db_processing_job=DBProcessorJob( job_id=job_id, @@ -125,7 +126,7 @@ def create_db_model_workflow_script( def test_db_workflow_script_create(mongo_client): - workflow_id = 'test_workflow_1' + workflow_id = f'test_workflow_{datetime.now()}' db_model_workflow_script = create_db_model_workflow_script(workflow_id=workflow_id) db_created_workflow_script = sync_db_create_workflow_script( db_workflow_script=db_model_workflow_script @@ -140,7 +141,7 @@ def test_db_workflow_script_create(mongo_client): def test_db_find_workflow_script_by_content(mongo_client): - workflow_id = 'test_workflow_2' + workflow_id = f'test_workflow_{datetime.now()}' db_model_workflow_script = create_db_model_workflow_script(workflow_id=workflow_id) db_created_workflow_script = sync_db_create_workflow_script( db_workflow_script=db_model_workflow_script From 79670d7d9889d32024f377b00ec1d2713f0ab3fa Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Fri, 26 Jan 2024 15:43:55 +0100 Subject: [PATCH 116/142] replace sparklines library with a simple replacement --- requirements.txt | 1 - src/ocrd/processor/helpers.py | 5 ++--- src/ocrd_utils/__init__.py | 1 + src/ocrd_utils/constants.py | 21 +++++++++++++++++++++ src/ocrd_utils/str.py | 18 +++++++++++++++++- tests/test_utils.py | 7 +++++++ 6 files changed, 48 insertions(+), 5 deletions(-) diff --git a/requirements.txt b/requirements.txt index 9a9b217218..2a7a6f2f9a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -27,7 +27,6 @@ pyyaml requests < 2.30 requests_unixsocket shapely -sparklines >= 0.4.2 # tensorflow versions might require different versions uvicorn uvicorn>=0.17.6 diff --git a/src/ocrd/processor/helpers.py b/src/ocrd/processor/helpers.py index 209fa5f67d..f5b6010636 100644 --- a/src/ocrd/processor/helpers.py +++ b/src/ocrd/processor/helpers.py @@ -11,7 +11,7 @@ from click import wrap_text from ocrd.workspace import Workspace -from ocrd_utils import freeze_args, getLogger, config, setOverrideLogLevel, getLevelName +from ocrd_utils import freeze_args, getLogger, config, setOverrideLogLevel, getLevelName, sparkline __all__ = [ @@ -106,7 +106,6 @@ def run_processor( if any(x in config.OCRD_PROFILE for x in ['RSS', 'PSS']): backend = 'psutil_pss' if 'PSS' in config.OCRD_PROFILE else 'psutil' from memory_profiler import memory_usage - from sparklines import sparklines try: mem_usage = memory_usage(proc=processor.process, # only run process once @@ -123,7 +122,7 @@ def run_processor( chdir(old_cwd) mem_usage_values = [mem for mem, _ in mem_usage] mem_output = 'memory consumption: ' - mem_output += ''.join(sparklines(mem_usage_values)) + mem_output += sparkline(mem_usage_values) mem_output += ' max: %.2f MiB min: %.2f MiB' % (max(mem_usage_values), min(mem_usage_values)) logProfile.info(mem_output) else: diff --git a/src/ocrd_utils/__init__.py b/src/ocrd_utils/__init__.py index 90cd554779..e566ce5ffe 100644 --- a/src/ocrd_utils/__init__.py +++ b/src/ocrd_utils/__init__.py @@ -198,6 +198,7 @@ partition_list, parse_json_string_or_file, parse_json_string_with_comments, + sparkline, remove_non_path_from_url, safe_filename) diff --git a/src/ocrd_utils/constants.py b/src/ocrd_utils/constants.py index 0b9f0ae02b..dc255bc1b9 100644 --- a/src/ocrd_utils/constants.py +++ b/src/ocrd_utils/constants.py @@ -110,3 +110,24 @@ RESOURCE_LOCATIONS = ['data', 'cwd', 'system', 'module'] DEFAULT_METS_BASENAME = 'mets.xml' + + +# 2581 ▁ LOWER ONE EIGHTH BLOCK +# 2582 ▂ LOWER ONE QUARTER BLOCK +# 2583 ▃ LOWER THREE EIGHTHS BLOCK +# 2584 ▄ LOWER HALF BLOCK +# 2585 ▅ LOWER FIVE EIGHTHS BLOCK +# 2586 ▆ LOWER THREE QUARTERS BLOCK +# 2587 ▇ LOWER SEVEN EIGHTHS BLOCK +# 2588 █ FULL BLOCK +SPARKLINE_CHARS = [ + ' ', + '\u2581', + '\u2582', + '\u2583', + '\u2584', + '\u2585', + '\u2586', + '\u2587', + '\u2588', +] diff --git a/src/ocrd_utils/str.py b/src/ocrd_utils/str.py index f5b9242d35..406fcc9754 100644 --- a/src/ocrd_utils/str.py +++ b/src/ocrd_utils/str.py @@ -4,7 +4,8 @@ import re import json -from .constants import REGEX_FILE_ID +from typing import List, Union +from .constants import REGEX_FILE_ID, SPARKLINE_CHARS from .deprecate import deprecation_warning from warnings import warn from numpy import array_split @@ -235,3 +236,18 @@ def partition_list(lst, chunks, chunk_index=None): if chunk_index is not None: return [ret[chunk_index]] return ret + +def sparkline(values : List[int]) -> str: + """ + Render a list of points with block characters + """ + if any(x is None or not isinstance(x, (int, float)) or x < 0 for x in values): + # return an empty string on non-positive-int values, better not to + # output a sparkline than to cancel execution due to problematic input + return '' + max_value = max(values) + max_mapping = len(SPARKLINE_CHARS) - 1 + # normalize to 0..1 and convert to index in SPARKLINE_CHARS + mapped = [int(x / max_value * max_mapping) for x in values] + return ''.join(SPARKLINE_CHARS[x] for x in mapped) + diff --git a/tests/test_utils.py b/tests/test_utils.py index d2093c465d..8fe3fd3733 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -21,6 +21,7 @@ is_string, membername, generate_range, + sparkline, nth_url_segment, remove_non_path_from_url, @@ -325,6 +326,12 @@ def test_partition_list(): assert partition_list(lst_13, chunks=4) == [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10], [11, 12, 13]] assert partition_list(lst_13, chunks=4, chunk_index=1) == [[5, 6, 7]] +def test_sparkline(): + assert sparkline([5, 2, 3]) == '█▃▄' + assert sparkline([1000, 1, 2222]) == '▃ █' + assert sparkline([8, 7, 6, 5, 4, 3, 2, 1, 0]) == '█▇▆▅▄▃▂▁ ' + assert sparkline([-1, None, 'forty-two']) == '' + if __name__ == '__main__': main(__file__) From aba2263088648398d9e893461950ef5fef950277 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Fri, 26 Jan 2024 17:19:20 +0100 Subject: [PATCH 117/142] METS Server: try to remove UDS socket at sys.exit --- src/ocrd/mets_server.py | 22 ++++++++++++---------- src/ocrd/workspace.py | 2 +- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/ocrd/mets_server.py b/src/ocrd/mets_server.py index 08011557b5..be2c1333e0 100644 --- a/src/ocrd/mets_server.py +++ b/src/ocrd/mets_server.py @@ -2,24 +2,24 @@ # METS server functionality """ import re -from os import environ, _exit, chmod -from io import BytesIO -from typing import Any, Dict, Optional, Union, List, Tuple +from os import _exit, chmod +from typing import Dict, Optional, Union, List, Tuple from pathlib import Path from urllib.parse import urlparse import socket +import atexit -from fastapi import FastAPI, Request, File, Form, Response +from fastapi import FastAPI, Request, Form, Response from fastapi.responses import JSONResponse -from requests import request, Session as requests_session +from requests import Session as requests_session from requests.exceptions import ConnectionError from requests_unixsocket import Session as requests_unixsocket_session from pydantic import BaseModel, Field, ValidationError import uvicorn -from ocrd_models import OcrdMets, OcrdFile, ClientSideOcrdFile, OcrdAgent, ClientSideOcrdAgent -from ocrd_utils import initLogging, getLogger, deprecated_alias +from ocrd_models import OcrdFile, ClientSideOcrdFile, OcrdAgent, ClientSideOcrdAgent +from ocrd_utils import getLogger, deprecated_alias # # Models @@ -197,9 +197,10 @@ def __init__(self, workspace, url): self.log = getLogger(f'ocrd.mets_server[{self.url}]') def shutdown(self): - self.log.info("Shutting down METS server") if self.is_uds: - Path(self.url).unlink() + if Path(self.url).exists(): + self.log.warning(f'UDS socket {self.url} still exists, removing it') + Path(self.url).unlink() # os._exit because uvicorn catches SystemExit raised by sys.exit _exit(0) @@ -296,7 +297,7 @@ async def stop(): """ Stop the server """ - getLogger('ocrd.models.ocrd_mets').info('Shutting down') + getLogger('ocrd.models.ocrd_mets').info(f'Shutting down METS Server {self.url}') workspace.save_mets() self.shutdown() @@ -308,6 +309,7 @@ async def stop(): self.log.debug(f"chmod 0o677 {self.url}") server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) server.bind(self.url) # creates the socket file + atexit.register(self.shutdown) server.close() chmod(self.url, 0o666) uvicorn_kwargs = {'uds': self.url} diff --git a/src/ocrd/workspace.py b/src/ocrd/workspace.py index 7772c54d79..633e45acf5 100644 --- a/src/ocrd/workspace.py +++ b/src/ocrd/workspace.py @@ -82,7 +82,7 @@ def __init__(self, resolver, directory, mets=None, mets_basename=DEFAULT_METS_BA mets = ClientSideOcrdMets(mets_server_url) if mets.workspace_path != self.directory: raise ValueError(f"METS server {mets_server_url} workspace directory {mets.workspace_path} differs " - "from local workspace directory {self.directory}. These are not the same workspaces.") + f"from local workspace directory {self.directory}. These are not the same workspaces.") else: mets = OcrdMets(filename=self.mets_target) self.mets = mets From f6ea344c95b0d736e657f1aa366895805af19a01 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Tue, 30 Jan 2024 11:09:20 +0100 Subject: [PATCH 118/142] clean leftovers --- Makefile | 3 +-- tests/network/fixtures_rabbitmq.py | 2 +- tests/network/test_db.py | 10 +++++----- tests/network/test_processing_server.py | 6 ++---- 4 files changed, 9 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index 158415d424..6be39d7a1c 100644 --- a/Makefile +++ b/Makefile @@ -221,8 +221,7 @@ INTEGRATION_TEST_IN_DOCKER = docker exec core_test integration-test: docker compose -f tests/network/docker-compose.yml up -d sleep 20 - # TODO: Remove the `-s` flag before finalizing the PR - $(INTEGRATION_TEST_IN_DOCKER) pytest -k 'test_rmq or test_db or test_processing_server' -v -s + $(INTEGRATION_TEST_IN_DOCKER) pytest -k 'test_rmq or test_db or test_processing_server' -v docker compose -f tests/network/docker-compose.yml down --remove-orphans benchmark: diff --git a/tests/network/fixtures_rabbitmq.py b/tests/network/fixtures_rabbitmq.py index 06e99a98c1..de86386a1f 100644 --- a/tests/network/fixtures_rabbitmq.py +++ b/tests/network/fixtures_rabbitmq.py @@ -48,7 +48,7 @@ def fixture_rabbitmq_defaults(): vhost=rmq_vhost ) test_channel = RMQConnector.open_blocking_channel(test_connection) - assert(test_channel) + assert test_channel RMQConnector.exchange_declare( channel=test_channel, exchange_name=DEFAULT_EXCHANGER_NAME, diff --git a/tests/network/test_db.py b/tests/network/test_db.py index 4dd8f26999..9401cba7c3 100644 --- a/tests/network/test_db.py +++ b/tests/network/test_db.py @@ -39,7 +39,7 @@ def test_db_processing_job_create(mongo_client): assert db_found_processing_job.input_file_grps == ['DEFAULT'] assert db_found_processing_job.output_file_grps == ['OCR-D-DUMMY'] - with raises(ValueError) as value_error: + with raises(ValueError): sync_db_get_processing_job(job_id='non-existing-id') @@ -65,7 +65,7 @@ def test_db_processing_job_update(mongo_client): assert db_found_updated_processing_job == db_updated_processing_job assert db_found_updated_processing_job.state == StateEnum.running - with raises(ValueError) as value_error: + with raises(ValueError): sync_db_update_processing_job(job_id='non-existing', state=StateEnum.running) sync_db_update_processing_job(job_id=job_id, non_existing_field='dummy_value') sync_db_update_processing_job(job_id=job_id, processor_name='non-updatable-field') @@ -80,11 +80,11 @@ def test_db_workspace_create(mongo_client): assert db_found_workspace assert db_found_workspace == db_created_workspace - with raises(ValueError) as value_error: + with raises(ValueError): sync_db_get_workspace(workspace_id='non-existing-id') sync_db_get_workspace(workspace_mets_path='non-existing-mets') - with raises(FileNotFoundError) as io_error: + with raises(FileNotFoundError): sync_db_create_workspace(mets_path='non-existing-mets') @@ -136,7 +136,7 @@ def test_db_workflow_script_create(mongo_client): assert db_found_workflow_script assert db_found_workflow_script == db_created_workflow_script - with raises(ValueError) as value_error: + with raises(ValueError): sync_db_get_workflow_script(workflow_id='non-existing-id') diff --git a/tests/network/test_processing_server.py b/tests/network/test_processing_server.py index 749122700f..a666a21105 100644 --- a/tests/network/test_processing_server.py +++ b/tests/network/test_processing_server.py @@ -58,8 +58,7 @@ def test_processing_server_processing_request(): headers={"accept": "application/json"}, json=test_processing_job_input ) - # TODO: Remove print before finalizing the PR - print(response.json()) + # print(response.json()) assert response.status_code == 200, \ f'Processing server: {test_url}, {response.status_code}' processing_job_id = response.json()["job_id"] @@ -85,8 +84,7 @@ def test_processing_server_workflow_request(): headers={"accept": "application/json"}, files={"workflow": open(path_to_dummy_wf, 'rb')} ) - # TODO: Remove print before finalizing the PR - print(response.json()) + # print(response.json()) assert response.status_code == 200, f"Processing server: {test_url}, {response.status_code}" wf_job_id = response.json()["job_id"] assert wf_job_id From be43f47e499eb5d590964574adf7a0a90203a69a Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Tue, 30 Jan 2024 11:14:41 +0100 Subject: [PATCH 119/142] transfer method to src --- src/ocrd_network/utils.py | 14 +++++++++++++- tests/network/utils.py | 13 ------------- 2 files changed, 13 insertions(+), 14 deletions(-) delete mode 100644 tests/network/utils.py diff --git a/src/ocrd_network/utils.py b/src/ocrd_network/utils.py index 8f92706f46..8b3764c718 100644 --- a/src/ocrd_network/utils.py +++ b/src/ocrd_network/utils.py @@ -3,7 +3,7 @@ from pika import URLParameters from pymongo import uri_parser as mongo_uri_parser from re import match as re_match -from requests import Session as Session_TCP +from requests import get, Session as Session_TCP from requests_unixsocket import Session as Session_UDS from typing import Dict, List from uuid import uuid4 @@ -49,6 +49,18 @@ def generate_id() -> str: return str(uuid4()) +def is_url_responsive(url: str, retries: int = 0) -> bool: + while True: + try: + response = get(url) + if response.status_code == 200: + return True + except Exception: + if retries <= 0: + return False + retries -= 1 + + def validate_and_load_config(config_path: str) -> Dict: # Load and validate the config with open(config_path) as fin: diff --git a/tests/network/utils.py b/tests/network/utils.py deleted file mode 100644 index 0d33fbc30d..0000000000 --- a/tests/network/utils.py +++ /dev/null @@ -1,13 +0,0 @@ -from requests import get - - -def is_url_responsive(url: str, retries: int = 0) -> bool: - while True: - try: - response = get(url) - if response.status_code == 200: - return True - except Exception: - if retries <= 0: - return False - retries -= 1 From 466d2e6514e77f20f24a11823bde1949b4dfbeb7 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Tue, 30 Jan 2024 11:33:50 +0100 Subject: [PATCH 120/142] reuse verifiers from network --- src/ocrd_network/utils.py | 2 +- tests/network/fixtures_mongodb.py | 13 ++----------- tests/network/fixtures_rabbitmq.py | 26 ++------------------------ 3 files changed, 5 insertions(+), 36 deletions(-) diff --git a/src/ocrd_network/utils.py b/src/ocrd_network/utils.py index 8b3764c718..4f66554bc3 100644 --- a/src/ocrd_network/utils.py +++ b/src/ocrd_network/utils.py @@ -76,7 +76,7 @@ def verify_database_uri(mongodb_address: str) -> str: # perform validation check mongo_uri_parser.parse_uri(uri=mongodb_address, validate=True) except Exception as error: - raise ValueError(f"The database address '{mongodb_address}' is in wrong format, {error}") + raise ValueError(f"The MongoDB address '{mongodb_address}' is in wrong format, {error}") return mongodb_address diff --git a/tests/network/fixtures_mongodb.py b/tests/network/fixtures_mongodb.py index 0365560dd2..e99112b7e3 100644 --- a/tests/network/fixtures_mongodb.py +++ b/tests/network/fixtures_mongodb.py @@ -1,16 +1,7 @@ -from pymongo import uri_parser as mongo_uri_parser from pytest import fixture -from ocrd_utils.config import config from ocrd_network.database import sync_initiate_database - - -def verify_database_uri(mongodb_address: str) -> str: - try: - # perform validation check - mongo_uri_parser.parse_uri(uri=mongodb_address, validate=True) - except Exception as error: - raise ValueError(f"The MongoDB address '{mongodb_address}' is in wrong format, {error}") - return mongodb_address +from ocrd_network.utils import verify_database_uri +from ocrd_utils.config import config @fixture(scope="package", name="mongo_client") diff --git a/tests/network/fixtures_rabbitmq.py b/tests/network/fixtures_rabbitmq.py index de86386a1f..8d296f3370 100644 --- a/tests/network/fixtures_rabbitmq.py +++ b/tests/network/fixtures_rabbitmq.py @@ -1,37 +1,15 @@ -from pika import URLParameters from pika.credentials import PlainCredentials from pytest import fixture -from re import match as re_match from ocrd_network.rabbitmq_utils import RMQConnector, RMQConsumer, RMQPublisher +from ocrd_network.utils import verify_and_parse_mq_uri from ocrd_utils.config import config + RABBITMQ_URL = config.RABBITMQ_URL DEFAULT_EXCHANGER_NAME = config.DEFAULT_EXCHANGER_NAME DEFAULT_QUEUE = config.DEFAULT_QUEUE -def verify_and_parse_mq_uri(rabbitmq_address: str): - """ - Check the full list of available parameters in the docs here: - https://pika.readthedocs.io/en/stable/_modules/pika/connection.html#URLParameters - """ - - uri_pattern = r"^(?:([^:\/?#\s]+):\/{2})?(?:([^@\/?#\s]+)@)?([^\/?#\s]+)?(?:\/([^?#\s]*))?(?:[?]([^#\s]+))?\S*$" - match = re_match(pattern=uri_pattern, string=rabbitmq_address) - if not match: - raise ValueError(f"The RabbitMQ server address is in wrong format: '{rabbitmq_address}'") - url_params = URLParameters(rabbitmq_address) - - parsed_data = { - "username": url_params.credentials.username, - "password": url_params.credentials.password, - "host": url_params.host, - "port": url_params.port, - "vhost": url_params.virtual_host - } - return parsed_data - - @fixture(scope="package", name="rabbitmq_defaults") def fixture_rabbitmq_defaults(): rmq_data = verify_and_parse_mq_uri(RABBITMQ_URL) From 477f37ca36ba6adfca025d0dd8272eac60284b6b Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Tue, 30 Jan 2024 11:35:51 +0100 Subject: [PATCH 121/142] use prefix 'src.' for imports --- tests/network/fixtures_mongodb.py | 6 +++--- tests/network/fixtures_rabbitmq.py | 6 +++--- tests/network/test_db.py | 4 ++-- tests/network/test_processing_server.py | 6 +++--- tests/network/test_rabbitmq.py | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/network/fixtures_mongodb.py b/tests/network/fixtures_mongodb.py index e99112b7e3..34ded26fd2 100644 --- a/tests/network/fixtures_mongodb.py +++ b/tests/network/fixtures_mongodb.py @@ -1,7 +1,7 @@ from pytest import fixture -from ocrd_network.database import sync_initiate_database -from ocrd_network.utils import verify_database_uri -from ocrd_utils.config import config +from src.ocrd_network.database import sync_initiate_database +from src.ocrd_network.utils import verify_database_uri +from src.ocrd_utils.config import config @fixture(scope="package", name="mongo_client") diff --git a/tests/network/fixtures_rabbitmq.py b/tests/network/fixtures_rabbitmq.py index 8d296f3370..33f2944b11 100644 --- a/tests/network/fixtures_rabbitmq.py +++ b/tests/network/fixtures_rabbitmq.py @@ -1,8 +1,8 @@ from pika.credentials import PlainCredentials from pytest import fixture -from ocrd_network.rabbitmq_utils import RMQConnector, RMQConsumer, RMQPublisher -from ocrd_network.utils import verify_and_parse_mq_uri -from ocrd_utils.config import config +from src.ocrd_network.rabbitmq_utils import RMQConnector, RMQConsumer, RMQPublisher +from src.ocrd_network.utils import verify_and_parse_mq_uri +from src.ocrd_utils.config import config RABBITMQ_URL = config.RABBITMQ_URL diff --git a/tests/network/test_db.py b/tests/network/test_db.py index 9401cba7c3..6a69822883 100644 --- a/tests/network/test_db.py +++ b/tests/network/test_db.py @@ -3,8 +3,8 @@ from pathlib import Path from pytest import raises from tests.base import assets -from ocrd_network.models import DBProcessorJob, DBWorkflowScript, StateEnum -from ocrd_network.database import ( +from src.ocrd_network.models import DBProcessorJob, DBWorkflowScript, StateEnum +from src.ocrd_network.database import ( sync_db_create_processing_job, sync_db_get_processing_job, sync_db_update_processing_job, diff --git a/tests/network/test_processing_server.py b/tests/network/test_processing_server.py index a666a21105..18c957e311 100644 --- a/tests/network/test_processing_server.py +++ b/tests/network/test_processing_server.py @@ -1,8 +1,8 @@ from time import sleep from requests import get, post -from ocrd_utils.config import config -from ocrd_network import NETWORK_AGENT_WORKER -from ocrd_network.models import StateEnum +from src.ocrd_utils.config import config +from src.ocrd_network import NETWORK_AGENT_WORKER +from src.ocrd_network.models import StateEnum from tests.base import assets PROCESSING_SERVER_URL = config.PROCESSING_SERVER_URL diff --git a/tests/network/test_rabbitmq.py b/tests/network/test_rabbitmq.py index 0b208718f3..8fddd12686 100644 --- a/tests/network/test_rabbitmq.py +++ b/tests/network/test_rabbitmq.py @@ -1,6 +1,6 @@ from pika import BasicProperties from pickle import dumps, loads -from ocrd_utils.config import config +from src.ocrd_utils.config import config DEFAULT_EXCHANGER_NAME = config.DEFAULT_EXCHANGER_NAME DEFAULT_QUEUE = config.DEFAULT_QUEUE From 8d54093703e25fe9b1100221b66cf96a46b0bc94 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Tue, 30 Jan 2024 11:46:39 +0100 Subject: [PATCH 122/142] missed src. refactor --- tests/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index de39d7874a..62b0c4c863 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,4 @@ -from ocrd_utils.config import config +from src.ocrd_utils.config import config pytest_plugins = [ From b89990c66c33760b094d4a50670f7b04b835900b Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Tue, 30 Jan 2024 11:49:34 +0100 Subject: [PATCH 123/142] activate back mac-os and u20 --- .github/workflows/integration-test.yml | 2 +- .github/workflows/unit-test.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index d0834500f8..1915a4f3d9 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -22,7 +22,7 @@ jobs: - '3.11' os: - ubuntu-22.04 - # - macos-latest TODO: Activate back before finalizing the PR + - macos-latest steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 6aac7aa262..cfe282cd54 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -25,8 +25,8 @@ jobs: - '3.11' os: - ubuntu-22.04 - # - ubuntu-20.04 TODO: Activate back before finalizing the PR - # - macos-latest TODO: Activate back before finalizing the PR + - ubuntu-20.04 + - macos-latest steps: - uses: actions/checkout@v3 From 0d2682dd49b1900ab630e27fa34957b96b18c880 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Tue, 30 Jan 2024 12:59:07 +0100 Subject: [PATCH 124/142] ci/integration-test: install docker via brew for macos --- .github/workflows/integration-test.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index 1915a4f3d9..18c743d2cb 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -44,6 +44,11 @@ jobs: brew install imagemagick geos bash # opencv fi make install deps-test + - name: Install Docker on macOS + if: runner.os == 'macos' + run: | + brew install docker + colima start - name: Test network integration with pytest run: | make integration-test From 5b433e7edbaba17b43d606a3b0bebacf58fbbf6f Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Tue, 30 Jan 2024 12:59:07 +0100 Subject: [PATCH 125/142] ci/integration-test: install docker via brew for macos --- .github/workflows/integration-test.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index 1915a4f3d9..15d692e53e 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -44,6 +44,11 @@ jobs: brew install imagemagick geos bash # opencv fi make install deps-test + - name: Install Docker on macOS + if: runner.os == 'macos' + run: | + brew install docker docker-compose + colima start - name: Test network integration with pytest run: | make integration-test From 0f1992ed167bb3c3a572242e8e7dca836261c291 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Tue, 30 Jan 2024 13:49:25 +0100 Subject: [PATCH 126/142] make integration-test: DOCKER_COMPOSE overrideable --- .github/workflows/integration-test.yml | 6 +++++- Makefile | 6 ++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index 15d692e53e..858f6d47a7 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -51,4 +51,8 @@ jobs: colima start - name: Test network integration with pytest run: | - make integration-test + if [[ "${{ matrix.os }}" == "macos"* ]];then + make integration-test DOCKER_COMPOSE=docker-compose + else + make integration-test + fi diff --git a/Makefile b/Makefile index 6be39d7a1c..06e864d194 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,8 @@ TESTDIR = $(CURDIR)/tests PYTEST_ARGS = --continue-on-collection-errors VERSION = $(shell cat VERSION) +DOCKER_COMPOSE = docker compose + SPHINX_APIDOC = BUILD_ORDER = ocrd_utils ocrd_models ocrd_modelfactory ocrd_validators ocrd_network ocrd @@ -219,10 +221,10 @@ test: assets INTEGRATION_TEST_IN_DOCKER = docker exec core_test integration-test: - docker compose -f tests/network/docker-compose.yml up -d + $(DOCKER_COMPOSE) --file tests/network/docker-compose.yml up -d sleep 20 $(INTEGRATION_TEST_IN_DOCKER) pytest -k 'test_rmq or test_db or test_processing_server' -v - docker compose -f tests/network/docker-compose.yml down --remove-orphans + $(DOCKER_COMPOSE) --file tests/network/docker-compose.yml down --remove-orphans benchmark: $(PYTHON) -m pytest $(TESTDIR)/model/test_ocrd_mets_bench.py From 1fdcf4cb2b94fd62bce30e2df4708fbe9e2c8643 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Tue, 30 Jan 2024 14:23:31 +0100 Subject: [PATCH 127/142] add .env and adapt --- tests/network/docker-compose.yml | 94 +++++++++++++------------------- tests/network/ps_config.yml | 17 ++++++ 2 files changed, 54 insertions(+), 57 deletions(-) create mode 100644 tests/network/ps_config.yml diff --git a/tests/network/docker-compose.yml b/tests/network/docker-compose.yml index 0d65cf6a45..a5cef49e23 100644 --- a/tests/network/docker-compose.yml +++ b/tests/network/docker-compose.yml @@ -1,23 +1,23 @@ networks: ocrd_network_test: - name: ocrd_network_test + name: ${DOCKER_OCRD_NETWORK_NAME} driver: bridge driver_opts: - com.docker.network.driver.mtu: 1450 + com.docker.network.driver.mtu: ${DOCKER_OCRD_NETWORK_MTU} services: ocrd_network_mongo_db: image: "mongo" - hostname: mongodb-docker-host + hostname: ${MONGODB_HOST} container_name: ocrd_network_mongo_db networks: - - ocrd_network_test + - ${DOCKER_OCRD_NETWORK_NAME} ports: - - "6701:27017" + - ${MONGODB_PORT}:27017 environment: - - MONGO_INITDB_ROOT_USERNAME=network_test - - MONGO_INITDB_ROOT_PASSWORD=network_test + - MONGO_INITDB_ROOT_USERNAME=${MONGODB_USER} + - MONGO_INITDB_ROOT_PASSWORD=${MONGODB_PASS} healthcheck: test: echo 'db.runCommand("ping").ok' | mongosh localhost:27017/test --quiet interval: 1s @@ -26,18 +26,18 @@ services: ocrd_network_rabbit_mq: image: "rabbitmq:3.12-management" - hostname: rabbitmq-docker-host + hostname: ${RABBITMQ_HOST} container_name: ocrd_network_rabbit_mq networks: - - ocrd_network_test + - ${DOCKER_OCRD_NETWORK_NAME} ports: - - "6672:5672" - - "16672:15672" - - "26672:25672" + - ${RABBITMQ_PORT}:5672 + - 15672:15672 + - 25672:25672 environment: - - RABBITMQ_DEFAULT_USER=network_test - - RABBITMQ_DEFAULT_PASS=network_test - - RABBITMQ_FEATURE_FLAGS=quorum_queue,implicit_default_bindings,classic_mirrored_queue_version + - RABBITMQ_DEFAULT_USER=${RABBITMQ_USER} + - RABBITMQ_DEFAULT_PASS=${RABBITMQ_PASS} + - RABBITMQ_FEATURE_FLAGS=${RABBITMQ_FEATURE_FLAGS} healthcheck: test: rabbitmq-diagnostics check_port_connectivity interval: 1s @@ -52,7 +52,7 @@ services: args: BASE_IMAGE: 'ubuntu:22.04' target: ocrd_core_test - hostname: processing-server-host + hostname: ${OCRD_PS_HOST} container_name: ocrd_network_processing_server depends_on: ocrd_network_mongo_db: @@ -60,44 +60,27 @@ services: ocrd_network_rabbit_mq: condition: service_healthy networks: - - ocrd_network_test + - ${DOCKER_OCRD_NETWORK_NAME} ports: - - "8000:8000" + - ${OCRD_PS_PORT}:8000 environment: - DB_NAME: ocrd_network_test - DB_URL: mongodb://network_test:network_test@mongodb-docker-host.ocrd_network_test:27017 - RABBITMQ_URL: amqp://network_test:network_test@rabbitmq-docker-host.ocrd_network_test:5672 + DB_NAME: ${MONGODB_NAME} + DB_URL: ${MONGODB_URL} + RABBITMQ_URL: ${RABBITMQ_URL} OCRD_NETWORK_LOGS_ROOT_DIR: /ocrd-data/ocrd_network_logs OCRD_NETWORK_SOCKETS_ROOT_DIR: /ocrd-data/ocrd_network_sockets healthcheck: - test: curl -f http://processing-server-host.ocrd_network_test:8000/ + test: curl -f ${OCRD_PS_URL}/ interval: 1s timeout: 3s retries: 30 volumes: - - "/tmp/ocrd_network_logs:/ocrd-data/ocrd_network_logs" - - "/tmp/ocrd_network_sockets:/ocrd-data/ocrd_network_sockets" + - ${OCRD_NETWORK_LOGS_ROOT}:/ocrd-data/ocrd_network_logs + - ${OCRD_NETWORK_SOCKETS_ROOT}:/ocrd-data/ocrd_network_sockets - "./dummy-workflow.txt:/ocrd-data/assets/dummy-workflow.txt" - "./ocrd_logging.conf:/etc/ocrd_logging.conf" - command: | - /bin/bash -c "echo -e \" - internal_callback_url: http://processing-server-host.ocrd_network_test:8000 - process_queue: - address: rabbitmq-docker-host.ocrd_network_test - port: 5672 - skip_deployment: true - credentials: - username: network_test - password: network_test - database: - address: mongodb-docker-host.ocrd_network_test - port: 27017 - skip_deployment: true - credentials: - username: network_test - password: network_test - hosts: []\" > ./ps_config.yaml && \ - ocrd network processing-server -a 0.0.0.0:8000 ./ps_config.yaml" + - "./ps_config.yml:/ocrd-data/ps_config.yml" + command: ocrd network processing-server -a 0.0.0.0:8000 /ocrd-data/ps_config.yml ocrd_dummy_processing_worker: image: "ocrd_core_test" @@ -105,18 +88,15 @@ services: ocrd_network_processing_server: condition: service_healthy networks: - - ocrd_network_test + - ${DOCKER_OCRD_NETWORK_NAME} environment: OCRD_NETWORK_LOGS_ROOT_DIR: /ocrd-data/ocrd_network_logs volumes: - - "/tmp/ocrd_network_logs:/ocrd-data/ocrd_network_logs" - - "/tmp/ocrd_network_sockets:/ocrd-data/ocrd_network_sockets" + - ${OCRD_NETWORK_LOGS_ROOT}:/ocrd-data/ocrd_network_logs + - ${OCRD_NETWORK_SOCKETS_ROOT}:/ocrd-data/ocrd_network_sockets - "./dummy-workflow.txt:/ocrd-data/assets/dummy-workflow.txt" - "./ocrd_logging.conf:/etc/ocrd_logging.conf" - command: > - ocrd-dummy worker - --database mongodb://network_test:network_test@mongodb-docker-host.ocrd_network_test:27017 - --queue amqp://network_test:network_test@rabbitmq-docker-host.ocrd_network_test:5672 + command: ocrd-dummy worker --database ${MONGODB_URL} --queue ${RABBITMQ_URL} ocrd_network_core_test: image: "ocrd_core_test" @@ -125,16 +105,16 @@ services: ocrd_network_processing_server: condition: service_healthy networks: - - ocrd_network_test + - ${DOCKER_OCRD_NETWORK_NAME} environment: - DB_NAME: ocrd_network_test - DB_URL: mongodb://network_test:network_test@mongodb-docker-host.ocrd_network_test:27017 - PROCESSING_SERVER_URL: http://processing-server-host.ocrd_network_test:8000 - RABBITMQ_URL: amqp://network_test:network_test@rabbitmq-docker-host.ocrd_network_test:5672 + DB_NAME: ${MONGODB_NAME} + DB_URL: ${MONGODB_URL} + PROCESSING_SERVER_URL: ${OCRD_PS_URL} + RABBITMQ_URL: ${RABBITMQ_URL} OCRD_NETWORK_LOGS_ROOT_DIR: /ocrd-data/ocrd_network_logs OCRD_NETWORK_SOCKETS_ROOT_DIR: /ocrd-data/ocrd_network_sockets volumes: - - "/tmp/ocrd_network_logs:/ocrd-data/ocrd_network_logs" - - "/tmp/ocrd_network_sockets:/ocrd-data/ocrd_network_sockets" + - ${OCRD_NETWORK_LOGS_ROOT}:/ocrd-data/ocrd_network_logs + - ${OCRD_NETWORK_SOCKETS_ROOT}:/ocrd-data/ocrd_network_sockets - "./dummy-workflow.txt:/ocrd-data/assets/dummy-workflow.txt" - "./ocrd_logging.conf:/etc/ocrd_logging.conf" diff --git a/tests/network/ps_config.yml b/tests/network/ps_config.yml new file mode 100644 index 0000000000..655a847b8f --- /dev/null +++ b/tests/network/ps_config.yml @@ -0,0 +1,17 @@ +# the content of this config file is based on the .env +internal_callback_url: http://ps-docker-host.ocrd_network_test:8000 +process_queue: + address: rabbitmq-docker-host.ocrd_network_test + port: 5672 + skip_deployment: true + credentials: + username: network_test + password: network_test +database: + address: mongodb-docker-host.ocrd_network_test + port: 27017 + skip_deployment: true + credentials: + username: network_test + password: network_test +hosts: [] From 27cbb8f1f5741fee1f2b6271b2e0c6b92241fda3 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Tue, 30 Jan 2024 14:42:26 +0100 Subject: [PATCH 128/142] ci/integration: disable macos --- .github/workflows/integration-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index 858f6d47a7..ab34859c55 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -22,7 +22,7 @@ jobs: - '3.11' os: - ubuntu-22.04 - - macos-latest + # - macos-latest steps: - uses: actions/checkout@v3 From dfbbca3444062cffa161a89216cbc370b21a3e00 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Tue, 30 Jan 2024 14:47:00 +0100 Subject: [PATCH 129/142] add the .env file --- tests/network/.env | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/network/.env diff --git a/tests/network/.env b/tests/network/.env new file mode 100644 index 0000000000..7e96308c06 --- /dev/null +++ b/tests/network/.env @@ -0,0 +1,23 @@ +DOCKER_OCRD_NETWORK_NAME=ocrd_network_test +DOCKER_OCRD_NETWORK_MTU=1450 + +OCRD_NETWORK_LOGS_ROOT=/tmp/ocrd_network_logs +OCRD_NETWORK_SOCKETS_ROOT=/tmp/ocrd_network_sockets + +OCRD_PS_HOST=ps-docker-host +OCRD_PS_PORT=8000 +OCRD_PS_URL=http://${OCRD_PS_HOST}.${DOCKER_OCRD_NETWORK_NAME}:${OCRD_PS_PORT} + +MONGODB_NAME=ocrd_network_test +MONGODB_USER=network_test +MONGODB_PASS=network_test +MONGODB_HOST=mongodb-docker-host +MONGODB_PORT=27017 +MONGODB_URL=mongodb://${MONGODB_USER}:${MONGODB_PASS}@${MONGODB_HOST}.${DOCKER_OCRD_NETWORK_NAME}:${MONGODB_PORT} + +RABBITMQ_FEATURE_FLAGS=quorum_queue,implicit_default_bindings,classic_mirrored_queue_version +RABBITMQ_USER=network_test +RABBITMQ_PASS=network_test +RABBITMQ_HOST=rabbitmq-docker-host +RABBITMQ_PORT=5672 +RABBITMQ_URL=amqp://${RABBITMQ_USER}:${RABBITMQ_PASS}@${RABBITMQ_HOST}.${DOCKER_OCRD_NETWORK_NAME}:${RABBITMQ_PORT} From 4fb485b2bd0909663dd6c82f94d4edf719f892d9 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Tue, 30 Jan 2024 15:34:54 +0100 Subject: [PATCH 130/142] add separate test config --- tests/conftest.py | 14 +-- tests/network/config.py | 130 ++++++++++++++++++++++++ tests/network/fixtures_mongodb.py | 6 +- tests/network/fixtures_rabbitmq.py | 8 +- tests/network/test_processing_server.py | 4 +- tests/network/test_rabbitmq.py | 6 +- 6 files changed, 149 insertions(+), 19 deletions(-) create mode 100644 tests/network/config.py diff --git a/tests/conftest.py b/tests/conftest.py index 62b0c4c863..b699a73bbe 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,4 @@ -from src.ocrd_utils.config import config +from tests.network.config import test_config pytest_plugins = [ @@ -6,11 +6,11 @@ "tests.network.fixtures_rabbitmq" ] -config.add("DB_NAME", description="...", default=(True, 'ocrd_network_test')) -config.add("DB_URL", description="...", default=(True, 'mongodb://network_test:network_test@0.0.0.0:6701')) +test_config.add("DB_NAME", description="...", default=(True, 'ocrd_network_test')) +test_config.add("DB_URL", description="...", default=(True, 'mongodb://network_test:network_test@0.0.0.0:27017')) -config.add('DEFAULT_EXCHANGER_NAME', description="...", default=(True, 'ocrd-network-default')) -config.add('DEFAULT_QUEUE', description="...", default=(True, 'ocrd-network-default')) +test_config.add('DEFAULT_EXCHANGER_NAME', description="...", default=(True, 'ocrd-network-default')) +test_config.add('DEFAULT_QUEUE', description="...", default=(True, 'ocrd-network-default')) -config.add('PROCESSING_SERVER_URL', description="...", default=(True, "http://0.0.0.0:8000")) -config.add('RABBITMQ_URL', description="...", default=(True, "amqp://network_test:network_test@0.0.0.0:6672/")) +test_config.add('PROCESSING_SERVER_URL', description="...", default=(True, "http://0.0.0.0:8000")) +test_config.add('RABBITMQ_URL', description="...", default=(True, "amqp://network_test:network_test@0.0.0.0:5672/")) diff --git a/tests/network/config.py b/tests/network/config.py new file mode 100644 index 0000000000..df6c4e83cc --- /dev/null +++ b/tests/network/config.py @@ -0,0 +1,130 @@ +from pathlib import Path +from tempfile import gettempdir +from src.ocrd_utils.config import OcrdEnvConfig +from src.ocrd_utils.config import _ocrd_download_timeout_parser + +test_config = OcrdEnvConfig() + +test_config.add( + name='OCRD_METS_CACHING', + description='If set to `true`, access to the METS file is cached, speeding in-memory search and modification.', + validator=lambda val: val in ('true', 'false', '0', '1'), + parser=lambda val: val in ('true', '1') +) + +test_config.add( + name='OCRD_MAX_PROCESSOR_CACHE', + description=""" + Maximum number of processor instances (for each set of parameters) to be kept in memory (including loaded models) + for processing workers or processor servers. + """, + parser=int, + default=(True, 128) +) + +test_config.add( + name='OCRD_PROFILE', + description=""" + Whether to enable gathering runtime statistics + on the `ocrd.profile` logger (comma-separated): + - `CPU`: yields CPU and wall-time, + - `RSS`: also yields peak memory (resident set size) + - `PSS`: also yields peak memory (proportional set size) + """, + validator=lambda val: all(t in ('', 'CPU', 'RSS', 'PSS') for t in val.split(',')), + default=(True, '') +) + +test_config.add( + name="OCRD_PROFILE_FILE", + description=""" + If set, then the CPU profile is written to this file for later peruse with a analysis tools like snakeviz + """ +) + +test_config.add( + name="OCRD_DOWNLOAD_RETRIES", + description="Number of times to retry failed attempts for downloads of workspace files.", + validator=int, + parser=int, +) + +test_config.add( + name="OCRD_DOWNLOAD_TIMEOUT", + description="Timeout in seconds for connecting or reading (comma-separated) when downloading.", + parser=_ocrd_download_timeout_parser +) + +test_config.add( + name="OCRD_NETWORK_SERVER_ADDR_PROCESSING", + description="Default address of Processing Server to connect to (for `ocrd network client processing`).", + default=(True, '') +) + +test_config.add( + name="OCRD_NETWORK_SERVER_ADDR_WORKFLOW", + description="Default address of Workflow Server to connect to (for `ocrd network client workflow`).", + default=(True, '') +) + +test_config.add( + name="OCRD_NETWORK_SERVER_ADDR_WORKSPACE", + description="Default address of Workspace Server to connect to (for `ocrd network client workspace`).", + default=(True, '') +) + +test_config.add( + name="OCRD_NETWORK_WORKER_QUEUE_CONNECT_ATTEMPTS", + description=""" + Number of attempts for a worker to create its queue. Helpful if the rabbitmq-server needs time to be fully started + """, + parser=int, + default=(True, 1) +) + +test_config.add( + name="OCRD_NETWORK_SOCKETS_ROOT_DIR", + description="The root directory where all mets server related socket files are created", + parser=lambda val: Path(val), + default=(True, Path(gettempdir(), "ocrd_network_sockets")) +) +test_config.OCRD_NETWORK_SOCKETS_ROOT_DIR.mkdir(parents=True, exist_ok=True) + +test_config.add( + name="OCRD_NETWORK_LOGS_ROOT_DIR", + description="The root directory where all ocrd_network related file logs are stored", + parser=lambda val: Path(val), + default=(True, Path(gettempdir(), "ocrd_network_logs")) +) +test_config.OCRD_NETWORK_LOGS_ROOT_DIR.mkdir(parents=True, exist_ok=True) + +test_config.add( + name="HOME", + description="Directory to look for `ocrd_logging.conf`, fallback for unset XDG variables.", + # description="HOME directory, cf. https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html", + validator=lambda val: Path(val).is_dir(), + parser=lambda val: Path(val), + default=(True, lambda: Path.home()) +) + +test_config.add( + name="XDG_DATA_HOME", + description="Directory to look for `./ocrd/resources.yml` (i.e. `ocrd resmgr` user database)", + parser=lambda val: Path(val), + default=(True, lambda: Path(test_config.HOME, '.local/share')) +) + +test_config.add( + name="XDG_CONFIG_HOME", + description="Directory to look for `./ocrd-resources/*` (i.e. `ocrd resmgr` data location)", + parser=lambda val: Path(val), + default=(True, lambda: Path(test_config.HOME, '.config')) +) + +test_config.add( + name="OCRD_LOGGING_DEBUG", + description="Print information about the logging setup to STDERR", + default=(True, False), + validator=lambda val: isinstance(val, bool) or val in ('true', 'false', '0', '1'), + parser=lambda val: val in ('true', '1') +) diff --git a/tests/network/fixtures_mongodb.py b/tests/network/fixtures_mongodb.py index 34ded26fd2..1409bafe72 100644 --- a/tests/network/fixtures_mongodb.py +++ b/tests/network/fixtures_mongodb.py @@ -1,10 +1,10 @@ from pytest import fixture from src.ocrd_network.database import sync_initiate_database from src.ocrd_network.utils import verify_database_uri -from src.ocrd_utils.config import config +from tests.network.config import test_config @fixture(scope="package", name="mongo_client") def fixture_mongo_client(): - verify_database_uri(config.DB_URL) - sync_initiate_database(config.DB_URL, config.DB_NAME) + verify_database_uri(test_config.DB_URL) + sync_initiate_database(test_config.DB_URL, test_config.DB_NAME) diff --git a/tests/network/fixtures_rabbitmq.py b/tests/network/fixtures_rabbitmq.py index 33f2944b11..a3b1300cf2 100644 --- a/tests/network/fixtures_rabbitmq.py +++ b/tests/network/fixtures_rabbitmq.py @@ -2,12 +2,12 @@ from pytest import fixture from src.ocrd_network.rabbitmq_utils import RMQConnector, RMQConsumer, RMQPublisher from src.ocrd_network.utils import verify_and_parse_mq_uri -from src.ocrd_utils.config import config +from tests.network.config import test_config -RABBITMQ_URL = config.RABBITMQ_URL -DEFAULT_EXCHANGER_NAME = config.DEFAULT_EXCHANGER_NAME -DEFAULT_QUEUE = config.DEFAULT_QUEUE +RABBITMQ_URL = test_config.RABBITMQ_URL +DEFAULT_EXCHANGER_NAME = test_config.DEFAULT_EXCHANGER_NAME +DEFAULT_QUEUE = test_config.DEFAULT_QUEUE @fixture(scope="package", name="rabbitmq_defaults") diff --git a/tests/network/test_processing_server.py b/tests/network/test_processing_server.py index 18c957e311..6d039f5bc9 100644 --- a/tests/network/test_processing_server.py +++ b/tests/network/test_processing_server.py @@ -1,11 +1,11 @@ from time import sleep from requests import get, post -from src.ocrd_utils.config import config from src.ocrd_network import NETWORK_AGENT_WORKER from src.ocrd_network.models import StateEnum from tests.base import assets +from tests.network.config import test_config -PROCESSING_SERVER_URL = config.PROCESSING_SERVER_URL +PROCESSING_SERVER_URL = test_config.PROCESSING_SERVER_URL def poll_till_timeout_fail_or_success(test_url: str, tries: int, wait: int) -> StateEnum: diff --git a/tests/network/test_rabbitmq.py b/tests/network/test_rabbitmq.py index 8fddd12686..afe6870f11 100644 --- a/tests/network/test_rabbitmq.py +++ b/tests/network/test_rabbitmq.py @@ -1,9 +1,9 @@ from pika import BasicProperties from pickle import dumps, loads -from src.ocrd_utils.config import config +from tests.network.config import test_config -DEFAULT_EXCHANGER_NAME = config.DEFAULT_EXCHANGER_NAME -DEFAULT_QUEUE = config.DEFAULT_QUEUE +DEFAULT_EXCHANGER_NAME = test_config.DEFAULT_EXCHANGER_NAME +DEFAULT_QUEUE = test_config.DEFAULT_QUEUE def test_rmq_publish_2_messages(rabbitmq_publisher): From 9979b2849cc06169cc75265dfd949ddf13209cb7 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Tue, 30 Jan 2024 15:35:36 +0100 Subject: [PATCH 131/142] add suggestion of Jonas --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 06e864d194..ed777d7785 100644 --- a/Makefile +++ b/Makefile @@ -223,7 +223,7 @@ INTEGRATION_TEST_IN_DOCKER = docker exec core_test integration-test: $(DOCKER_COMPOSE) --file tests/network/docker-compose.yml up -d sleep 20 - $(INTEGRATION_TEST_IN_DOCKER) pytest -k 'test_rmq or test_db or test_processing_server' -v + -$(INTEGRATION_TEST_IN_DOCKER) pytest -k 'test_rmq or test_db or test_processing_server' -v $(DOCKER_COMPOSE) --file tests/network/docker-compose.yml down --remove-orphans benchmark: From bbc2a8c1ece0fccda332a4f5af4d14f871d8798e Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Tue, 30 Jan 2024 15:40:22 +0100 Subject: [PATCH 132/142] remove unnecessary sleep --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index ed777d7785..468b1b8116 100644 --- a/Makefile +++ b/Makefile @@ -222,7 +222,6 @@ test: assets INTEGRATION_TEST_IN_DOCKER = docker exec core_test integration-test: $(DOCKER_COMPOSE) --file tests/network/docker-compose.yml up -d - sleep 20 -$(INTEGRATION_TEST_IN_DOCKER) pytest -k 'test_rmq or test_db or test_processing_server' -v $(DOCKER_COMPOSE) --file tests/network/docker-compose.yml down --remove-orphans From 4bfdc3a271b991de40c41715bb4a61de78c32643 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Tue, 30 Jan 2024 15:43:46 +0100 Subject: [PATCH 133/142] remove test deps --- tests/network/test_rabbitmq.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/network/test_rabbitmq.py b/tests/network/test_rabbitmq.py index afe6870f11..951266e5dc 100644 --- a/tests/network/test_rabbitmq.py +++ b/tests/network/test_rabbitmq.py @@ -6,7 +6,7 @@ DEFAULT_QUEUE = test_config.DEFAULT_QUEUE -def test_rmq_publish_2_messages(rabbitmq_publisher): +def test_rmq_publish_then_consume_2_messages(rabbitmq_publisher, rabbitmq_consumer): test_headers = {"Test Header": "Test Value"} test_properties = BasicProperties( app_id='webapi-processing-broker', @@ -27,8 +27,6 @@ def test_rmq_publish_2_messages(rabbitmq_publisher): ) assert rabbitmq_publisher.message_counter == 2 - -def test_rmq_consume_2_messages(rabbitmq_consumer): # Consume the 1st message method_frame, header_frame, message = rabbitmq_consumer.get_one_message( queue_name=DEFAULT_QUEUE, @@ -56,7 +54,7 @@ def test_rmq_consume_2_messages(rabbitmq_consumer): assert message.decode() == "RabbitMQ test 456" -def test_rmq_publish_ocrd_message(rabbitmq_publisher): +def test_rmq_publish_then_consume_ocrd_message(rabbitmq_publisher, rabbitmq_consumer): ocrd_processing_message = { "job_id": "Test_job_id", "workflow_id": "Test_workflow_id", @@ -70,8 +68,6 @@ def test_rmq_publish_ocrd_message(rabbitmq_publisher): properties=None ) - -def test_rmq_consume_ocrd_message(rabbitmq_consumer): method_frame, header_frame, message = rabbitmq_consumer.get_one_message( queue_name=DEFAULT_QUEUE, auto_ack=True From 1a6d2370321974c80a55846b9649a65c4c0be8c0 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Tue, 30 Jan 2024 16:21:54 +0100 Subject: [PATCH 134/142] clean socket files before tests --- tests/network/config.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/network/config.py b/tests/network/config.py index df6c4e83cc..669ad3872b 100644 --- a/tests/network/config.py +++ b/tests/network/config.py @@ -1,3 +1,4 @@ +from glob import glob from pathlib import Path from tempfile import gettempdir from src.ocrd_utils.config import OcrdEnvConfig @@ -89,6 +90,10 @@ default=(True, Path(gettempdir(), "ocrd_network_sockets")) ) test_config.OCRD_NETWORK_SOCKETS_ROOT_DIR.mkdir(parents=True, exist_ok=True) +# Remove socket files left from previous tests +socket_files = glob(pathname="*.sock", root_dir=test_config.OCRD_NETWORK_SOCKETS_ROOT_DIR) +for file in socket_files: + Path(test_config.OCRD_NETWORK_SOCKETS_ROOT_DIR, file).unlink() test_config.add( name="OCRD_NETWORK_LOGS_ROOT_DIR", From 453bed3ceae3ffae93f5a3a2f42a95c693785d46 Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Tue, 30 Jan 2024 16:35:01 +0100 Subject: [PATCH 135/142] replace glob call available after P3.10 --- tests/conftest.py | 11 ++++++++++- tests/network/config.py | 5 ----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index b699a73bbe..cdcb600096 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,6 +1,7 @@ +from pathlib import Path +from pytest import fixture from tests.network.config import test_config - pytest_plugins = [ "tests.network.fixtures_mongodb", "tests.network.fixtures_rabbitmq" @@ -14,3 +15,11 @@ test_config.add('PROCESSING_SERVER_URL', description="...", default=(True, "http://0.0.0.0:8000")) test_config.add('RABBITMQ_URL', description="...", default=(True, "amqp://network_test:network_test@0.0.0.0:5672/")) + + +@fixture(scope="session", autouse=True) +def do_before_tests(): + # Remove socket files left from previous tests + for file in test_config.OCRD_NETWORK_SOCKETS_ROOT_DIR: + if file.endswith('.sock'): + Path(file).unlink() diff --git a/tests/network/config.py b/tests/network/config.py index 669ad3872b..df6c4e83cc 100644 --- a/tests/network/config.py +++ b/tests/network/config.py @@ -1,4 +1,3 @@ -from glob import glob from pathlib import Path from tempfile import gettempdir from src.ocrd_utils.config import OcrdEnvConfig @@ -90,10 +89,6 @@ default=(True, Path(gettempdir(), "ocrd_network_sockets")) ) test_config.OCRD_NETWORK_SOCKETS_ROOT_DIR.mkdir(parents=True, exist_ok=True) -# Remove socket files left from previous tests -socket_files = glob(pathname="*.sock", root_dir=test_config.OCRD_NETWORK_SOCKETS_ROOT_DIR) -for file in socket_files: - Path(test_config.OCRD_NETWORK_SOCKETS_ROOT_DIR, file).unlink() test_config.add( name="OCRD_NETWORK_LOGS_ROOT_DIR", From 824a9abc04a304acf0770a359ac69da18f23469b Mon Sep 17 00:00:00 2001 From: Mehmed Mustafa Date: Tue, 30 Jan 2024 17:24:30 +0100 Subject: [PATCH 136/142] fix socket removal --- tests/conftest.py | 10 ---------- tests/network/config.py | 4 ++++ 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index cdcb600096..1f35071075 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,5 +1,3 @@ -from pathlib import Path -from pytest import fixture from tests.network.config import test_config pytest_plugins = [ @@ -15,11 +13,3 @@ test_config.add('PROCESSING_SERVER_URL', description="...", default=(True, "http://0.0.0.0:8000")) test_config.add('RABBITMQ_URL', description="...", default=(True, "amqp://network_test:network_test@0.0.0.0:5672/")) - - -@fixture(scope="session", autouse=True) -def do_before_tests(): - # Remove socket files left from previous tests - for file in test_config.OCRD_NETWORK_SOCKETS_ROOT_DIR: - if file.endswith('.sock'): - Path(file).unlink() diff --git a/tests/network/config.py b/tests/network/config.py index df6c4e83cc..646833aeea 100644 --- a/tests/network/config.py +++ b/tests/network/config.py @@ -89,6 +89,10 @@ default=(True, Path(gettempdir(), "ocrd_network_sockets")) ) test_config.OCRD_NETWORK_SOCKETS_ROOT_DIR.mkdir(parents=True, exist_ok=True) +# Remove socket files left from previous integration tests +for path in test_config.OCRD_NETWORK_SOCKETS_ROOT_DIR.iterdir(): + if path.is_socket() and path.suffix == '.sock': + path.unlink() test_config.add( name="OCRD_NETWORK_LOGS_ROOT_DIR", From f8e47c9ce75ca3210c3d7636fe190eb42a6faa95 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Tue, 30 Jan 2024 17:43:56 +0100 Subject: [PATCH 137/142] :memo: changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1888ac1a62..1a0d266740 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ Versioned according to [Semantic Versioning](http://semver.org/). ## Unreleased +Added: + + * Basic integration test for `ocrd_network`, #1164 + ## [2.61.2] - 2024-01-24 Fixed: From ae6efca76b62b8375347028fd8145b241b6fb85b Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Tue, 30 Jan 2024 17:47:20 +0100 Subject: [PATCH 138/142] :memo: changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a0d266740..32b5217f34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ Added: * Basic integration test for `ocrd_network`, #1164 +Fixed: + + * METS Server: UDS sockets are removed on process exit, #117 + ## [2.61.2] - 2024-01-24 Fixed: From b854fb0cd937818f05c2cce3ad4666fc44e38651 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Tue, 30 Jan 2024 18:00:19 +0100 Subject: [PATCH 139/142] Dockerfile.cuda must set stage ocrd_core_base --- Dockerfile.cuda | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.cuda b/Dockerfile.cuda index 52d7a27619..89db9603cf 100644 --- a/Dockerfile.cuda +++ b/Dockerfile.cuda @@ -1,5 +1,5 @@ ARG BASE_IMAGE -FROM $BASE_IMAGE +FROM $BASE_IMAGE AS ocrd_core_base ENV MAMBA_EXE=/usr/local/bin/conda ENV MAMBA_ROOT_PREFIX=/conda From 6ec3c628a961171b09e15495d18c19923cbf0072 Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Tue, 30 Jan 2024 18:33:12 +0100 Subject: [PATCH 140/142] :memo: changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32b5217f34..1f62b04b69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,10 @@ Fixed: * METS Server: UDS sockets are removed on process exit, #117 +Changed: + + * replace license-incompatible sparkline library with a simpler implementation, #1176 + ## [2.61.2] - 2024-01-24 Fixed: From 69d975746ffb766dfc83212724374984952aa82e Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Tue, 30 Jan 2024 19:18:01 +0100 Subject: [PATCH 141/142] :memo: changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f62b04b69..fed68492e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Fixed: Changed: * replace license-incompatible sparkline library with a simpler implementation, #1176 + * remove all pkg_resources calls with modern alternatives, no more run-time setuptools dependency, #1174 ## [2.61.2] - 2024-01-24 From 4ea387f24575f20ab58f17a93cd6b9f6dfa0b92d Mon Sep 17 00:00:00 2001 From: Konstantin Baierer Date: Tue, 30 Jan 2024 19:25:20 +0100 Subject: [PATCH 142/142] :memo: changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fed68492e7..0cfabc9623 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Versioned according to [Semantic Versioning](http://semver.org/). Added: * Basic integration test for `ocrd_network`, #1164 + * `ocrd-tool.json` for `ocrd-dummy` now in repo root, for ocrd_all's `make ocrd-all-tool.json`, #1168 Fixed:

  • eujK-up1j8h}vcr$R~<26grF3K2BS{)h|Ose}@BUoa~|RH`~>%V9v0V=poA~OdTJ`Tb>0P27A$I#i3nb z`Z%*SwEU*#C9|ikI1tGu5HKz#o{qk@KCdZ^qVz zWQg}$T1n&uluT(A=mC_}ywsF$_WVf#v@ZEADA404sTqaw_DLpBZLt?z!0LWD0qf`C z6Y!o`yF7asZFtE0xM{?T>WDH#xmZ61ln2UuKQ(1Qo!BDCDG!nn0nRftI5F2Be{u`p zZy?4$r8vc5=FvBz$3w6bK$$o()8kL3$KODYe@bzpM?-Z;jdBE-Xcgece4Du)w!ZC!OxH8hUNkG;XKhou}os1 zw39P>u%psDDL;h>*zdFP6E_lC{OZo+A;^D}N@S&Vht&fPGl`3=9pS``m2LYcdG7}Muxf~MXcp85^nt!2<-+EEo_rtAtzlC4BZ9^$iw(BcOxjM%OJNh43cnr5PriIG@o^kCnav0BxC0RpY#ctOhY=4p3CTLJLEWyr| zOAzz%ule=5x!X?ubGo>1{=8npF3WU-M~d3m*3f$irt{J%lSbKG{ln4JA-3Qw=3 z1>Pj>cf+~GZzgH>090js?XGxP8}(PE6d5-S2%5qs>ID`giCdUo&d;{%`vzL(+1F>v zxsQe>PUf+i5eEje|=k`N(@oxvJB z+?AYVjbew~9IhhSxb1q$+ly8N1l@HBr{Ll3p&qc0qJT}nT@iESIDvFo=$`Qbc+HJy9joAi zrl@C^BEx)^_qgCaoE+OwHt~7)2L8?6`=abeg5fH|EsbPy6>9n+`r?-Qc(116szwbJ zp>RS+XT%Xteo;?m0=CoVy&KJ%Y?XKITO-FiH@~STw(EkbYskiTs^w_g%+)_`2`b== zN&4XJvGfJ=hAXB$^4PxAatmrD*bd8xw9CXF^62ajnpkN8iX^J`qiv^QmEpqcxEIRk zrl$NtpzPB&+ThI*HNUnz z^ROU*g~RE51zr4wRga45B9Od+fTRi6&S~mgAi;NQ~eg(_!2kOzsyCy0vXh$FJI9Kfc0uo z0`e~>KTX%k@>VAd^hp6s#`6Ks=%hf7!a<8F9T2#DTE@59iLD3Mo^_kFvD?t zef*Yopo=p5;UtHX)e#KtfUa26cHqfIUai1h=68Uw3r}AwTraq>L8|r%YIGLzuBNDw zl`2svv34HZ0uHoxd_y=QT2un6J=w#9ttD?MeG2yQ&UQkxgy~pztZ_;?u2~#$yK~i! zogV)N*Ebe&;yrEaI8vV)9n<fp zNkIa4%LYx_0@CNuuma|vR6W>SfSglOg{p__L%dzarASM8{MI3BPuf5k!?G@=pQv5F zGAbbu{_%j94aqJ$3qe(L5UO>{u*IZ+*XZormKEw>mxu5BzFztzGVI=J(MK>j7SV!pTphXVYg!QXM%f-zA9 z(rsxccZjP7Ii?aoSZ0wjcRXDe+o{N9j$~qr0G(2x_2r$ zt_!8j>&b!AOlKdi?$)d8X0vG^m{5Y0;1bZg>u>TfqizH(Baj*sldaa$FOR7Qa-OvC zAIx>q#=N)Vq=m{VLG0B-xlM37g5g+i-`g&>Z`=XiX2`;nLtUI=X>jV0BVAl(ckBU* zh}^h|y)fl6EN?Dz2#c=hZca}=tcUhtaVwHxiM!h)@L)l=ln8>;eU@WFKwL*;8)(SM z2Zy5EXSrkUvY|6Oyo~zU+g8gIx8i?nn)SC0oSAH9DL@3TKAggxiknwxo8);Ff-CGB zKlf1VOBi9WTsfY1m`8k&C2=S3=Ldw=Zm?7wWfrcFo6MIm-(pceeis??t*X9QRCyXd zhJ(z}6n~eUCbLP~1>EJtmK$wHU)vdQ{!Cd<7SwT;o;vvq`!k zV=|j=rxP|W;c|cU1Ye}545rIjx}c+f#6OtZ>1_bO=#ShZ<{W`CYc|*G`5m5a$SGSm zGI5oE(?WSaYm}x*Bn0bseGxq-mG(?fiU$X*a$FhW(m_+2gChwXoLl}s4WrdGt0E+h zBhb^1WwsisyA9VEyvyuF-m9joLDz+4i1CVzFS1!NUF=FURRu6@$gp9G$p zb{e5q*@lWr&$g>dVVWulNUJYFg&bhpk4RIW5J(hFAk)}##dHV~DA*E+oBB(HoH7jZ z0(y}KEXGb5fZZ0+8^3Fp- zQHGL0yEMOzzP1Y>G-NzHLGrab)j%Mm8hJqi=5NM+-eGx zJW?A8M_msHGg<*e$cc_=T{5TTLQ7^!9w~FeQJ0xuMhi_?oY0wi=>uq+FW*}~-=aP}Zw2(n(ipZp&2wINAT3w^y>+}|!IzyI<6 z_6@u;3fFu<`3*f6dW|?Xchlu&a=s$J+;s7OSXtBi#THjbUq34)6Rt{O^eJLNLPCWn zKPd1iTsT79K!6`g@eX!Sn)Sc+zFMT_Xasye;5FUZotOye6aaVyvX*@Hl^7Y}ePrs; zUy=tBt}nen8Ht0^Kxv#)yjgga;fiGDEqG3JpnwD=v!FTAfhMs9GCd-i5)&kLn#b?6 zY;~|ACJqQ_<{aUkk`~pv&$1i5%iD;&cls<_=(?~B@mX?vjT~^C$KwND`E|vKc>%|t z?1|%VXkq+QiletrwfBgm%9dhCRi$UbDurpQBp|Q81Ql`sY++}KsOKyJ&+-a5b#MG) zin%}%7Bi%1mQ{*E3AMX+3CtPrETe$qoxTLYl=5ei%M?XQsRKtMr5s@k=_uffJRUQZ zlm$dwG412Q@k}k*p{qS2Pim`=9N&}LCRxTJP|oQf)yb0zp41(!gcb?9m#H3xlY;Y5 z9`_(so7difKaS6*?MZFxLDdQ_G0>CBvs~M)$T=fDN`j^gD?y8p*|ziHyZrJCO%lSj zefX}BDzsQmHJ!FaRhc8D)tCwsk-!SFpUkR&6}Dn#s$FEfQji5KX?X%3OLh-s#TX7r&*1uuX0tHN3UOK2sFj?_abwNae8GeeRZ1Q zAH6=I?g#3VON`SeW9h5U4FBl$ndja9%`+Dmr%j&i&GZ9{nKbtIeCjYE0SzDGp+iA- z)MpNly=Bu{^~jbD?$m55TQ%ZO?z#i8wo_rqj@WZBc(7viK9j639>4oulx;@~T&*9$ zZ7S=qBi_%opnP9ka!FdJ$9?lyWe-3}G6i(sj7VjMW?a910?10Cu z3=J5=wKS(Uu4Zx<*+=kEnKo`cW?1EiJN9YYfNfm)*rx%(75<3FsthhzD&RO^d)Pjw^?o3!f#!b_0LJ( z$2KiqxRRd^ov0K_llw$z@3L%Q5*4>~qT>qQvCic=S3E zR(=pnoiE3*>W7!|TX{pB^IcrxG(PHl6IJYd!TrB(4ks3-@_eOJvE<+=U)fxCzO30? z*y4Pk>9UvIp<}0U-8i6Qi{s^j^Tja-p`|sRh(0uCa(YGz!%XhMau~s`)O~8YYH^Qy zQlHp^l2j%BGPSrO=U1S5G2|z$*0>eDk0#a8|)?dg>W%#3gqAX|*FdYS&CQ zU;-GMcfj;I$hG=3I~|@jVTSCf)9rv=8kgBlyKRRXP0MO&T!7wf)#9@0Km^sOY*wZ! zEQZkR37_@`;_mqAZB&UFnUzrOcJg)0?tx~4ppA1&Y>6|6t65YG`WH;pXpK0Su} zD_*!ym`A;Y6vJL{yL00FNqYS*RB&`F5hW4JyKFivJgZNf!BGjsnUf&0eE1SBdz8me zx!o?C4moeb+4(BIRDL_uQ((&}Pn`e@jGXkAbNP~*0GD%H zHy8NVWRVm3p=ELT(ZqP6ydqbeoH&DB`H6Btd=&*Z${@aA&7?)FLC(3Kb~9(u59lC&W=IJp@Ek+N@2g~te!A&)jfdfN4L|A+2g+5=F2|c zZJsTAj9m3=bLCAVQ~3%O-bPk!$yY`<4~r?=lj6ifRrB6yX38n{w|(Zx9&TCpYoUBgr!IXhr-IZdYM)G(T0Ip5PT* zP|Epsj{UP-MXW%ViWM<`=6%Vd6UEU}w=&<{*m$Whic3ud4^AqU_~s8#j225s5;^gn zjWTA}k=bHkH`O=4L%+2>s5qakDCqs&^c$nz59O2c1d%Iue~%ALTn77og-Jn*^C zU-kUwKYzu6&pSXUv>`uwDh~5ZjtK_5<-9m8?n5z4<+HwsBq>7_vEYSJnV#z<5z*Rm z`2WAf^EAqbm7)?<`J1CUkXIed$kcv(V1VHlCV1myr zjmd1I3Ao+FvXs~scq>_V&Da2MeV6MQ))%$;-TH*>T&5GztRR6hy;$}tO%ot*^-*T5 zeyUv6Z_ZNvsmblI2bzQB=Ci%T_8<6Vt~x23lQBGtA8PUm51{zc?)V5wkfSM#S_p+r zpcX>mJ^0m)M2t&Ld;0&l_aG3p7DSnK5G5!(t2<1lnLa}GrrRlu;boGEXmU)d9eHo#o}nnq+?E ztl1bbp}9YC7NUzeg-8|ql+*f!ohE`>RY2WuiMaJPY&G(^>lmtaGGHzt)Gatrz?+Ns zkc|jCm1|K{eY_y;l7w0mRozByonnz&sJx>r@~d^8MSd}dS`@^bY>{7VQw>Yto=kM^ak zJ`nZzgvU=kVRwA^)X&hgzgqN^4WJf1<>+Zu8`-IXr@o}M*y*fer+(2{Jz)|$^#H0L z-A<8Hk9#d}ssqN#M}K+-PJO%B=D4ZH$TeRbeyAO`d-81{Ob_H)bE`ST@@%U4h4XAq z#<7B9Sqq!yC&zNjRNuB^mVRavHcjakcABVZHTiU}wXH!@kGYAN=BG?7Z`k}PeFENA znS66Loo?Z6q^UvDoVU$XOt?zPeMUSJo7GlR-Z09GmB}_!K|lAcro0Q5x0xoydM|7B zy{5H2q_}iS#tWak2SJ@JiSr6~+HFI55w&1v(jlZ=u!IpW*!(b4u=B~W2b64caL4!^ z6}+@_oFQ}>g{B1^QA_E8OPf_1hbK?i(vJSYGOgJzS6gf!rc=?X-P>$TCIfXvS6u zrNwkz9>)+{w#@Fa+Pzk60b<6h3u;HaPG*O?X1{IDrmBV7{2hu_I?tUaJ{4=}LOR{h zw>dgxv(~XGvq5Su+xbTMu3)jBZ)35vg%3NYN2)|q@uF3=L(%NXqWjQsj$~M=9g2SH z9g23_mm0fu#n+BR*N#MUXRv0PZ+V!koG3TvQE_Z~0yopT)?;D;OtPa6M7vp54~V5; z)8bi>IyEBZ@=9az>XI4?ib@@i2yZp5`H97o}*naEBR zhVulSF#>>Y8vo{8%OLWO!#=s6B=brx2*kPRst4UpkvNz6(>xRh)4~EE4$`PyE>F)L ze4bfsb4=0YPmG~v-I;^{tXTjQfHZyg}D&Xmq2 zLRxrj@{Aw9w7@$v`4Ul%nDWVIoPjQ%v1LPXq746PdV(KPNG?g-k}sXg&nocdJ%GzH zMw%X2ee!Mr^sbxaD=u+5O$3UI_REa5t%6U{t%Bxy!ujnS)GtV)VJZ)%7Fyl@dk5it zyRNs|ZTRH9ZPM%4O@v2HHFm^7Y4q$#^}uAS2Uk4-H8?Fa3b|9Gs~)p79<}i5c04HU zR<`P&cFy>!XCFI0!0Kmc+Fvcgir#i}aqHU2FCHdEl!=cmOUg{v~sf>G6p%U4SC$lkAjC{(CIm_BG?9JT(H!+V0Jy6!m{a zaav*C+WnG+F|*?t#T4v%UX>pk8#d>yjhz9Vn5TSEg9|fuzhp|cu+v0ZtI6j!x^1{! z(qpb;t=_GEsqt2gOWv`a7@sWgndi;l7!Zw@fAh*r|Lezp0d; z;=n56vHUiz=mjFL<`#*O?kF2A^gmu^QVIFpT~ z;+9+?Ke{;yS8`OhH0wj!!M0byqxyS5_95-qTR?(E_4k01L)ul|0unre_ki37fMl5z zOSPcvQ#L4@)cAZtvbtTG7?<*QYGK(O7?zc7)eXufACr*@o6TixFB1AQV;#p{;#*Pe zy%q*820IleU7h1Ck7@_&k=#$pU99fuUy#JJSdojEk2|np2Z+jUyev+*AOP(OgdCK& zC&3Y&MW+WS^Ni%HqpdD)$+sFowY*Zpvk``Ae4bn(+QbOOQ8HdXSJi@DJV}bE23RK^ z001Sm;jU&8OJR8f@6bTpY_?D?+sZtmlnJkZR{f(q|5PB*rV5I2g%lwB5f^H zJL^zww6aZg&e*?hr;u$_4BafP2Cv?@wEHG8{aa=QJLY*;n$5|zR?o7ZsE2{%NNM$M ziOR|Md1@<}R97?sr9TLiq;JpXfMOQ0%>mm8Gvy#u?R)HgC;6Luu-cJ(bll>f=)EIH znUipB^50s}R=kd4twN#bo{G0y%AO>tcGI75@15K~`ULi0!Y~tgQi8Qrx`dr3U^|nH z^A6fZGSGor>w%WkpluTWaPGJq-OPd2GLMWo8Jp$(pZUo%a+PF1pV}YGJ7VSDSTV{A zsyXkEl>~wV`=fKdBGZn(3MR9AjXgd3Dww3b#*PxmacLyF@=cxQ753brN$fg5HB~B} zKE6ft7{CHDIlW~AKO7HAv8%S6#J61r>o+HJQuE}yL@O?()W| zp>9hGAwAD-@j^X6B|gq^_DbRON-aG6lnoEVBuC`MrEF$v68n-L)R`>#v#@2udbWo7 z#FD)(MY&+LIPwmRBa15LF)(>HDcRdAzL^1943?()HF$iIpQ$(BJpyGO8Xns>3|A+a z;wRRl!5fOji4{3%QwirMM?l-NVN)nj6%*AR5X|=StRBct1E&R_A$J&clVM$tSsITg zR{$I49}8cLW+4Va6}{HT6~ccRQK4&rWLtJAa6N=+F0mX;s*{cRECb2rgUZo-;cU$h z&E|=|ngaRB)*Dk)CTa1o6kx+m(?7`XAAC|f(1@A zY4=auQh+Dyj$a1wGc-lP#aUYds4W3-P`#>+i1FNMOeS88CgcT4cWn6Im$Vl8pLOWp zFFLCyOv3&iK=q^BDdg{Qx8h^)qOhE7#o?b;;r_(C2%=@;^E`w72@lidN7}1!YSJV9 zf`bY4gMq3qE7j~c-&lA)uyrX2(GxyS4Mb(Q`l4}2?u7C z!LQ&QdQ;Qv%kiS|>SgBpGHdNp>8xi#L3OE=dFrd#RM%F)(ko6O;&mszuCU@{!aTK5 zT&gP}QK|KXNn;^DnbemAyOpK$Niu>T?=A4a2Ig?vv)@Z?^AeQD$n8vZAlF^`=Gix! zV6yC;{9RjqLWiUNiS}GueoEcTZVjN;@QTuVDlR`^WWw~RI94#-4ENJ1i%$YRzZ*aeJ;cmjersHspB5G)SC@1nF(j-d zkD|ju6PLPh`eWBE!R2Vbxh4DTC`W~m_8Tluuk*73^bTs~I;OS!DODtAJ0YgvR4#rYn2 z^k+-{46H8Wexn$p*yk1?fC|#D0EZ}?19~jWSv_qUE_=KT7(-SEycyjj~>*Rkwzyg+$O=JP$5bsgdVbWt`O>B``)T{`d+Jv4XQKaH7THnXv2|I;4zMVzfh&ZKsO4sS z_RR)MzE(sb#@TH}%x77E=7Y-BeBq4E56uR7tG+0f zvr5whC`I*A=Bj?GJk@W`QT?gOm9D0fi{%PI4MGzmZ#T)(+u)TPE>!AoV45vtlHF%a z7@4Hukiv%VsBH@vzeJO>R}qTI{i*0W@UN<8;Vj1r@j+sFFZijtnV zXa)`*Gy-l`VY@*oM@lv(pHdevbG7Anxu}DSwd0MdrcmjayQAK%wY1~%;+zc1bi%eL)e4GV4JIIBO)WGF}Zd6gzjzdCG{GMZF#1s(9q|+hXq8Q zYESN0KK!DyI+d#Z=yuxM5M*!n7{bOZ_aJYQW_dhGBi|^Hcve*Mn-~|ss@<8)4^ckdmWUg z8tS7`=BbTlQ(YGYOV>mp;(9<&f zb|@3(slDP-U2Tb)oxT$6Dys8IGD5Uwe*0Jak!YLuuXv19NYj(fcgeITt^BW99-l_j z-=WU6KihKMJTX)OvMSLpxUf0UYxOd~{rODjqM=5`s+^q8)}CTVRr5bNIYr6q>9@bU zio|63dxco5{T71p{$s^yEX3+f7r9|8dk(tHX$yw%on~Lm0zSX;O}sVjbXPA} zfHBG6skgBx2W|Gns3_)Fl8TFKWa(Ba@jHEa!lPItj=Lf%HOnny7Sa4l)neoptQdGq zDi@<@;@XnayENr0&OsebGCyTBn}{kZZ4Qb+d*jv4K^>29eoA@FT-D4FF}uxAE;BBR z?Xd?cV7kM%LZus@a$9HLloZAMYDsaKU-d{X+o@g*w;f&~RYO_6^OWqV#E2esn_Q<$ zZ%9vEuF=hWFMDj~817D1i?JiCg~FBt#_a^T9*e+hZZ+TZCwyer)h=#01sC%n1)IoL zsVhz0Dg|m);x{LZ8R%qO~;GW%m=e0g^z*;xa%5wu=(y2A%H9^>_5aFGpWO&yc=;=_pJyXksWTc--{+V;4YO_waZkVo|mb} zQ9aE}OQj5KC-xij^$J6Icf}s94`+P|^CLHpp0?;3JjB)kUhB6}HB}*kf6X zMxc?%R3o1hH>1e57E?_oIm+4a9ku0WdWsJo2WF(YbY}~KkPR)w8aS*5<4h(~HyN7A zh*?xn<}zK`I6lD}$0rJ>75G*r6WsRhS`Ng;?A+emvHf@Sgjt&2g?ko%XHI&D=g7%T zz^BxO%v|lTz1vUK_VnbI@&E1TR{Q_=Jv}p6iSV+VCgs+uw-;i^-dGaKTHDt%>pM%r z!n69sWC9;RRG+$?_Vxt)t;2%wb+F6D8ei=mDcS5|CC+nI+U8`sMGOs|McV^@>gx1% z*xi#j%a)gyXe>U@Q`dw?buEEO-%KGm#CK9y=a~cHP1-y)uozWW!vIt3%GuFl@a|>; z^S!oXLbZr(b8;eKPOsDUb}rAVE7IY_?X_#7wz&Y;2I!EpX=Q3^S=(ITIzAXGf&uHv zwYdPj`WzMs1$=&sPJ)H8EnS9UeBqhJY*%cp?s@j>W){)>w6GYtx}>k|8xqW)cIxdC zat1#K!%!6SD^4=o@6rg)vt zS@R1DK(G28!5QTeJ8! zHjWiMWICILJwczkxW}SZU)U4m!Q$Q&KLtF@*N7B0m5S4oW6M>MxHn6%>ViDDg`)N* zLUNM*1insBU4VeQ{S!NscOb0YlHVk6cadKL@88&Ei&?p-sR&^ zS`SHU%EA^GGF|qvJ9O-{Ldli?*dpI5<&d2;dFo_w>!kC|nnKIm*tU|5Ve+@y!X$sn z9$x&iV-IrLA8ay+EaU~{h?cMV$l79FQa?N7jtE`1?7-LydiuK=w}o4*>fVE^&mLNu zh6YFOWq5L|=tT|GD2p)MMKINz&HkphN0fNt9(8rzh76pSJYaT9{50 z!=_qa)L-Rtk^ZgL7WD_VeEqj(fptaIU}Bl7G);izMfFi;seY;))o;#F{i(^3pKOV- z>|~OLBR3nqL)ZJFhNXIbXMsY<2P3t{82prxj3;i*FDmkmU-ye=Yx*GWfBY3ZJJRlk zIcjTvFdhEqEejm7cNzbOtFec58JUw>`V;pABS|EGCtUlRg{ACN>wj^1y>PO<0&R>Q zRaiUQA1^?wpG@ZXQJCs$x6@i+G>_VPpnefRdjodgob)6*X`ZWUhL77$yspkTCg}@= znpJ#}PK_)CErGXt)HstD8EX zBqMmM-eF{P`*XPJKi-(P`M`9Po!kHDKx1tIaAXxf$T7NkScF_xRq~sbuR?5Zsyw{> z6)O*Xm3D{45QtbyBvPBKqSO`ut(cZI7NO+Q*LEx#o>V=VP8aK5dk%{|hGu@tJ%*3l z+X;tLCvt(#c1=+A+;h-Hlgv*Q%_gFWN}Gcss&}6K%BG^2pBjpbtHsl70j}!Mv(qfX z%`BxJAGdWOE}Dg2jI&la(wE_Ql?w|YaQgUF?D9CDW8>@U`6L_8)bR=w7Q##7pqlV& zaRhIoa5|qe%k%LvF>GxriRF=1IbM(AOX){BfoZ2U7b`{aPP$y;^AF+)TCB9qZM_bQ za(b;!@R-|rU2qUj@KA^GW?Zie4&n)Nxnj9typkK^M?p4o80)puyo@-8w*c%jd04Fs zmg@uycWH0-NAGG+m_N0zflo~9a9Gt2Td?yw3gyJtmg;ukQk_+=>~`3TbykWQ<)6;W zb)I>eLV?S_gBI$%JgYC!rGe9y)F5~2;+)4UjmMK~S#xmA--kB!^9*yb_U%WURkQR*t&@zcDJ zhWioW7unB3i)$@KLaBg@1F?w4knzdUiRrOInUBX6r;-(>{M2}b-~U~~x9eB<0-rpe zP#eupOyI5PNxnrg;f_b6dF{UadEaaJd;5 z-~{#TZo^DpfD=qkV95&y1&eTeT~7r?EEmy|l6Ee`RA=SJ_()wr0L9yCP-LpeOPpmc zx60N6pSwe;Oz<&AUc20wlV!`$ zCNGo@<0){?R^+ifw|0^rd|SC;qBwdgS0!!{RoNy&3^O!8Ha0E}Bvy(-jBDTUQLtl4 z4p(OtuYJSEa)}IVK2Tv?@D(KiMuzuR&c%9ld~+VQR4A2~LnmVx8}dBb2trv3iP+|j zR3Qn64ZV9(hf4)qrpou6@)|M(Nn7IOG{GV%spWGtDcoU&%T<$Vsd~X`x=G~{a}qq9 zGnj`LHVO++Z*c4SRCOcTDB@t;7I=l1pI#SC)}x8%C&o8T3Jc*!|M*&3zOR;xbsCH8 zkxN!l>g58r*fB;5CNrTG*Yap-e5zve6y(zK9KN{_D=oBkU+dH2zSfDPyO1l$e&-=S zqq8`M?fy7)LoW0H-W(3IB99Y9XI?wSDvDtRKM1c+9`BG-tQ9^k zJewW+F`Oi7^6|+sUwxrDjK~*Saad-oaQcd5>v_N{q2kGth0!c|20oD=EzFVUD+IGw zv5IE5JDLi1mq35f#?lJZG(eE35;Y|JZlGde>mmTMP+bT;F4%SpNIt#1(66ZW7p;R#eXi`Y$q_`!Qpp{e((3G0T(kZia zd9*qnxl>*iIab<92*sLr~oNmh*-~ksPW>}ZU$Re zx@XtO{D|0~(0CCf5>uY~tJge&Wg-;CSfhTM6_b3CAnb?Z`ah0eHFqJw6+tK1Ak}m$X6UKl*@%N-p!!inc-TU8a+t%E|DrW zm6&YkcS~0FpxbF5s>{5#4OQR#lEkR(;VsWkVlES(=ebie;bD4vjB8$7%>k3XMN@Ey z@6S||wLo&2Fi+VPm+I0>ROtL35774(2{RrPFGMJqyO1IRqp4f$19IKZc? z96;kr%aTc^`=P>4@;sGwc~w``!KN$ez~Tz(a!6Cr1I$xNpIdb`J#@OF9x$$|6-Wts z8k06p1uaI^bu++pwG75+WTG5;-z&VMIb0XF-igfcd}JFRiE{b4^AjywdJHC+8S$m9 zxDt3)(?)g#?Zc>Mz65V+s}BH$uAj~(W| z8N0R`B(LJ_2mI0#a&!w;v>OqBV6!&+Bv9sw9DMmAyZF4>pi&wUUq*(PsoDJOg;x~w zlV5RhRYqCJU=xf3|(E*n@vc{rSODb!5T`zOQ&;cRys5>`+iE3%&+h@ z8@I?!=RUi4_Y@m0e&>M=G(|q_qlXG`+sKE_Fq|wqV43AO0f~R2;acp>@@kSETZu(V z)oxElhfKF;jVtTaBqCuBZCp;hRjgzV4cw=kpF%#jh-w~W z4od22_co{*(#}uOkY7aA05k_>WicgjlDXHXq?@0Tt_%?c9l#uv(*-kU^?b_tDd=;H zsOUlFpri?>%&Gd+j+=STS*+Y6TA9>9p=zta@rf+lOFmE>IQL1udJ3(BH0a@0BvV>E zqQf)VhwB^{H=KgAKkKDux1OgMtJGlxw?KeQ zuE|es)gN(_MSrZ=s6Ei2abbcBPZWcP4UK?$G9|{yP>Uo?lB$MC-VS;QKHI8DBI8V& zB+->rO+@l`&_wXzN=Dghk>DOwuZ%=GO)#UuYr_>$n3&0mSM8mrZDBBGKN_F$KkD!k z*G%vOxP>zD`2;?4&2s;bBeZAApdeOpPQ%PK5ro^fL37~TJvL>MR_8WN6gRMQIm|C$*bL-$K4!?r;#9s; zrzsQ}ALclc3kbapN0Irj$^;NyRXylok1GOM#-Rln}LUx7t z1yvGR_RJkIncu*7Jsh7HAD_wR`(q~f)^%|xS>GIh}Y`lZtpNMPhx-X&~tG#tNeoe8UHB4-vM@&woyi7IAgR>ZQ|_jVqtGG*QcK zda^A`nW`X`>2~clhorOAg31!Wd1t5e>{fGPqV4!(@<+UjU$$(S7(Ve>yuho}L&JvzBrtUrMg~$cbm^Ij0=3R#D>yo=Sdv67B-0sv=K%)|ILk zO)`~H56*S%#^fF+uA|eZilwBBPIO8rPbly$HzIuHBnufxU5SLoxmOfasQUMt`4(Om3ElC(aZTJt{k(!tl>2`sZgq;O5H9^+&7Z|^!k zrP49^4TT9CCq6hCACo_$P;w#s!G-Wvp2JJ|t?U4V@=<;&_OY_L9Qjs&w-tw?!e(&r)+eh$q9vx5fOA+NZ@|KU3A6C*f&SA6N(cB=fjBwZs++* zu@si)dQ7O-6ecD>WOEh?5j=;KbPQ0LnF>rg^iJ%_MvBOn4Lxb{)XC!3$uP5%CPj-* zuRIsv@Z_k}p<-JT?>O)i4i`7#IcD%u&Pl*pUe$8e``dO|unYdZ{;c75G>bkG(~q-G zIc~`=yR6%H-Lu|&);l`YXX$Us#OJJg5hpG?N{><};$-Nmkt4_1?vC5Ik$c7UFJGk| zqmo3Yteaq>;TedgE2BlhKvHd4H_23c&p=gq9r|oZQd~F1Bui%?87o&L{>fTA6G`4Y#t+7hBqb$Tm)BL8^`0>}FOQCMH&Jj2rq`Xp1l9QCmdEEj zOY(o+x__RLzeDgZ>t4?ULD^XH%bhGTgMVH3Z;aoossb*>8`k|hQv|jV<}flbuB+fb z*8L|FEuT#`HrX53y@SbeF)~>jV$Xuq<(=!^#ptN+x-xA1yVsq`_`vimO$6E&=soM+ z%Op|d-7IXP_pSQ~6IH8o4%Tr8Ce`@px{ooux=lF*XRkYl3HGT{N(tPS>D+Z6XPTg~ zLU*l5NcD+zUtp?e3CsGBQ)j8l7uS7>@zt%%rTFr?uP{ZlfZZHyWxl%ZJSM7cT{0V+ z>}%`3#bnX?H)X&rxH7K0o^P-F4wFRdMv8|fZxMWV-8D=QEiuvcj(GULu6vmAQCo3j zk_G(8x73xz9=+i)Tdc5Rly(vlUgY(ohCCPxyoS4x zO~sNjx})moXdo9ykjd&zg{QeP_qBEO;#ls?_GE0!m9TYnbPQm9A(3-99EI=uh-Gn1HKMi0>4p5-wA*a zK*sKMFa6y*`fdOOM<4jTI{F@fi)={+$Iast$(DCDs9ab_7Y0zlGk{)HM;8Iw7ji9* zk1Bhwm)6mx0S??e-1qC~`@pqY{G=^?P9|l< z)3&nAe^f`81K*>!O!(&Yt2+7}5ZwkLcmGHhzptZf0q8RT2&Up85_Mf2UFV@t5gzLL zI=bEy2fPr+kbG{aqZ>Q~_*jS=>*z)x7P|6GA@;F$OtQJDj&AZmVB>;ruA`ehkac^D zp6YMPuDYhWrB_M)B@yyk5t=cWR9)%)C*jv!3nQGCTHtu z2+4z9a**GmdRhWtzX$ZP%hl7qfHiBFG^n5RxPyd+suB@<@XhBL^>hS4+&d+4GXZif z=*W6H4j66(76ui+21|)W<(<8xo?a5-P;V~ognBw5#A$v7WdtYI(}^KY^V7KX^|T&1 z?nXTMj>(rR9IKnu=6O;*ofJaBGJu|3PbUM){jJA!O>v7@6O|SE(t3Jn2oz$vz?aq2 z%K+Rh;CK>8l4QV0GX1A|`lk>JrXlv__4IOJ7YBUR2LwYU<5$$vD?%_BN5HSFr&oqx z+@u--zp9>I1@N9KO_-9rjaGewBz%@h3iIlEdNoM)Qptiu-8QQ}x-=v8GyghEh7RrX$d_CoXb*((?P&U-l7T|i7ctU2UPnoQz{{Y0TkOUGm^Z%@; zHv#NgZvyt3`kU+NBY^feS=*hgl>T6jkg%udk=; z<4{<$1HGZ1ZUA%-rL7h~+H!(6R(xYU-3WrE0fDk)pW>!^x(O6vp+U!ran5h9r<>zI zXlMa%si#}wKyb9&$Xn~_);JIxE#PhSbQ^%$JajRDBgX*r)Zq4dx;>5rOB;DdJ>3z{ z96S+hku`f~J>3}xf~5t#tDf!xP#e6KGHkhb*VBW*_6POkwBCp6sjY!wVg0AH;PwU@ z1i_LNzFun@YM>n8?4KqBix$~#$$K==GN5vCRM3qKH_*Yr?;gi{G91!ChXUQ@qh+i9 zVSSR`n^>;s;=JkTiyPp8X26_!JLsnjPb2Y6s-#~dF7g@k}+)8v(FOH>KJ9R|CBZ(7}M0XRPmTp!WmVWdZG`@_`2WDAMh^j}LhtYoN~q zIpA~ha{NLAeHFMCgOd&82$_;teXL#lUjuyyv`G;Ogxf5>+dw}AAZc3y00Xf5$sae+ zk39&g%!XXnK$ihx2?8z^Gj?3wK$m+6FtHFnX`r6~k<_`C#IuZ_HqcK!RK$L+XrL>A zO6*$j3eD%r2Kp61NlglnV?Doapqr4M)TEUjbayv5&^ZJ_DZG|=`iu~aX0e0zr8?n0oW19?Y=-T_Fz!$oZ35SuC_ zr*~%Poh}TV0@%AU^e(^#9d7n!49V}^8G5%11iuLI%nY3gpsS_WEu(@C@5#`6TnKnM zkoRWjy@2$2awR)C6r=ZL=zT6K5z0lKm7%kM8gO`pQ_lBi=>0AZ+(O(3GV}r9d<}Wa zkP_^J8Tz0LNi?(|AIi{&T!DBD#srE%f=c%oS_c`IHWaH)|mA52KkW; zeFR9?ddztzS=%4Y&_`Vu z8Tz!#932IYNjkg9q-vkZ&}UqFz&(P;V zvv;6upn)O!LWaHoB4fc&`6zpHpH%mY8Tz7>{^0mooGv7YS8k$S-H;%RuI|$Wl!Q zY^424hQ0#)9vZLOTMEHfGxSvuxMpf?6_d)Hm!b1qoM?C0YnJTtlDMyB=xZ)cG`s+J zeumBm&NL>~@(k3dbwP$M06H{NOHu>>dWOF43V}MqHbS{|R0{iz41L4JL8%b;KNvY5#$`ku=e_2cbnlnPy#p$lCs6pCXn z%FsnF7HY+@7iZ{VV3+#Jd2plRB^kN|6vi1-DyEWLnxRWU;+8T!79 zOKQ!>{UAd>aB)e!`M4iu=!d`shG=DN6#G$zegt%Ah*9{DGxTG?-I3btz?M2)mZ8gB zSkk!EMr{eZJVTeeu%vByu%Be;Cx99IlD25_H46PSLq7%BsIO`&hQ1<0R{-r*lG&{+ zCA~63SGqV+NdfMv3|-~oL?s2dpJnK0u9T{I8K}|e=NbAr(2+`t!GDpVUjS}AG&C`L zU%u4pml^t{3xrk?;IA_DD;Eg0BEVl~=+~~KQZE~86#Gqvegn7};#6hD@xRT`Z-IBK z%i2XQ75iO=e&^z#Scv<5hJNqjpjU|dLx%nUoG~h?mIXCh{V_v-1Ujs)DEvulW#~2+ubD^jw`b^f;N9uz z=ybVKRO_X8WathT0+tTs&J5iNh>7h$%8H4^-IbxcTmX1jfV(qvHvq=30f*!u{u$~L zD5>K;8M?>Cf>|7UZ-(v#*7OkIt8e=8INz6{`&=|Qr=jo9(EUKW9fXu4x$a0x{6K~t zaFLe84)VbaJqV;*4q5^S`%s1+anw4HimpOLyrRK+7oY{Qp?(pW#};%09F>@@eDl< zKvuhwVJvo+U1`y-jOo-ECltHeu5GFE*`-D;I4EnaAwLOa9&=2+m&9@$fzcjmkh#oWRmZ_<`|wIhJAS>y$Yl~ z7Q_w2zPgb%0kP0Rh}J3&(8yLITuc3c;Tn>CYj7@NK=*sxpVG8|i8g=o`3%Zpz2?XpGbJ zmqz*v$c7zFw6B{=cTFQ*1G;{P4keJ3x|Hg#jr7+L2E85FwT*NwV1ofSb^ccJ`&%RZ zEd+v}2fVJ4t^?4mYRHEZc#_-ojdXno0yhhELnGY~f>09{=*C965uinm0 z>Fy8XUnAWY!cYkg?EXf&AF#!-8t~UD9%!To z0N&jJtK#?34>r<+K=(Rml+(Cs9uGCrLm>pZXvp6i>Fkzz#PsD^zn6H3K!^@Cse0 zmL}>4&TTD@^EA*zD*$oZYKp_WvWboc*sZy!)4aBcUIl26Bckm>y}F6UfN;CKxbs|S zqIUr6jwmq)`khVm4Ita>e5E7x`Nkxz8^cx1iMqIn{tV(S4R9T(tDERXq%YR#u-X(~ z=;kK6*#)B-Q^2=0(Jd~xiqmv!6Wt2%o?46<9j4ow=r)k-rAbr|>kDKJj7@YqXnHjb zv~@h7JDTVY7Xlp}$em4eCm=lz7jbupwE4T5=q?ulCN|>kCc4{2pmc1+Jxz2E5Cb|F zStJ5vbh@{R?geg8<5W3#!26o$J^=eP5X!@-Z2#r{Cc586L3AJWKodRSqGEo_gH7}x zP`l|I(|wkQn&=^5jUg&EblyhizcW$Ci;hqQmWXfhnwhOpiDV~SLk;< z(nOEAIB*Mbk2cYxz_~W&coUB`(PJ(ItQ^SWP4u`634D@WnrWA2&d5}}EM4EjSkO!h z036cQpo-Q<)-_WdkewQdvd~UOeKXa&$&gzoH#AcNk{3GE6MW)h+MG$sG*iX}frkg$ zwV8GW$Vd!1V&SaDW@>biU>8F+HB%Fi#u&jf?lCksQ?rW&*EqJNnOcA~h6}zajzViQ zwYp$1P64+yQyakjPI;P3v}I@0-c0Q-EGYudo9Jk!4!~TybX5)pQi`k$rPBy#+Ya&&Edew>Hz4fND~C$r1gVP09Bj(R;^+ z^lvoN6`<@k2zN}svYCDh!0rYBlk|s0X+FPirr&#L)K&`m56$!k4_#$g|6?=#5$L^) zBr}fde`=;bfo5+*qq-|!CNm)Xxtab9qO2i;%FeKTbu(S8~CPQt1Ir*1)yRbgc&iHy`%5X8M~4iw(NhHPdx~EjB8o zkGj`4)AaybrlJa4XKzaRhGx3KlR)WXkEk~`(~TZX>0`rgYNne28#ID~TR5WL+)Oun zK=2EJw=~l&0D4y746L^{)2$u`%pA;Z&2*cG2}a-Bo9T97hK%$n!|xrWuPb$J;L7AOm}(d5MHO>-As2Qz0ake7~hoV%Q4k?o_m_<9uEaB9_rp^ zx)&&~Fw19b*-%s`t?z55`#cOdIhgyK>3$Cboyw!7@hP=b_&_r~;9&$G8}nc@JqXNz zt6MM}KGaMPc{uP2aDQ*6zk7K?E@!m+M>GAy!+@8AdAOM#2F5FMZ%BNknI7>_f|ENY zKH5x=0=3ZS7!Hb$vBS~AAj?RcQL(j!mLT1-%#>j<*Fw(*(kjyExcHnFIvTi6BbGfd zu5F>?kZd*N_{eyC3!Mg_)w{-q#;q;%VZfSoc5-YyuQ^E&0SA34oxf`IxfVJPPcZ=vr;kl^Pbf6zid08&Ki;ye%Mb9&=?zxoeb=!X#ko3%^u zqZay6M3$rkoRj1~ZlNDXkVz>x$je&jG9bNL4_of#E%b8$#fp|u&uP8CXrUW`5rI}8 z6D-Bu*g_8gEh0`n+FOo$u!R=1GFF7n4AySRb*BtR3zqsdj#q1mL#zoH=o8F-eN0>G*-A&cFpT{H?5I{c3b4gGQ#t$_q;Z^%Zl$9E-d%&02M|NAZKbt9XEi!Br5Cr- z@xXLyjAKPF;X6%`Jm@6{mAJ8$HUVhLE;<5iZly`UIy67qY>KTkjnn}*HOTC=R@w?& zpNsSC^z>GGJy1;wC42t^s+pCrJn&SY}0U-R`c&Nwf}hM`d;5^{@hA`0h2xt=331)t#mU`gB}W2>k4e*$=kS5YW5D&|dC~+9(HXhl`c9{HxX^b?^l5I%dC~_iLm5!Db); zx;1@38$BPmJpvr+T5Khgw-n3K<={3tI8J~%j}RQvMu&i4&u9+rMaMFa)orvoPJ%q5 zBrj;A7l0%z80cYbZhB!Gy)X_0ZwvUMHhK|&y9IKGHhX9r9Sv-MfOV{PZ5zD`u&}Tb zSYSA>ZlgR1mZb3YY-d9oodS4Y0Jm-T)HZr8P`NlN5TevZX8^x@9Pi2Sx;AN@W(Kh;MIvS>6k9r?# zqmKc-G%5#pmz03ggwJlHv(qWS-={dIjm`lDS4%|x(B{r}4T?&$+D2Z)@-*2PKf!r${83^=~Hu?={;*}5^F@M`e zzXL@!mW#B-2q-yuOVd2hSyGc-Ja-X{Y9PhA&Hl!+637Uu!$HRwWZ1 zVQ_(0TRXLZY-zRJoDg7pJGED(Kt>KlM>};?r3gZZo$b^Kis3Y=QUZ!y?bHRj*lLYr-;ENGvRCbKLgpWIBo z7PiwuaCl}K2UT3@Ec)814=naiV}XK;q{*`DZ>RpMdb?X*W#G>T87m$uVVp!crEyQD@zSw4HV)1Fm{kcCgQS3B(mqCKkRWIG;v zx6|HL34|in{<3}AX&(^mSuKarytqhKYO<`ImQ^J|9xlmnI}L**bf>1N_-n=Vx)I#nUlTmHpajzp8XXm5^>lJFNg+)p65V4O!VvE2~n7 zoa{4QtJ-N5DE6vaGwwRc{_V7XRhmQ}CrW!jI~@R;R0{}Q$phQzV34FXF2|cZq@9ij zI<<0ACNnQ-r*Y8jlU4vxpYoJ;IvrGt)2M8>@{D%+H-J;UEddTZ%Qv*s=RlU)0mPik z&$rVhAnH#MO!}99YD-{CqaBTU&`uA4aIfk)I)3AW?eyRbG|0@Md8nNpnt>+pHvisEe+SLJ>9VExoBwF1 ze}HnobV@Z^`5x!P?es86m!*@U3bf}X`>RLV>5&=8P$oXvqwVzQ3}lX{DRXeC(zqg3ud515k)BLI;d_2 zO5dTBdDeGOJtz-I*EQlx8f_aor~&i`rqipkvgI}mG98owgB9rvM2mJDKq>sL9klBV zw4zbRXd64IaR%DZZ!3bR&M+K04rG4^_0K>fa=!sDt(fQI+QBxRU#H&_TdgsjL)d^570S5tPH}MG*^g`CxcF^B|?R7cZHgH`B-2#YNu@LOy^?qvy-3hQ) zn6bLPtAp+awB5;C*6(>TanN=UM84i^;Zg2;I+@Er0Ca1)zmsynIa7AQPIE@`}gUjeE@Z0D?-5(cYj$Y zEdzlw=@$gb%kaJY;Z7O`g)>joWvHvs4Wupaq~&oUXq!T`Zzt^=CvqHnH>dqNX+IF{ z7gT}o)=N6$H?8QT6(DsY=1y7of*RVDowO3PPCP)DiY<#P?y62&6{kbNq|@!+N&Cm? ze0Sc>?|@D^0CcNt)uYP7H zJu^-z8lvOIy9z(6lb#i)44rtF^4XpAY)~E$NE>zG18twvNzVbj6aRM1DXOutspodm zbHTs~uIS>@?)dXM>3MM&%0|PU-$~C0%!zMlZY4E>$^t*AlMafb!PG||+(`!my<1RJ z==WUz_mECHB#s4V7rVNXRs$Oj0N`6OvDFuJ(hK51Ftva$?4%b073G-bs&xVsS*8z>C?Xi+1Ts0fW8c$1Lcg1!>^G zld0>XI)J@tzfd*Bm#OcfdXRWCP(h-`C*PZC=%NPDc#}0r?xF{~Xb|XFpAH+T7>8u2i-yvmU~EAbbb6i$Enn6}e+FPt00@@9ukND106G{z zJ?Hb9F1i80E(>Va=8au+JJN^z^dQSSy68?I2YjTL<6T{JKX5GuCu{A?GKC+sI-^i3 z$7=0yc5u3xm|v^X5VzJc-P8dDKNhDEs5S9Zjal8)C2G zfZ=DRbY9w<<1c2^>g}dpAo)2njWkZ21I>0*7EpdnOG86rS=ddBfa2G|G|DlS-MZ-j zKgX1=;J|Kr2B7?SmB-7gx@UIN^8w^%w=~c;mV>(KMM$^HBi`f>?WV(k z!C)*tY^CjF`JFuv=QLl4Om%C1ih)7HUXVfkJd*!E1SD% zvzHDww&raA`OFR0!f=&={ud1Atd{)0H6%)#kvi>ZYpzbHxHT>nzUCy6I;jNSJ^*&i(Ul z`Z+)Yj$EM${GywF2VAd%bFAR^-Ek_W&jEDKB*fbx>21(k-@ zArrL;#q6xuL$3n|U33EEn%O`1&>Ml_({@QN0s|{^Vs3Bhp*Mvvn3w~5a}T{ageBga z^&H?^dgv{H=^_)EY6lKTV_I+Rp|=98D_4M(W%+@)xAoB5fYyeq^*OOPT^f}yiL zz1GfKdU$^iy*~sBer_1x13mNs0QqdLWxruJ^1&WD2ROG}onGYJ9{M^Uu2rUZEZ^v% ziviXRS!j}gy@q^A5B(I-9!EsG3%R0)ehGx`kpwe=2zroT_0V;|^0`1uVS)AXy}pO; z1G3FV%KqauGEH3SA}-hxS$ZB!Q!nk-%N)e5I0ohRAd7oxAArPlA_fFw+sN^wBlBCv zC(e+qVYruuBUsdHfL-28%YhZQ*tmRogT=UdN@L$%+BX6NOB=RdFYO1IxOl|nwxu{( zEKlV})zvdAdTB)j3XVQ>WiPD+RNQCda)mx8j=xSZUDZpgB1omVh1|cF_6Jhj4CC@V z-FXk_fL=Nv0t8DJcwjFb7y+V&PA!xs$0s+*>Ul;lJtG1HM;G|aUV0{gkvgyNB}i+E z6WrNKrqAl7XGO4J8epH@OV0*&3GRGxbvM`(Hf@lspVLdviNIaf2L9Y$dM@CR+S;(m zk@pG=-DL z#|lMR6o>TEArUlKN6@Q#X*JMZ^M^(7MQ!Q|`4{xk3nC~Xu0g%9mtF{zxP{x5bG(wD z7$21qzNnX86v2U;gFCdB4h1e^ThqMLwlu*{e<%qL>!rgYSnv$6YkFx7u#q01JiWoW zTJG>(Iy?deTOWEvFC77Br2fm(Q(Q~-b#zDe(vcBx!q*2M)k{YK9BB*XN@@JmLS=KQ zIK4?dN_KQF9UXxS-VylPURn!ytTeXdCnnb9r_{xjFYcumN1%eU2VK`o>i~_|>_nkb zDU`&k-KzGE>7`>LXfTeTsh0?7uU`y@`eS=(J%HlIEMt()SbtJ4ZH{D}GT4vz(kFoS zOfov!f3lZ81+2I+%j9kk_n+>iF9PD(Onkim5;wvugT0E14frq2(u)8UH)iRq<%s`K zPm(A%XU2y6wOM)>=-E-hK3D;9$NG0?=|ey)u@LAq{7Y74lmBp*KJ3F$yD9LGWa%S5 z9CL>h_(!w!QNZO*3_@Hvhwv%PY~o{C`WT4zu{2O~iFB&7vvfA7zUTu{Dgp3IS^5%ycJq)HX|To? zznrBn11@hVaY`qK|4Np=0=#cKYw_r%quN)q^i>}T)k5TXSvt>0Lbnk4wJd$jmsTog zfsJ*0H?s5%A1{nM1^+); z`XAu!u0q8qd=jJf|7PibLA5NRc9l2b|1V4b2Qs^*KzZklb!oWY%+fb~EEJDnzm=tL z`B*3&!+twU-v(A*3S&2H2X2)8PL{p{3VE-MW5FpT-_6o@LE;;+_3FA*^LttPo{tpm z(>&)ckr!s^LLVs_Xn?#ZOBVrYH#yZp4cI7nah5IyK4!$0#D?IKEM4M@f_k*xUzb8( znx#v9B-9I$-_O$beI(Qikw3`N4}grQXMv5hKg`k(fsd&dCHPU6egpyz#_2{4^}OfG zrHVh!(vN*KRE(o9%hF{&8fwPTmuKm6pyjPcwy}ByM%|xe=_jDEJqFb!r4s!#OFspX zZ?M)w=2G1&vUG)ygt{T}$}C;!BcW=Dyedmq0U4=L3v5*US(bhVe5^*J1V7Kx&q3f1 zm*!b>sogKK^a~%D*sJ%h`Y*HeOCOk+s|Wm5mVO0bWVkfB8`XZDrC$SX7lo?582&d| z`VH_w#hJ&=rE0&;(r+0#J?vmb)dkq!W$AZ5S5b8V^7mQ#y)UoqYsi3&et*c)AApZm zU5wz5S^6UgY)1@@(SGDyYWSxt{mF+yzX*sz~Ma!ZzO0ZF$vaOfqATeEbl4}f+Wa9fsc1HhkyISUN8XX$ny1TGHfjx5~) z5U*)CgP6Alac7q9^kIURy8>}nmhJ+~Z-tTlmUn0AZXcR(3~t}KCrkGLDi2DE;SFt@ zCozS|GSAK{ypS^@E#uxS-RpzFHwwNlOZNdRPrC}vVG!m1EVV6UlwDs=1f_i;?E!$T zc1rkU=|Xx2p!N_Gjh#GmA-w`XzVA|3j2$?6c-(!T)7%R(+EZ;j;j z3+cDOwHTa?mmJPx0#X+HQ2|k4-a^UM3+b-_?-m8SF_dc;(yhSm5yhg8H;zvfCi7d= z(!uQu>GpI2)U!u$$3nUzod7xbmcEegOecuh{6e}51i7dVp*7vTkRAknQ55gk)k6!Z zwU42DSAhnLVr_lY1EM{v5P3D<+ecZD3`I$7t6SJdi-6oK9T^C;TOaKOnx*M9o;-W^ z(LSKa#wess{&8V~;k(fDQ=9R&P|O%t_0hrL#8-RUDAx)P>7ye6;+w*45R5~8k)6v? zeRNa=39ImsNB7au5hTVP54pCF)&iMR-xEdTsG~lb0q4bi^kU%mP)P}1xen^9Az0T( z>mmeD@7Us`xDJ|^y)ea*S{~C!$3zIAWt@Qeh#~}u9}&yp*giTI1k04Pj?P{tMw8?E z=s1uKyJSlBRJ!B)=y=dAap~*|K8n{5rlkHa>7$oK;C3zB@Duvzgb3WOY8!rHADsyJ z-mWlSeG3Ys`T9Ou52AfsBBiQSB4M<;=bFV(jVJM5EA?xR-&$QRJtK&LYr>7z0* zd;xuk2^xK+kEVg<3+O|%*W6F*qc;HC>tgMG=imG2&4BQA`Zl}xfcKU@dMm(-B497; zxAoC~1KRFDWf!xrOw`4PU2d^n<}-bCKDhAv4T*Fb=yo(0^wD>K(?zIpFhztp#hW40 z48PY$-;1MRi9Y(mKDscDh9Uasi~8syp!W(SmUgHKj16AgM;C)e8?KgKnPG(Jl0Lcw zMEvN4S2R#rJG7Vf(WP-1bk(r$_tEzO(>=8oL2$D!ANoNb{UDAM3@zjj`{;*2YSVIf zdXIYjsE>XW2ZOB#{&64uI1Yxiquk-bm}G5V)<>7c!QkwHFYlwv0oE4o$m}@yKk1{N z#G!((ed_3^ee~0~2tr=h)xV;Tu84yLUl)93A6*IXo>41^y5?8)(N%F0@Q;%GtdD*M z65Z$MQVDy!pZC#kfer1_>GXcrN4Ei{2V2cDfd&2E?R`|=&jfm?@d&*Br=g!(0M}lO zhTFYgYd>`ZrCkIU6$sJOPtO59>}Ld5Plo6A(;-0f^FVGBmEGXS`jT|*L&dtlBl_uR zFwrB30J+`Y+I~6#7+vN91KV}&2r_s7{v-6d;`68LNo98VFX5Lip1O4prkJ9r;9 zMJ|V{+xqFY6f|0@hrYd^ZcjnmZnETkM?c*Gw02I6B%YUSoRd5I=}ypS=fuz`??lOD zi034CMeLO`LfIKQ%y~N`Zo@4gK^0eHu_v{VvzA4SZ&Rz67x7e_XKB3cfr*7Xi04 zEZ~$L`{Dt*0u-X%#5j9~c;x_H1$=*)gWVQ>HbB1tEK<_~rgKv5@wWqXBM3y}(W%0M z1%W5VO#^f@;N5{3vPHaA0*1scmol~BLxe_ z4z_cUI#YxQ+g{fo^#ME(3gNW9{z2LoxTR@0ZPm)=_Zy^xK#@zM@QnW8K{^EZ{t$1s zz14%X2C#@N#oONDgES6;B`JJ8F-{qzQvvS|;Ii%AK9B%<#J1L0n=1`cMX)dkx2;VN z(!T<*$N*qVPL6Vnc*7vQ!Gog0W6*yeq<;r=q2U_V_kRr1TL9`ZAg7++I!GTw`jDR< z)bH7YbPkXMKGG}Da|h`&z;zm&U7w#Fq%R|R&`S>T`^q4F6~KNE=w)}_AbkU{W(||| zR+I^R0pF>U#jX6rqF#IUf(+jsqzi>eA=0hKiw5a30QtHlpJiC`KN+N-1I!l%`Cz98 zelbWl0LK>v#c@H&+&D-#fr2jzic@$+bn_tH0(^gnw`=CsLAo0-z6!}_8*gd%4AQ+I z;5(wC1fCf84blUEcL#7;W8aj30c&$kti3%xNDGFbkB@U}t8R!|0pr-wcvK{CQ&ZU?8U`vCM+Nn?e2Df1e)l-uE1vy^Xa&# z&l;j<1HL#4_XK#(5IqmzP7f?==O2Sf@}RvnELJ^-57E)!GY|mX`dK?fCjgfVaOfP1 zrSZaK#eM>7{Sd8>+d{B;XfE`K$AQvfn*&&kECh#`qESgm#iQ6~S zDDP=ZQqp*hG8~UaqBJ!1i3-KptdTj79wl;?IEfQGDJB7ePJkeZ8#`Np zBC(U$Dbg%O(dEAaP0~La8wHxA{i6#~6ll-{DUkN}J9l5cC)uw5C}EGtdw%!abI&>V z-2Hwr-_*6fex&?8Lb1`^Z>@X%Ti{v0f2903L=|~d;#hxlr2KcpPvr5DP5<;r`3<5U zjnTeaee+29X9Ojd$a~eFA1VKm@bN6qNPz!-r2HQQABe!(r+!wG50;G`PeaiN;C~+} z|4!8+K!yJFe~y%QkBV3UDDO=pqvc2jocE=BM$3B$J{$^}TKwM8@*x5r34wvVA091h z#7yKciM8vay8*=ee*^j+Q?_9tR^>XyxV6@+wgi5h`!yYoq1096E33PmPwJB6>8# zI|>=AqvdK2nu!>%jh3$wdL-hSx=&-YyiVk!5i;F)UKPMj1E5|jm z=wFPM|B>iNW3=y3|7o=RuLLEQ$a~a(8!i8uaD=c>0)a;Y{LN_jw*()Ez}lz2tjS~1 zeWsjWy>G0%XAG=HbJ)&Skv*dm3&{%nhSthDV!CIJmSmf|n zdAJhI0s{1rvGNGflR4h0uZ@nCj}v|*2M_#hYOMSSkxx`36QBF&SosX|OjYNJ%>MDQ z@>%9MoXg?+-kGuT0)bge<^AvZv2ul392O`4TfcoXMd5@b5+y4joD=ZD)7;thljR#(Zn6d@GA4*9858W91JLjqWGq9mV0#j+LLyV#zte z{?J(YL&RdFkn(MAukoCtp1J<;Soyy1pCA;2rBHyNwf()Z z@*faY`!Sw}{4YC6vK$VZS|A{w_g@FXmg{<-yAFdJ;4Veux?dXHCe5td!2AJyZFno@~_E8t#o_1uvPudSouBUfFFUaR$IJI-r670gznJ#)zVY&X zSsdAVxVy*8yNMf>rS3>t^c~nSGA!ReUcNsICCd;xGG2}ls&=^|u6(;lHGbWc?inxd z$)d?PLEk%G-b=LF&xv?P?fJg(^1dvVoFnZ0Gug-Vf%twWMcvr#KKI8lxfJ)TF0EoFS7{9b~O<-z&ZQlBX6Lo(%C z%<+lxI5QosE@`LP2|B7LC(0*>WMMgRmJdvnA7GZL>QbV9XnLZY9+HEFL^)0yfI#I3>TH%NJcD6oIzR64#E+v^Fk=~jpKg%qSW<}EN?OqK! zqKZ5!X(j(+qWmM`C-V5n8UJ{q{1c)d zjnRI~__q_~Hwa3+C*LN%IZ^&k!pE~bBLV(uqWoV3ABe!Z9ehrcgVNv;^h1;7hjLJJ7t`bV;mPvD zgg#Cdo&erOOrqU6>5oj7A7O^6D1$jBZA5W7J~~-`lsV+hPOk`4NB`F8$0o~<<*=rb zKKA=2%kRr!O)Y)w>B;hRPHL~30(Vk>|77|7%yD!`J)WItnJlm5@Lo*<{K{mxLVQkFR;D1w3zOvwxg0?)f*dbS zmM=1gyo4Otcc<6g*!A1jOOxeGIk2ig1b%t4e3{@lCZwZ}S0>9fBIP3oo(#d@V|}u` zMbKnqR0T@L9($AJmvW*ic+~TMd9wUlJ@TH zM$5#2&lcs6B5n7rcj|AyN0q+yxsWmb&OfgM!mxLMK)$obe*nCb7k>a7`Vtc8cli7f zkazUv5pcubrUIb)K`Q%P)KHXtg$k(KKMYmLip}}<1rp^KIHK;!b87ewSU=$YoK@`z ziAUl@uib)tC??yM0^)-nQAY-(jwY!g%fkuX=lPIFH3M`oeb`H83zKiRA^p)PU4Qxw zl!G3{4{#APrP^|wD)(mj3OuOU$Y$H#*b5x)gvK9KPV2uVqqc; z(ICx>D?jt6|LiXLq1GapA7AjvRKvmxSaG7q82s>Y)G&|F5Z~H0L@!|BQ^qDWZt3`Z zuTPTcGz!5dp#5ZZ;}+@oq^wDg8lYDO73rJUD}$s=8BDfi;9J8eL!QMG|8{>$-y?sL zpp>Vc&$FEeJ~C@ED@)~+=jBP2r-;rR_++%@s8U0g$_@NnK2KG5MfEI#PyAY@G>NU0 z@)m`UZROXEG=zG5x2w_SXAG@vT-Kc)V*MT|$~KA;na$_v$3q?L{K}gcC9-O|Uk@5F zTaVZu{4z-!QV)_{J?<@=gT_W<&@jz%+)ohiFbNBT<~FPTkV#+`*LKI!Uk)oVr7S`E!D(skHk&U) zg<7RaOU!AK#EXq;O_J^lM_q@CB4Vf}35YcLfX~u26Ki5BLA1j`yb3`q_F$aJHIyPx z3LDEr9^||AV+q7Hn#U_>VIh+hD8CfzN4BC~14^}^oSlJ~TCi?mGn0;RBh4UBl~*|` z^A7Yh)CQA)v+eM#)h#({0jYxgP%R(Hg$z@Pf=nAwmNp?hfhkX|Rs-qPY9O^*4Ww49 zfmEx82V{x6x#3<7LZwFIL^lQyqt&x?KT)20P;u~>pKjZMT5Cfm>LH)ZB46lqdw42A zi|D}*CU83-De|Pyw{!0*QM0I$b=yaM69;h*(iDag)pQnQk-COfHD zkk*su1oW8ia8?M@;L=*gfhEbF3oNr$C_i1BRtK36_$1M<4;!Mvu4(rrjktA zfOn!2<`Y8sr+t#hZ&+7U;8bOXVKqpj8zZ9Yn9~+Tx?j}L7DaF@FMGN)x)GTbl<>4g z3H){~ilCt_N&rJ!ltSFDMG0z1iz2OP=&$6 zAk2EdE!cMC&RUWb_N`7eLiD@T1D2KRun$LRAu}hp%HeZ+z~=NiX2*^lg#X9^>*5?y zs2N9eK0(BDT@G?W?J~X6K@~CYprj3PP?9ShltkyCp^z6F`}nFdg>;T9_~DLANQL7n zlD&DE*5AA5pAsX-btWWR`OpL&C6v9IEp^8L^0gTOqbk@m2o0-n-5v$*5m^f((dKBk}7(Ey} znqZ5=Ma{;U zL$k3Vrb-mAhpdFSezFSU1sx5Uibam1L=`75R%l3K1lNCRL}G*>ZX_TxfX?`iQ?v!; zoyR$%0jA#L9L!-N_j%lE$v|@clNOdbkPByqQ{X|zl7gyS$f?^fihU>*Cv~C>Ch?*a zle&>>%to5LAEmrfM@sFHcv5PQoGZD;$`QHmMq%U@YUDpexGsr^+$e|XorNEW zcoJ#O_@o6cM6&D4NcMau6(%>zVW!+DN7Ca!M3!#kK!|mpR20u|GDPPvDbe#35<}%w zjdCbW^(aSCv!fi6BEL&T@*FM|$$1)Sp}hhZOZi3%@2<}hD$_XH@lu*q> zMmfxHMmg`lynbOId!aP?0sEKR^Ee!(#j`V$U*!mAXO`wBdq*5Ql^F6n6q9|14?3bZ z-#h41If^z5`mE1GA&LDDlOGL}cPQq9>JGLj6^gLQ!fzQ&z0`-TEvq-;0{Tb&7=lKLYmu~_`0GScX z=hl z7Y!U=QvIV)av?7!tE0G?oc>W4xg!_mG~{zQ71L6C*m9F9L|GFb4h4^VB&s$oKdjoM zV=z)tZBmC+TamNXCdt%`+K#I2ldG#ul&$u?tTv}z#$$!0Nl;7onc0G9GC;XOx|k}U zS=CZz@vVUTQnx3a?L)pSuC0i-+6kHiCNm9&F>nDw81DnY$sy%2;nyqX@i#T zwUSVYbo1QVRB<_*ohW|3uMJ~RPoauQ^$xEFnj)=A{iFrz50z!e_M~d4Y?7m)erP@d zjQP^)_)eZy$1Qt?79vl5o;9ha^;@~n73eNA43|)t1GzzRXkr!}9*s>c*;9jvU{Nxa z#tzwCQF1@eN)rW`T2NKJP!>ClS$xW2gO!y~9?i|Vfo2m{U4v*$O|J*Vn5B*inLkxu+5WU$@sbo*w@FvFtP#Bqau^TpOiv&&MX_X$~G zpROe*!!zvr+l_YnY-2|@7AM)pqrA9-d80Yl!nfhC%idXz$L?C+-r31mr@y-c5iKHP zf&t3a?+c*{88I9{n>fQ{7p=P2#+X7>DSdQUmp!+*7Ez-vDsn)!#32solk*NJh`a-m zG}HkNpuz!3u5>^Ws~wQk+c+R0!yJ(G+c+R0RSqb$Ar2_?+c=WP|+Vi-9Ysbqf3ipPpE0Hc_=a)vJ9fefl2l&Ij& z(Yd@~0lGvBuWl-^%laF6$I2(!e*y)N+?xuSfryM&qJlKc4Mk#2v$$}ee@AbaK&m@@ zB?QPjd)uO-qM0#HuA*$Aw5P05Qm@j{McNpcgZ6X}j~2!ESl<=X1{r^QTs#;h(UXT* zon|0!Jyhf3LVcXaF7{Q%4a+L$>;_Qf?1o;$M+QJ~ehDUmqL*-1A~?GgD z*$KG=XZK*$&aOcI$UC+U5xKE%;!GRc!D8=;{zVJ7KqVb^&Q; zznfDhGZ`2+e^f4LJrp41YBJsG;UL*f1P=!3fhXQ?Nzz-8%b_qS>;xREq<1-B^SOTI zaFiA@dn}^*eaQos=FKXcW*8E4Gij#+BHTRLX^iO^jWIc=F{TF^a~d!t*D9^dB*oL^ zfap(%Lz&$|lT#?!sfqBT0hhLU*$8Gk5>S#p#p#;N!rmlQYp!XH1Mu{e`>wk- zd)@7#)h$}vJKf%(P_G>=ERBq;qC0M{t`>8})sX@}HF>_MHtqePz(`VQu{*FgFnHw% zeu}t&pCHCh669=z;bM0EeUwQv*A)#`hG^dXJ(F$@!P&pfAqkVP9PL`W^mLch(5!>{ zk};9vp*T(4HBNLRJj2s8dHk*PJLs31ePhVFtSuXXmesjBGSF3uUr; zvwi%u*rSh@HfI}I{kSAH+p}uCQ3rXtI5}PL2J7lvO^(Ub)St5Y29`*M6Lk8gch={o zj`gQ%#j(PbdKInodb4&-G43@7yS+|Pd-~(ZQgDS1+x&A=r)E!170nKIOR*+6H}%5G z`I(PRef*hMJGJ5i1t7ENU*#Swciw)ls)c&yZ1Ka3AN#^PeJ^?IbN#9igWz*G z2|kczbqyPD@6=zenE=%7&+gzmQ^t9ld#(PUue`iX*>!Yk@|Cut(J$s#?!*MDmEb^r z+ZqVW+N};(yL#J=!3el?d&T-L-kU619Z!+UT=OflJ)CdoV|i3=6^jw-0-yH4S0x2X zA6aiV`u$=Fk@)59_POr*?lvaqbr@d^O^+~d54Kk~P2pjoDytZD4)&a# zZ#Su|6Z?yYOPoy56NklcBCD`(rr~{LT+av^< zA9VZj5+aS`+lu((FcE^yV4uStxbI4Dx9QXa)o2Mqn;-{Rhb^*>M3++sF#N+nRDb(W z)Uu*a8!XS@Wvdq|Ym8WX_Qmn#`>eR+3SoI@0RuzgnSrAQ1x1Am@D&1_ZqP?{St!7_& z#&kgo@2sx#$cstpHMhG!u3@BmLskSl<8J5xr`R*vY8OHIrqL>6YD3m*$NH~!lyBGX zuvYy{h_vWzxKYx5!J87}~g^i+xmq*WjdU+G@70Zw*ctZ`Af`r#bqm z?dyMU;a`!mwxxc1%hVHH8(CdGb77u!SHdIl)>dOsbn(%T^_x#27Vk9IF^o{M80w(e zM_&TV8Ttvel+f(Yj?jGR#HX=_et)oK#^vah)*G1MVP~YdVG1HLZal~HC;IUqkVW?=Zq*3NnSX$FOwxuII)=y?+?7!KQ? zoJriN+GwdiYpf_uv^|sp>uE4c1 zLc*Vv5soVVzOZ~zY4ZvOv8b}C5OW(Y=qtf`7UJ~=eYP7n=I}ahKoE2B->dCr$46Wf zQFOF*IumhK1p@D_a~wWhJlW`7?`!r(kh9|D%?>7&CZ=#4`8t};jB@?rLr~TA)}X+L zq}G}}DcR(P1Q3wiC7Ert!vQu6a)SU=7R^NW86B^F)QDn%Db6ZpIT#CZWPquV8ysMr zBO2s+Sx$baHC4D=7^s;_nTQeS}pco&_TG0_-fW(+eh!jLQDh0MhoukQ$)`OQ^#7<$67`5UvW>-Ccz@2`wOmji#=YF0>YM;3Jg;OmQ%eB z`Srd1z5VZk{@a-47FYMs{qE!cd#CX4DWy8oruR-lZGSK5z_mc_*0<*lYX(T{CSuL5zlt^XRttwINm}# zu`aEBm`f~?vxH7+ngEtgRka!ab{ef7L!c^uw!hPE4eC?Hl$Jz1XLTP6B+!tjr(C?# zTxT0Q7~gEvuWq)xjX`~HmG-~{%>8-l+VOA!Nas}TIT#Rlu?D6=;l1W;e|N23yZWg! zGoNhCynb@#`>uU})7@G82hmT7qZT7-WMyL@=d8udGkWGq#?#<7(6e%Fc>JTJNJb!a zcA6ZGpl6>hy1Rp&U7Wp_ubFgvvBS^r4SHBeNtf>d&HEw%x zmyrBG+@Wjg<;AO@J`zxMpQ91i7C#Nd!#bMyGgl30FAe~>FNiRjbaQbZqz98`ugPyL z{xHZ7#7#<5KezbvdR4}%XzRyf%^)SC5Ttr~z+Y+gk(jSF2-(UaJ*p#8GF3xrTBEHW z+YQzYu#E7KTSl1GGZ(B5y?&2cPPnM1hghY>*b7cM-|6c$Rmn3sF1k2JK5@TZD!n|s&i8*_7eHZ&&6pj+1bqo-=xkAC> zVkTT?z+CJ#EyZia^>%ly(WWxhY(E_*ce*p%_`o10G-|fEy1KcGk$Mwbp?W@xX3A%N znz$>^JySc+)rFWDZWk*L$pW6nm?^WJnfeJhOWnSN9UzkiVV9J(nisp*FOn>qfUDYu zHWlHH3;rmUx)9(~JNtvJZbyCxSw*ti%JP}B^N2)@Xy@jid*Om9YU%uWl?=k!<%N}n zvu7?E+W9jp_(NraOD!#f@RsV$Sq&p~y?xV6LQL<=sc{{nDx3g^XC22(n=*;3(tX;@ zDqU7x&0~%ieXQKf==ln98pGX%aHxgCP_2A@xtdLBrVEPX2Xt)(0z$d<5Ghr7rdGx# z1saQPc63(k6)9=^9FVj+>+M~%A_B9l?mTHu7)H>N7! zFZ&9jECC`B%-|R(1nXxg(dr0=zV_OsrIjo5E3dtVl4#^J!L*yQ7)`4nqAZYV7hN*J znDNrDT>q;2MPY4J+3IX|>ou(tEvUAdYI6uE5Dja;o;k>Tu1KvwnRQD|ojh%_3Oa-N zD_)ewgiZuYGtRLxfzT{j^jXJt&Med^-XOLX9LF}wGHj2GIi|WQ8%9nYOa^)qw(&U= zEOt62sf?)KZS!1JP?n*_W~{%g2iQruLO^vooZ>d@dh>al=5O}A2?X;oBDOkuQGp36 zt5-YCLivau%sNfTVi4AZp1g6(TNYAGyvkH(5Y0jQ)Bx}!?vaJQbUX1_1w9T^(28iDUR=BE@6l5dK zWX?bqE}dU;BRjYXdd|7pv3jGA_51DS@&4?w`ZiAL^pBtX;6`z*F1gvpj$`Iq*T2l* z*KMpw!;6liX^1Q=3R^JE&292~{`9f(^sy^?nqmrf>JL3vi%;29#KYEcCJ*}u>}tv` z5N1HBb9_q-W;eRGI+%@a=+$Jb@nFvOowMcL1L^+BcN?}F&N5Ta%L^B)^N6yK;;Ufo z?kzJOg6&nvJiB!1{K5sSZ?8Om1#Yj-K+!UKt$G%F{+Mezi<$IKW2>ib{=gOl0yP$# zdiB~5VvE|rYaAkJgJY>&WRYb8Mj5|w7f_Uwi$)-kDMHG_l(`lSx4ivnG)9Bg`_mq) zD0~sXKoQO@Sx{dx0OOVnxAQH8i+-D~ zY6S8}#+!5aXM2Y$p_}m0#zq~h8(v;im7_qfekyMvgF}?EfhBF#CG?q9mTK|a@yr@O z8O^{v8yLj-VM9&G_$@){5g3{ET;z$xx=RsLsvKwvSn_4F_}&r6-U4-ZVN%A+vCX^4C|Xb!(tOLzxbJV z>D}kqPxrA8X-Q_3`w67JXqZf!h^tfJ3wU~PbMPUHKvYItp7~aPkV_w$!Pc~r(z2`v zBEV0jksxdrH|#-CgCQm8>n%7PZ84uCV-WwA6&@{{=Q5SzaAbttE8>Sm zhb_z|^O%^;bPck2EO!`Ncv^vyhd^5_1C{LO?1TkS98<#@1=+dUxfViL8Nf)m>aZCQ zfrgh^^YnIqNahcYyVhf7E&$!us<>9t8mz9?-4YL(P7QE$aSA);-L+4nGn;Z@Tc}1X zAk4230&{6x_T$kCV0NtD-nxA|k|THZw?|V?oHbsr1IPP$U?B@MeyD->E{*~CP|8ss zdU+q|a_A+8+4_983@gmm=Xc*eAP%FG5OR3xI0-|yy1g56fX*}}e5a=V@@oh0y36eo z>+N8=UvO;CM$_pc*m|zo&^^C!aeno}^3n^JSI?eVnZK~KyfA-dy6}=OFVCM}cvY$0=z<<@S<*F#%l=Mu1Lkp6ouP{*19YEgKUUnj^vbiDs-ye*P9r7`q$-8l z^5gNYCU6efDVEMI&zw7fefukgm8WQ2$74d+`Qcg$C*3#%Aui5?5d4MF*0__7wr1Ek zU2sUYDhvJn-Z~fS^(n^wjnVmG;NW&A~uG1 zG);gk3mTVy5Le`1?hO0k6-L3)ptarWbwnIB=AFZ4g%O{>n+nMM)POQI^g;`dJ{lw> zFu&TyF5;Oo05HFX0HYuP%&#FJ(im`2iZT?9r4*t}0i3RrWe4>b8B8ammJxK0?DUL$ zZLYZjqrDUSa~5Z)ctSBQLqu@yr5B{ggg;g#kWQ^sG4)wa+*|8lyVczgX@fpK?r&Xx zVy1Ou;#{4s>?Dw*OXBo6ev!JnQy$hx)BwTpCtqXMqJCOVxt@NFv2J!*`_XI1Pvg-u z0P%~cn+irHc*Sf(v%O2kgFnK;SY? zZKt)@Y_B3`zctur0%Ace5XZQ5fl~ur+|a}!kHVa$fLxX=vpi>uWjPz+?ItNpNRYO6 zh$F*m$6*P9U}{1o-((zzWL6^IUxdih$xd{da0t1X7nj15X z^>qY1FRE%5Y7pzizdNJ#Z?>{Ve=Gb~i`0lpBrICHBqj7T!s z#!CW`YgCn(c*?g~V9aM&d2X=#E!Ik0EyFo<=5M_QLH-54D{mx}(u=lt%<&eHR ztIv6K^K@puv)04ZeMZ5p^IA)*?p1E*VjHO2t52QBqkWi`pi!zd$3Y7#P%e&gcb}&- zykp&DhSeo?p4|m9GoMo3quL-LR7Mi16H^nO^Ro+QPV42SrwUy*!l1A7dt;YdZBimE z$=GGq1Z7O>^ngY3=|84zJerea6P;Y2&w2=njM&2lq6+&=1FvdL%$=z^dCw+oZ-^dJ zV@mgeYpn3%LUAfwfX9&%aR@PpZSkS|Mxt@PhqHQ};wr9}nBT1;W^t@cFVxkKGY)Oe zZeszIbEx1TqdYBr0_P7xTOtFet(#ax_s`49(1Zjj$JipwYVF~v09jf%#WwX)w^JKf zi=rRjMkL0l9qp-j`dI%Ndh_NiTq86uS+bW6me84qm9x#%!8K6Bz4BLF#fB@_QGPSXz2+m8nP7{A}j@($dUxXO?-A zs$Rn%bQ1iXS?xSpyt;~?6R)nGz;7plAg?$U#hd(v|9LY*zTD#{nMvmQW(L5_XVDE@ z$7+5;#u<1az(cj3{sb69@J455;-JN4)P#3GuwaUOAlGvykt@bn8^M$s5x*<%goMsW7a++xD_X7du<~ z*y_ZY81kL1EL-Mwrol*2ved$32({BEl#jFHOXY&#zO%9=oIY{&Qzx#$QB_}==E>Cu z+tHl(VyJ3sMD;>#E$zJZPc&{eW^_wj$N5MCIqSBqPW4%;)KI-r;`$cO4=|vyCRb0M zmVamE5!`z1v^hCRsQE$V`YIRu1~l|~ZEG;tVbtcoGEJr!{sNcD%YS2|RaCOm?aVyC zvT_+s8^7JIUYRB-(+C#kHAc1@S-2H`kX?k4;iosEGefz#VBuA7tF3lB?R|A4Ff<~| z!<1E~<8$(DrWe(!YPV^xBhcXC;`#Rs!;>*bQ)X;o3wEHVVI{zmZZy7I|30Jxt_h!iPJKJWd*gTP@Y<16llhx-J=9kYbpMCxn#A>ero9LU5^pU&FG=tt}kBlf5blrFE*rtA8P{hg(*bIn^OW z*6@}FP9CHu)vB0VMM7NrG`V6$$*Eaa7aZY7z3z;;wTV$g$coIxP=-5B6H#Y(#@v}$ zYdK&=4IBL{`km?==V8=C8fS4)ldo@d*7xUSnaaLVfo{z8a%K~ky}@)bXk4EzI5ou$ z5uAbMzw|;st{GB_x^xKsLWR0G}PM#}%=s z3XJGe3zu-{=gPvBmHA6&U*SLk^KVGPk;`e^UBeM)^cOhwjVC!cuzZ|{WOxr*ZfIf* zhD4c_SJxg5hr@Ka*M#HDJePxeF00Ph>kV9_(@N5-c;-6LtFQLu77*N|alDvw_k=vc znl4}qV?s>Raq`t#5BuQtj-1PJ9l7-MoI~O=xEd(vqRlG(*{$aK4PJG}T7FL!^aC|u zksm|9rg}AmQzToQzHr`xu5dA^yY}0-4;A?PDhSA8QY4XQ0CP=S$f-}Z+HPPC;1+4gk3Y&^h5`^ z=eXK{)jcK6xx1$Zf?^;6Co0-pN}l7oiX1R1jY0;K$Bp}B!qHz{YXV0t@outSWl|(J zcB}9a;pq0V*7K|bE=_}aCwmW=zOK5lwugtD{zkYqf0q zcm)S9HCQ_k!yg=p^Ciuel&i4HEqe*93tMdJt!Z)CdZ=1S6OKoVm8Ek_m;>&G;S<;G zcJUAvt{kLmTHwhaZ)wz;jK`RslHQtdhv#(n;)-g(oT||sV*Ie1i01)qLSy0L0EE*c1O#b-@S69XsWt6+cSO( zEl|0b?R{MDYJHNs_tFDkFBc;+`UCdMWS);lp)l^A*_g$H2wS^rv#st4j$4nyQf34* z)7_k*I%jm_p21As-a(&{)8XM+baV>%-*=-+7<6x7)1I9QxT{8s8>eti6|F(|7=t?K zS%w9;k*ikYpOtkQRO%ErK*(UT)#K47fA9oKb9-m7uivx?M*&32tE*Iv?c-NWs_q4K z^K@Vro7_R8DXde51a#sHwQ)9l_@!sXy7Xiu$nu{7`6tYqc};6`&V;z^R0T#+&Ckf9OwfaEbI z=K_TCN5fsg)mg-AqK<|6Dt!kxJiasELCCB09Nb61QZ4QZbfQ~m6bWkOKi1({Q&+`r z)1kykf=@8WTvVKtGxoaH@*GRjf%yL?&v7n8BO8~I$|U0p6Y%3X1B-6z4Mv& zCC1wvIpcyT_c`<-Mp;o|IQRc1#+-Ld&oep)ovW!z`YXVulcA5isMbZXyV#KTtkkNt z47FX~!Rxk0sA+j$@;ukJ5zY8YBRircoJ!-82c8(JjPmw`6LeuQF$n_&LhH`JjJaZp0x1m5ODE^OztS1 zIW~%Ac8;O&567g`N3FZL8Jq}&?Y4v}n?CIieIngc8V@b$ap|UvRPIq3K|8^zV z!awl-klIbSeTm8APw^wBXK)$5iHq^-db+1=JTJln=Hi*~7$bMF%~wy?a1OdT@W$@C zx3WfYOZ+(OlCF~_n}*`dvOj9pdekx%m0eLwr$IkZRJ3C92^!@J`mZGu4KO~{lDiFl zpQ%5>usc|3z+#(-A1NK_uSVB~=~#4l0aY(0F|bz}2f#Eo~|CKiukOXqEbSo{(|_XQxWp*~5ny1ypYx1tUfEcD( zkn_p{!Rd&_RH&UetF^5+@|$bPMInjKynSYrAQy$6G_U2(!hRfWV0l+x*ul+~b9i}j z>Eh~@v(L|$XY_W{9o%dDj_=?w(5^sK9W2mCXD=;2JN$|Ru9=}#@f<;8F0PCh?{pIj zO0XVR_n7D{?<0wH7Oj16Wbt0O)q@F6yVl(#i)&>dTR;BxumZMzJa6tPF|Q7A+)I_e z@#3y0Z6)7af9^KDTddFiDxXzr|Z(#tnMgd=uN>1+BV6mOMkGGp#lFo~8Dx z=eUoA|G0iY!dIrx$CELfEzGn!GuWlw#jOqzUAXZ>H8RrBTraCXrwjedx5HigcfK7w zcjm&v%If9iC7x=-T!C#PNL$5;nCW7rylj*2{3cw*7Hw+6{|DFG=#+S~&9yAu63p;L z6XXpbOqXz|XxNZ95f$gEoWpOR7W6aZ^v&P0N-s8g@<_*DI`G9o1a6WciFNa8g|)?ngDQBYtf1 zbYru<(Z@b(`+665I|l1+h~;Y)BWI4VmG0uY+AvSHo>Z(uJYdE+Piio$MR6MAXAf{K zHh+*Cj9tZ(=VDlpFRfL9s&yS*-TICG?l$VV)7ap9UVs3yGXE*w;hbV z6xq;5rOOt+cZ{REkzsT;7Q%I8wLLTC1T93;)Aon~CpO%E;4AU?)u@!46!;SdO(7^A z{lyM zYL+^`aD$1X%HuV{o4O5Z7vxb??DS}#GIxz76&?}%EnR<*f)@nvIEG0=E8smnd=ud* z&pugSR(Gmh^b^O>tw`d5bV0YwQ8n4h(EXHKX{rd(M)rz}lj`g(-UU;Yrp?WvUiCB> zqp{jeqJH@k>ZpQmcN-n0QKVr`SL$P&rAyjFg`53M%wz8AX3OY+D$7467;I7Wl~CIqI04Q_K*KV80hAMlbjolP13Z2*2Af~oJ5Y?(3W@wt z)3ihrmFq|GJRn{_8e2SuIqXyl(~Z$;RL^;9i_^eQ48cQZ8aul<2khYlYglX8#}_vM zdmx7CS@c7s$}BoXZ2jyo?pr^bpFtMb)ns z-S1_l?LLo`>A0=0_+Gt%??BZ0F`VT{$@G?2JB3_gKGuKw1nx3Zn|0j81v}ixb$ZnL zsy(gZgW3v`&qhAO4{<$xLZz$xQ}~1j?w%P3F`87aKDEhj$6+Et5xjvo&nD<9i<(3R zuMGvBXrA$cm=Oz6RW$=;m+I_F!PWT{9bep0pH2wN5AF?7UhEHIb_>_MO04+&J3CE2UxYiC{dXOGRWxM`1FDFbZi3=TRu5lBmS1 zmKVQO%S`;CXks}QkWtsSQCqUz=Q^fhtjf}zkyU-4UtHmrY;1}$+;s7VZ-di;>feIk zBj=RGU({pU4A-Q9~1 zqoX?**xoLLYs|#rREmktB0M2;>BmAo)*70Tnn2$dfOMBS#<{gRix85xK3gdrNZFcg SeRlWl+jVGzcXn~u{{I5bfsZr* diff --git a/ocrd_models/ocrd_models/.doctrees/environment.pickle b/ocrd_models/ocrd_models/.doctrees/environment.pickle deleted file mode 100644 index 4ff69b98d5140ec841aa4da9ae81bec2c3e49416..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 779445 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