OpenGeodeWeb-Back 6.12.6__tar.gz → 6.13.0rc2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/PKG-INFO +1 -1
  2. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/pyproject.toml +1 -1
  3. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/OpenGeodeWeb_Back.egg-info/PKG-INFO +1 -1
  4. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/blueprint_routes.py +37 -18
  5. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/tests/test_routes.py +52 -7
  6. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/LICENSE +0 -0
  7. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/README.md +0 -0
  8. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/requirements.txt +0 -0
  9. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/setup.cfg +0 -0
  10. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/OpenGeodeWeb_Back.egg-info/SOURCES.txt +0 -0
  11. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/OpenGeodeWeb_Back.egg-info/dependency_links.txt +0 -0
  12. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/OpenGeodeWeb_Back.egg-info/entry_points.txt +0 -0
  13. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/OpenGeodeWeb_Back.egg-info/requires.txt +0 -0
  14. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/OpenGeodeWeb_Back.egg-info/top_level.txt +0 -0
  15. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/__init__.py +0 -0
  16. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/app.py +0 -0
  17. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/app_config.py +0 -0
  18. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_functions.py +0 -0
  19. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/__init__.py +0 -0
  20. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/geode_brep.py +0 -0
  21. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/geode_cross_section.py +0 -0
  22. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/geode_edged_curve2d.py +0 -0
  23. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/geode_edged_curve3d.py +0 -0
  24. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/geode_graph.py +0 -0
  25. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/geode_grid2d.py +0 -0
  26. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/geode_grid3d.py +0 -0
  27. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/geode_horizon_stack3d.py +0 -0
  28. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/geode_hybrid_solid3d.py +0 -0
  29. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/geode_implicit_cross_section.py +0 -0
  30. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/geode_implicit_structural_model.py +0 -0
  31. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/geode_light_regular_grid2d.py +0 -0
  32. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/geode_light_regular_grid3d.py +0 -0
  33. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/geode_mesh.py +0 -0
  34. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/geode_model.py +0 -0
  35. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/geode_object.py +0 -0
  36. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/geode_point_set2d.py +0 -0
  37. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/geode_point_set3d.py +0 -0
  38. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/geode_polygonal_surface2d.py +0 -0
  39. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/geode_polygonal_surface3d.py +0 -0
  40. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/geode_polyhedral_solid3d.py +0 -0
  41. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/geode_raster_image2d.py +0 -0
  42. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/geode_raster_image3d.py +0 -0
  43. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/geode_regular_grid2d.py +0 -0
  44. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/geode_regular_grid3d.py +0 -0
  45. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/geode_section.py +0 -0
  46. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/geode_solid_mesh3d.py +0 -0
  47. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/geode_structural_model.py +0 -0
  48. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/geode_surface_mesh2d.py +0 -0
  49. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/geode_surface_mesh3d.py +0 -0
  50. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/geode_tetrahedral_solid3d.py +0 -0
  51. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/geode_triangulated_surface2d.py +0 -0
  52. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/geode_triangulated_surface3d.py +0 -0
  53. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/geode_objects/geode_vertex_set.py +0 -0
  54. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/py.typed +0 -0
  55. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/create/blueprint_create.py +0 -0
  56. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/create/schemas/__init__.py +0 -0
  57. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/create/schemas/edged_curve.json +0 -0
  58. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/create/schemas/edged_curve.py +0 -0
  59. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/create/schemas/point_set.json +0 -0
  60. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/create/schemas/point_set.py +0 -0
  61. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/create/schemas/polygonal_surface.json +0 -0
  62. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/create/schemas/polygonal_surface.py +0 -0
  63. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/__init__.py +0 -0
  64. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/allowed_files.json +0 -0
  65. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/allowed_files.py +0 -0
  66. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/allowed_objects.json +0 -0
  67. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/allowed_objects.py +0 -0
  68. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/cell_attribute_names.json +0 -0
  69. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/cell_attribute_names.py +0 -0
  70. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/edge_attribute_names.json +0 -0
  71. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/edge_attribute_names.py +0 -0
  72. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/export_project.json +0 -0
  73. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/export_project.py +0 -0
  74. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/geode_object_inheritance.json +0 -0
  75. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/geode_object_inheritance.py +0 -0
  76. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/geode_objects_and_output_extensions.json +0 -0
  77. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/geode_objects_and_output_extensions.py +0 -0
  78. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/geographic_coordinate_systems.json +0 -0
  79. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/geographic_coordinate_systems.py +0 -0
  80. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/import_project.json +0 -0
  81. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/import_project.py +0 -0
  82. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/kill.json +0 -0
  83. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/kill.py +0 -0
  84. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/missing_files.json +0 -0
  85. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/missing_files.py +0 -0
  86. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/model_component_edge_attribute_names.json +0 -0
  87. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/model_component_edge_attribute_names.py +0 -0
  88. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/model_component_polygon_attribute_names.json +0 -0
  89. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/model_component_polygon_attribute_names.py +0 -0
  90. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/model_component_polyhedron_attribute_names.json +0 -0
  91. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/model_component_polyhedron_attribute_names.py +0 -0
  92. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/model_component_vertex_attribute_names.json +0 -0
  93. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/model_component_vertex_attribute_names.py +0 -0
  94. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/ping.json +0 -0
  95. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/ping.py +0 -0
  96. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/polygon_attribute_names.json +0 -0
  97. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/polygon_attribute_names.py +0 -0
  98. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/polyhedron_attribute_names.json +0 -0
  99. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/polyhedron_attribute_names.py +0 -0
  100. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/save_viewable_file.json +0 -0
  101. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/save_viewable_file.py +0 -0
  102. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/texture_coordinates.json +0 -0
  103. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/texture_coordinates.py +0 -0
  104. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/upload_file.json +0 -0
  105. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/upload_file.py +0 -0
  106. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/validate.json +0 -0
  107. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/validate.py +0 -0
  108. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/vertex_attribute_names.json +0 -0
  109. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/routes/schemas/vertex_attribute_names.py +0 -0
  110. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/test_utils.py +0 -0
  111. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/src/opengeodeweb_back/utils_functions.py +0 -0
  112. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/tests/test_create_routes.py +0 -0
  113. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/tests/test_geode_functions.py +0 -0
  114. {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0rc2}/tests/test_utils_functions.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: OpenGeodeWeb-Back
3
- Version: 6.12.6
3
+ Version: 6.13.0rc2
4
4
  Summary: OpenGeodeWeb-Back is an open source framework that proposes handy python functions and wrappers for the OpenGeode ecosystem
5
5
  Author-email: Geode-solutions <team-web@geode-solutions.com>
6
6
  Project-URL: Homepage, https://github.com/Geode-solutions/OpenGeodeWeb-Back
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "OpenGeodeWeb-Back"
7
- version = "6.12.6"
7
+ version = "6.13.0-rc.2"
8
8
  dynamic = ["dependencies"]
9
9
  authors = [{ name = "Geode-solutions", email = "team-web@geode-solutions.com" }]
10
10
  description = "OpenGeodeWeb-Back is an open source framework that proposes handy python functions and wrappers for the OpenGeode ecosystem"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: OpenGeodeWeb-Back
3
- Version: 6.12.6
3
+ Version: 6.13.0rc2
4
4
  Summary: OpenGeodeWeb-Back is an open source framework that proposes handy python functions and wrappers for the OpenGeode ecosystem
5
5
  Author-email: Geode-solutions <team-web@geode-solutions.com>
6
6
  Project-URL: Homepage, https://github.com/Geode-solutions/OpenGeodeWeb-Back
@@ -73,13 +73,26 @@ def upload_file() -> flask.Response:
73
73
  if not os.path.exists(UPLOAD_FOLDER_PATH):
74
74
  os.makedirs(UPLOAD_FOLDER_PATH, exist_ok=True)
75
75
 
76
- file = flask.request.files["file"]
77
- if file.filename is None:
78
- flask.abort(400, "Filename is required")
79
- filename = werkzeug.utils.secure_filename(os.path.basename(file.filename))
76
+ # Multipart callers (e.g. Vease) still send the file as a "file" form part;
77
+ # streaming callers PUT the raw bytes as the body with ?filename= as a query param.
78
+ if flask.request.mimetype == "multipart/form-data":
79
+ file = flask.request.files["file"]
80
+ if file.filename is None:
81
+ flask.abort(400, "Filename is required")
82
+ filename = werkzeug.utils.secure_filename(os.path.basename(file.filename))
83
+ file_path = os.path.join(UPLOAD_FOLDER_PATH, filename)
84
+ file.save(file_path)
85
+ else:
86
+ raw_filename = flask.request.args.get("filename")
87
+ if not raw_filename:
88
+ flask.abort(400, "Filename is required")
89
+ filename = werkzeug.utils.secure_filename(os.path.basename(raw_filename))
90
+ file_path = os.path.join(UPLOAD_FOLDER_PATH, filename)
91
+ chunk_size = 1024 * 1024
92
+ with open(file_path, "wb") as destination:
93
+ while chunk := flask.request.stream.read(chunk_size):
94
+ destination.write(chunk)
80
95
  print(f"{filename=}", flush=True)
81
- file_path = os.path.join(UPLOAD_FOLDER_PATH, filename)
82
- file.save(file_path)
83
96
  if filename.lower().endswith(".csv.json"):
84
97
  shutil.copyfile(
85
98
  file_path, os.path.join(UPLOAD_FOLDER_PATH, filename[:-9] + ".json")
@@ -256,32 +269,35 @@ def extract_valid_attribute_values(
256
269
  attribute_manager: og.AttributeManager,
257
270
  attribute_name: str,
258
271
  item_index: int,
259
- ) -> list[float]:
272
+ ) -> tuple[list[float], bool]:
260
273
  attribute_ids_matching_name = attribute_manager.attribute_ids_matching_name(
261
274
  attribute_name
262
275
  )
263
276
  if not isinstance(attribute_ids_matching_name, list):
264
- return []
277
+ return [], False
265
278
  component_attribute = attribute_manager.find_generic_attribute(
266
279
  attribute_ids_matching_name[0]
267
280
  )
268
281
  if component_attribute is None:
269
- return []
282
+ return [], False
270
283
  if not component_attribute.is_genericable():
271
- return []
284
+ return [], False
272
285
  valid_values: list[float] = []
286
+ has_nan = False
273
287
  for element_index in range(attribute_manager.nb_elements()):
274
288
  value = component_attribute.generic_item_value(element_index, item_index)
275
- if value is not None and not math.isnan(value):
289
+ if value is None or math.isnan(value):
290
+ has_nan = True
291
+ else:
276
292
  valid_values.append(value)
277
- return valid_values
293
+ return valid_values, has_nan
278
294
 
279
295
 
280
296
  def attributes_metadata(
281
297
  manager: og.AttributeManager | list[og.AttributeManager],
282
- ) -> list[dict[str, str | int | float | list[float]]]:
298
+ ) -> list[dict[str, str | int | float | bool | list[float]]]:
283
299
  attribute_managers = manager if isinstance(manager, list) else [manager]
284
- attributes: list[dict[str, str | int | float | list[float]]] = []
300
+ attributes: list[dict[str, str | int | float | bool | list[float]]] = []
285
301
  first_manager = attribute_managers[0]
286
302
  for id in first_manager.attribute_ids():
287
303
  attribute = first_manager.find_generic_attribute(id)
@@ -294,14 +310,16 @@ def attributes_metadata(
294
310
  continue
295
311
  nb_items = attribute.nb_items()
296
312
  min_values, max_values = [], []
313
+ attribute_has_nan = False
297
314
  for item_index in range(nb_items):
298
315
  valid_values: list[float] = []
299
316
  for attribute_manager in attribute_managers:
300
- valid_values.extend(
301
- extract_valid_attribute_values(
302
- attribute_manager, attribute_name, item_index
303
- )
317
+ extracted_values, has_nan = extract_valid_attribute_values(
318
+ attribute_manager, attribute_name, item_index
304
319
  )
320
+ valid_values.extend(extracted_values)
321
+ if has_nan:
322
+ attribute_has_nan = True
305
323
  if valid_values:
306
324
  min_values.append(min(valid_values))
307
325
  max_values.append(max(valid_values))
@@ -316,6 +334,7 @@ def attributes_metadata(
316
334
  "max_value": max(max_values),
317
335
  "min_values": min_values,
318
336
  "max_values": max_values,
337
+ "no_data": attribute_has_nan,
319
338
  }
320
339
  )
321
340
  return attributes
@@ -79,6 +79,36 @@ def test_upload_file(client: FlaskClient, filename: str = "test.og_brep") -> Non
79
79
  assert response.status_code == 201
80
80
 
81
81
 
82
+ def test_upload_file_raw(client: FlaskClient, filename: str = "test.og_brep") -> None:
83
+ file = os.path.join(data_dir, filename)
84
+ with open(file, "rb") as opened_file:
85
+ file_bytes = opened_file.read()
86
+
87
+ raw_filename = "raw_upload_test.og_brep"
88
+ response = client.put(
89
+ f"/opengeodeweb_back/upload_file?filename={raw_filename}",
90
+ data=file_bytes,
91
+ content_type="application/octet-stream",
92
+ )
93
+ assert response.status_code == 201
94
+
95
+ uploaded_path = os.path.join(data_dir, raw_filename)
96
+ try:
97
+ with open(uploaded_path, "rb") as uploaded_file:
98
+ assert uploaded_file.read() == file_bytes
99
+ finally:
100
+ os.remove(uploaded_path)
101
+
102
+
103
+ def test_upload_file_raw_missing_filename(client: FlaskClient) -> None:
104
+ response = client.put(
105
+ f"/opengeodeweb_back/upload_file",
106
+ data=b"some raw bytes",
107
+ content_type="application/octet-stream",
108
+ )
109
+ assert response.status_code == 400
110
+
111
+
82
112
  def test_missing_files(client: FlaskClient) -> None:
83
113
  route = f"/opengeodeweb_back/missing_files"
84
114
 
@@ -261,6 +291,8 @@ def test_vertex_attribute_names(client: FlaskClient, test_id: str) -> None:
261
291
  assert "nb_items" in attribute
262
292
  assert "min_values" in attribute
263
293
  assert "max_values" in attribute
294
+ assert "no_data" in attribute
295
+ assert isinstance(attribute["no_data"], bool)
264
296
  print(
265
297
  f"[ATTRIBUTES]: ",
266
298
  [attribute["nb_items"] for attribute in attributes],
@@ -297,6 +329,8 @@ def test_cell_attribute_names(client: FlaskClient, test_id: str) -> None:
297
329
  assert "nb_items" in attribute
298
330
  assert "min_values" in attribute
299
331
  assert "max_values" in attribute
332
+ assert "no_data" in attribute
333
+ assert isinstance(attribute["no_data"], bool)
300
334
  print(
301
335
  f"[ATTRIBUTES]: ",
302
336
  [attribute["nb_items"] for attribute in attributes],
@@ -333,6 +367,8 @@ def test_polygon_attribute_names(client: FlaskClient, test_id: str) -> None:
333
367
  assert "nb_items" in attribute
334
368
  assert "min_values" in attribute
335
369
  assert "max_values" in attribute
370
+ assert "no_data" in attribute
371
+ assert isinstance(attribute["no_data"], bool)
336
372
  print(
337
373
  f"[ATTRIBUTES]: ",
338
374
  [attribute["nb_items"] for attribute in attributes],
@@ -370,6 +406,8 @@ def test_polyhedron_attribute_names(client: FlaskClient, test_id: str) -> None:
370
406
  assert "nb_items" in attribute
371
407
  assert "min_values" in attribute
372
408
  assert "max_values" in attribute
409
+ assert "no_data" in attribute
410
+ assert isinstance(attribute["no_data"], bool)
373
411
  if attribute["attribute_name"] == "Range":
374
412
  assert attribute["min_value"] == 0.0
375
413
  assert attribute["max_value"] == 579.0
@@ -411,6 +449,8 @@ def test_edge_attribute_names(client: FlaskClient, test_id: str) -> None:
411
449
  assert "nb_items" in attribute
412
450
  assert "min_values" in attribute
413
451
  assert "max_values" in attribute
452
+ assert "no_data" in attribute
453
+ assert isinstance(attribute["no_data"], bool)
414
454
  print(
415
455
  f"[ATTRIBUTES]: ",
416
456
  [attribute["nb_items"] for attribute in attributes],
@@ -485,7 +525,7 @@ def test_model_components(client: FlaskClient) -> None:
485
525
  assert isinstance(mesh_components, list)
486
526
  assert len(mesh_components) > 0
487
527
  for mesh_component in mesh_components:
488
- assert isinstance(mesh_component, object)
528
+ assert isinstance(mesh_component, dict)
489
529
  assert isinstance(mesh_component["geode_id"], str)
490
530
  assert isinstance(mesh_component["viewer_id"], int)
491
531
  assert isinstance(mesh_component["name"], str)
@@ -501,7 +541,7 @@ def test_model_components(client: FlaskClient) -> None:
501
541
  collection_components = response.get_json()["collection_components"]
502
542
  assert isinstance(collection_components, list)
503
543
  for collection_component in collection_components:
504
- assert isinstance(collection_component, object)
544
+ assert isinstance(collection_component, dict)
505
545
  assert isinstance(collection_component["geode_id"], str)
506
546
  assert isinstance(collection_component["name"], str)
507
547
  assert isinstance(collection_component["items"], list)
@@ -649,15 +689,18 @@ def _load_brep_components(client: FlaskClient) -> tuple[str, dict[str, list[str]
649
689
  response = test_save_viewable_file(client, "BRep", "cube.og_brep")
650
690
  assert response.status_code == 200
651
691
  model_id: str = response.get_json()["id"]
652
- mesh_components: list[dict] = response.get_json()["mesh_components"]
692
+ mesh_components: list[dict[str, object]] = response.get_json()["mesh_components"]
653
693
  by_type: dict[str, list[str]] = {}
654
- for mc in mesh_components:
655
- component_type = mc["type"]
656
- by_type.setdefault(component_type, []).append(mc["geode_id"])
694
+ for mesh_component in mesh_components:
695
+ component_type = mesh_component["type"]
696
+ geode_id = mesh_component["geode_id"]
697
+ assert isinstance(component_type, str)
698
+ assert isinstance(geode_id, str)
699
+ by_type.setdefault(component_type, []).append(geode_id)
657
700
  return model_id, by_type
658
701
 
659
702
 
660
- def _assert_attributes_response(response) -> None:
703
+ def _assert_attributes_response(response: TestResponse) -> None:
661
704
  assert response.status_code == 200
662
705
  attributes = response.get_json()["attributes"]
663
706
  assert isinstance(attributes, list)
@@ -668,6 +711,8 @@ def _assert_attributes_response(response) -> None:
668
711
  assert "nb_items" in attribute
669
712
  assert "min_values" in attribute
670
713
  assert "max_values" in attribute
714
+ assert "no_data" in attribute
715
+ assert isinstance(attribute["no_data"], bool)
671
716
 
672
717
 
673
718
  def test_model_component_vertex_attribute_names(client: FlaskClient) -> None: