From 1e7cc420458aa5e2e09d9a2f8bb3c1e8ebcbf8a2 Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Tue, 4 Aug 2026 11:56:43 +0200 Subject: [PATCH 1/5] fix(modelComponentsAttributes): Update schemas to accept list of components --- opengeodeweb_back_schemas.json | 48 ++++--- .../routes/blueprint_routes.py | 123 ++++++++++-------- .../model_component_edge_attribute_names.json | 12 +- .../model_component_edge_attribute_names.py | 2 +- ...del_component_polygon_attribute_names.json | 12 +- ...model_component_polygon_attribute_names.py | 2 +- ..._component_polyhedron_attribute_names.json | 12 +- ...el_component_polyhedron_attribute_names.py | 2 +- ...odel_component_vertex_attribute_names.json | 12 +- .../model_component_vertex_attribute_names.py | 2 +- tests/test_routes.py | 20 ++- 11 files changed, 143 insertions(+), 104 deletions(-) diff --git a/opengeodeweb_back_schemas.json b/opengeodeweb_back_schemas.json index 5bafdb6a..63c64a08 100644 --- a/opengeodeweb_back_schemas.json +++ b/opengeodeweb_back_schemas.json @@ -309,14 +309,18 @@ "type": "string", "minLength": 1 }, - "component_id": { - "type": "string", - "minLength": 1 + "component_ids": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "minItems": 1 } }, "required": [ "id", - "component_id" + "component_ids" ], "additionalProperties": false }, @@ -332,14 +336,18 @@ "type": "string", "minLength": 1 }, - "component_id": { - "type": "string", - "minLength": 1 + "component_ids": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "minItems": 1 } }, "required": [ "id", - "component_id" + "component_ids" ], "additionalProperties": false }, @@ -355,14 +363,18 @@ "type": "string", "minLength": 1 }, - "component_id": { - "type": "string", - "minLength": 1 + "component_ids": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "minItems": 1 } }, "required": [ "id", - "component_id" + "component_ids" ], "additionalProperties": false }, @@ -378,14 +390,18 @@ "type": "string", "minLength": 1 }, - "component_id": { - "type": "string", - "minLength": 1 + "component_ids": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "minItems": 1 } }, "required": [ "id", - "component_id" + "component_ids" ], "additionalProperties": false }, diff --git a/src/opengeodeweb_back/routes/blueprint_routes.py b/src/opengeodeweb_back/routes/blueprint_routes.py index 264816f5..76f1bc09 100644 --- a/src/opengeodeweb_back/routes/blueprint_routes.py +++ b/src/opengeodeweb_back/routes/blueprint_routes.py @@ -1,10 +1,10 @@ # Standard library imports +from opengeodeweb_back.geode_objects.geode_solid_mesh3d import GeodeSolidMesh3D import os import time import shutil import math from threading import Timer -from typing import Any, Union, get_args # Third party imports import flask @@ -31,9 +31,7 @@ from opengeodeweb_back.geode_objects.geode_grid3d import GeodeGrid3D from opengeodeweb_back.geode_objects.geode_surface_mesh2d import GeodeSurfaceMesh2D from opengeodeweb_back.geode_objects.geode_surface_mesh3d import GeodeSurfaceMesh3D -from opengeodeweb_back.geode_objects.geode_solid_mesh3d import GeodeSolidMesh3D - -ComponentMesh = Union[ +ComponentMesh = ( og.Corner2D, og.Corner3D, og.Line2D, @@ -41,7 +39,10 @@ og.Surface2D, og.Surface3D, og.Block3D, -] +) +ComponentLine = (og.Line2D, og.Line3D) +ComponentSurface = (og.Surface2D, og.Surface3D) +ComponentBlock = og.Block3D routes = flask.Blueprint("routes", __name__, url_prefix="/opengeodeweb_back") @@ -251,44 +252,48 @@ def texture_coordinates() -> flask.Response: def attributes_metadata( - manager: og.AttributeManager, + manager: og.AttributeManager | list[og.AttributeManager], ) -> list[dict[str, str | int | float | list[float]]]: + managers = manager if isinstance(manager, list) else [manager] attributes: list[dict[str, str | int | float | list[float]]] = [] - nb_elements = manager.nb_elements() - for name in manager.attribute_names(): - attribute = manager.find_generic_attribute(name) + first_manager = managers[0] + for name in first_manager.attribute_names(): + attribute = first_manager.find_generic_attribute(name) nb_items = 1 - min_value, max_value = -1.0, -1.0 min_values, max_values = [-1.0], [-1.0] if attribute.is_genericable(): nb_items = attribute.nb_items() min_values, max_values = [], [] for item in range(nb_items): - values = [ - attribute.generic_item_value(idx, item) - for idx in range(nb_elements) - ] - valid = [ - value - for value in values - if value is not None and not math.isnan(value) - ] - min_values.append(min(valid) if valid else -1.0) - max_values.append(max(valid) if valid else -1.0) - min_value, max_value = min_values[0], max_values[0] + valid_values: list[float] = [] + for attribute_manager in managers: + component_attribute = attribute_manager.find_generic_attribute( + name + ) + if component_attribute.is_genericable(): + nb_elements = attribute_manager.nb_elements() + values = [ + component_attribute.generic_item_value(index, item) + for index in range(nb_elements) + ] + valid_values.extend( + value + for value in values + if value is not None and not math.isnan(value) + ) + min_values.append(min(valid_values) if valid_values else -1.0) + max_values.append(max(valid_values) if valid_values else -1.0) attributes.append( { "attribute_name": name, "nb_items": nb_items, - "min_value": min_value, - "max_value": max_value, + "min_value": min_values[0], + "max_value": max_values[0], "min_values": min_values, "max_values": max_values, } ) - print(f"[ATTRIBUTES] Number of items: {nb_items}") - print(f"[ATTRIBUTES] Attributes: {attributes}") return attributes @@ -399,13 +404,16 @@ def model_component_vertex_attribute_names() -> flask.Response: geode_object = geode_functions.load_geode_object(params.id) if not isinstance(geode_object, GeodeModel): flask.abort(400, f"{params.id} is not a GeodeModel") - component = geode_object.component(og.uuid(params.component_id)) - if not isinstance(component, get_args(ComponentMesh)): - flask.abort(400, f"{params.component_id} is not a valid ComponentMesh") - mesh = component.mesh() - attribute_manager = mesh.vertex_attribute_manager() + managers = [ + component.mesh().vertex_attribute_manager() + for component_id in params.component_ids + if isinstance( + (component := geode_object.component(og.uuid(component_id))), + ComponentMesh, + ) + ] return flask.make_response( - {"attributes": attributes_metadata(attribute_manager)}, + {"attributes": attributes_metadata(managers)}, 200, ) @@ -422,15 +430,16 @@ def model_component_edge_attribute_names() -> flask.Response: geode_object = geode_functions.load_geode_object(params.id) if not isinstance(geode_object, GeodeModel): flask.abort(400, f"{params.id} is not a GeodeModel") - component = geode_object.component(og.uuid(params.component_id)) - if not isinstance(component, get_args(ComponentMesh)): - flask.abort(400, f"{params.component_id} is not a valid ComponentMesh") - mesh = component.mesh() - if not hasattr(mesh, "edge_attribute_manager"): - flask.abort(400, "Component does not have edges") - attribute_manager = mesh.edge_attribute_manager() + managers = [ + component.mesh().edge_attribute_manager() + for component_id in params.component_ids + if isinstance( + (component := geode_object.component(og.uuid(component_id))), + ComponentLine, + ) + ] return flask.make_response( - {"attributes": attributes_metadata(attribute_manager)}, + {"attributes": attributes_metadata(managers)}, 200, ) @@ -447,15 +456,16 @@ def model_component_polygon_attribute_names() -> flask.Response: geode_object = geode_functions.load_geode_object(params.id) if not isinstance(geode_object, GeodeModel): flask.abort(400, f"{params.id} is not a GeodeModel") - component = geode_object.component(og.uuid(params.component_id)) - if not isinstance(component, get_args(ComponentMesh)): - flask.abort(400, f"{params.component_id} is not a valid ComponentMesh") - mesh = component.mesh() - if not hasattr(mesh, "polygon_attribute_manager"): - flask.abort(400, "Component does not have polygons") - attribute_manager = mesh.polygon_attribute_manager() + managers = [ + component.mesh().polygon_attribute_manager() + for component_id in params.component_ids + if isinstance( + (component := geode_object.component(og.uuid(component_id))), + ComponentSurface, + ) + ] return flask.make_response( - {"attributes": attributes_metadata(attribute_manager)}, + {"attributes": attributes_metadata(managers)}, 200, ) @@ -472,15 +482,16 @@ def model_component_polyhedron_attribute_names() -> flask.Response: geode_object = geode_functions.load_geode_object(params.id) if not isinstance(geode_object, GeodeModel): flask.abort(400, f"{params.id} is not a GeodeModel") - component = geode_object.component(og.uuid(params.component_id)) - if not isinstance(component, get_args(ComponentMesh)): - flask.abort(400, f"{params.component_id} is not a valid ComponentMesh") - mesh = component.mesh() - if not hasattr(mesh, "polyhedron_attribute_manager"): - flask.abort(400, "Component does not have polyhedra") - attribute_manager = mesh.polyhedron_attribute_manager() + managers = [ + component.mesh().polyhedron_attribute_manager() + for component_id in params.component_ids + if isinstance( + (component := geode_object.component(og.uuid(component_id))), + ComponentBlock, + ) + ] return flask.make_response( - {"attributes": attributes_metadata(attribute_manager)}, + {"attributes": attributes_metadata(managers)}, 200, ) diff --git a/src/opengeodeweb_back/routes/schemas/model_component_edge_attribute_names.json b/src/opengeodeweb_back/routes/schemas/model_component_edge_attribute_names.json index 08047402..df955c16 100644 --- a/src/opengeodeweb_back/routes/schemas/model_component_edge_attribute_names.json +++ b/src/opengeodeweb_back/routes/schemas/model_component_edge_attribute_names.json @@ -7,11 +7,15 @@ "type": "string", "minLength": 1 }, - "component_id": { - "type": "string", - "minLength": 1 + "component_ids": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "minItems": 1 } }, - "required": ["id", "component_id"], + "required": ["id", "component_ids"], "additionalProperties": false } diff --git a/src/opengeodeweb_back/routes/schemas/model_component_edge_attribute_names.py b/src/opengeodeweb_back/routes/schemas/model_component_edge_attribute_names.py index 75105832..b3608d7d 100644 --- a/src/opengeodeweb_back/routes/schemas/model_component_edge_attribute_names.py +++ b/src/opengeodeweb_back/routes/schemas/model_component_edge_attribute_names.py @@ -7,5 +7,5 @@ class ModelComponentEdgeAttributeNames(DataClassJsonMixin): def __post_init__(self) -> None: print(self, flush=True) - component_id: str + component_ids: list[str] id: str diff --git a/src/opengeodeweb_back/routes/schemas/model_component_polygon_attribute_names.json b/src/opengeodeweb_back/routes/schemas/model_component_polygon_attribute_names.json index 60f685a3..54e99511 100644 --- a/src/opengeodeweb_back/routes/schemas/model_component_polygon_attribute_names.json +++ b/src/opengeodeweb_back/routes/schemas/model_component_polygon_attribute_names.json @@ -7,11 +7,15 @@ "type": "string", "minLength": 1 }, - "component_id": { - "type": "string", - "minLength": 1 + "component_ids": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "minItems": 1 } }, - "required": ["id", "component_id"], + "required": ["id", "component_ids"], "additionalProperties": false } diff --git a/src/opengeodeweb_back/routes/schemas/model_component_polygon_attribute_names.py b/src/opengeodeweb_back/routes/schemas/model_component_polygon_attribute_names.py index 5a22b6a2..564bb319 100644 --- a/src/opengeodeweb_back/routes/schemas/model_component_polygon_attribute_names.py +++ b/src/opengeodeweb_back/routes/schemas/model_component_polygon_attribute_names.py @@ -7,5 +7,5 @@ class ModelComponentPolygonAttributeNames(DataClassJsonMixin): def __post_init__(self) -> None: print(self, flush=True) - component_id: str + component_ids: list[str] id: str diff --git a/src/opengeodeweb_back/routes/schemas/model_component_polyhedron_attribute_names.json b/src/opengeodeweb_back/routes/schemas/model_component_polyhedron_attribute_names.json index 8115e3fc..725adaa5 100644 --- a/src/opengeodeweb_back/routes/schemas/model_component_polyhedron_attribute_names.json +++ b/src/opengeodeweb_back/routes/schemas/model_component_polyhedron_attribute_names.json @@ -7,11 +7,15 @@ "type": "string", "minLength": 1 }, - "component_id": { - "type": "string", - "minLength": 1 + "component_ids": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "minItems": 1 } }, - "required": ["id", "component_id"], + "required": ["id", "component_ids"], "additionalProperties": false } diff --git a/src/opengeodeweb_back/routes/schemas/model_component_polyhedron_attribute_names.py b/src/opengeodeweb_back/routes/schemas/model_component_polyhedron_attribute_names.py index 426c54bd..598684c9 100644 --- a/src/opengeodeweb_back/routes/schemas/model_component_polyhedron_attribute_names.py +++ b/src/opengeodeweb_back/routes/schemas/model_component_polyhedron_attribute_names.py @@ -7,5 +7,5 @@ class ModelComponentPolyhedronAttributeNames(DataClassJsonMixin): def __post_init__(self) -> None: print(self, flush=True) - component_id: str + component_ids: list[str] id: str diff --git a/src/opengeodeweb_back/routes/schemas/model_component_vertex_attribute_names.json b/src/opengeodeweb_back/routes/schemas/model_component_vertex_attribute_names.json index dcb7322c..b92398bd 100644 --- a/src/opengeodeweb_back/routes/schemas/model_component_vertex_attribute_names.json +++ b/src/opengeodeweb_back/routes/schemas/model_component_vertex_attribute_names.json @@ -7,11 +7,15 @@ "type": "string", "minLength": 1 }, - "component_id": { - "type": "string", - "minLength": 1 + "component_ids": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "minItems": 1 } }, - "required": ["id", "component_id"], + "required": ["id", "component_ids"], "additionalProperties": false } diff --git a/src/opengeodeweb_back/routes/schemas/model_component_vertex_attribute_names.py b/src/opengeodeweb_back/routes/schemas/model_component_vertex_attribute_names.py index d56851d6..842503e4 100644 --- a/src/opengeodeweb_back/routes/schemas/model_component_vertex_attribute_names.py +++ b/src/opengeodeweb_back/routes/schemas/model_component_vertex_attribute_names.py @@ -7,5 +7,5 @@ class ModelComponentVertexAttributeNames(DataClassJsonMixin): def __post_init__(self) -> None: print(self, flush=True) - component_id: str + component_ids: list[str] id: str diff --git a/tests/test_routes.py b/tests/test_routes.py index 3ed276da..9bf7ebff 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -676,13 +676,12 @@ def test_model_component_vertex_attribute_names(client: FlaskClient) -> None: corner_ids = by_type.get("Corner", []) assert len(corner_ids) > 0, "cube.og_brep should have Corner components" - component_id = corner_ids[0] - response = client.post(route, json={"id": model_id, "component_id": component_id}) + response = client.post(route, json={"id": model_id, "component_ids": corner_ids}) _assert_attributes_response(response) def get_full_data() -> test_utils.JsonData: - return {"id": model_id, "component_id": component_id} + return {"id": model_id, "component_ids": corner_ids} test_utils.test_route_wrong_params(client, route, get_full_data) @@ -693,13 +692,12 @@ def test_model_component_edge_attribute_names(client: FlaskClient) -> None: line_ids = by_type.get("Line", []) assert len(line_ids) > 0, "cube.og_brep should have Line components" - component_id = line_ids[0] - response = client.post(route, json={"id": model_id, "component_id": component_id}) + response = client.post(route, json={"id": model_id, "component_ids": line_ids}) _assert_attributes_response(response) def get_full_data() -> test_utils.JsonData: - return {"id": model_id, "component_id": component_id} + return {"id": model_id, "component_ids": line_ids} test_utils.test_route_wrong_params(client, route, get_full_data) @@ -710,13 +708,12 @@ def test_model_component_polygon_attribute_names(client: FlaskClient) -> None: surface_ids = by_type.get("Surface", []) assert len(surface_ids) > 0, "cube.og_brep should have Surface components" - component_id = surface_ids[0] - response = client.post(route, json={"id": model_id, "component_id": component_id}) + response = client.post(route, json={"id": model_id, "component_ids": surface_ids}) _assert_attributes_response(response) def get_full_data() -> test_utils.JsonData: - return {"id": model_id, "component_id": component_id} + return {"id": model_id, "component_ids": surface_ids} test_utils.test_route_wrong_params(client, route, get_full_data) @@ -727,12 +724,11 @@ def test_model_component_polyhedron_attribute_names(client: FlaskClient) -> None block_ids = by_type.get("Block", []) assert len(block_ids) > 0, "cube.og_brep should have Block components" - component_id = block_ids[0] - response = client.post(route, json={"id": model_id, "component_id": component_id}) + response = client.post(route, json={"id": model_id, "component_ids": block_ids}) _assert_attributes_response(response) def get_full_data() -> test_utils.JsonData: - return {"id": model_id, "component_id": component_id} + return {"id": model_id, "component_ids": block_ids} test_utils.test_route_wrong_params(client, route, get_full_data) From 257baf76581634cd01e63da7bb9d69c55fa2791b Mon Sep 17 00:00:00 2001 From: MaxNumerique <144453705+MaxNumerique@users.noreply.github.com> Date: Tue, 4 Aug 2026 10:00:35 +0000 Subject: [PATCH 2/5] Apply prepare changes --- requirements.txt | 1 - src/opengeodeweb_back/routes/blueprint_routes.py | 5 ++--- .../routes/schemas/model_component_edge_attribute_names.py | 3 ++- .../schemas/model_component_polygon_attribute_names.py | 3 ++- .../schemas/model_component_polyhedron_attribute_names.py | 3 ++- .../routes/schemas/model_component_vertex_attribute_names.py | 3 ++- 6 files changed, 10 insertions(+), 8 deletions(-) diff --git a/requirements.txt b/requirements.txt index ff401cc6..b0223a5d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -66,4 +66,3 @@ werkzeug==3.1.8 # flask # flask-cors -opengeodeweb-microservice==1.*,>=1.2.0rc1 diff --git a/src/opengeodeweb_back/routes/blueprint_routes.py b/src/opengeodeweb_back/routes/blueprint_routes.py index 76f1bc09..c3d84905 100644 --- a/src/opengeodeweb_back/routes/blueprint_routes.py +++ b/src/opengeodeweb_back/routes/blueprint_routes.py @@ -31,6 +31,7 @@ from opengeodeweb_back.geode_objects.geode_grid3d import GeodeGrid3D from opengeodeweb_back.geode_objects.geode_surface_mesh2d import GeodeSurfaceMesh2D from opengeodeweb_back.geode_objects.geode_surface_mesh3d import GeodeSurfaceMesh3D + ComponentMesh = ( og.Corner2D, og.Corner3D, @@ -267,9 +268,7 @@ def attributes_metadata( for item in range(nb_items): valid_values: list[float] = [] for attribute_manager in managers: - component_attribute = attribute_manager.find_generic_attribute( - name - ) + component_attribute = attribute_manager.find_generic_attribute(name) if component_attribute.is_genericable(): nb_elements = attribute_manager.nb_elements() values = [ diff --git a/src/opengeodeweb_back/routes/schemas/model_component_edge_attribute_names.py b/src/opengeodeweb_back/routes/schemas/model_component_edge_attribute_names.py index b3608d7d..a14c1181 100644 --- a/src/opengeodeweb_back/routes/schemas/model_component_edge_attribute_names.py +++ b/src/opengeodeweb_back/routes/schemas/model_component_edge_attribute_names.py @@ -1,5 +1,6 @@ from dataclasses_json import DataClassJsonMixin from dataclasses import dataclass +from typing import List @dataclass @@ -7,5 +8,5 @@ class ModelComponentEdgeAttributeNames(DataClassJsonMixin): def __post_init__(self) -> None: print(self, flush=True) - component_ids: list[str] + component_ids: List[str] id: str diff --git a/src/opengeodeweb_back/routes/schemas/model_component_polygon_attribute_names.py b/src/opengeodeweb_back/routes/schemas/model_component_polygon_attribute_names.py index 564bb319..6214a477 100644 --- a/src/opengeodeweb_back/routes/schemas/model_component_polygon_attribute_names.py +++ b/src/opengeodeweb_back/routes/schemas/model_component_polygon_attribute_names.py @@ -1,5 +1,6 @@ from dataclasses_json import DataClassJsonMixin from dataclasses import dataclass +from typing import List @dataclass @@ -7,5 +8,5 @@ class ModelComponentPolygonAttributeNames(DataClassJsonMixin): def __post_init__(self) -> None: print(self, flush=True) - component_ids: list[str] + component_ids: List[str] id: str diff --git a/src/opengeodeweb_back/routes/schemas/model_component_polyhedron_attribute_names.py b/src/opengeodeweb_back/routes/schemas/model_component_polyhedron_attribute_names.py index 598684c9..98985b69 100644 --- a/src/opengeodeweb_back/routes/schemas/model_component_polyhedron_attribute_names.py +++ b/src/opengeodeweb_back/routes/schemas/model_component_polyhedron_attribute_names.py @@ -1,5 +1,6 @@ from dataclasses_json import DataClassJsonMixin from dataclasses import dataclass +from typing import List @dataclass @@ -7,5 +8,5 @@ class ModelComponentPolyhedronAttributeNames(DataClassJsonMixin): def __post_init__(self) -> None: print(self, flush=True) - component_ids: list[str] + component_ids: List[str] id: str diff --git a/src/opengeodeweb_back/routes/schemas/model_component_vertex_attribute_names.py b/src/opengeodeweb_back/routes/schemas/model_component_vertex_attribute_names.py index 842503e4..1f5f6177 100644 --- a/src/opengeodeweb_back/routes/schemas/model_component_vertex_attribute_names.py +++ b/src/opengeodeweb_back/routes/schemas/model_component_vertex_attribute_names.py @@ -1,5 +1,6 @@ from dataclasses_json import DataClassJsonMixin from dataclasses import dataclass +from typing import List @dataclass @@ -7,5 +8,5 @@ class ModelComponentVertexAttributeNames(DataClassJsonMixin): def __post_init__(self) -> None: print(self, flush=True) - component_ids: list[str] + component_ids: List[str] id: str From 04fd1c11bbbec5e361863881c038e62f2e1e7972 Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Wed, 5 Aug 2026 10:56:55 +0200 Subject: [PATCH 3/5] extract_valid_attribute_values function to simplify --- .../routes/blueprint_routes.py | 66 ++++++++++++------- 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/src/opengeodeweb_back/routes/blueprint_routes.py b/src/opengeodeweb_back/routes/blueprint_routes.py index c3d84905..d0d722e0 100644 --- a/src/opengeodeweb_back/routes/blueprint_routes.py +++ b/src/opengeodeweb_back/routes/blueprint_routes.py @@ -252,36 +252,54 @@ def texture_coordinates() -> flask.Response: return flask.make_response({"texture_coordinates": texture_coordinates}, 200) +def extract_valid_attribute_values( + attribute_manager: og.AttributeManager, + attribute_name: str, + item_index: int, +) -> list[float]: + component_attribute = attribute_manager.find_generic_attribute(attribute_name) + if not component_attribute.is_genericable(): + return [] + valid_values: list[float] = [] + for index in range(attribute_manager.nb_elements()): + value = component_attribute.generic_item_value(index, item_index) + if value is not None and not math.isnan(value): + valid_values.append(value) + return valid_values + + def attributes_metadata( manager: og.AttributeManager | list[og.AttributeManager], ) -> list[dict[str, str | int | float | list[float]]]: - managers = manager if isinstance(manager, list) else [manager] + attribute_managers = manager if isinstance(manager, list) else [manager] attributes: list[dict[str, str | int | float | list[float]]] = [] - first_manager = managers[0] + first_manager = attribute_managers[0] for name in first_manager.attribute_names(): attribute = first_manager.find_generic_attribute(name) - nb_items = 1 - min_values, max_values = [-1.0], [-1.0] - if attribute.is_genericable(): - nb_items = attribute.nb_items() - min_values, max_values = [], [] - for item in range(nb_items): - valid_values: list[float] = [] - for attribute_manager in managers: - component_attribute = attribute_manager.find_generic_attribute(name) - if component_attribute.is_genericable(): - nb_elements = attribute_manager.nb_elements() - values = [ - component_attribute.generic_item_value(index, item) - for index in range(nb_elements) - ] - valid_values.extend( - value - for value in values - if value is not None and not math.isnan(value) - ) - min_values.append(min(valid_values) if valid_values else -1.0) - max_values.append(max(valid_values) if valid_values else -1.0) + if not attribute.is_genericable(): + attributes.append( + { + "attribute_name": name, + "nb_items": 1, + "min_value": -1.0, + "max_value": -1.0, + "min_values": [-1.0], + "max_values": [-1.0], + } + ) + continue + + nb_items = attribute.nb_items() + min_values: list[float] = [] + max_values: list[float] = [] + for item_index in range(nb_items): + valid_values: list[float] = [] + for attribute_manager in attribute_managers: + valid_values.extend( + extract_valid_attribute_values(attribute_manager, name, item_index) + ) + min_values.append(min(valid_values) if valid_values else -1.0) + max_values.append(max(valid_values) if valid_values else -1.0) attributes.append( { From a4bb2203754bc08cb6337ddeb185b34c0726997f Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Wed, 5 Aug 2026 11:01:19 +0200 Subject: [PATCH 4/5] revert some --- .../routes/blueprint_routes.py | 38 +++++++------------ 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/src/opengeodeweb_back/routes/blueprint_routes.py b/src/opengeodeweb_back/routes/blueprint_routes.py index d0d722e0..3ff23c64 100644 --- a/src/opengeodeweb_back/routes/blueprint_routes.py +++ b/src/opengeodeweb_back/routes/blueprint_routes.py @@ -276,31 +276,19 @@ def attributes_metadata( first_manager = attribute_managers[0] for name in first_manager.attribute_names(): attribute = first_manager.find_generic_attribute(name) - if not attribute.is_genericable(): - attributes.append( - { - "attribute_name": name, - "nb_items": 1, - "min_value": -1.0, - "max_value": -1.0, - "min_values": [-1.0], - "max_values": [-1.0], - } - ) - continue - - nb_items = attribute.nb_items() - min_values: list[float] = [] - max_values: list[float] = [] - for item_index in range(nb_items): - valid_values: list[float] = [] - for attribute_manager in attribute_managers: - valid_values.extend( - extract_valid_attribute_values(attribute_manager, name, item_index) - ) - min_values.append(min(valid_values) if valid_values else -1.0) - max_values.append(max(valid_values) if valid_values else -1.0) - + nb_items = 1 + min_values, max_values = [-1.0], [-1.0] + if attribute.is_genericable(): + nb_items = attribute.nb_items() + min_values, max_values = [], [] + for item_index in range(nb_items): + valid_values: list[float] = [] + for attribute_manager in attribute_managers: + valid_values.extend( + extract_valid_attribute_values(attribute_manager, name, item_index) + ) + min_values.append(min(valid_values) if valid_values else -1.0) + max_values.append(max(valid_values) if valid_values else -1.0) attributes.append( { "attribute_name": name, From 68f6fd5c63b354e87065b63238e35cab2b56635d Mon Sep 17 00:00:00 2001 From: MaxNumerique <144453705+MaxNumerique@users.noreply.github.com> Date: Wed, 5 Aug 2026 09:01:54 +0000 Subject: [PATCH 5/5] Apply prepare changes --- src/opengeodeweb_back/routes/blueprint_routes.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/opengeodeweb_back/routes/blueprint_routes.py b/src/opengeodeweb_back/routes/blueprint_routes.py index 3ff23c64..9f1c0c17 100644 --- a/src/opengeodeweb_back/routes/blueprint_routes.py +++ b/src/opengeodeweb_back/routes/blueprint_routes.py @@ -285,7 +285,9 @@ def attributes_metadata( valid_values: list[float] = [] for attribute_manager in attribute_managers: valid_values.extend( - extract_valid_attribute_values(attribute_manager, name, item_index) + extract_valid_attribute_values( + attribute_manager, name, item_index + ) ) min_values.append(min(valid_values) if valid_values else -1.0) max_values.append(max(valid_values) if valid_values else -1.0)