diff --git a/app.py b/app.py index a1ee32e7..23dd0e1f 100644 --- a/app.py +++ b/app.py @@ -7,6 +7,7 @@ from werkzeug.exceptions import HTTPException from src.opengeodeweb_back.routes import blueprint_routes +from src.opengeodeweb_back.routes.models import blueprint_models from src.opengeodeweb_back.utils_functions import handle_exception from src.opengeodeweb_back import app_config @@ -34,6 +35,12 @@ name="blueprint_routes", ) +app.register_blueprint( + blueprint_models.routes, + url_prefix="/models", + name="blueprint_models", +) + @app.errorhandler(HTTPException) def errorhandler(e): diff --git a/requirements.txt b/requirements.txt index 2f112fe7..e8be4d7e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,12 +20,12 @@ flask[async]==3.1.0 # flask-cors flask-cors==5.0.1 # via -r requirements.in -geode-background==9.1.5 +geode-background==9.2.0 # via # geode-explicit # geode-implicit # geode-simplex -geode-common==33.7.2 +geode-common==33.7.5 # via # -r requirements.in # geode-background @@ -35,27 +35,27 @@ geode-common==33.7.2 # geode-numerics # geode-simplex # geode-viewables -geode-conversion==6.2.7 +geode-conversion==6.2.9 # via # geode-explicit # geode-implicit # geode-simplex -geode-explicit==6.1.35 +geode-explicit==6.1.37 # via # -r requirements.in # geode-implicit -geode-implicit==3.7.4 +geode-implicit==3.7.6 # via -r requirements.in geode-numerics==6.0.3 # via # -r requirements.in # geode-implicit # geode-simplex -geode-simplex==9.2.5 +geode-simplex==9.2.6 # via # -r requirements.in # geode-implicit -geode-viewables==3.0.12 +geode-viewables==3.1.0 # via -r requirements.in itsdangerous==2.2.0 # via flask @@ -69,7 +69,7 @@ markupsafe==3.0.2 # via # jinja2 # werkzeug -opengeode-core==15.17.8 +opengeode-core==15.17.12 # via # -r requirements.in # geode-background @@ -84,7 +84,7 @@ opengeode-core==15.17.8 # opengeode-geosciencesio # opengeode-inspector # opengeode-io -opengeode-geosciences==8.4.3 +opengeode-geosciences==8.4.4 # via # -r requirements.in # geode-implicit diff --git a/src/opengeodeweb_back/routes/blueprint_routes.py b/src/opengeodeweb_back/routes/blueprint_routes.py index 2a355074..b825cde7 100644 --- a/src/opengeodeweb_back/routes/blueprint_routes.py +++ b/src/opengeodeweb_back/routes/blueprint_routes.py @@ -12,7 +12,9 @@ # Local application imports from .. import geode_functions, utils_functions -routes = flask.Blueprint("routes", __name__) +from .models import blueprint_models + +routes = flask.Blueprint("routes", __name__, url_prefix="/opengeodeweb_back") @routes.before_request @@ -23,11 +25,19 @@ def before_request(): @routes.teardown_request def teardown_request(exception): + if "ping" not in flask.request.path: utils_functions.decrement_request_counter(flask.current_app) utils_functions.update_last_request_time(flask.current_app) +routes.register_blueprint( + blueprint_models.routes, + url_prefix=blueprint_models.routes.url_prefix, + name=blueprint_models.routes.name, +) + + schemas = os.path.join(os.path.dirname(__file__), "schemas") with open( diff --git a/src/opengeodeweb_back/routes/models/blueprint_models.py b/src/opengeodeweb_back/routes/models/blueprint_models.py new file mode 100644 index 00000000..f2c91b4a --- /dev/null +++ b/src/opengeodeweb_back/routes/models/blueprint_models.py @@ -0,0 +1,74 @@ +import json +import os +import xml.etree.ElementTree as ET +import flask +from ... import geode_functions, utils_functions + +routes = flask.Blueprint("models", __name__, url_prefix="/models") + + +schemas = os.path.join(os.path.dirname(__file__), "schemas") + +with open(os.path.join(schemas, "vtm_component_indices.json"), "r") as file: + vtm_component_indices_json = json.load(file) + + +@routes.route( + vtm_component_indices_json["route"], methods=vtm_component_indices_json["methods"] +) +def uuid_to_flat_index(): + + print(f"uuid_to_flat_index : {flask.request=}", flush=True) + 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" + ) + + tree = ET.parse(vtm_file_path) + root = tree.getroot() + uuid_to_flat_index = {} + current_index = 1 + + 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, + ) + + +def extract_model_uuids(geode_object, file_path): + model = geode_functions.load(geode_object, file_path) + 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 + + +with open(os.path.join(schemas, "mesh_components.json"), "r") as file: + mesh_components_json = json.load(file) + + +@routes.route(mesh_components_json["route"], methods=mesh_components_json["methods"]) +def extract_uuids_endpoint(): + print(f"extract_uuids_endpoint : {flask.request=}", flush=True) + + 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"] + ) + + 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) + 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 new file mode 100644 index 00000000..89ab7e32 --- /dev/null +++ b/src/opengeodeweb_back/routes/models/schemas/mesh_components.json @@ -0,0 +1,20 @@ +{ + "route": "/mesh_components", + "methods": [ + "POST" + ], + "type": "object", + "properties": { + "filename": { + "type": "string" + }, + "geode_object": { + "type": "string" + } + }, + "required": [ + "filename", + "geode_object" + ], + "additionalProperties": false +} \ No newline at end of file diff --git a/src/opengeodeweb_back/routes/models/schemas/vtm_component_indices.json b/src/opengeodeweb_back/routes/models/schemas/vtm_component_indices.json new file mode 100644 index 00000000..4525ebcd --- /dev/null +++ b/src/opengeodeweb_back/routes/models/schemas/vtm_component_indices.json @@ -0,0 +1,16 @@ +{ + "route": "/vtm_component_indices", + "methods": [ + "POST" + ], + "type": "object", + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "additionalProperties": false +} \ No newline at end of file diff --git a/tests/data/cube.og_brep b/tests/data/cube.og_brep new file mode 100644 index 00000000..9db99921 Binary files /dev/null and b/tests/data/cube.og_brep differ diff --git a/tests/data/cube.vtm b/tests/data/cube.vtm new file mode 100644 index 00000000..2e0d7957 --- /dev/null +++ b/tests/data/cube.vtm @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/test_models_routes.py b/tests/test_models_routes.py new file mode 100644 index 00000000..1947999c --- /dev/null +++ b/tests/test_models_routes.py @@ -0,0 +1,30 @@ +def test_model_mesh_components(client): + route = f"/models/vtm_component_indices" + get_full_data = lambda: {"id": "cube"} + json = get_full_data() + response = client.post(route, json=json) + assert response.status_code == 200 + uuid_dict = response.json["uuid_to_flat_index"] + assert type(uuid_dict) is dict + + indices = list(uuid_dict.values()) + indices.sort() + assert indices[0] == 1 + assert all(indices[i] == indices[i - 1] + 1 for i in range(1, len(indices))) + + +def test_extract_brep_uuids(client): + route = "/models/mesh_components" + json_data = {"filename": "cube.og_brep", "geode_object": "BRep"} + + response = client.post(route, json=json_data) + + assert response.status_code == 200 + uuid_dict = response.json["uuid_dict"] + assert isinstance(uuid_dict, dict) + assert ( + "Block" in uuid_dict + or "Line" in uuid_dict + or "Surface" in uuid_dict + or "Corner" in uuid_dict + )