diff --git a/src/opengeodeweb_back/geode_functions.py b/src/opengeodeweb_back/geode_functions.py index 1e17aa82..69e976d1 100644 --- a/src/opengeodeweb_back/geode_functions.py +++ b/src/opengeodeweb_back/geode_functions.py @@ -4,6 +4,8 @@ # Third party imports import opengeode_geosciences as og_gs import opengeode as og +import werkzeug +import flask # Local application imports from .geode_objects import geode_objects_dict @@ -38,6 +40,26 @@ def load(geode_object: str, file_absolute_path: str): return geode_object_value(geode_object)["load"](file_absolute_path) +def data_file_path(data_id: str, filename: str) -> str: + data_folder_path = flask.current_app.config["DATA_FOLDER_PATH"] + return os.path.join( + data_folder_path, + data_id, + werkzeug.utils.secure_filename(filename), + ) + + +def load_data(geode_object: str, data_id: str, filename: str): + file_absolute_path = data_file_path(data_id, filename) + return load(geode_object, file_absolute_path) + + +def upload_file_path(filename): + upload_folder = flask.current_app.config["UPLOAD_FOLDER"] + secure_filename = werkzeug.utils.secure_filename(filename) + return os.path.abspath(os.path.join(upload_folder, secure_filename)) + + def is_saveable(geode_object: str, data, filename: str): return geode_object_value(geode_object)["is_saveable"](data, filename) diff --git a/src/opengeodeweb_back/routes/blueprint_routes.py b/src/opengeodeweb_back/routes/blueprint_routes.py index 053dff40..058d761e 100644 --- a/src/opengeodeweb_back/routes/blueprint_routes.py +++ b/src/opengeodeweb_back/routes/blueprint_routes.py @@ -97,9 +97,10 @@ def allowed_objects(): if flask.request.method == "OPTIONS": return flask.make_response({}, 200) - UPLOAD_FOLDER = flask.current_app.config["UPLOAD_FOLDER"] utils_functions.validate_request(flask.request, allowed_objects_json) - file_absolute_path = os.path.join(UPLOAD_FOLDER, flask.request.json["filename"]) + file_absolute_path = geode_functions.upload_file_path( + flask.request.json["filename"] + ) allowed_objects = geode_functions.list_geode_objects( file_absolute_path, flask.request.json["supported_feature"] ) @@ -118,12 +119,11 @@ def allowed_objects(): methods=missing_files_json["methods"], ) def missing_files(): - UPLOAD_FOLDER = flask.current_app.config["UPLOAD_FOLDER"] utils_functions.validate_request(flask.request, missing_files_json) - + file_path = geode_functions.upload_file_path(flask.request.json["filename"]) missing_files = geode_functions.missing_files( flask.request.json["input_geode_object"], - os.path.join(UPLOAD_FOLDER, flask.request.json["filename"]), + file_path, ) has_missing_files = missing_files.has_missing_files() @@ -184,11 +184,9 @@ def crs_converter_geographic_coordinate_systems(): methods=inspect_file_json["methods"], ) def inspect_file(): - UPLOAD_FOLDER = flask.current_app.config["UPLOAD_FOLDER"] utils_functions.validate_request(flask.request, inspect_file_json) - secure_filename = werkzeug.utils.secure_filename(flask.request.json["filename"]) - file_path = os.path.abspath(os.path.join(UPLOAD_FOLDER, secure_filename)) + file_path = geode_functions.upload_file_path(flask.request.json["filename"]) data = geode_functions.load(flask.request.json["input_geode_object"], file_path) class_inspector = geode_functions.inspect( flask.request.json["input_geode_object"], data @@ -209,13 +207,13 @@ def inspect_file(): methods=geode_objects_and_output_extensions_json["methods"], ) def geode_objects_and_output_extensions(): - UPLOAD_FOLDER = flask.current_app.config["UPLOAD_FOLDER"] utils_functions.validate_request( flask.request, geode_objects_and_output_extensions_json ) + file_path = geode_functions.upload_file_path(flask.request.json["filename"]) data = geode_functions.load( flask.request.json["input_geode_object"], - os.path.join(UPLOAD_FOLDER, flask.request.json["filename"]), + file_path, ) geode_objects_and_output_extensions = ( geode_functions.geode_objects_output_extensions( @@ -241,14 +239,12 @@ def geode_objects_and_output_extensions(): ) def save_viewable_file(): utils_functions.validate_request(flask.request, save_viewable_file_json) - UPLOAD_FOLDER = flask.current_app.config["UPLOAD_FOLDER"] - DATA_FOLDER_PATH = flask.current_app.config["DATA_FOLDER_PATH"] - secure_filename = werkzeug.utils.secure_filename(flask.request.json["filename"]) - file_path = os.path.abspath(os.path.join(UPLOAD_FOLDER, secure_filename)) + + file_path = geode_functions.upload_file_path(flask.request.json["filename"]) data = geode_functions.load(flask.request.json["input_geode_object"], file_path) return flask.make_response( utils_functions.generate_native_viewable_and_light_viewable( - flask.request.json["input_geode_object"], data, DATA_FOLDER_PATH + flask.request.json["input_geode_object"], data ), 200, ) @@ -261,7 +257,6 @@ def save_viewable_file(): @routes.route(create_point_json["route"], methods=create_point_json["methods"]) def create_point(): utils_functions.validate_request(flask.request, create_point_json) - DATA_FOLDER_PATH = flask.current_app.config["DATA_FOLDER_PATH"] title = flask.request.json["title"] x = flask.request.json["x"] y = flask.request.json["y"] @@ -273,7 +268,7 @@ def create_point(): builder.set_name(title) return flask.make_response( utils_functions.generate_native_viewable_and_light_viewable( - "PointSet3D", PointSet3D, DATA_FOLDER_PATH + "PointSet3D", PointSet3D ), 200, ) @@ -288,12 +283,13 @@ def create_point(): methods=texture_coordinates_json["methods"], ) def texture_coordinates(): - DATA_FOLDER_PATH = flask.current_app.config["DATA_FOLDER_PATH"] utils_functions.validate_request(flask.request, texture_coordinates_json) - data = geode_functions.load( + data = geode_functions.load_data( flask.request.json["input_geode_object"], - os.path.join(DATA_FOLDER_PATH, flask.request.json["filename"]), + flask.request.json["id"], + flask.request.json["filename"], ) + texture_coordinates = data.texture_manager().texture_names() return flask.make_response({"texture_coordinates": texture_coordinates}, 200) @@ -311,14 +307,13 @@ def texture_coordinates(): methods=vertex_attribute_names_json["methods"], ) def vertex_attribute_names(): - DATA_FOLDER_PATH = flask.current_app.config["DATA_FOLDER_PATH"] utils_functions.validate_request(flask.request, vertex_attribute_names_json) - file_absolute_path = os.path.join( - DATA_FOLDER_PATH, werkzeug.utils.secure_filename(flask.request.json["filename"]) - ) - data = geode_functions.load( - flask.request.json["input_geode_object"], file_absolute_path + data = geode_functions.load_data( + flask.request.json["input_geode_object"], + flask.request.json["id"], + flask.request.json["filename"], ) + vertex_attribute_names = data.vertex_attribute_manager().attribute_names() return flask.make_response( @@ -341,14 +336,13 @@ def vertex_attribute_names(): methods=polygon_attribute_names_json["methods"], ) def polygon_attribute_names(): - DATA_FOLDER_PATH = flask.current_app.config["DATA_FOLDER_PATH"] utils_functions.validate_request(flask.request, polygon_attribute_names_json) - file_absolute_path = os.path.join( - DATA_FOLDER_PATH, werkzeug.utils.secure_filename(flask.request.json["filename"]) - ) - data = geode_functions.load( - flask.request.json["input_geode_object"], file_absolute_path + data = geode_functions.load_data( + flask.request.json["input_geode_object"], + flask.request.json["id"], + flask.request.json["filename"], ) + polygon_attribute_names = data.polygon_attribute_manager().attribute_names() return flask.make_response( @@ -371,14 +365,13 @@ def polygon_attribute_names(): methods=polyhedron_attribute_names_json["methods"], ) def polyhedron_attribute_names(): - DATA_FOLDER_PATH = flask.current_app.config["DATA_FOLDER_PATH"] - utils_functions.validate_request(flask.request, vertex_attribute_names_json) - file_absolute_path = os.path.join( - DATA_FOLDER_PATH, werkzeug.utils.secure_filename(flask.request.json["filename"]) - ) - data = geode_functions.load( - flask.request.json["input_geode_object"], file_absolute_path + utils_functions.validate_request(flask.request, polyhedron_attribute_names_json) + data = geode_functions.load_data( + flask.request.json["input_geode_object"], + flask.request.json["id"], + flask.request.json["filename"], ) + polyhedron_attribute_names = data.polyhedron_attribute_manager().attribute_names() return flask.make_response( diff --git a/src/opengeodeweb_back/routes/models/blueprint_models.py b/src/opengeodeweb_back/routes/models/blueprint_models.py index 6688ac5a..3a812224 100644 --- a/src/opengeodeweb_back/routes/models/blueprint_models.py +++ b/src/opengeodeweb_back/routes/models/blueprint_models.py @@ -18,36 +18,27 @@ ) def uuid_to_flat_index(): utils_functions.validate_request(flask.request, vtm_component_indices_json) - vtm_file_path = os.path.join( - flask.current_app.config["DATA_FOLDER_PATH"], flask.request.json["id"] + ".vtm" + + vtm_file_path = geode_functions.data_file_path( + flask.request.json["id"], "viewable.vtm" ) tree = ET.parse(vtm_file_path) root = tree.find("vtkMultiBlockDataSet") uuid_to_flat_index = {} current_index = 0 - for elem in root.iter(): if "uuid" in elem.attrib and elem.tag == "DataSet": uuid_to_flat_index[elem.attrib["uuid"]] = current_index - current_index += 1 - - return flask.make_response( - {"uuid_to_flat_index": uuid_to_flat_index}, - 200, - ) + return flask.make_response({"uuid_to_flat_index": uuid_to_flat_index}, 200) -def extract_model_uuids(geode_object, file_path): - model = geode_functions.load(geode_object, file_path) +def extract_model_uuids(model): mesh_components = model.mesh_components() - uuid_dict = {} - for mesh_component, ids in mesh_components.items(): component_name = mesh_component.get() uuid_dict[component_name] = [id.string() for id in ids] - return uuid_dict @@ -58,10 +49,12 @@ def extract_model_uuids(geode_object, file_path): @routes.route(mesh_components_json["route"], methods=mesh_components_json["methods"]) def extract_uuids_endpoint(): utils_functions.validate_request(flask.request, mesh_components_json) - file_path = os.path.join( - flask.current_app.config["DATA_FOLDER_PATH"], flask.request.json["filename"] + + model = geode_functions.load_data( + flask.request.json["geode_object"], + flask.request.json["id"], + flask.request.json["filename"], ) - if not os.path.exists(file_path): - return flask.make_response({"error": "File not found"}, 404) - uuid_dict = extract_model_uuids(flask.request.json["geode_object"], file_path) + + uuid_dict = extract_model_uuids(model) return flask.make_response({"uuid_dict": uuid_dict}, 200) diff --git a/src/opengeodeweb_back/routes/models/schemas/mesh_components.json b/src/opengeodeweb_back/routes/models/schemas/mesh_components.json index 89ab7e32..a2e4459b 100644 --- a/src/opengeodeweb_back/routes/models/schemas/mesh_components.json +++ b/src/opengeodeweb_back/routes/models/schemas/mesh_components.json @@ -5,14 +5,21 @@ ], "type": "object", "properties": { + "id": { + "type": "string", + "minLength": 1 + }, "filename": { - "type": "string" + "type": "string", + "minLength": 1 }, "geode_object": { - "type": "string" + "type": "string", + "minLength": 1 } }, "required": [ + "id", "filename", "geode_object" ], diff --git a/src/opengeodeweb_back/routes/models/schemas/vtm_component_indices.json b/src/opengeodeweb_back/routes/models/schemas/vtm_component_indices.json index 4525ebcd..0618e60a 100644 --- a/src/opengeodeweb_back/routes/models/schemas/vtm_component_indices.json +++ b/src/opengeodeweb_back/routes/models/schemas/vtm_component_indices.json @@ -6,7 +6,8 @@ "type": "object", "properties": { "id": { - "type": "string" + "type": "string", + "minLength": 1 } }, "required": [ diff --git a/src/opengeodeweb_back/routes/schemas/polygon_attribute_names.json b/src/opengeodeweb_back/routes/schemas/polygon_attribute_names.json index 43953bed..200e722c 100644 --- a/src/opengeodeweb_back/routes/schemas/polygon_attribute_names.json +++ b/src/opengeodeweb_back/routes/schemas/polygon_attribute_names.json @@ -12,11 +12,16 @@ "filename": { "type": "string", "minLength": 1 + }, + "id": { + "type": "string", + "minLength": 1 } }, "required": [ "input_geode_object", - "filename" + "filename", + "id" ], "additionalProperties": false } \ No newline at end of file diff --git a/src/opengeodeweb_back/routes/schemas/polyhedron_attribute_names.json b/src/opengeodeweb_back/routes/schemas/polyhedron_attribute_names.json index 11f5c0bd..9eb74209 100644 --- a/src/opengeodeweb_back/routes/schemas/polyhedron_attribute_names.json +++ b/src/opengeodeweb_back/routes/schemas/polyhedron_attribute_names.json @@ -12,11 +12,16 @@ "filename": { "type": "string", "minLength": 1 + }, + "id": { + "type": "string", + "minLength": 1 } }, "required": [ "input_geode_object", - "filename" + "filename", + "id" ], "additionalProperties": false } \ No newline at end of file diff --git a/src/opengeodeweb_back/routes/schemas/texture_coordinates.json b/src/opengeodeweb_back/routes/schemas/texture_coordinates.json index 36a92964..2c16e461 100644 --- a/src/opengeodeweb_back/routes/schemas/texture_coordinates.json +++ b/src/opengeodeweb_back/routes/schemas/texture_coordinates.json @@ -12,10 +12,15 @@ "filename": { "type": "string", "minLength": 1 + }, + "id": { + "type": "string", + "minLength": 1 } }, "required": [ "input_geode_object", + "id", "filename" ], "additionalProperties": false diff --git a/src/opengeodeweb_back/routes/schemas/vertex_attribute_names.json b/src/opengeodeweb_back/routes/schemas/vertex_attribute_names.json index cad442eb..b25abdb3 100644 --- a/src/opengeodeweb_back/routes/schemas/vertex_attribute_names.json +++ b/src/opengeodeweb_back/routes/schemas/vertex_attribute_names.json @@ -12,11 +12,16 @@ "filename": { "type": "string", "minLength": 1 + }, + "id": { + "type": "string", + "minLength": 1 } }, "required": [ "input_geode_object", - "filename" + "filename", + "id" ], "additionalProperties": false } \ No newline at end of file diff --git a/src/opengeodeweb_back/utils_functions.py b/src/opengeodeweb_back/utils_functions.py index d3f0c72b..477be11d 100644 --- a/src/opengeodeweb_back/utils_functions.py +++ b/src/opengeodeweb_back/utils_functions.py @@ -143,24 +143,25 @@ def handle_exception(e): return response -def generate_native_viewable_and_light_viewable( - geode_object, data, folder_absolute_path -): +def generate_native_viewable_and_light_viewable(geode_object, data): generated_id = str(uuid.uuid4()).replace("-", "") + DATA_FOLDER_PATH = flask.current_app.config["DATA_FOLDER_PATH"] + data_path = os.path.join(DATA_FOLDER_PATH, generated_id) name = data.name() object_type = geode_functions.get_object_type(geode_object) saved_native_file_path = geode_functions.save( geode_object, data, - folder_absolute_path, - generated_id + "." + data.native_extension(), + data_path, + "native." + data.native_extension(), ) saved_viewable_file_path = geode_functions.save_viewable( - geode_object, data, folder_absolute_path, generated_id + geode_object, data, data_path, "viewable" ) + viewable_file_name = os.path.basename(saved_viewable_file_path) saved_light_viewable_file_path = geode_functions.save_light_viewable( - geode_object, data, folder_absolute_path, "light_" + generated_id + geode_object, data, data_path, "light_viewable" ) f = open(saved_light_viewable_file_path, "rb") binary_light_viewable = f.read() @@ -169,7 +170,7 @@ def generate_native_viewable_and_light_viewable( return { "name": name, "native_file_name": os.path.basename(saved_native_file_path[0]), - "viewable_file_name": os.path.basename(saved_viewable_file_path), + "viewable_file_name": viewable_file_name, "id": generated_id, "object_type": object_type, "binary_light_viewable": str(binary_light_viewable, "utf-8"), diff --git a/tests/conftest.py b/tests/conftest.py index 847e503a..e72ce446 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -8,11 +8,13 @@ # Local application imports from app import app +TEST_ID = "1" + @pytest.fixture(scope="session", autouse=True) def copy_data(): shutil.rmtree("./data", ignore_errors=True) - shutil.copytree("./tests/data", "./data", dirs_exist_ok=True) + shutil.copytree("./tests/data/", f"./data/{TEST_ID}/", dirs_exist_ok=True) @pytest.fixture @@ -31,3 +33,8 @@ def client(): def app_context(): with app.app_context(): yield + + +@pytest.fixture +def test_id(): + return TEST_ID diff --git a/tests/test_models_routes.py b/tests/test_models_routes.py index 9160179e..98e0dd80 100644 --- a/tests/test_models_routes.py +++ b/tests/test_models_routes.py @@ -1,8 +1,19 @@ -def test_model_mesh_components(client): +import os +import shutil +import flask + +from src.opengeodeweb_back import geode_functions + + +def test_model_mesh_components(client, test_id): route = f"/models/vtm_component_indices" - get_full_data = lambda: {"id": "cube"} - json = get_full_data() - response = client.post(route, json=json) + + with client.application.app_context(): + data_path = geode_functions.data_file_path(test_id, "viewable.vtm") + os.makedirs(os.path.dirname(data_path), exist_ok=True) + shutil.copy("./tests/data/cube.vtm", data_path) + + response = client.post(route, json={"id": test_id}) assert response.status_code == 200 uuid_dict = response.json["uuid_to_flat_index"] @@ -15,9 +26,16 @@ def test_model_mesh_components(client): assert isinstance(uuid, str) -def test_extract_brep_uuids(client): +def test_extract_brep_uuids(client, test_id): route = "/models/mesh_components" - json_data = {"filename": "cube.og_brep", "geode_object": "BRep"} + + brep_filename = "cube.og_brep" + json_data = {"id": test_id, "geode_object": "BRep", "filename": brep_filename} + + with client.application.app_context(): + data_path = geode_functions.data_file_path(json_data["id"], brep_filename) + os.makedirs(os.path.dirname(data_path), exist_ok=True) + shutil.copy(f"./tests/data/{brep_filename}", data_path) response = client.post(route, json=json_data) assert response.status_code == 200 diff --git a/tests/test_routes.py b/tests/test_routes.py index ae41b17a..367c7c83 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -1,5 +1,6 @@ # Standard library imports import os +import shutil # Third party imports from werkzeug.datastructures import FileStorage @@ -170,11 +171,17 @@ def get_full_data(): test_utils.test_route_wrong_params(client, route, get_full_data) -def test_texture_coordinates(client): +def test_texture_coordinates(client, test_id): + with client.application.app_context(): + data_path = geode_functions.data_file_path(test_id, "hat.vtp") + os.makedirs(os.path.dirname(data_path), exist_ok=True) + shutil.copy("./tests/vertex_attribute.vtp", data_path) + response = client.post( "/texture_coordinates", json={ "input_geode_object": "PolygonalSurface3D", + "id": test_id, "filename": "hat.vtp", }, ) @@ -185,7 +192,7 @@ def test_texture_coordinates(client): assert type(texture_coordinate) is str -def test_vertex_attribute_names(client): +def test_vertex_attribute_names(client, test_id): route = f"/vertex_attribute_names" for geode_object, value in geode_objects.geode_objects_dict().items(): if value["object_type"] == "mesh": @@ -205,6 +212,7 @@ def test_vertex_attribute_names(client): def get_full_data(): return { "input_geode_object": geode_object, + "id": test_id, "filename": f"test.{input_extension}", } @@ -221,7 +229,7 @@ def get_full_data(): test_utils.test_route_wrong_params(client, route, get_full_data) -def test_polygon_attribute_names(client): +def test_polygon_attribute_names(client, test_id): route = f"/polygon_attribute_names" for geode_object, value in geode_objects.geode_objects_dict().items(): if value["object_type"] == "mesh": @@ -241,6 +249,7 @@ def test_polygon_attribute_names(client): def get_full_data(): return { "input_geode_object": geode_object, + "id": test_id, "filename": f"test.{input_extension}", } @@ -257,7 +266,7 @@ def get_full_data(): test_utils.test_route_wrong_params(client, route, get_full_data) -def test_polyhedron_attribute_names(client): +def test_polyhedron_attribute_names(client, test_id): route = f"/polyhedron_attribute_names" for geode_object, value in geode_objects.geode_objects_dict().items(): if value["object_type"] == "mesh": @@ -277,6 +286,7 @@ def test_polyhedron_attribute_names(client): def get_full_data(): return { "input_geode_object": geode_object, + "id": test_id, "filename": f"test.{input_extension}", } @@ -292,9 +302,6 @@ def get_full_data(): # Test all params test_utils.test_route_wrong_params(client, route, get_full_data) - # Test all params - test_utils.test_route_wrong_params(client, route, get_full_data) - def test_create_point(client): route = f"/create_point" diff --git a/tests/test_utils_functions.py b/tests/test_utils_functions.py index 8e539a97..0068d4a5 100644 --- a/tests/test_utils_functions.py +++ b/tests/test_utils_functions.py @@ -71,22 +71,22 @@ def test_handle_exception(client): assert type(data["code"]) is int -def test_generate_native_viewable_and_light_viewable(): - geode_object = "BRep" - folder_absolute_path = os.path.abspath("./data") - data = geode_functions.load( - geode_object, os.path.join(folder_absolute_path, "test.og_brep") - ) - folder_absolute_path = "None" - result = utils_functions.generate_native_viewable_and_light_viewable( - geode_object, data, folder_absolute_path - ) +def test_generate_native_viewable_and_light_viewable(client): + app = client.application + with app.app_context(): + geode_object = "BRep" + data = geode_functions.load(geode_object, "./tests/data/test.og_brep") + + result = utils_functions.generate_native_viewable_and_light_viewable( + geode_object, data + ) + assert type(result) is dict assert type(result["name"]) is str assert type(result["native_file_name"]) is str - assert re.match(r"[0-9a-f]{32}\.[a-zA-Z0-9]+", result["native_file_name"]) + assert result["native_file_name"] == "native.og_brep" assert type(result["viewable_file_name"]) is str - assert re.match(r"[0-9a-f]{32}\.[a-zA-Z0-9]+", result["viewable_file_name"]) + assert result["viewable_file_name"] == "viewable.vtm" assert type(result["id"]) is str assert re.match(r"[0-9a-f]{32}", result["id"]) assert type(result["object_type"]) is str