corrected / cleaned upload and download logic with regard to operations and ids
This commit is contained in:
parent
7f2b4e8820
commit
33246d1ff9
@ -42,9 +42,11 @@ class RedFileDescriptorBuilder(FileDescriptorBuilder):
|
|||||||
|
|
||||||
file_pattern = self.operation2file_patterns[operation][end]
|
file_pattern = self.operation2file_patterns[operation][end]
|
||||||
|
|
||||||
|
print(f"{end=}")
|
||||||
|
|
||||||
file_descriptor = {
|
file_descriptor = {
|
||||||
**project(queue_item_body, ["dossierId", "fileId", "pages"]),
|
**project(queue_item_body, ["dossierId", "fileId", "pages"]),
|
||||||
"pages": queue_item_body.get("pages", []),
|
"pages": [queue_item_body["id"]] if end == "output" else [],
|
||||||
"extension": file_pattern["extension"],
|
"extension": file_pattern["extension"],
|
||||||
"subdir": file_pattern["subdir"],
|
"subdir": file_pattern["subdir"],
|
||||||
}
|
}
|
||||||
@ -59,21 +61,25 @@ class RedFileDescriptorBuilder(FileDescriptorBuilder):
|
|||||||
"dossierId", "fileId", "subdir", "pages", "extension"
|
"dossierId", "fileId", "subdir", "pages", "extension"
|
||||||
)(file_descriptor)
|
)(file_descriptor)
|
||||||
|
|
||||||
matcher = os.path.join(dossier_id, make_filename(file_id, subdir, self.__build_page_regex(pages) + extension))
|
matcher = os.path.join(
|
||||||
|
dossier_id, make_filename(file_id, subdir, self.__build_page_regex(pages, subdir) + extension)
|
||||||
|
)
|
||||||
|
|
||||||
return matcher
|
return matcher
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __build_page_regex(pages):
|
def __build_page_regex(pages, subdir):
|
||||||
|
|
||||||
n_pages = len(pages)
|
n_pages = len(pages)
|
||||||
|
print("pages:", pages)
|
||||||
|
|
||||||
if n_pages > 1:
|
if n_pages > 1:
|
||||||
page_re = "id:(" + "|".join(map(str, pages)) + ")."
|
page_re = "id:(" + "|".join(map(str, pages)) + ")."
|
||||||
elif n_pages == 1:
|
elif n_pages == 1:
|
||||||
page_re = f"id:{pages[0]}."
|
page_re = f"id:{pages[0]}."
|
||||||
else: # no pages
|
else: # no pages specified -> either all pages or no pages, depending on whether a subdir is specified
|
||||||
page_re = r"id:\d+."
|
print("subdir", subdir)
|
||||||
|
page_re = r"id:\d+." if subdir else ""
|
||||||
|
|
||||||
return page_re
|
return page_re
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,8 @@ class FileDescriptorManager:
|
|||||||
|
|
||||||
TODO: This is supposed to be fully decoupled from the concrete file descriptor builder implementation, however some
|
TODO: This is supposed to be fully decoupled from the concrete file descriptor builder implementation, however some
|
||||||
bad coupling is still left.
|
bad coupling is still left.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, bucket_name, file_descriptor_builder: FileDescriptorBuilder):
|
def __init__(self, bucket_name, file_descriptor_builder: FileDescriptorBuilder):
|
||||||
self.bucket_name = bucket_name
|
self.bucket_name = bucket_name
|
||||||
self.operation_file_descriptor_builder = file_descriptor_builder
|
self.operation_file_descriptor_builder = file_descriptor_builder
|
||||||
@ -20,10 +21,7 @@ class FileDescriptorManager:
|
|||||||
return self.get_object_name(queue_item_body, end="output")
|
return self.get_object_name(queue_item_body, end="output")
|
||||||
|
|
||||||
def get_object_name(self, queue_item_body: dict, end):
|
def get_object_name(self, queue_item_body: dict, end):
|
||||||
|
|
||||||
file_descriptor = self.build_file_descriptor(queue_item_body, end=end)
|
file_descriptor = self.build_file_descriptor(queue_item_body, end=end)
|
||||||
# FIXME: bad coupling
|
|
||||||
file_descriptor["pages"] = [queue_item_body.get("id", 0)]
|
|
||||||
object_name = self.__build_matcher(file_descriptor)
|
object_name = self.__build_matcher(file_descriptor)
|
||||||
|
|
||||||
return object_name
|
return object_name
|
||||||
@ -48,7 +46,9 @@ class FileDescriptorManager:
|
|||||||
return self.get_object_descriptor(queue_item_body, end="input")
|
return self.get_object_descriptor(queue_item_body, end="input")
|
||||||
|
|
||||||
def get_output_object_descriptor(self, storage_upload_info):
|
def get_output_object_descriptor(self, storage_upload_info):
|
||||||
return self.get_object_descriptor(storage_upload_info, end="output")
|
r = self.get_object_descriptor(storage_upload_info, end="output")
|
||||||
|
print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>", r)
|
||||||
|
return r
|
||||||
|
|
||||||
def get_object_descriptor(self, queue_item_body, end):
|
def get_object_descriptor(self, queue_item_body, end):
|
||||||
# TODO: this is complected with the Storage class API
|
# TODO: this is complected with the Storage class API
|
||||||
|
|||||||
@ -5,7 +5,7 @@ from azure.storage.blob import ContainerClient, BlobServiceClient
|
|||||||
|
|
||||||
from pyinfra.storage.adapters.adapter import StorageAdapter
|
from pyinfra.storage.adapters.adapter import StorageAdapter
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger(__name__)
|
||||||
logging.getLogger("azure").setLevel(logging.WARNING)
|
logging.getLogger("azure").setLevel(logging.WARNING)
|
||||||
logging.getLogger("urllib3").setLevel(logging.WARNING)
|
logging.getLogger("urllib3").setLevel(logging.WARNING)
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@ from minio import Minio
|
|||||||
from pyinfra.exceptions import DataLoadingFailure
|
from pyinfra.exceptions import DataLoadingFailure
|
||||||
from pyinfra.storage.adapters.adapter import StorageAdapter
|
from pyinfra.storage.adapters.adapter import StorageAdapter
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class S3StorageAdapter(StorageAdapter):
|
class S3StorageAdapter(StorageAdapter):
|
||||||
@ -27,6 +27,7 @@ class S3StorageAdapter(StorageAdapter):
|
|||||||
logger.debug(f"Uploading '{object_name}'...")
|
logger.debug(f"Uploading '{object_name}'...")
|
||||||
data = io.BytesIO(data)
|
data = io.BytesIO(data)
|
||||||
self.__client.put_object(bucket_name, object_name, data, length=data.getbuffer().nbytes)
|
self.__client.put_object(bucket_name, object_name, data, length=data.getbuffer().nbytes)
|
||||||
|
# input("uploaded")
|
||||||
|
|
||||||
def get_object(self, bucket_name, object_name):
|
def get_object(self, bucket_name, object_name):
|
||||||
logger.debug(f"Downloading '{object_name}'...")
|
logger.debug(f"Downloading '{object_name}'...")
|
||||||
|
|||||||
@ -2,6 +2,10 @@ import argparse
|
|||||||
import gzip
|
import gzip
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from funcy import lmap
|
||||||
|
|
||||||
|
from pyinfra.server.packing import string_to_bytes
|
||||||
|
|
||||||
|
|
||||||
def parse_args():
|
def parse_args():
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
@ -9,14 +13,20 @@ def parse_args():
|
|||||||
return parser.parse_args()
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
def interpret(parsed):
|
||||||
|
return {**parsed, "data": str(string_to_bytes(parsed["data"]))}
|
||||||
|
|
||||||
|
|
||||||
def main(fp):
|
def main(fp):
|
||||||
with open(fp, "rb") as f:
|
with open(fp, "rb") as f:
|
||||||
compressed_json_path = f.read()
|
compressed_json_path = f.read()
|
||||||
|
|
||||||
json_str = gzip.decompress(compressed_json_path)
|
json_str = gzip.decompress(compressed_json_path)
|
||||||
json_dict = json.loads(json_str)
|
parsed = json.loads(json_str)
|
||||||
|
parsed = [parsed] if isinstance(parsed, dict) else parsed
|
||||||
|
parsed = lmap(interpret, parsed)
|
||||||
|
|
||||||
print(json.dumps(json_dict, indent=2))
|
print(json.dumps(parsed, indent=2))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@ -1,19 +1,56 @@
|
|||||||
service:
|
service:
|
||||||
response_formatter: identity
|
response_formatter: identity
|
||||||
operations:
|
operations:
|
||||||
multi_inp_op:
|
# multi_inp_op:
|
||||||
|
# input:
|
||||||
|
# subdir: op_inp_files
|
||||||
|
# extension: IN.gz
|
||||||
|
# output:
|
||||||
|
# subdir: op_outp_files
|
||||||
|
# extension: OUT.gz
|
||||||
|
upper:
|
||||||
input:
|
input:
|
||||||
subdir: op_inp_files
|
subdir: ""
|
||||||
extension: IN.gz
|
extension: up_in.gz
|
||||||
output:
|
output:
|
||||||
subdir: op_outp_files
|
subdir: ""
|
||||||
extension: OUT.gz
|
extension: up_out.gz
|
||||||
|
extract:
|
||||||
|
input:
|
||||||
|
subdir: ""
|
||||||
|
extension: extr_in.gz
|
||||||
|
output:
|
||||||
|
subdir: "extractions"
|
||||||
|
extension: gz
|
||||||
|
rotate:
|
||||||
|
input:
|
||||||
|
subdir: ""
|
||||||
|
extension: rot_in.gz
|
||||||
|
output:
|
||||||
|
subdir: ""
|
||||||
|
extension: rot_out.gz
|
||||||
|
classify:
|
||||||
|
input:
|
||||||
|
subdir: ""
|
||||||
|
extension: cls_in.gz
|
||||||
|
output:
|
||||||
|
subdir: ""
|
||||||
|
extension: cls_out.gz
|
||||||
|
stream_pages:
|
||||||
|
input:
|
||||||
|
subdir: ""
|
||||||
|
extension: pgs_in.gz
|
||||||
|
output:
|
||||||
|
subdir: "pages"
|
||||||
|
extension: pgs_out.gz
|
||||||
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
input:
|
input:
|
||||||
subdir: ""
|
subdir: ""
|
||||||
extension: IN.gz
|
extension: IN.gz
|
||||||
output:
|
output:
|
||||||
subdir: op_outp_files
|
subdir: ""
|
||||||
extension: OUT.gz
|
extension: OUT.gz
|
||||||
|
|
||||||
storage:
|
storage:
|
||||||
@ -41,5 +78,5 @@ webserver:
|
|||||||
|
|
||||||
mock_analysis_endpoint: "http://127.0.0.1:5000"
|
mock_analysis_endpoint: "http://127.0.0.1:5000"
|
||||||
|
|
||||||
use_docker_fixture: 1
|
use_docker_fixture: 0
|
||||||
logging: 0
|
logging: 1
|
||||||
@ -89,8 +89,8 @@ def mock_make_load_data():
|
|||||||
return load_data
|
return load_data
|
||||||
|
|
||||||
|
|
||||||
# def pytest_make_parametrize_id(config, val, argname):
|
def pytest_make_parametrize_id(config, val, argname):
|
||||||
# return f"\n\t{argname}={val}\n"
|
return f"\n\t{argname}={val}\n"
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|||||||
4
test/fixtures/consumer.py
vendored
4
test/fixtures/consumer.py
vendored
@ -26,8 +26,8 @@ def pair_data_with_queue_message(data: Iterable[bytes]):
|
|||||||
def inner():
|
def inner():
|
||||||
for i, d in enumerate(data):
|
for i, d in enumerate(data):
|
||||||
body = {
|
body = {
|
||||||
"dossierId": "folder",
|
"dossierId": "dossier_id",
|
||||||
"fileId": f"file{i}",
|
"fileId": f"file_id_{i}",
|
||||||
"targetFileExtension": "in.gz",
|
"targetFileExtension": "in.gz",
|
||||||
"responseFileExtension": "out.gz",
|
"responseFileExtension": "out.gz",
|
||||||
"pages": [0, 2, 3],
|
"pages": [0, 2, 3],
|
||||||
|
|||||||
15
test/fixtures/input.py
vendored
15
test/fixtures/input.py
vendored
@ -6,6 +6,7 @@ import pytest
|
|||||||
from PIL import Image
|
from PIL import Image
|
||||||
from funcy import lmap, compose, flatten, lflatten, omit, second, first, lzip, merge
|
from funcy import lmap, compose, flatten, lflatten, omit, second, first, lzip, merge
|
||||||
|
|
||||||
|
from pyinfra.server.debugging import inspect
|
||||||
from pyinfra.server.dispatcher.dispatcher import Nothing
|
from pyinfra.server.dispatcher.dispatcher import Nothing
|
||||||
from pyinfra.server.normalization import normalize_item
|
from pyinfra.server.normalization import normalize_item
|
||||||
from pyinfra.server.packing import pack, unpack
|
from pyinfra.server.packing import pack, unpack
|
||||||
@ -80,7 +81,7 @@ def targets(data_message_pairs, input_data_items, operation, metadata, server_si
|
|||||||
if operation is Nothing:
|
if operation is Nothing:
|
||||||
return Nothing
|
return Nothing
|
||||||
|
|
||||||
op = compose(lift(star(pack)), normalize_item, operation)
|
op = compose(lift(star(pack)), normalize_item, inspect("A"), operation)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response_data, response_metadata = zip(*map(unpack, flatten(starmap(op, zip(input_data_items, metadata)))))
|
response_data, response_metadata = zip(*map(unpack, flatten(starmap(op, zip(input_data_items, metadata)))))
|
||||||
@ -147,15 +148,19 @@ def metadata(n_items, many_to_n):
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def queue_message_metadata(n_items, operation_name):
|
def queue_message_metadata(n_items, operation_name):
|
||||||
|
print()
|
||||||
|
print("n_items:", n_items)
|
||||||
def metadata(i):
|
def metadata(i):
|
||||||
return merge(
|
r = merge(
|
||||||
{
|
{
|
||||||
"dossierId": "folder",
|
"dossierId": "dossier_id",
|
||||||
"fileId": f"file{i}",
|
"fileId": f"file_id_{i}",
|
||||||
"pages": [0, 2, 3],
|
|
||||||
},
|
},
|
||||||
({"operation": operation_name} if operation_name else {}),
|
({"operation": operation_name} if operation_name else {}),
|
||||||
|
({"pages": [0, 2, 3]} if n_items > 1 else {}),
|
||||||
)
|
)
|
||||||
|
print("metadata:", r)
|
||||||
|
return r
|
||||||
|
|
||||||
return lmap(metadata, range(n_items))
|
return lmap(metadata, range(n_items))
|
||||||
|
|
||||||
|
|||||||
18
test/fixtures/server.py
vendored
18
test/fixtures/server.py
vendored
@ -2,6 +2,7 @@ import io
|
|||||||
import logging
|
import logging
|
||||||
import socket
|
import socket
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
|
from itertools import count
|
||||||
from multiprocessing import Process
|
from multiprocessing import Process
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
from typing import Generator
|
from typing import Generator
|
||||||
@ -51,8 +52,10 @@ def server(server_stream_function, buffer_size, operation_name):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def operation_name(many_to_n):
|
def operation_name(core_operation):
|
||||||
return "multi_inp_op" if many_to_n else ""
|
operation_name = core_operation.__name__
|
||||||
|
print("OPERATION NAME", operation_name)
|
||||||
|
return operation_name
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@ -105,11 +108,18 @@ def core_operation(item_type, one_to_many, analysis_task):
|
|||||||
yield upper(string, metadata), metadata
|
yield upper(string, metadata), metadata
|
||||||
|
|
||||||
def upper(string: bytes, metadata):
|
def upper(string: bytes, metadata):
|
||||||
return string.decode().upper().encode(), metadata
|
print("upper>>>>>>>>>>>", metadata)
|
||||||
|
print("???????????????????????????????", string)
|
||||||
|
r = string.decode().upper().encode(), metadata
|
||||||
|
print("!!!!!!!!!!!!!!!!!!!!!", r)
|
||||||
|
return r
|
||||||
|
|
||||||
def extract(string: bytes, metadata):
|
def extract(string: bytes, metadata):
|
||||||
for i, c in project(dict(enumerate(string.decode())), metadata["pages"]).items():
|
print("extract>>>>>>>>>>>", metadata)
|
||||||
|
print(string.decode())
|
||||||
|
for i, c in project(dict(enumerate(string.decode())), metadata.get("pages", range(len(string.decode())))).items():
|
||||||
metadata["id"] = i
|
metadata["id"] = i
|
||||||
|
print(i)
|
||||||
yield c.encode(), metadata
|
yield c.encode(), metadata
|
||||||
|
|
||||||
def rotate(im: bytes, metadata):
|
def rotate(im: bytes, metadata):
|
||||||
|
|||||||
@ -37,7 +37,7 @@ from test.config import CONFIG as TEST_CONFIG
|
|||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"n_items",
|
"n_items",
|
||||||
[
|
[
|
||||||
1,
|
# 1,
|
||||||
3,
|
3,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@ -78,11 +78,15 @@ from test.config import CONFIG as TEST_CONFIG
|
|||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"many_to_n",
|
"many_to_n",
|
||||||
[
|
[
|
||||||
True,
|
# True,
|
||||||
# False,
|
False,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_serving(server_process, bucket_name, components, targets, data_message_pairs, n_items, many_to_n):
|
def test_serving(operation_name, server_process, bucket_name, components, targets, data_message_pairs, n_items, many_to_n):
|
||||||
|
|
||||||
|
print(f"{operation_name=}")
|
||||||
|
print()
|
||||||
|
print()
|
||||||
|
|
||||||
storage, queue_manager, consumer, file_descriptor_manager = components
|
storage, queue_manager, consumer, file_descriptor_manager = components
|
||||||
|
|
||||||
@ -108,6 +112,12 @@ def test_serving(server_process, bucket_name, components, targets, data_message_
|
|||||||
|
|
||||||
# TODO: correctness of target should be validated as well, since production has become non-trivial
|
# TODO: correctness of target should be validated as well, since production has become non-trivial
|
||||||
assert sorted(outputs) == sorted(targets)
|
assert sorted(outputs) == sorted(targets)
|
||||||
|
print(sorted(targets))
|
||||||
|
print(sorted(outputs))
|
||||||
|
|
||||||
|
# sleep(2)
|
||||||
|
# print("---")
|
||||||
|
# input()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@ -146,8 +156,9 @@ def upload_data_to_folder_in_storage_and_publish_single_request_to_queue(
|
|||||||
pages = ref_message["pages"]
|
pages = ref_message["pages"]
|
||||||
|
|
||||||
for data, page in zip(map(first, data_message_pairs), pages):
|
for data, page in zip(map(first, data_message_pairs), pages):
|
||||||
object_descriptor = file_descriptor_manager.get_input_object_descriptor(ref_message)
|
object_descriptor = file_descriptor_manager.get_input_object_descriptor({**ref_message, "id": page})
|
||||||
object_descriptor["object_name"] = build_filepath(object_descriptor, page)
|
object_descriptor["object_name"] = build_filepath(object_descriptor, page)
|
||||||
|
print(f"{object_descriptor=}")
|
||||||
|
|
||||||
storage.put_object(**object_descriptor, data=compress(data))
|
storage.put_object(**object_descriptor, data=compress(data))
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,7 @@ from test.utils.storage import pack_for_upload
|
|||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def body():
|
def body():
|
||||||
return {"dossierId": "folder", "fileId": "file"}
|
return {"dossierId": "dossier_id", "fileId": "file_id"}
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("client_name", ["mock", "azure", "s3"], scope="session")
|
@pytest.mark.parametrize("client_name", ["mock", "azure", "s3"], scope="session")
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user