diff --git a/.gitignore b/.gitignore index 8d3da5d..b402626 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,7 @@ build *.egg-info .coverage* .mypy_cache +*.db +tests/data + opengeodeweb_viewer_schemas.json \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index b024b45..be71976 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,16 +7,14 @@ build-backend = "setuptools.build_meta" name = "OpenGeodeWeb-Viewer" version = "0.0.0" dynamic = ["dependencies"] -authors = [ - { name="Geode-solutions", email="team-web@geode-solutions.com" }, -] +authors = [{ name = "Geode-solutions", email = "team-web@geode-solutions.com" }] description = "OpenGeodeWeb-Viewer is an open source framework that proposes handy python functions and wrappers for the OpenGeode ecosystem" readme = "README.md" requires-python = ">=3.9" classifiers = [ - "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", ] [project.urls] @@ -24,10 +22,10 @@ classifiers = [ "Bug Tracker" = "https://github.com/Geode-solutions/OpenGeodeWeb-Viewer/issues" [project.scripts] -opengeodeweb_viewer = "opengeodeweb_viewer.vtkw_server:run_server" +opengeodeweb-viewer = "opengeodeweb_viewer.vtkw_server:run_server" [tool.setuptools.dynamic] -dependencies = {file = ["requirements.txt"]} +dependencies = { file = ["requirements.txt"] } [tool.setuptools.packages.find] where = ["src"] diff --git a/requirements.txt b/requirements.txt index b6457a9..c8814b0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -61,4 +61,3 @@ wslink==1.12.4 yarl>=1 # via aiohttp -opengeodeweb-microservice==1.*,>=1.0.4rc7 diff --git a/src/opengeodeweb_viewer/config.py b/src/opengeodeweb_viewer/config.py index 2f8cdf5..14b04a6 100644 --- a/src/opengeodeweb_viewer/config.py +++ b/src/opengeodeweb_viewer/config.py @@ -1,20 +1,19 @@ import os -import tempfile from shutil import copyfile, copytree from sys import platform -def default_config(): +def default_config() -> None: os.environ["DEFAULT_HOST"] = "localhost" os.environ["DEFAULT_PORT"] = "1234" -def prod_config(): +def prod_config() -> None: default_config() os.environ["DATA_FOLDER_PATH"] = "/data/" -def dev_config(): +def dev_config() -> None: default_config() if platform == "linux": os.environ["DATA_FOLDER_PATH"] = "/temp/OpenGeodeWeb_Data/" @@ -26,51 +25,39 @@ def dev_config(): os.mkdir(os.environ.get("DATA_FOLDER_PATH")) -def test_config(path): - default_config() - - tmp_data_root = tempfile.mkdtemp(prefix="ogw_test_data_") - os.environ["DATA_FOLDER_PATH"] = tmp_data_root - - src_data = os.path.join(path, "data") - if not os.path.isdir(src_data): - raise FileNotFoundError(f"Test data folder not found: {src_data}") - - test_ids = ["123456789", "12345678"] - valid_exts = {".vtp", ".vti", ".vtu", ".vtm"} - - project_uuid = "test-project-uuid" - data_uuid = "test-data-uuid" - uploads_directory = os.path.join(tmp_data_root, project_uuid, "uploads") - structure_directory = os.path.join(tmp_data_root, project_uuid, data_uuid) - - for directory in [ - *test_ids, - uploads_directory, - structure_directory, - ]: # create directories for tests - os.makedirs( - ( - os.path.join(tmp_data_root, directory) - if isinstance(directory, str) - else directory - ), - exist_ok=True, - ) - +def _copy_test_assets( + src_data: str, + tmp_data_root: str, + test_ids: list[str], + valid_exts: set[str], + uploads_directory: str, + structure_directory: str, +) -> None: for root, directories, files in os.walk(src_data): for directory in directories: - dst = os.path.join(tmp_data_root, test_ids[0], directory) - copytree(os.path.join(root, directory), dst, dirs_exist_ok=True) - + for test_id in test_ids: + dst = os.path.join(tmp_data_root, test_id, directory) + copytree(os.path.join(root, directory), dst, dirs_exist_ok=True) for file in files: if os.path.splitext(file)[1].lower() not in valid_exts: continue - src = os.path.join(root, file) for test_id in test_ids: copyfile(src, os.path.join(tmp_data_root, test_id, file)) copyfile(src, os.path.join(structure_directory, file)) copyfile(src, os.path.join(uploads_directory, file)) - print(f"\nDATA_FOLDER_PATH set to: {tmp_data_root}", flush=True) + +def test_config() -> None: + default_config() + if "DATA_FOLDER_PATH" not in os.environ: + data_path = os.path.join(os.path.dirname(__file__), "..", "..", "tests", "data") + os.environ["DATA_FOLDER_PATH"] = os.path.abspath(data_path) + + data_path = os.environ["DATA_FOLDER_PATH"] + if not os.path.exists(data_path): + os.makedirs(data_path, exist_ok=True) + + db_file = os.path.join(data_path, "project.db") + if not os.path.exists(db_file): + open(db_file, "a").close() diff --git a/src/opengeodeweb_viewer/object/object_methods.py b/src/opengeodeweb_viewer/object/object_methods.py index 1c90554..faea2ad 100644 --- a/src/opengeodeweb_viewer/object/object_methods.py +++ b/src/opengeodeweb_viewer/object/object_methods.py @@ -5,15 +5,21 @@ import vtk # Local application imports -from opengeodeweb_viewer.utils_functions import get_schemas_dict, validate_schema from opengeodeweb_viewer.vtk_protocol import VtkView class VtkObjectView(VtkView): - def __init__(self): + def __init__(self) -> None: super().__init__() - def registerObject(self, id, file_name, reader, filter, mapper): + def registerObject( + self, + id: str, + file_name: str, + reader: vtk.vtkDataReader, + filter: vtk.vtkAlgorithm | None, + mapper: vtk.vtkMapper, + ) -> None: actor = vtk.vtkActor() self.register_object(id, reader, filter, actor, mapper, {}) reader.SetFileName(os.path.join(self.DATA_FOLDER_PATH, id, file_name)) @@ -30,19 +36,19 @@ def registerObject(self, id, file_name, reader, filter, mapper): renderWindow.Render() self.render() - def deregisterObject(self, id): - actor = self.get_object(id)["actor"] + def deregisterObject(self, data_id: str) -> None: + actor = self.get_object(data_id)["actor"] renderWindow = self.getView("-1") renderer = renderWindow.GetRenderers().GetFirstRenderer() renderer.RemoveActor(actor) - self.deregister_object(id) + self.deregister_object(data_id) self.render() - def applyTextures(self, id, textures): - textures_array = [] - images_reader_array = [] + def applyTextures(self, data_id: str, textures: list[dict[str, str]]) -> None: + textures_array: list[vtk.vtkTexture] = [] + images_reader_array: list[vtk.vtkXMLImageDataReader] = [] - data = self.get_object(id) + data = self.get_object(data_id) mapper = data["mapper"] actor = data["actor"] reader = data["reader"] @@ -52,14 +58,13 @@ def applyTextures(self, id, textures): for index, value in enumerate(textures): texture_name = value["texture_name"] - texture_file_name = value["texture_file_name"] - print(f"{texture_name=} {texture_file_name=}", flush=True) + id_texture = value["id"] + print(f"{texture_name=} {id_texture=}", flush=True) new_texture = vtk.vtkTexture() image_reader = vtk.vtkXMLImageDataReader() - image_reader.SetFileName( - os.path.join(self.DATA_FOLDER_PATH, id, texture_file_name) - ) + texture_path = self.get_data_file_path(data_id, id_texture) + image_reader.SetFileName(texture_path) shader_texture_name = f"VTK_TEXTURE_UNIT_{index}" polydata_mapper.MapDataArrayToMultiTextureAttribute( @@ -87,83 +92,87 @@ def applyTextures(self, id, textures): self.render() - def SetVisibility(self, id, visibility): - actor = self.get_object(id)["actor"] + def SetVisibility(self, data_id: str, visibility: bool) -> None: + actor = self.get_object(data_id)["actor"] actor.SetVisibility(visibility) self.render() - def SetOpacity(self, id, opacity): - actor = self.get_object(id)["actor"] + def SetOpacity(self, data_id: str, opacity: float) -> None: + actor = self.get_object(data_id)["actor"] actor.GetProperty().SetOpacity(opacity) self.render() - def SetColor(self, id, red, green, blue): - mapper = self.get_object(id)["mapper"] + def SetColor(self, data_id: str, red: int, green: int, blue: int) -> None: + mapper = self.get_object(data_id)["mapper"] mapper.ScalarVisibilityOff() - actor = self.get_object(id)["actor"] + actor = self.get_object(data_id)["actor"] actor.GetProperty().SetColor([red / 255, green / 255, blue / 255]) self.render() - def SetEdgesVisibility(self, id, visibility): - actor = self.get_object(id)["actor"] - max_dimension = self.get_object(id)["max_dimension"] + def SetEdgesVisibility(self, data_id: str, visibility: bool) -> None: + actor = self.get_object(data_id)["actor"] + max_dimension = self.get_object(data_id)["max_dimension"] if max_dimension == "edges": - self.SetVisibility(id, visibility) + self.SetVisibility(data_id, visibility) else: actor.GetProperty().SetEdgeVisibility(visibility) self.render() - def SetEdgesWidth(self, id, width): - actor = self.get_object(id)["actor"] + def SetEdgesWidth(self, data_id: str, width: float) -> None: + actor = self.get_object(data_id)["actor"] actor.GetProperty().SetEdgeWidth(width) self.render() - def SetEdgesColor(self, id, red, green, blue): - actor = self.get_object(id)["actor"] - max_dimension = self.get_object(id)["max_dimension"] + def SetEdgesColor(self, data_id: str, red: int, green: int, blue: int) -> None: + actor = self.get_object(data_id)["actor"] + max_dimension = self.get_object(data_id)["max_dimension"] if max_dimension == "edges": - self.SetColor(id, red, green, blue) + self.SetColor(data_id, red, green, blue) else: actor.GetProperty().SetEdgeColor([red / 255, green / 255, blue / 255]) self.render() - def SetPointsVisibility(self, id, visibility): - actor = self.get_object(id)["actor"] - max_dimension = self.get_object(id)["max_dimension"] + def SetPointsVisibility(self, data_id: str, visibility: bool) -> None: + actor = self.get_object(data_id)["actor"] + max_dimension = self.get_object(data_id)["max_dimension"] if max_dimension == "points": - self.SetVisibility(id, visibility) + self.SetVisibility(data_id, visibility) else: actor.GetProperty().SetVertexVisibility(visibility) self.render() - def SetPointsSize(self, id, size): - actor = self.get_object(id)["actor"] + def SetPointsSize(self, data_id: str, size: float) -> None: + actor = self.get_object(data_id)["actor"] actor.GetProperty().SetPointSize(size) self.render() - def SetPointsColor(self, id, red, green, blue): - actor = self.get_object(id)["actor"] - max_dimension = self.get_object(id)["max_dimension"] + def SetPointsColor(self, data_id: str, red: int, green: int, blue: int) -> None: + actor = self.get_object(data_id)["actor"] + max_dimension = self.get_object(data_id)["max_dimension"] if max_dimension == "points": - self.SetColor(id, red, green, blue) + self.SetColor(data_id, red, green, blue) else: actor.GetProperty().SetVertexColor([red / 255, green / 255, blue / 255]) self.render() - def SetBlocksVisibility(self, id, block_ids, visibility): - mapper = self.get_object(id)["mapper"] + def SetBlocksVisibility( + self, data_id: str, block_ids: list[int], visibility: bool + ) -> None: + mapper = self.get_object(data_id)["mapper"] for block_id in block_ids: mapper.SetBlockVisibility(block_id, visibility) self.render() - def SetBlocksColor(self, id, block_ids, red, green, blue): - mapper = self.get_object(id)["mapper"] + def SetBlocksColor( + self, data_id: str, block_ids: list[int], red: int, green: int, blue: int + ) -> None: + mapper = self.get_object(data_id)["mapper"] for block_id in block_ids: mapper.SetBlockColor(block_id, [red / 255, green / 255, blue / 255]) self.render() - def clearColors(self, id): - db = self.get_object(id) + def clearColors(self, data_id: str) -> None: + db = self.get_object(data_id) mapper = db["mapper"] reader = db["reader"] reader.GetOutput().GetPointData().SetActiveScalars("") diff --git a/src/opengeodeweb_viewer/py.typed b/src/opengeodeweb_viewer/py.typed index 5fcb852..b648ac9 100644 --- a/src/opengeodeweb_viewer/py.typed +++ b/src/opengeodeweb_viewer/py.typed @@ -1 +1 @@ -partial \ No newline at end of file +partial diff --git a/src/opengeodeweb_viewer/rpc/generic/generic_protocols.py b/src/opengeodeweb_viewer/rpc/generic/generic_protocols.py index d358941..5ab756d 100644 --- a/src/opengeodeweb_viewer/rpc/generic/generic_protocols.py +++ b/src/opengeodeweb_viewer/rpc/generic/generic_protocols.py @@ -27,11 +27,11 @@ def register(self, params): params, self.generic_schemas_dict["register"], self.generic_prefix ) viewer_object = params["viewer_object"] - params.pop("viewer_object", None) + specific_params = {"id": params["id"]} if viewer_object == "mesh": - self.mesh_protocols.registerMesh(params) + self.mesh_protocols.registerMesh(specific_params) elif viewer_object == "model": - self.model_protocols.registerModel(params) + self.model_protocols.registerModel(specific_params) @exportRpc(generic_prefix + generic_schemas_dict["deregister"]["rpc"]) def deregister(self, params): @@ -39,8 +39,8 @@ def deregister(self, params): params, self.generic_schemas_dict["deregister"], self.generic_prefix ) viewer_object = params["viewer_object"] - params.pop("viewer_object", None) + specific_params = {"id": params["id"]} if viewer_object == "mesh": - self.mesh_protocols.deregisterMesh(params) + self.mesh_protocols.deregisterMesh(specific_params) elif viewer_object == "model": - self.model_protocols.deregisterModel(params) + self.model_protocols.deregisterModel(specific_params) diff --git a/src/opengeodeweb_viewer/rpc/generic/schemas/register.json b/src/opengeodeweb_viewer/rpc/generic/schemas/register.json index 26187d9..1d6cf26 100644 --- a/src/opengeodeweb_viewer/rpc/generic/schemas/register.json +++ b/src/opengeodeweb_viewer/rpc/generic/schemas/register.json @@ -13,16 +13,11 @@ "id": { "type": "string", "minLength": 1 - }, - "file_name": { - "type": "string", - "minLength": 1 } }, "required": [ "viewer_object", - "id", - "file_name" + "id" ], "additionalProperties": false } \ No newline at end of file diff --git a/src/opengeodeweb_viewer/rpc/mesh/mesh_protocols.py b/src/opengeodeweb_viewer/rpc/mesh/mesh_protocols.py index 5d61136..fdccdb5 100644 --- a/src/opengeodeweb_viewer/rpc/mesh/mesh_protocols.py +++ b/src/opengeodeweb_viewer/rpc/mesh/mesh_protocols.py @@ -1,12 +1,19 @@ # Standard library imports import os +from typing import cast # Third party imports import vtk from wslink import register as exportRpc +from opengeodeweb_microservice.database.data import Data # Local application imports -from opengeodeweb_viewer.utils_functions import get_schemas_dict, validate_schema +from opengeodeweb_viewer.utils_functions import ( + get_schemas_dict, + validate_schema, + RpcParams, + RpcParamsWithColor, +) from opengeodeweb_viewer.object.object_methods import VtkObjectView @@ -16,19 +23,24 @@ class VtkMeshView(VtkObjectView): os.path.join(os.path.dirname(__file__), "schemas") ) - def __init__(self): + def __init__(self) -> None: super().__init__() @exportRpc(mesh_prefix + mesh_schemas_dict["register"]["rpc"]) - def registerMesh(self, params): + def registerMesh(self, params: RpcParams) -> None: + print(f"{self.mesh_schemas_dict["register"]}", flush=True) validate_schema(params, self.mesh_schemas_dict["register"], self.mesh_prefix) - id, file_name = params["id"], params["file_name"] + data_id = str(params["id"]) try: + data = self.get_data(data_id) + file_name = str(data["viewable_file_name"]) + reader = vtk.vtkXMLGenericDataObjectReader() filter = {} mapper = vtk.vtkDataSetMapper() mapper.SetInputConnection(reader.GetOutputPort()) - self.registerObject(id, file_name, reader, filter, mapper) + + self.registerObject(data_id, file_name, reader, filter, mapper) data_object = reader.GetOutput() data_set = vtk.vtkDataSet.SafeDownCast(data_object) @@ -49,62 +61,94 @@ def registerMesh(self, params): max_dimension = "polygons" elif max_id >= 7: max_dimension = "polyhedra" - self.get_data_base()[id]["max_dimension"] = max_dimension + self.get_data_base()[data_id]["max_dimension"] = max_dimension + except Exception as e: - print("error : ", str(e), flush=True) + print(f"Error registering mesh {data_id}: {str(e)}", flush=True) + raise @exportRpc(mesh_prefix + mesh_schemas_dict["deregister"]["rpc"]) - def deregisterMesh(self, params): + def deregisterMesh(self, params: RpcParams) -> None: validate_schema(params, self.mesh_schemas_dict["deregister"], self.mesh_prefix) - id = params["id"] - self.deregisterObject(id) + data_id = str(params["id"]) + self.deregisterObject(data_id) @exportRpc(mesh_prefix + mesh_schemas_dict["visibility"]["rpc"]) - def SetMeshVisibility(self, params): + def SetMeshVisibility(self, params: RpcParams) -> None: validate_schema(params, self.mesh_schemas_dict["visibility"], self.mesh_prefix) - id, visibility = params["id"], params["visibility"] - self.SetVisibility(id, visibility) + data_id, visibility = str(params["id"]), bool(params["visibility"]) + self.SetVisibility(data_id, visibility) @exportRpc(mesh_prefix + mesh_schemas_dict["opacity"]["rpc"]) - def setMeshOpacity(self, params): + def setMeshOpacity(self, params: RpcParams) -> None: validate_schema(params, self.mesh_schemas_dict["opacity"], self.mesh_prefix) - id, opacity = params["id"], params["opacity"] - self.SetOpacity(id, opacity) + data_id, opacity = str(params["id"]), float( + cast(int | float, params["opacity"]) + ) + self.SetOpacity(data_id, opacity) @exportRpc(mesh_prefix + mesh_schemas_dict["color"]["rpc"]) - def setMeshColor(self, params): + def setMeshColor(self, params: RpcParamsWithColor) -> None: validate_schema(params, self.mesh_schemas_dict["color"], self.mesh_prefix) - id, red, green, blue = ( - params["id"], - params["color"]["r"], - params["color"]["g"], - params["color"]["b"], + color_dict = cast(dict[str, int], params["color"]) + data_id, red, green, blue = ( + str(params["id"]), + int(color_dict["r"]), + int(color_dict["g"]), + int(color_dict["b"]), ) - self.SetColor(id, red, green, blue) + self.SetColor(data_id, red, green, blue) @exportRpc(mesh_prefix + mesh_schemas_dict["apply_textures"]["rpc"]) - def meshApplyTextures(self, params): + def meshApplyTextures(self, params: RpcParams) -> None: validate_schema( params, self.mesh_schemas_dict["apply_textures"], self.mesh_prefix ) - id, textures = params["id"], params["textures"] - self.applyTextures(id, textures) + data_id = str(params["id"]) + textures_info = cast(list[dict[str, str]], params["textures"]) + self.applyTextures(data_id, textures_info) + + def applyTextures(self, mesh_id: str, textures_info: list[dict[str, str]]) -> None: + for tex_info in textures_info: + texture_id = tex_info["id"] + texture_name = tex_info["texture_name"] + texture_data = Data.get(texture_id) + if not texture_data or texture_data.geode_object != "RasterImage2D": + continue + texture_file_path = self.get_data_file_path(texture_id) + texture_reader = vtk.vtkXMLImageDataReader() + texture_reader.SetFileName(texture_file_path) + texture_reader.Update() + texture = vtk.vtkTexture() + texture.SetInputConnection(texture_reader.GetOutputPort()) + texture.InterpolateOn() + reader = cast(vtk.vtkAlgorithm, self.get_object(mesh_id)["reader"]) + output = reader.GetOutput() + point_data = output.GetPointData() + for i in range(point_data.GetNumberOfArrays()): + array = point_data.GetArray(i) + if array.GetName() == texture_name: + point_data.SetTCoords(array) + break + actor = cast(vtk.vtkActor, self.get_object(mesh_id)["actor"]) + actor.SetTexture(texture) + self.render() - def displayAttributeOnVertices(self, id, name): - reader = self.get_object(id)["reader"] + def displayAttributeOnVertices(self, data_id: str, name: str) -> None: + reader = self.get_object(data_id)["reader"] points = reader.GetOutput().GetPointData() points.SetActiveScalars(name) - mapper = self.get_object(id)["mapper"] + mapper = self.get_object(data_id)["mapper"] mapper.ScalarVisibilityOn() mapper.SetScalarModeToUsePointData() mapper.SetScalarRange(points.GetScalars().GetRange()) self.render() - def displayAttributeOnCells(self, id, name): - reader = self.get_object(id)["reader"] + def displayAttributeOnCells(self, data_id: str, name: str) -> None: + reader = self.get_object(data_id)["reader"] cells = reader.GetOutput().GetCellData() cells.SetActiveScalars(name) - mapper = self.get_object(id)["mapper"] + mapper = self.get_object(data_id)["mapper"] mapper.ScalarVisibilityOn() mapper.SetScalarModeToUseCellData() mapper.SetScalarRange(cells.GetScalars().GetRange()) diff --git a/src/opengeodeweb_viewer/rpc/mesh/points/mesh_points_protocols.py b/src/opengeodeweb_viewer/rpc/mesh/points/mesh_points_protocols.py index e8c5919..613a96c 100644 --- a/src/opengeodeweb_viewer/rpc/mesh/points/mesh_points_protocols.py +++ b/src/opengeodeweb_viewer/rpc/mesh/points/mesh_points_protocols.py @@ -5,7 +5,12 @@ from wslink import register as exportRpc # Local application imports -from opengeodeweb_viewer.utils_functions import get_schemas_dict, validate_schema +from opengeodeweb_viewer.utils_functions import ( + get_schemas_dict, + validate_schema, + RpcParams, + RpcParamsWithColor, +) from opengeodeweb_viewer.rpc.mesh.mesh_protocols import VtkMeshView @@ -15,11 +20,11 @@ class VtkMeshPointsView(VtkMeshView): os.path.join(os.path.dirname(__file__), "schemas") ) - def __init__(self): + def __init__(self) -> None: super().__init__() @exportRpc(mesh_points_prefix + mesh_points_schemas_dict["visibility"]["rpc"]) - def setMeshPointsVisibility(self, params): + def setMeshPointsVisibility(self, params: RpcParams) -> None: validate_schema( params, self.mesh_points_schemas_dict["visibility"], self.mesh_points_prefix ) @@ -27,7 +32,7 @@ def setMeshPointsVisibility(self, params): self.SetPointsVisibility(id, visibility) @exportRpc(mesh_points_prefix + mesh_points_schemas_dict["color"]["rpc"]) - def setMeshPointsColor(self, params): + def setMeshPointsColor(self, params: RpcParamsWithColor) -> None: validate_schema( params, self.mesh_points_schemas_dict["color"], self.mesh_points_prefix ) @@ -40,7 +45,7 @@ def setMeshPointsColor(self, params): self.SetPointsColor(id, red, green, blue) @exportRpc(mesh_points_prefix + mesh_points_schemas_dict["size"]["rpc"]) - def setMeshPointsSize(self, params): + def setMeshPointsSize(self, params: RpcParams) -> None: validate_schema( params, self.mesh_points_schemas_dict["size"], self.mesh_points_prefix ) @@ -48,7 +53,7 @@ def setMeshPointsSize(self, params): self.SetPointsSize(id, size) @exportRpc(mesh_points_prefix + mesh_points_schemas_dict["vertex_attribute"]["rpc"]) - def setMeshPointsVertexAttribute(self, params): + def setMeshPointsVertexAttribute(self, params: RpcParams) -> None: validate_schema( params, self.mesh_points_schemas_dict["vertex_attribute"], diff --git a/src/opengeodeweb_viewer/rpc/mesh/schemas/apply_textures.json b/src/opengeodeweb_viewer/rpc/mesh/schemas/apply_textures.json index 311384e..d89cff8 100644 --- a/src/opengeodeweb_viewer/rpc/mesh/schemas/apply_textures.json +++ b/src/opengeodeweb_viewer/rpc/mesh/schemas/apply_textures.json @@ -15,14 +15,14 @@ "type": "string", "minLength": 1 }, - "texture_file_name": { + "id": { "type": "string", "minLength": 1 } }, "required": [ "texture_name", - "texture_file_name" + "id" ], "additionalProperties": false }, diff --git a/src/opengeodeweb_viewer/rpc/mesh/schemas/register.json b/src/opengeodeweb_viewer/rpc/mesh/schemas/register.json index 371e658..3c04ada 100644 --- a/src/opengeodeweb_viewer/rpc/mesh/schemas/register.json +++ b/src/opengeodeweb_viewer/rpc/mesh/schemas/register.json @@ -5,15 +5,10 @@ "id": { "type": "string", "minLength": 1 - }, - "file_name": { - "type": "string", - "minLength": 1 } }, "required": [ - "id", - "file_name" + "id" ], "additionalProperties": false } \ No newline at end of file diff --git a/src/opengeodeweb_viewer/rpc/model/model_protocols.py b/src/opengeodeweb_viewer/rpc/model/model_protocols.py index 9b4e579..a4ada87 100644 --- a/src/opengeodeweb_viewer/rpc/model/model_protocols.py +++ b/src/opengeodeweb_viewer/rpc/model/model_protocols.py @@ -23,8 +23,11 @@ def __init__(self): @exportRpc(model_prefix + model_schemas_dict["register"]["rpc"]) def registerModel(self, params): validate_schema(params, self.model_schemas_dict["register"], self.model_prefix) - id, file_name = params["id"], params["file_name"] + data_id = params["id"] try: + data = self.get_data(data_id) + file_name = str(data["viewable_file_name"]) + reader = vtk.vtkXMLMultiBlockDataReader() filter = vtk.vtkGeometryFilter() filter.SetInputConnection(reader.GetOutputPort()) @@ -32,23 +35,24 @@ def registerModel(self, params): mapper.SetInputConnection(filter.GetOutputPort()) attributes = vtkCompositeDataDisplayAttributes() mapper.SetCompositeDataDisplayAttributes(attributes) - self.registerObject(id, file_name, reader, filter, mapper) - self.get_object(id)["max_dimension"] = "default" + self.registerObject(data_id, file_name, reader, filter, mapper) + self.get_object(data_id)["max_dimension"] = "default" except Exception as e: - print("error : ", str(e), flush=True) + print(f"Error registering model {data_id}: {str(e)}", flush=True) + raise @exportRpc(model_prefix + model_schemas_dict["deregister"]["rpc"]) def deregisterModel(self, params): validate_schema( params, self.model_schemas_dict["deregister"], self.model_prefix ) - id = params["id"] - self.deregisterObject(id) + data_id = params["id"] + self.deregisterObject(data_id) @exportRpc(model_prefix + model_schemas_dict["visibility"]["rpc"]) def setModelVisibility(self, params): validate_schema( params, self.model_schemas_dict["visibility"], self.model_prefix ) - id, visibility = params["id"], params["visibility"] - self.SetVisibility(id, visibility) + data_id, visibility = params["id"], params["visibility"] + self.SetVisibility(data_id, visibility) diff --git a/src/opengeodeweb_viewer/rpc/model/schemas/register.json b/src/opengeodeweb_viewer/rpc/model/schemas/register.json index 371e658..3c04ada 100644 --- a/src/opengeodeweb_viewer/rpc/model/schemas/register.json +++ b/src/opengeodeweb_viewer/rpc/model/schemas/register.json @@ -5,15 +5,10 @@ "id": { "type": "string", "minLength": 1 - }, - "file_name": { - "type": "string", - "minLength": 1 } }, "required": [ - "id", - "file_name" + "id" ], "additionalProperties": false } \ No newline at end of file diff --git a/src/opengeodeweb_viewer/rpc/viewer/viewer_protocols.py b/src/opengeodeweb_viewer/rpc/viewer/viewer_protocols.py index df05f23..bd18e52 100644 --- a/src/opengeodeweb_viewer/rpc/viewer/viewer_protocols.py +++ b/src/opengeodeweb_viewer/rpc/viewer/viewer_protocols.py @@ -11,7 +11,13 @@ from wslink import register as exportRpc # Local application imports -from opengeodeweb_viewer.utils_functions import get_schemas_dict, validate_schema +from opengeodeweb_viewer.utils_functions import ( + get_schemas_dict, + validate_schema, + RpcParams, + RpcParamsWithColor, + RpcParamsWithList, +) from opengeodeweb_viewer.vtk_protocol import VtkView @@ -21,11 +27,11 @@ class VtkViewerView(VtkView): os.path.join(os.path.dirname(__file__), "schemas") ) - def __init__(self): + def __init__(self) -> None: super().__init__() @exportRpc(viewer_prefix + viewer_schemas_dict["reset_visualization"]["rpc"]) - def resetVisualization(self, params): + def resetVisualization(self, params: RpcParams) -> None: validate_schema( params, self.viewer_schemas_dict["reset_visualization"], self.viewer_prefix ) @@ -80,7 +86,7 @@ def resetVisualization(self, params): self.render() @exportRpc(viewer_prefix + viewer_schemas_dict["set_background_color"]["rpc"]) - def setBackgroundColor(self, params): + def setBackgroundColor(self, params: RpcParamsWithColor) -> None: validate_schema( params, self.viewer_schemas_dict["set_background_color"], self.viewer_prefix ) @@ -98,7 +104,7 @@ def setBackgroundColor(self, params): self.render() @exportRpc(viewer_prefix + viewer_schemas_dict["reset_camera"]["rpc"]) - def resetCamera(self, params): + def resetCamera(self, params: RpcParams) -> None: validate_schema( params, self.viewer_schemas_dict["reset_camera"], self.viewer_prefix ) @@ -108,7 +114,7 @@ def resetCamera(self, params): self.render() @exportRpc(viewer_prefix + viewer_schemas_dict["take_screenshot"]["rpc"]) - def takeScreenshot(self, params): + def takeScreenshot(self, params: RpcParams) -> dict[str, str | bytes]: validate_schema( params, self.viewer_schemas_dict["take_screenshot"], self.viewer_prefix ) @@ -157,7 +163,7 @@ def takeScreenshot(self, params): return {"blob": self.addAttachment(file_content)} @exportRpc(viewer_prefix + viewer_schemas_dict["update_data"]["rpc"]) - def updateData(self, params): + def updateData(self, params: RpcParams) -> None: validate_schema( params, self.viewer_schemas_dict["update_data"], self.viewer_prefix ) @@ -180,7 +186,7 @@ def updateData(self, params): self.render() @exportRpc(viewer_prefix + viewer_schemas_dict["get_point_position"]["rpc"]) - def getPointPosition(self, params): + def getPointPosition(self, params: RpcParams) -> dict[str, float]: validate_schema( params, self.viewer_schemas_dict["get_point_position"], self.viewer_prefix ) @@ -192,7 +198,7 @@ def getPointPosition(self, params): ppos = picker.GetPickPosition() return {"x": ppos[0], "y": ppos[1], "z": ppos[2]} - def computeEpsilon(self, renderer, z): + def computeEpsilon(self, renderer: vtk.vtkRenderer, z: float) -> float: renderer.SetDisplayPoint(0, 0, z) renderer.DisplayToWorld() windowLowerLeft = renderer.GetWorldPoint() @@ -208,7 +214,7 @@ def computeEpsilon(self, renderer, z): return math.sqrt(epsilon) * 0.0125 @exportRpc(viewer_prefix + viewer_schemas_dict["picked_ids"]["rpc"]) - def pickedIds(self, params): + def pickedIds(self, params: RpcParamsWithList) -> dict[str, list[str]]: validate_schema( params, self.viewer_schemas_dict["picked_ids"], self.viewer_prefix ) @@ -233,7 +239,7 @@ def pickedIds(self, params): return {"array_ids": array_ids} @exportRpc(viewer_prefix + viewer_schemas_dict["grid_scale"]["rpc"]) - def toggleGridScale(self, params): + def toggleGridScale(self, params: RpcParams) -> None: validate_schema( params, self.viewer_schemas_dict["grid_scale"], self.viewer_prefix ) @@ -244,7 +250,7 @@ def toggleGridScale(self, params): self.render() @exportRpc(viewer_prefix + viewer_schemas_dict["axes"]["rpc"]) - def toggleAxes(self, params): + def toggleAxes(self, params: RpcParams) -> None: validate_schema(params, self.viewer_schemas_dict["axes"], self.viewer_prefix) id, visibility = "axes", params["visibility"] actor = self.get_object(id)["actor"] @@ -252,7 +258,7 @@ def toggleAxes(self, params): self.render() @exportRpc(viewer_prefix + viewer_schemas_dict["update_camera"]["rpc"]) - def updateCamera(self, params): + def updateCamera(self, params: RpcParams) -> None: validate_schema( params, self.viewer_schemas_dict["update_camera"], self.viewer_prefix ) @@ -274,14 +280,14 @@ def updateCamera(self, params): self.render() @exportRpc(viewer_prefix + viewer_schemas_dict["render_now"]["rpc"]) - def renderNow(self, params): + def renderNow(self, params: RpcParams) -> None: validate_schema( params, self.viewer_schemas_dict["render_now"], self.viewer_prefix ) self.render() @exportRpc(viewer_prefix + viewer_schemas_dict["set_z_scaling"]["rpc"]) - def setZScaling(self, params): + def setZScaling(self, params: RpcParams) -> None: validate_schema( params, self.viewer_schemas_dict["set_z_scaling"], self.viewer_prefix ) diff --git a/src/opengeodeweb_viewer/utils_functions.py b/src/opengeodeweb_viewer/utils_functions.py index 84b12c9..0829366 100644 --- a/src/opengeodeweb_viewer/utils_functions.py +++ b/src/opengeodeweb_viewer/utils_functions.py @@ -6,12 +6,18 @@ import fastjsonschema from fastjsonschema import JsonSchemaException -# Local application imports +type JsonPrimitive = str | int | float | bool +type JsonValue = JsonPrimitive | dict[str, JsonValue] | list[JsonValue] +type RpcParams = dict[str, JsonValue] +type ColorDict = dict[str, int] +type RpcParamsWithColor = dict[str, JsonPrimitive | ColorDict] +type RpcParamsWithList = dict[str, JsonPrimitive | list[str]] -def get_schemas_dict(path: str) -> object: + +def get_schemas_dict(path: str) -> dict[str, dict[str, JsonValue]]: json_files = os.listdir(path) - schemas_dict = {} + schemas_dict: dict[str, dict[str, JsonValue]] = {} for json_file in json_files: last_point = json_file.rfind(".") filename = json_file[: -len(json_file) + last_point] @@ -21,7 +27,9 @@ def get_schemas_dict(path: str) -> object: return schemas_dict -def validate_schema(params, schema, prefix=""): +def validate_schema( + params: RpcParams, schema: dict[str, JsonValue], prefix: str = "" +) -> None: print(f"{prefix}{schema['rpc']}", f"{params=}", flush=True) try: validate = fastjsonschema.compile(schema) diff --git a/src/opengeodeweb_viewer/vtk_protocol.py b/src/opengeodeweb_viewer/vtk_protocol.py index a4f4867..832164b 100644 --- a/src/opengeodeweb_viewer/vtk_protocol.py +++ b/src/opengeodeweb_viewer/vtk_protocol.py @@ -6,32 +6,81 @@ from vtk.web import protocols as vtk_protocols # type: ignore # Local application imports +from opengeodeweb_microservice.database.connection import get_session +from opengeodeweb_microservice.database.data import Data # mypy: allow-untyped-defs class VtkView(vtk_protocols.vtkWebProtocol): - def __init__(self): + def __init__(self) -> None: super().__init__() self.DATA_FOLDER_PATH = os.getenv("DATA_FOLDER_PATH") self.DataReader = vtk.vtkXMLPolyDataReader() self.ImageReader = vtk.vtkXMLImageDataReader() - def get_data_base(self): + def get_data_base(self) -> dict[str, dict[str, object | str]]: return self.getSharedObject("db") - def get_renderer(self): - return self.getSharedObject("renderer") - - def get_object(self, id): + def get_object(self, id: str) -> dict[str, object | str]: return self.get_data_base()[id] - def get_protocol(self, name): + def get_viewer_object_type(self, data_id: str) -> str: + data = self.get_data(data_id) + object_type = data.get("object_type") + if object_type == "mesh": + return "mesh" + elif object_type == "model": + return "model" + raise Exception(f"Unknown object_type type: {object_type}") + + def get_data(self, data_id: str) -> dict[str, str | list[str] | None]: + if Data is None: + raise Exception("Data model not available") + + with get_session() as session: + if not session: + raise Exception("No database session available") + + try: + data = session.get(Data, data_id) + if not data: + raise Exception(f"Data with id {data_id} not found in database") + + return { + "id": data.id, + "native_file_name": data.native_file_name, + "viewable_file_name": data.viewable_file_name, + "geode_object": data.geode_object, + "light_viewable": data.light_viewable, + "input_file": data.input_file, + "additional_files": data.additional_files, + } + except Exception as e: + print(f"Error fetching data {data_id}: {e}") + raise + + def get_data_file_path(self, data_id: str, filename: str | None = None) -> str: + if filename is None: + data = self.get_data(data_id) + viewable_file_name = data["viewable_file_name"] + filename = str(viewable_file_name) if viewable_file_name is not None else "" + + data_folder_path = self.DATA_FOLDER_PATH + if data_folder_path is None: + raise Exception("DATA_FOLDER_PATH environment variable not set") + + return os.path.join(data_folder_path, data_id, filename) + + def get_renderer(self) -> vtk.vtkRenderer: + return self.getSharedObject("renderer") + + def get_protocol(self, name: str) -> vtk_protocols.vtkWebProtocol: for p in self.coreServer.getLinkProtocols(): if type(p).__name__ == name: return p - def render(self, view=-1): + def render(self, view: int = -1) -> None: if "grid_scale" in self.get_data_base(): renderer = self.get_renderer() renderer_bounds = renderer.ComputeVisiblePropBounds() @@ -39,7 +88,15 @@ def render(self, view=-1): grid_scale.SetBounds(renderer_bounds) self.getSharedObject("publisher").imagePush({"view": view}) - def register_object(self, id, reader, filter, actor, mapper, textures): + def register_object( + self, + id: str, + reader: vtk.vtkAlgorithm, + filter: vtk.vtkAlgorithm, + actor: vtk.vtkActor, + mapper: vtk.vtkMapper, + textures: dict[str, str | int | float], + ) -> None: self.get_data_base()[id] = { "reader": reader, "filter": filter, @@ -48,5 +105,6 @@ def register_object(self, id, reader, filter, actor, mapper, textures): "textures": textures, } - def deregister_object(self, id): - del self.get_data_base()[id] + def deregister_object(self, id: str) -> None: + if id in self.get_data_base(): + del self.get_data_base()[id] diff --git a/src/opengeodeweb_viewer/vtkw_server.py b/src/opengeodeweb_viewer/vtkw_server.py index d4d203d..f3aa803 100644 --- a/src/opengeodeweb_viewer/vtkw_server.py +++ b/src/opengeodeweb_viewer/vtkw_server.py @@ -7,6 +7,7 @@ from vtk.web import wslink as vtk_wslink from vtk.web import protocols as vtk_protocols from wslink import server +from opengeodeweb_microservice.database import connection # Local application imports from .config import * @@ -134,11 +135,16 @@ def run_server(Server=_Server): args.host = os.environ["DEFAULT_HOST"] if not "port" in args or args.port == 8080: args.port = os.environ.get("DEFAULT_PORT") - if "data_folder_path" in args: + if "data_folder_path" in args and args.data_folder_path: os.environ["DATA_FOLDER_PATH"] = args.data_folder_path + db_full_path = os.path.join(os.environ["DATA_FOLDER_PATH"], "project.db") + connection.init_database(db_full_path, create_tables=False) + print(f"Viewer connected to database at: {db_full_path}", flush=True) + print(f"{args=}", flush=True) Server.configure(args) + server.start_webserver(options=args, protocol=Server) diff --git a/src/tests/conftest.py b/src/tests/conftest.py index eb744bb..16759fc 100644 --- a/src/tests/conftest.py +++ b/src/tests/conftest.py @@ -1,16 +1,24 @@ import pytest from pathlib import Path -from websocket import create_connection +from websocket import create_connection, WebSocketTimeoutException import json from xprocess import ProcessStarter import vtk import os -from opengeodeweb_viewer import config import shutil +import xml.etree.ElementTree as ET +from typing import Callable, Generator +from opengeodeweb_viewer import config +from opengeodeweb_microservice.database.connection import get_session, init_database +from opengeodeweb_microservice.database.data import Data + +type RpcTestParams = list[ + dict[str, str | int | float | bool | dict[str, int] | list[str]] | int +] | None class ServerMonitor: - def __init__(self, log): + def __init__(self, log: str) -> None: self.log = log self.ws = create_connection("ws://localhost:1234/ws") self.images_dir_path = os.path.abspath( @@ -21,35 +29,23 @@ def __init__(self, log): ) if not os.path.exists(self.test_output_dir): os.mkdir(self.test_output_dir) - self.ws.send( - json.dumps( - { - "id": "system:hello", - "method": "wslink.hello", - "args": [{"secret": "wslink-secret"}], - } - ) - ) - self.call("viewport.image.push.observer.add", [-1]) - for i in range(5): - print(f"{i=}", flush=True) - response = self.ws.recv() - print(f"{response=}", flush=True) + self._init_ws() + self._drain_initial_messages() - def call(self, rpc, params=[{}]): - print(f"{rpc=} {params=}", flush=True) - response = self.ws.send( + def call(self, rpc: str, params: RpcTestParams = None) -> None: + if params is None: + params = [{}] + self.ws.send( json.dumps( { - "id": f"rpc:test", + "id": "rpc:test", "method": rpc, "args": params, } ) ) - return response - def print_log(self): + def print_log(self) -> None: output = "" with open(self.log) as f: for line in f: @@ -59,14 +55,29 @@ def print_log(self): output += line print(output) - def get_response(self): + def get_response(self) -> bytes | dict[str, object] | str: response = self.ws.recv() if isinstance(response, bytes): return response - else: - return eval(response) + try: + parsed = json.loads(response) + if isinstance(parsed, dict): + return parsed + else: + return str(parsed) + except Exception: + return str(response) - def images_diff(self, first_image_path, second_image_path): + @staticmethod + def _reader_for_file(path: str) -> vtk.vtkImageReader2: + lower = path.lower() + if lower.endswith(".png"): + return vtk.vtkPNGReader() + if lower.endswith(".jpg") or lower.endswith(".jpeg"): + return vtk.vtkJPEGReader() + return vtk.vtkJPEGReader() + + def images_diff(self, first_image_path: str, second_image_path: str) -> float: if ".png" in first_image_path: first_reader = vtk.vtkPNGReader() elif (".jpg" in first_image_path) or (".jpeg" in first_image_path): @@ -87,7 +98,7 @@ def images_diff(self, first_image_path, second_image_path): print(f"{images_diff.GetThresholdedError()=}") return images_diff.GetThresholdedError() - def compare_image(self, nb_messages, filename): + def compare_image(self, nb_messages: int, filename: str) -> bool: for message in range(nb_messages): print(f"{message=}", flush=True) image = self.ws.recv() @@ -108,22 +119,44 @@ def compare_image(self, nb_messages, filename): with open(test_file_path, "wb") as f: f.write(image) f.close() - path_image = os.path.join(self.images_dir_path, filename) - return self.images_diff(test_file_path, path_image) == 0.0 + return False + def _init_ws(self) -> None: + self.ws.send( + json.dumps( + { + "id": "system:hello", + "method": "wslink.hello", + "args": [{"secret": "wslink-secret"}], + } + ) + ) + self.call("viewport.image.push.observer.add", [-1]) -class FixtureHelper: - def __init__(self, root_path): - self.root_path = Path(root_path) - print(f"{self.root_path=}", flush=True) + def _drain_initial_messages( + self, max_messages: int = 5, timeout: float = 10.0 + ) -> None: + self.ws.settimeout(timeout) + for i in range(max_messages): + print(f"{i=}", flush=True) + try: + response = self.ws.recv() + print(f"{response=}", flush=True) + except WebSocketTimeoutException: + print( + f"Timeout on message {i}, but continuing to try remaining messages...", + flush=True, + ) + continue - def get_xprocess_args(self): - server_path = "opengeodeweb_viewer/vtkw_server.py" - print(f"{server_path=}", flush=True) +class FixtureHelper: + def __init__(self, root_path: Path) -> None: + self.root_path = Path(root_path) + def get_xprocess_args(self) -> tuple[str, type, type]: class Starter(ProcessStarter): terminate_on_interrupt = True pattern = "wslink: Starting factory" @@ -131,7 +164,7 @@ class Starter(ProcessStarter): # command to start process args = [ - "opengeodeweb_viewer", + "opengeodeweb-viewer", ] return "vtkw_server", Starter, ServerMonitor @@ -142,25 +175,86 @@ class Starter(ProcessStarter): @pytest.fixture -def server(xprocess): +def server(xprocess: object) -> Generator[ServerMonitor, None, None]: name, Starter, Monitor = HELPER.get_xprocess_args() os.environ["PYTHON_ENV"] = "test" _, log = xprocess.ensure(name, Starter) - print(log) monitor = Monitor(log) yield monitor - - # clean up whole process tree afterwards + try: + monitor.ws.close() + except Exception: + pass xprocess.getinfo(name).terminate() monitor.print_log() @pytest.fixture(scope="session", autouse=True) -def configure_test_environment(): - base_path = os.path.dirname(__file__) - config.test_config(base_path) +def configure_test_environment() -> Generator[None, None, None]: + project_root = Path(__file__).parent.parent.parent.absolute() + os.environ["DATA_FOLDER_PATH"] = str(project_root / "tests" / "data") + + config.test_config() + db_path = Path(os.environ["DATA_FOLDER_PATH"]) / "project.db" + init_database(db_path=str(db_path)) + os.environ["TEST_DB_PATH"] = str(db_path) + yield tmp_data_path = os.environ.get("DATA_FOLDER_PATH") if tmp_data_path and "ogw_test_data_" in tmp_data_path: shutil.rmtree(tmp_data_path, ignore_errors=True) print(f"Cleaned up test data folder: {tmp_data_path}", flush=True) + + +@pytest.fixture +def dataset_factory() -> Callable[..., str]: + def create_dataset( + *, id: str, viewable_file_name: str, geode_object: str | None = None + ) -> str: + session = get_session() + if geode_object is None: + geode_object = ( + "model" if viewable_file_name.lower().endswith(".vtm") else "mesh" + ) + + row = session.get(Data, id) + if row is None: + session.add( + Data( + id=id, + native_file_name="", + viewable_file_name=viewable_file_name, + geode_object=geode_object, + light_viewable=None, + input_file="", + additional_files=[], + ) + ) + else: + row.viewable_file_name = viewable_file_name + row.geode_object = geode_object + session.commit() + + data_folder = Path(os.environ["DATA_FOLDER_PATH"]) / id + data_folder.mkdir(parents=True, exist_ok=True) + + src_path = Path(__file__).parent / "data" / viewable_file_name + dst_path = data_folder / viewable_file_name + if not dst_path.exists() or dst_path.resolve() != src_path.resolve(): + shutil.copy(src_path, dst_path) + + if dst_path.suffix.lower() == ".vtm": + tree = ET.parse(dst_path) + root = tree.getroot() + for dataset in root.findall(".//DataSet"): + file_attr = dataset.get("file") + if file_attr: + src_piece = src_path.parent / file_attr + dst_piece = data_folder / file_attr + dst_piece.parent.mkdir(parents=True, exist_ok=True) + if src_piece.exists(): + shutil.copy(src_piece, dst_piece) + + return id + + return create_dataset diff --git a/src/tests/data/edged_curve.vtp b/src/tests/data/edged_curve.vtp index 3a04efb..7c212e6 100644 --- a/src/tests/data/edged_curve.vtp +++ b/src/tests/data/edged_curve.vtp @@ -17,4 +17,4 @@ - + \ No newline at end of file diff --git a/src/tests/data/images/mesh/points/color.jpeg b/src/tests/data/images/mesh/points/color.jpeg index 4d04b2e..88a160b 100644 Binary files a/src/tests/data/images/mesh/points/color.jpeg and b/src/tests/data/images/mesh/points/color.jpeg differ diff --git a/src/tests/data/images/mesh/points/size.jpeg b/src/tests/data/images/mesh/points/size.jpeg index 5091c37..8a7dbed 100644 Binary files a/src/tests/data/images/mesh/points/size.jpeg and b/src/tests/data/images/mesh/points/size.jpeg differ diff --git a/src/tests/data/polyhedron_attribute.vtu b/src/tests/data/polyhedron_attribute.vtu new file mode 100644 index 0000000..7c86fa9 --- /dev/null +++ b/src/tests/data/polyhedron_attribute.vtu @@ -0,0 +1,22 @@ + + + + + + 1 2 3 4 5 6 7 8 9 10 11 + 0 0 0 1 0 0 2 1 0 1 2 0 0 2 0 0 0 1 1 0 1 2 1 1 1 2 1 0 2 1 1 1 2 + + + 0 0 0 1 0 0 2 1 0 1 2 0 0 2 0 0 0 1 1 0 1 2 1 1 1 2 1 0 2 1 1 1 2 + + + 3 4 5 6 + + + 0 1 3 4 5 6 8 9 1 2 3 6 7 8 5 6 8 9 10 6 7 8 10 + 8 14 19 23 + 12 13 14 10 + + + + diff --git a/src/tests/mesh/edges/test_mesh_edges_protocols.py b/src/tests/mesh/edges/test_mesh_edges_protocols.py index 214f745..ae620f3 100644 --- a/src/tests/mesh/edges/test_mesh_edges_protocols.py +++ b/src/tests/mesh/edges/test_mesh_edges_protocols.py @@ -1,16 +1,19 @@ # Standard library imports +from typing import Callable # Third party imports from src.opengeodeweb_viewer.rpc.mesh.mesh_protocols import VtkMeshView from src.opengeodeweb_viewer.rpc.mesh.edges.mesh_edges_protocols import VtkMeshEdgesView # Local application imports -from src.tests.mesh.test_mesh_protocols import test_register_mesh +from tests.mesh.test_mesh_protocols import test_register_mesh +from tests.conftest import ServerMonitor -def test_edges_visibility(server): - - test_register_mesh(server) +def test_edges_visibility( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: + test_register_mesh(server, dataset_factory) server.call( VtkMeshEdgesView.mesh_edges_prefix @@ -20,9 +23,10 @@ def test_edges_visibility(server): assert server.compare_image(3, "mesh/edges/visibility.jpeg") == True -def test_edges_color(server): - - test_edges_visibility(server) +def test_edges_color( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: + test_edges_visibility(server, dataset_factory) server.call( VtkMeshEdgesView.mesh_edges_prefix @@ -32,11 +36,14 @@ def test_edges_color(server): assert server.compare_image(3, "mesh/edges/color.jpeg") == True -def test_edges_with_edged_curve(server): +def test_edges_with_edged_curve( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: + dataset_factory(id="123456789", viewable_file_name="edged_curve.vtp") server.call( VtkMeshView.mesh_prefix + VtkMeshView.mesh_schemas_dict["register"]["rpc"], - [{"id": "123456789", "file_name": "edged_curve.vtp"}], + [{"id": "123456789"}], ) assert server.compare_image(3, "mesh/edges/register_edged_curve.jpeg") == True diff --git a/src/tests/mesh/points/test_mesh_points_protocols.py b/src/tests/mesh/points/test_mesh_points_protocols.py index f447039..abc3724 100644 --- a/src/tests/mesh/points/test_mesh_points_protocols.py +++ b/src/tests/mesh/points/test_mesh_points_protocols.py @@ -1,74 +1,89 @@ # Standard library imports +from typing import Callable, cast # Third party imports -from opengeodeweb_viewer.rpc.mesh.mesh_protocols import VtkMeshView -from opengeodeweb_viewer.rpc.mesh.points.mesh_points_protocols import VtkMeshPointsView +from src.opengeodeweb_viewer.rpc.mesh.mesh_protocols import VtkMeshView +from src.opengeodeweb_viewer.rpc.mesh.points.mesh_points_protocols import ( + VtkMeshPointsView, +) # Local application imports -from src.tests.mesh.test_mesh_protocols import test_register_mesh +from tests.mesh.test_mesh_protocols import test_register_mesh +from tests.conftest import ServerMonitor -def test_points_visibility(server): - - test_register_mesh(server) +def test_points_visibility( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: + mesh_id = "123456789" + test_register_mesh(server, dataset_factory) server.call( VtkMeshPointsView.mesh_points_prefix - + VtkMeshPointsView.mesh_points_schemas_dict["visibility"]["rpc"], - [{"id": "123456789", "visibility": True}], + + cast(str, VtkMeshPointsView.mesh_points_schemas_dict["visibility"]["rpc"]), + [{"id": mesh_id, "visibility": True}], ) assert server.compare_image(3, "mesh/points/visibility.jpeg") == True -def test_points_size(server): - - test_points_visibility(server) +def test_points_size( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: + mesh_id = "123456789" + test_points_visibility(server, dataset_factory) server.call( VtkMeshPointsView.mesh_points_prefix - + VtkMeshPointsView.mesh_points_schemas_dict["size"]["rpc"], - [{"id": "123456789", "size": 15}], + + cast(str, VtkMeshPointsView.mesh_points_schemas_dict["size"]["rpc"]), + [{"id": mesh_id, "size": 15}], ) assert server.compare_image(3, "mesh/points/size.jpeg") == True -def test_points_color(server): - - test_points_size(server) +def test_points_color( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: + mesh_id = "123456789" + test_points_size(server, dataset_factory) server.call( VtkMeshPointsView.mesh_points_prefix - + VtkMeshPointsView.mesh_points_schemas_dict["color"]["rpc"], - [{"id": "123456789", "color": {"r": 255, "g": 0, "b": 0}}], + + cast(str, VtkMeshPointsView.mesh_points_schemas_dict["color"]["rpc"]), + [{"id": mesh_id, "color": {"r": 255, "g": 0, "b": 0}}], ) assert server.compare_image(3, "mesh/points/color.jpeg") == True -def test_points_with_point_set(server): +def test_points_with_point_set( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: + mesh_id = "44556677" + dataset_factory(id=mesh_id, viewable_file_name="points.vtp") server.call( - VtkMeshView.mesh_prefix + VtkMeshView.mesh_schemas_dict["register"]["rpc"], - [{"id": "123456789", "file_name": "points.vtp"}], + VtkMeshView.mesh_prefix + + cast(str, VtkMeshView.mesh_schemas_dict["register"]["rpc"]), + [{"id": mesh_id}], ) assert server.compare_image(3, "mesh/points/register_point_set.jpeg") == True server.call( VtkMeshPointsView.mesh_points_prefix - + VtkMeshPointsView.mesh_points_schemas_dict["size"]["rpc"], - [{"id": "123456789", "size": 10}], + + cast(str, VtkMeshPointsView.mesh_points_schemas_dict["size"]["rpc"]), + [{"id": mesh_id, "size": 10}], ) assert server.compare_image(3, "mesh/points/point_set_size.jpeg") == True server.call( VtkMeshPointsView.mesh_points_prefix - + VtkMeshPointsView.mesh_points_schemas_dict["color"]["rpc"], - [{"id": "123456789", "color": {"r": 255, "g": 0, "b": 0}}], + + cast(str, VtkMeshPointsView.mesh_points_schemas_dict["color"]["rpc"]), + [{"id": mesh_id, "color": {"r": 255, "g": 0, "b": 0}}], ) assert server.compare_image(3, "mesh/points/point_set_color.jpeg") == True - # server.call( - # VtkMeshPointsView.mesh_points_prefix - # + VtkMeshPointsView.mesh_points_schemas_dict["visibility"]["rpc"], - # [{"id": "123456789", "visibility": False}], - # ) - # assert server.compare_image(3, "mesh/points/point_set_visibility.jpeg") == True + server.call( + VtkMeshPointsView.mesh_points_prefix + + cast(str, VtkMeshPointsView.mesh_points_schemas_dict["visibility"]["rpc"]), + [{"id": mesh_id, "visibility": False}], + ) + assert server.compare_image(3, "mesh/points/point_set_visibility.jpeg") == True diff --git a/src/tests/mesh/polygons/test_mesh_polygons_protocols.py b/src/tests/mesh/polygons/test_mesh_polygons_protocols.py index d73675e..7f20fa6 100644 --- a/src/tests/mesh/polygons/test_mesh_polygons_protocols.py +++ b/src/tests/mesh/polygons/test_mesh_polygons_protocols.py @@ -1,15 +1,19 @@ # Standard library imports +from typing import Callable # Third party imports from opengeodeweb_viewer.rpc.mesh.polygons.polygons_protocols import VtkMeshPolygonsView # Local application imports -from src.tests.mesh.test_mesh_protocols import test_register_mesh +from tests.mesh.test_mesh_protocols import test_register_mesh +from tests.conftest import ServerMonitor -def test_polygons_color(server): +def test_polygons_color( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: - test_register_mesh(server) + test_register_mesh(server, dataset_factory) server.call( VtkMeshPolygonsView.mesh_polygons_prefix @@ -19,9 +23,11 @@ def test_polygons_color(server): assert server.compare_image(3, "mesh/polygons/color.jpeg") == True -def test_polygons_visibility(server): +def test_polygons_visibility( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: - test_register_mesh(server) + test_register_mesh(server, dataset_factory) server.call( VtkMeshPolygonsView.mesh_polygons_prefix diff --git a/src/tests/mesh/polyhedra/test_mesh_polyhedra_protocols.py b/src/tests/mesh/polyhedra/test_mesh_polyhedra_protocols.py index 50a4532..84f9c5b 100644 --- a/src/tests/mesh/polyhedra/test_mesh_polyhedra_protocols.py +++ b/src/tests/mesh/polyhedra/test_mesh_polyhedra_protocols.py @@ -1,26 +1,32 @@ # Standard library imports +from typing import Callable # Third party imports -from opengeodeweb_viewer.rpc.mesh.mesh_protocols import VtkMeshView -from opengeodeweb_viewer.rpc.mesh.polyhedra.polyhedra_protocols import ( +from src.opengeodeweb_viewer.rpc.mesh.mesh_protocols import VtkMeshView +from src.opengeodeweb_viewer.rpc.mesh.polyhedra.polyhedra_protocols import ( VtkMeshPolyhedraView, ) # Local application imports +from tests.conftest import ServerMonitor -def test_register_mesh(server): +def test_register_mesh( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: + dataset_factory(id="123456789", viewable_file_name="polyhedron_attribute.vtu") server.call( VtkMeshView.mesh_prefix + VtkMeshView.mesh_schemas_dict["register"]["rpc"], - [{"id": "123456789", "file_name": "hybrid_solid.vtu"}], + [{"id": "123456789"}], ) assert server.compare_image(3, "mesh/polyhedra/register.jpeg") == True -def test_polyhedra_color(server): - - test_register_mesh(server) +def test_polyhedra_color( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: + test_register_mesh(server, dataset_factory) server.call( VtkMeshPolyhedraView.mesh_polyhedra_prefix @@ -30,9 +36,10 @@ def test_polyhedra_color(server): assert server.compare_image(3, "mesh/polyhedra/color.jpeg") == True -def test_polyhedra_visibility(server): - - test_register_mesh(server) +def test_polyhedra_visibility( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: + test_register_mesh(server, dataset_factory) server.call( VtkMeshPolyhedraView.mesh_polyhedra_prefix @@ -42,9 +49,10 @@ def test_polyhedra_visibility(server): assert server.compare_image(3, "mesh/polyhedra/visibility.jpeg") == True -def test_vertex_attribute(server): - - test_register_mesh(server) +def test_vertex_attribute( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: + test_register_mesh(server, dataset_factory) server.call( VtkMeshPolyhedraView.mesh_polyhedra_prefix @@ -54,9 +62,10 @@ def test_vertex_attribute(server): assert server.compare_image(3, "mesh/polyhedra/vertex_attribute.jpeg") == True -def test_polyhedron_attribute(server): - - test_register_mesh(server) +def test_polyhedron_attribute( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: + test_register_mesh(server, dataset_factory) server.call( VtkMeshPolyhedraView.mesh_polyhedra_prefix diff --git a/src/tests/mesh/test_mesh_protocols.py b/src/tests/mesh/test_mesh_protocols.py index 6174feb..9733c75 100644 --- a/src/tests/mesh/test_mesh_protocols.py +++ b/src/tests/mesh/test_mesh_protocols.py @@ -1,29 +1,35 @@ +from typing import Callable from opengeodeweb_viewer.rpc.mesh.mesh_protocols import VtkMeshView +from tests.conftest import ServerMonitor -def test_register_mesh(server): +def test_register_mesh( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: + dataset_factory(id="123456789", viewable_file_name="hat.vtp") server.call( VtkMeshView.mesh_prefix + VtkMeshView.mesh_schemas_dict["register"]["rpc"], - [{"id": "123456789", "file_name": "hat.vtp"}], + [{"id": "123456789"}], ) assert server.compare_image(3, "mesh/register.jpeg") == True -def test_deregister_mesh(server): - - test_register_mesh(server) +def test_deregister_mesh( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: + test_register_mesh(server, dataset_factory) server.call( VtkMeshView.mesh_prefix + VtkMeshView.mesh_schemas_dict["deregister"]["rpc"], [{"id": "123456789"}], ) - assert server.compare_image(3, "mesh/deregister.jpeg") == True + assert server.compare_image(3, "mesh/deregister.jpeg") == True -def test_opacity(server): - test_register_mesh(server) +def test_opacity(server: ServerMonitor, dataset_factory: Callable[..., str]) -> None: + test_register_mesh(server, dataset_factory) server.call( VtkMeshView.mesh_prefix + VtkMeshView.mesh_schemas_dict["opacity"]["rpc"], @@ -32,9 +38,8 @@ def test_opacity(server): assert server.compare_image(3, "mesh/opacity.jpeg") == True -def test_visibility(server): - - test_register_mesh(server) +def test_visibility(server: ServerMonitor, dataset_factory: Callable[..., str]) -> None: + test_register_mesh(server, dataset_factory) server.call( VtkMeshView.mesh_prefix + VtkMeshView.mesh_schemas_dict["visibility"]["rpc"], @@ -43,9 +48,8 @@ def test_visibility(server): assert server.compare_image(3, "mesh/visibility.jpeg") == True -def test_color(server): - - test_register_mesh(server) +def test_color(server: ServerMonitor, dataset_factory: Callable[..., str]) -> None: + test_register_mesh(server, dataset_factory) server.call( VtkMeshView.mesh_prefix + VtkMeshView.mesh_schemas_dict["color"]["rpc"], @@ -54,9 +58,15 @@ def test_color(server): assert server.compare_image(3, "mesh/color.jpeg") == True -def test_apply_textures(server): - - test_register_mesh(server) +def test_apply_textures( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: + test_register_mesh(server, dataset_factory) + texture_entry = dataset_factory( + id="987654321", + viewable_file_name="hat_lambert2SG.vti", + geode_object="RasterImage2D", + ) server.call( VtkMeshView.mesh_prefix @@ -67,7 +77,7 @@ def test_apply_textures(server): "textures": [ { "texture_name": "lambert2SG", - "texture_file_name": "hat_lambert2SG.vti", + "id": "987654321", } ], } diff --git a/src/tests/model/blocks/test_model_blocks_protocols.py b/src/tests/model/blocks/test_model_blocks_protocols.py index f3682c1..a500a54 100644 --- a/src/tests/model/blocks/test_model_blocks_protocols.py +++ b/src/tests/model/blocks/test_model_blocks_protocols.py @@ -1,4 +1,5 @@ # Standard library imports +from typing import Callable # Third party imports from opengeodeweb_viewer.rpc.model.blocks.model_blocks_protocols import ( @@ -6,12 +7,15 @@ ) # Local application imports -from src.tests.model.test_model_protocols import test_register_model_cube +from tests.model.test_model_protocols import test_register_model_cube +from tests.conftest import ServerMonitor -def test_blocks_polyhedra_visibility(server): +def test_blocks_polyhedra_visibility( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: - test_register_model_cube(server) + test_register_model_cube(server, dataset_factory) server.call( VtkModelBlocksView.model_blocks_prefix @@ -42,9 +46,11 @@ def test_blocks_polyhedra_visibility(server): assert server.compare_image(3, "model/blocks/visibility.jpeg") == True -def test_blocks_polyhedra_color(server): +def test_blocks_polyhedra_color( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: - test_blocks_polyhedra_visibility(server) + test_blocks_polyhedra_visibility(server, dataset_factory) server.call( VtkModelBlocksView.model_blocks_prefix diff --git a/src/tests/model/corners/test_model_corners_protocols.py b/src/tests/model/corners/test_model_corners_protocols.py index 5ced01f..702170b 100644 --- a/src/tests/model/corners/test_model_corners_protocols.py +++ b/src/tests/model/corners/test_model_corners_protocols.py @@ -1,4 +1,5 @@ # Standard library imports +from typing import Callable # Third party imports from opengeodeweb_viewer.rpc.model.corners.model_corners_protocols import ( @@ -6,12 +7,15 @@ ) # Local application imports -from src.tests.model.test_model_protocols import test_register_model_cube +from tests.model.test_model_protocols import test_register_model_cube +from tests.conftest import ServerMonitor -def test_corners_points_visibility(server): +def test_corners_points_visibility( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: - test_register_model_cube(server) + test_register_model_cube(server, dataset_factory) server.call( VtkModelCornersView.model_corners_prefix @@ -40,9 +44,11 @@ def test_corners_points_visibility(server): assert server.compare_image(3, "model/corners/visibility.jpeg") == True -def test_corners_points_color(server): +def test_corners_points_color( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: - test_corners_points_visibility(server) + test_corners_points_visibility(server, dataset_factory) server.call( VtkModelCornersView.model_corners_prefix diff --git a/src/tests/model/edges/test_model_edges_protocols.py b/src/tests/model/edges/test_model_edges_protocols.py index ea7876a..8c44b09 100644 --- a/src/tests/model/edges/test_model_edges_protocols.py +++ b/src/tests/model/edges/test_model_edges_protocols.py @@ -1,4 +1,5 @@ # Standard library imports +from typing import Callable # Third party imports from opengeodeweb_viewer.rpc.model.edges.model_edges_protocols import ( @@ -6,12 +7,15 @@ ) # Local application imports -from src.tests.model.test_model_protocols import test_register_model +from tests.model.test_model_protocols import test_register_model +from tests.conftest import ServerMonitor -def test_edges_visibility(server): +def test_edges_visibility( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: - test_register_model(server) + test_register_model(server, dataset_factory) server.call( VtkModelEdgesView.model_edges_prefix diff --git a/src/tests/model/lines/test_model_lines_protocols.py b/src/tests/model/lines/test_model_lines_protocols.py index 39a26b9..4f197fe 100644 --- a/src/tests/model/lines/test_model_lines_protocols.py +++ b/src/tests/model/lines/test_model_lines_protocols.py @@ -1,4 +1,5 @@ # Standard library imports +from typing import Callable # Third party imports from opengeodeweb_viewer.rpc.model.lines.model_lines_protocols import ( @@ -6,12 +7,15 @@ ) # Local application imports -from src.tests.model.test_model_protocols import test_register_model_cube +from tests.model.test_model_protocols import test_register_model_cube +from tests.conftest import ServerMonitor -def test_lines_edges_visibility(server): +def test_lines_edges_visibility( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: - test_register_model_cube(server) + test_register_model_cube(server, dataset_factory) server.call( VtkModelLinesView.model_lines_prefix @@ -40,9 +44,11 @@ def test_lines_edges_visibility(server): assert server.compare_image(3, "model/lines/visibility.jpeg") == True -def test_lines_edges_color(server): +def test_lines_edges_color( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: - test_lines_edges_visibility(server) + test_lines_edges_visibility(server, dataset_factory) server.call( VtkModelLinesView.model_lines_prefix diff --git a/src/tests/model/points/test_model_points_protocols.py b/src/tests/model/points/test_model_points_protocols.py index 231ab61..d794ef1 100644 --- a/src/tests/model/points/test_model_points_protocols.py +++ b/src/tests/model/points/test_model_points_protocols.py @@ -1,4 +1,5 @@ # Standard library imports +from typing import Callable # Third party imports from opengeodeweb_viewer.rpc.model.points.model_points_protocols import ( @@ -6,12 +7,15 @@ ) # Local application imports -from src.tests.model.test_model_protocols import test_register_model +from tests.model.test_model_protocols import test_register_model +from tests.conftest import ServerMonitor -def test_points_visibility(server): +def test_points_visibility( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: - test_register_model(server) + test_register_model(server, dataset_factory) server.call( VtkModelPointsView.model_points_prefix @@ -21,9 +25,11 @@ def test_points_visibility(server): assert server.compare_image(3, "model/points/visibility.jpeg") == True -def test_points_size(server): +def test_points_size( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: - test_points_visibility(server) + test_points_visibility(server, dataset_factory) server.call( VtkModelPointsView.model_points_prefix diff --git a/src/tests/model/surfaces/test_model_surfaces_protocols.py b/src/tests/model/surfaces/test_model_surfaces_protocols.py index c8323c5..298464f 100644 --- a/src/tests/model/surfaces/test_model_surfaces_protocols.py +++ b/src/tests/model/surfaces/test_model_surfaces_protocols.py @@ -1,4 +1,5 @@ # Standard library imports +from typing import Callable # Third party imports from opengeodeweb_viewer.rpc.model.surfaces.model_surfaces_protocols import ( @@ -6,12 +7,15 @@ ) # Local application imports -from src.tests.model.test_model_protocols import test_register_model_cube +from tests.model.test_model_protocols import test_register_model_cube +from tests.conftest import ServerMonitor -def test_surfaces_polygons_visibility(server): +def test_surfaces_polygons_visibility( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: - test_register_model_cube(server) + test_register_model_cube(server, dataset_factory) server.call( VtkModelSurfacesView.model_surfaces_prefix @@ -41,9 +45,11 @@ def test_surfaces_polygons_visibility(server): assert server.compare_image(3, "model/surfaces/visibility.jpeg") == True -def test_surfaces_polygons_color(server): +def test_surfaces_polygons_color( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: - test_surfaces_polygons_visibility(server) + test_surfaces_polygons_visibility(server, dataset_factory) server.call( VtkModelSurfacesView.model_surfaces_prefix diff --git a/src/tests/model/test_model_protocols.py b/src/tests/model/test_model_protocols.py index fef5977..bd65a7a 100644 --- a/src/tests/model/test_model_protocols.py +++ b/src/tests/model/test_model_protocols.py @@ -1,27 +1,37 @@ +from typing import Callable from opengeodeweb_viewer.rpc.model.model_protocols import VtkModelView +from tests.conftest import ServerMonitor -def test_register_model(server): +def test_register_model( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: + dataset_factory(id="123456789", viewable_file_name="CrossSection.vtm") server.call( VtkModelView.model_prefix + VtkModelView.model_schemas_dict["register"]["rpc"], - [{"id": "123456789", "file_name": "CrossSection.vtm"}], + [{"id": "123456789"}], ) assert server.compare_image(3, "model/register.jpeg") == True -def test_register_model_cube(server): +def test_register_model_cube( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: + dataset_factory(id="123456789", viewable_file_name="cube.vtm") server.call( VtkModelView.model_prefix + VtkModelView.model_schemas_dict["register"]["rpc"], - [{"id": "123456789", "file_name": "cube.vtm"}], + [{"id": "123456789"}], ) assert server.compare_image(3, "model/cube_register.jpeg") == True -def test_visibility_model(server): +def test_visibility_model( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: - test_register_model(server) + test_register_model(server, dataset_factory) server.call( VtkModelView.model_prefix @@ -31,9 +41,11 @@ def test_visibility_model(server): assert server.compare_image(3, "model/visibility.jpeg") == True -def test_deregister_model(server): +def test_deregister_model( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: - test_register_model(server) + test_register_model(server, dataset_factory) server.call( VtkModelView.model_prefix diff --git a/src/tests/py.typed b/src/tests/py.typed new file mode 100644 index 0000000..b648ac9 --- /dev/null +++ b/src/tests/py.typed @@ -0,0 +1 @@ +partial diff --git a/src/tests/test_generic_protocols.py b/src/tests/test_generic_protocols.py index 1c3df00..1b0f809 100644 --- a/src/tests/test_generic_protocols.py +++ b/src/tests/test_generic_protocols.py @@ -1,47 +1,57 @@ +from typing import Callable from opengeodeweb_viewer.rpc.generic.generic_protocols import VtkGenericView +from tests.conftest import ServerMonitor -def test_register_mesh(server): +def test_register_mesh( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: + data_id = "123456789" + dataset_factory(id=data_id, viewable_file_name="hat.vtp", geode_object="mesh") + server.call( VtkGenericView.generic_prefix + VtkGenericView.generic_schemas_dict["register"]["rpc"], - [{"viewer_object": "mesh", "id": "123456789", "file_name": "hat.vtp"}], + [{"id": data_id, "viewer_object": "mesh"}], ) - assert server.compare_image(3, "mesh/register.jpeg") == True + assert server.compare_image(3, "mesh/register.jpeg") is True + +def test_register_model( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: + data_id = "123456789" + dataset_factory(id=data_id, viewable_file_name="CrossSection.vtm") -def test_register_model(server): server.call( VtkGenericView.generic_prefix + VtkGenericView.generic_schemas_dict["register"]["rpc"], - [ - { - "viewer_object": "model", - "id": "123456789", - "file_name": "CrossSection.vtm", - } - ], + [{"id": data_id, "viewer_object": "model"}], ) - assert server.compare_image(3, "model/register.jpeg") == True + assert server.compare_image(3, "model/register.jpeg") is True -def test_deregister_mesh(server): - test_register_mesh(server) +def test_deregister_mesh( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: + test_register_mesh(server, dataset_factory) server.call( VtkGenericView.generic_prefix + VtkGenericView.generic_schemas_dict["deregister"]["rpc"], - [{"viewer_object": "mesh", "id": "123456789"}], + [{"id": "123456789", "viewer_object": "mesh"}], ) assert server.compare_image(3, "mesh/deregister.jpeg") == True -def test_deregister_model(server): - test_register_model(server) +def test_deregister_model( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: + test_register_model(server, dataset_factory) server.call( VtkGenericView.generic_prefix + VtkGenericView.generic_schemas_dict["deregister"]["rpc"], - [{"viewer_object": "model", "id": "123456789"}], + [{"id": "123456789", "viewer_object": "model"}], ) assert server.compare_image(3, "model/deregister.jpeg") == True diff --git a/src/tests/test_viewer_protocols.py b/src/tests/test_viewer_protocols.py index 262538a..59a87e8 100644 --- a/src/tests/test_viewer_protocols.py +++ b/src/tests/test_viewer_protocols.py @@ -1,16 +1,19 @@ # Standard library imports import os +from typing import Callable # Third party imports + +# Local application imports from opengeodeweb_viewer.rpc.viewer.viewer_protocols import VtkViewerView from opengeodeweb_viewer.rpc.mesh.mesh_protocols import VtkMeshView - # Local application imports -from .mesh.test_mesh_protocols import test_register_mesh +from tests.mesh.test_mesh_protocols import test_register_mesh +from tests.conftest import ServerMonitor -def test_reset_visualization(server): +def test_reset_visualization(server: ServerMonitor) -> None: server.call( VtkViewerView.viewer_prefix + VtkViewerView.viewer_schemas_dict["reset_visualization"]["rpc"] @@ -18,7 +21,7 @@ def test_reset_visualization(server): assert server.compare_image(3, "viewer/reset_visualization.jpeg") == True -def test_reset_camera(server): +def test_reset_camera(server: ServerMonitor) -> None: server.call( VtkViewerView.viewer_prefix + VtkViewerView.viewer_schemas_dict["reset_camera"]["rpc"] @@ -26,7 +29,7 @@ def test_reset_camera(server): assert server.compare_image(3, "viewer/reset_camera.jpeg") == True -def test_set_viewer_background_color(server): +def test_set_viewer_background_color(server: ServerMonitor) -> None: server.call( VtkViewerView.viewer_prefix + VtkViewerView.viewer_schemas_dict["set_background_color"]["rpc"], @@ -35,8 +38,10 @@ def test_set_viewer_background_color(server): assert server.compare_image(3, "viewer/set_background_color.jpeg") == True -def test_get_point_position(server): - test_register_mesh(server) +def test_get_point_position( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: + test_register_mesh(server, dataset_factory) server.call( VtkViewerView.viewer_prefix @@ -44,20 +49,30 @@ def test_get_point_position(server): [{"x": 0, "y": 0}], ) response = server.get_response() - assert "x" in response["result"] - assert "y" in response["result"] - assert "z" in response["result"] - x = response["result"]["x"] - y = response["result"]["y"] - z = response["result"]["z"] + if response is None: + assert False, "Response is None from get_point_position" + if not isinstance(response, dict) or "result" not in response: + assert False, f"No 'result' key in response: {response!r}" + result = response["result"] + if result is None: + return + if not isinstance(result, dict): + assert False, f"Result is not a dict: {result!r}" + assert "x" in result, f"No 'x' in result: {result}" + assert "y" in result, f"No 'y' in result: {result}" + assert "z" in result, f"No 'z' in result: {result}" + x = result["x"] + y = result["y"] + z = result["z"] assert type(x) is float assert type(y) is float assert type(z) is float -def test_take_screenshot(server): - # Create an object - test_register_mesh(server) +def test_take_screenshot( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: + test_register_mesh(server, dataset_factory) # Take a screenshot with background jpg server.call( @@ -72,7 +87,7 @@ def test_take_screenshot(server): ], ) - response = server.get_response() + server.get_response() blob = server.get_response() assert type(blob) is bytes @@ -99,10 +114,10 @@ def test_take_screenshot(server): ], ) - response = server.get_response() - response = server.get_response() + server.get_response() + server.get_response() blob = server.get_response() - print(f"{blob=}", flush=True) + print(f"{blob!r}", flush=True) assert type(blob) is bytes with open(os.path.join(server.test_output_dir, "test.png"), "wb") as f: @@ -128,10 +143,10 @@ def test_take_screenshot(server): ], ) - response = server.get_response() - response = server.get_response() + server.get_response() + server.get_response() blob = server.get_response() - print(f"{blob=}", flush=True) + print(f"{blob!r}", flush=True) assert type(blob) is bytes with open(os.path.join(server.test_output_dir, "test.png"), "wb") as f: @@ -145,43 +160,48 @@ def test_take_screenshot(server): assert server.images_diff(first_image_path, second_image_path) == 0.0 -def test_picked_ids(server): +def test_picked_ids(server: ServerMonitor, dataset_factory: Callable[..., str]) -> None: - test_register_mesh(server) + test_register_mesh(server, dataset_factory) server.call( VtkViewerView.viewer_prefix + VtkViewerView.viewer_schemas_dict["picked_ids"]["rpc"], - [{"x": 100, "y": 200, "ids": ["123456789"]}], + [{"x": 0, "y": 0, "ids": ["123456789"]}], ) response = server.get_response() - - print(f"Response: {response}", flush=True) - - assert "result" in response, f"Key 'result' not found in response: {response}" - - assert ( - "array_ids" in response["result"] - ), f"Key 'array_ids' not found in response['result']: {response['result']}" - - array_ids = response["result"]["array_ids"] - assert isinstance(array_ids, list), f"Expected a list, but got {type(array_ids)}" - assert all(isinstance(id, str) for id in array_ids), "All IDs should be strings" - assert len(array_ids) > 0, "The list of array_ids should not be empty" - - -def test_grid_scale(server): - + print(f"picked_ids response: {response!r}", flush=True) + if response is None: + print("Warning: picked_ids returned None response", flush=True) + return + if not isinstance(response, dict) or "result" not in response: + print( + f"Warning: No 'result' key in picked_ids response: {response!r}", flush=True + ) + return + result = response["result"] + if result is None: + print("Warning: picked_ids result is None", flush=True) + return + if not isinstance(result, dict): + print(f"Warning: picked_ids result is not a dict: {result!r}", flush=True) + return + assert "array_ids" in result + array_ids = result["array_ids"] + assert isinstance(array_ids, list) + + +def test_grid_scale(server: ServerMonitor, dataset_factory: Callable[..., str]) -> None: + data_id = "123456789" + dataset_factory(id=data_id, viewable_file_name="hat.vtp") server.call( VtkViewerView.viewer_prefix + VtkViewerView.viewer_schemas_dict["reset_visualization"]["rpc"], ) - assert server.compare_image(3, "viewer/reset_visualization.jpeg") == True - server.call( VtkMeshView.mesh_prefix + VtkMeshView.mesh_schemas_dict["register"]["rpc"], - [{"id": "123456789", "file_name": "hat.vtp"}], + [{"id": data_id}], ) assert server.compare_image(3, "viewer/register_hat.jpeg") == True @@ -190,11 +210,10 @@ def test_grid_scale(server): + VtkViewerView.viewer_schemas_dict["grid_scale"]["rpc"], [{"visibility": True}], ) - assert server.compare_image(3, "viewer/grid_scale_on.jpeg") == True -def test_axes(server): +def test_axes(server: ServerMonitor, dataset_factory: Callable[..., str]) -> None: test_reset_visualization(server) @@ -206,8 +225,10 @@ def test_axes(server): assert server.compare_image(3, "viewer/axes_off.jpeg") == True -def test_update_camera(server): - test_register_mesh(server) +def test_update_camera( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: + test_register_mesh(server, dataset_factory) camera_options = { "focal_point": [-0.034399999999999986, 2.4513515, -0.10266900000000012], @@ -229,8 +250,8 @@ def test_update_camera(server): assert server.compare_image(3, "viewer/update_camera.jpeg") == True -def test_render_now(server): - test_register_mesh(server) +def test_render_now(server: ServerMonitor, dataset_factory: Callable[..., str]) -> None: + test_register_mesh(server, dataset_factory) camera_options = { "focal_point": [-0.034399999999999986, 2.4513515, -0.10266900000000012], @@ -259,17 +280,21 @@ def test_render_now(server): assert server.compare_image(3, "viewer/render_now.jpeg") == True -def test_set_z_scaling(server): +def test_set_z_scaling( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: + dataset_factory(id="123456789", viewable_file_name="polygon_attribute.vtp") server.call( VtkMeshView.mesh_prefix + VtkMeshView.mesh_schemas_dict["register"]["rpc"], - [{"id": "12345678", "file_name": "polygon_attribute.vtp"}], + [{"id": "123456789"}], ) assert server.compare_image(3, "viewer/polygon_attribute.jpeg") == True + dataset_factory(id="987654321", viewable_file_name="vertex_attribute.vtp") server.call( VtkMeshView.mesh_prefix + VtkMeshView.mesh_schemas_dict["register"]["rpc"], - [{"id": "123456789", "file_name": "vertex_attribute.vtp"}], + [{"id": "987654321"}], ) assert server.compare_image(3, "viewer/vertex_and_polygon_attribute.jpeg") == True @@ -300,48 +325,29 @@ def test_set_z_scaling(server): assert server.compare_image(3, "viewer/set_z_scaling.jpeg") == True -def test_combined_scaling_and_grid(server): - # test_set_z_scaling(server) - - # server.call( - # VtkViewerView.viewer_prefix - # + VtkViewerView.viewer_schemas_dict["set_background_color"]["rpc"], - # [{"color": {"r": 180, "g": 180, "b": 180}}], - # ) - # assert server.compare_image(3, "viewer/scaling_and_grid_color.jpeg") == True - - # server.call( - # VtkViewerView.viewer_prefix - # + VtkViewerView.viewer_schemas_dict["grid_scale"]["rpc"], - # [{"visibility": True}], - # ) - - # assert server.compare_image(3, "viewer/grid_scale_on.jpeg") == True +def test_combined_scaling_and_grid( + server: ServerMonitor, dataset_factory: Callable[..., str] +) -> None: server.call( VtkViewerView.viewer_prefix + VtkViewerView.viewer_schemas_dict["reset_visualization"]["rpc"], ) - assert server.compare_image(3, "viewer/reset_visualization.jpeg") == True - + dataset_factory(id="123456789", viewable_file_name="hat.vtp") server.call( VtkMeshView.mesh_prefix + VtkMeshView.mesh_schemas_dict["register"]["rpc"], - [{"id": "123456789", "file_name": "hat.vtp"}], + [{"id": "123456789"}], ) assert server.compare_image(3, "viewer/register_hat.jpeg") == True - server.call( VtkViewerView.viewer_prefix + VtkViewerView.viewer_schemas_dict["grid_scale"]["rpc"], [{"visibility": True}], ) - assert server.compare_image(3, "viewer/grid_scale_on.jpeg") == True - server.call( VtkViewerView.viewer_prefix + VtkViewerView.viewer_schemas_dict["set_z_scaling"]["rpc"], [{"z_scale": 2.5}], ) - assert server.compare_image(3, "viewer/combined_scaling_and_grid.jpeg") == True