OpenGeodeWeb-Back 6.15.3rc1__tar.gz → 6.16.0__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 (125) hide show
  1. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/PKG-INFO +1 -1
  2. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/pyproject.toml +1 -1
  3. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/OpenGeodeWeb_Back.egg-info/PKG-INFO +1 -1
  4. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/OpenGeodeWeb_Back.egg-info/SOURCES.txt +8 -2
  5. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_functions.py +6 -3
  6. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/geode_brep.py +11 -0
  7. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/blueprint_routes.py +107 -31
  8. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/__init__.py +4 -1
  9. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/allowed_files.json +10 -3
  10. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/allowed_files.py +2 -1
  11. opengeodeweb_back-6.16.0/src/opengeodeweb_back/routes/schemas/apply_time_series.json +29 -0
  12. opengeodeweb_back-6.16.0/src/opengeodeweb_back/routes/schemas/apply_time_series.py +31 -0
  13. opengeodeweb_back-6.15.3rc1/src/opengeodeweb_back/routes/schemas/missing_files.json → opengeodeweb_back-6.16.0/src/opengeodeweb_back/routes/schemas/data_missing_files.json +2 -2
  14. opengeodeweb_back-6.15.3rc1/src/opengeodeweb_back/routes/schemas/missing_files.py → opengeodeweb_back-6.16.0/src/opengeodeweb_back/routes/schemas/data_missing_files.py +6 -6
  15. opengeodeweb_back-6.16.0/src/opengeodeweb_back/routes/schemas/time_series_allowed_objects.json +32 -0
  16. opengeodeweb_back-6.16.0/src/opengeodeweb_back/routes/schemas/time_series_allowed_objects.py +31 -0
  17. opengeodeweb_back-6.16.0/src/opengeodeweb_back/routes/schemas/time_series_missing_files.json +43 -0
  18. opengeodeweb_back-6.16.0/src/opengeodeweb_back/routes/schemas/time_series_missing_files.py +33 -0
  19. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/tests/test_routes.py +117 -3
  20. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/LICENSE +0 -0
  21. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/README.md +0 -0
  22. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/requirements.txt +0 -0
  23. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/setup.cfg +0 -0
  24. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/OpenGeodeWeb_Back.egg-info/dependency_links.txt +0 -0
  25. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/OpenGeodeWeb_Back.egg-info/entry_points.txt +0 -0
  26. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/OpenGeodeWeb_Back.egg-info/requires.txt +0 -0
  27. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/OpenGeodeWeb_Back.egg-info/top_level.txt +0 -0
  28. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/__init__.py +0 -0
  29. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/app.py +0 -0
  30. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/app_config.py +0 -0
  31. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/__init__.py +0 -0
  32. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/geode_cross_section.py +0 -0
  33. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/geode_edged_curve2d.py +0 -0
  34. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/geode_edged_curve3d.py +0 -0
  35. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/geode_graph.py +0 -0
  36. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/geode_grid2d.py +0 -0
  37. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/geode_grid3d.py +0 -0
  38. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/geode_horizon_stack3d.py +0 -0
  39. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/geode_hybrid_solid3d.py +0 -0
  40. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/geode_implicit_cross_section.py +0 -0
  41. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/geode_implicit_structural_model.py +0 -0
  42. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/geode_light_regular_grid2d.py +0 -0
  43. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/geode_light_regular_grid3d.py +0 -0
  44. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/geode_mesh.py +0 -0
  45. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/geode_model.py +0 -0
  46. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/geode_object.py +0 -0
  47. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/geode_point_set2d.py +0 -0
  48. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/geode_point_set3d.py +0 -0
  49. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/geode_polygonal_surface2d.py +0 -0
  50. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/geode_polygonal_surface3d.py +0 -0
  51. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/geode_polyhedral_solid3d.py +0 -0
  52. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/geode_raster_image2d.py +0 -0
  53. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/geode_raster_image3d.py +0 -0
  54. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/geode_regular_grid2d.py +0 -0
  55. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/geode_regular_grid3d.py +0 -0
  56. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/geode_section.py +0 -0
  57. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/geode_solid_mesh3d.py +0 -0
  58. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/geode_structural_model.py +0 -0
  59. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/geode_surface_mesh2d.py +0 -0
  60. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/geode_surface_mesh3d.py +0 -0
  61. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/geode_tetrahedral_solid3d.py +0 -0
  62. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/geode_triangulated_surface2d.py +0 -0
  63. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/geode_triangulated_surface3d.py +0 -0
  64. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/geode_objects/geode_vertex_set.py +0 -0
  65. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/py.typed +0 -0
  66. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/__init__.py +0 -0
  67. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/create/__init__.py +0 -0
  68. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/create/blueprint_create.py +0 -0
  69. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/create/schemas/__init__.py +0 -0
  70. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/create/schemas/edged_curve.json +0 -0
  71. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/create/schemas/edged_curve.py +0 -0
  72. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/create/schemas/point_set.json +0 -0
  73. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/create/schemas/point_set.py +0 -0
  74. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/create/schemas/polygonal_surface.json +0 -0
  75. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/create/schemas/polygonal_surface.py +0 -0
  76. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/allowed_objects.json +0 -0
  77. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/allowed_objects.py +0 -0
  78. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/cell_attribute_names.json +0 -0
  79. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/cell_attribute_names.py +0 -0
  80. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/edge_attribute_names.json +0 -0
  81. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/edge_attribute_names.py +0 -0
  82. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/export_project.json +0 -0
  83. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/export_project.py +0 -0
  84. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/geode_object_inheritance.json +0 -0
  85. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/geode_object_inheritance.py +0 -0
  86. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/geode_objects_and_output_extensions.json +0 -0
  87. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/geode_objects_and_output_extensions.py +0 -0
  88. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/geographic_coordinate_systems.json +0 -0
  89. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/geographic_coordinate_systems.py +0 -0
  90. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/import_project.json +0 -0
  91. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/import_project.py +0 -0
  92. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/kill.json +0 -0
  93. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/kill.py +0 -0
  94. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/model_component_edge_attribute_names.json +0 -0
  95. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/model_component_edge_attribute_names.py +0 -0
  96. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/model_component_polygon_attribute_names.json +0 -0
  97. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/model_component_polygon_attribute_names.py +0 -0
  98. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/model_component_polyhedron_attribute_names.json +0 -0
  99. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/model_component_polyhedron_attribute_names.py +0 -0
  100. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/model_component_vertex_attribute_names.json +0 -0
  101. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/model_component_vertex_attribute_names.py +0 -0
  102. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/ping.json +0 -0
  103. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/ping.py +0 -0
  104. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/polygon_attribute_names.json +0 -0
  105. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/polygon_attribute_names.py +0 -0
  106. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/polyhedron_attribute_names.json +0 -0
  107. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/polyhedron_attribute_names.py +0 -0
  108. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/save_viewable_file.json +0 -0
  109. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/save_viewable_file.py +0 -0
  110. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/texture_coordinates.json +0 -0
  111. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/texture_coordinates.py +0 -0
  112. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/upload_file.json +0 -0
  113. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/upload_file.py +0 -0
  114. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/validate.json +0 -0
  115. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/validate.py +0 -0
  116. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/vertex_attribute_names.json +0 -0
  117. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/routes/schemas/vertex_attribute_names.py +0 -0
  118. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/test_utils.py +0 -0
  119. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/typed_route.py +0 -0
  120. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/src/opengeodeweb_back/utils_functions.py +0 -0
  121. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/tests/test_create_routes.py +0 -0
  122. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/tests/test_events.py +0 -0
  123. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/tests/test_geode_functions.py +0 -0
  124. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/tests/test_typed_routes.py +0 -0
  125. {opengeodeweb_back-6.15.3rc1 → opengeodeweb_back-6.16.0}/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.15.3rc1
3
+ Version: 6.16.0
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.15.3-rc.1"
7
+ version = "6.16.0"
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.15.3rc1
3
+ Version: 6.16.0
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
@@ -67,8 +67,12 @@ src/opengeodeweb_back/routes/schemas/allowed_files.json
67
67
  src/opengeodeweb_back/routes/schemas/allowed_files.py
68
68
  src/opengeodeweb_back/routes/schemas/allowed_objects.json
69
69
  src/opengeodeweb_back/routes/schemas/allowed_objects.py
70
+ src/opengeodeweb_back/routes/schemas/apply_time_series.json
71
+ src/opengeodeweb_back/routes/schemas/apply_time_series.py
70
72
  src/opengeodeweb_back/routes/schemas/cell_attribute_names.json
71
73
  src/opengeodeweb_back/routes/schemas/cell_attribute_names.py
74
+ src/opengeodeweb_back/routes/schemas/data_missing_files.json
75
+ src/opengeodeweb_back/routes/schemas/data_missing_files.py
72
76
  src/opengeodeweb_back/routes/schemas/edge_attribute_names.json
73
77
  src/opengeodeweb_back/routes/schemas/edge_attribute_names.py
74
78
  src/opengeodeweb_back/routes/schemas/export_project.json
@@ -83,8 +87,6 @@ src/opengeodeweb_back/routes/schemas/import_project.json
83
87
  src/opengeodeweb_back/routes/schemas/import_project.py
84
88
  src/opengeodeweb_back/routes/schemas/kill.json
85
89
  src/opengeodeweb_back/routes/schemas/kill.py
86
- src/opengeodeweb_back/routes/schemas/missing_files.json
87
- src/opengeodeweb_back/routes/schemas/missing_files.py
88
90
  src/opengeodeweb_back/routes/schemas/model_component_edge_attribute_names.json
89
91
  src/opengeodeweb_back/routes/schemas/model_component_edge_attribute_names.py
90
92
  src/opengeodeweb_back/routes/schemas/model_component_polygon_attribute_names.json
@@ -103,6 +105,10 @@ src/opengeodeweb_back/routes/schemas/save_viewable_file.json
103
105
  src/opengeodeweb_back/routes/schemas/save_viewable_file.py
104
106
  src/opengeodeweb_back/routes/schemas/texture_coordinates.json
105
107
  src/opengeodeweb_back/routes/schemas/texture_coordinates.py
108
+ src/opengeodeweb_back/routes/schemas/time_series_allowed_objects.json
109
+ src/opengeodeweb_back/routes/schemas/time_series_allowed_objects.py
110
+ src/opengeodeweb_back/routes/schemas/time_series_missing_files.json
111
+ src/opengeodeweb_back/routes/schemas/time_series_missing_files.py
106
112
  src/opengeodeweb_back/routes/schemas/upload_file.json
107
113
  src/opengeodeweb_back/routes/schemas/upload_file.py
108
114
  src/opengeodeweb_back/routes/schemas/validate.json
@@ -58,9 +58,12 @@ def get_data_info(data_id: str) -> Data:
58
58
 
59
59
 
60
60
  def upload_file_path(filename: str) -> str:
61
- upload_folder = flask.current_app.config["UPLOAD_FOLDER_PATH"]
62
- secure_filename = werkzeug.utils.secure_filename(filename)
63
- return str((Path(upload_folder) / secure_filename).resolve())
61
+ upload_folder = Path(flask.current_app.config["UPLOAD_FOLDER_PATH"]).resolve()
62
+ segments = [werkzeug.utils.secure_filename(segment) for segment in filename.split("/")]
63
+ safe_segments = [segment for segment in segments if segment]
64
+ if not safe_segments:
65
+ flask.abort(400, f"Invalid filename: {filename}")
66
+ return str(upload_folder.joinpath(*safe_segments))
64
67
 
65
68
 
66
69
  def geode_object_output_extensions(
@@ -57,6 +57,17 @@ class GeodeBRep(GeodeModel):
57
57
  def additional_files(cls, filename: str) -> og.AdditionalFiles:
58
58
  return og.brep_additional_files(filename)
59
59
 
60
+ @classmethod
61
+ def time_series_input_extensions(cls) -> list[str]:
62
+ return og.BRepTimeSeriesInputFactory.list_creators()
63
+
64
+ @classmethod
65
+ def time_series_additional_files(cls, filename: str) -> og.AdditionalFiles:
66
+ return og.brep_time_series_additional_files(filename)
67
+
68
+ def load_time_series(self, filename: str) -> None:
69
+ og.load_brep_time_series(self.brep, filename)
70
+
60
71
  @override
61
72
  @classmethod
62
73
  def is_loadable(cls, filename: str) -> og.Percentage:
@@ -28,6 +28,7 @@ from sqlalchemy.exc import OperationalError
28
28
  # Local application imports
29
29
  from opengeodeweb_back import geode_functions, utils_functions
30
30
  from opengeodeweb_back.geode_objects import geode_objects
31
+ from opengeodeweb_back.geode_objects.geode_brep import GeodeBRep
31
32
  from opengeodeweb_back.geode_objects.geode_graph import GeodeGraph
32
33
  from opengeodeweb_back.geode_objects.geode_grid2d import GeodeGrid2D
33
34
  from opengeodeweb_back.geode_objects.geode_grid3d import GeodeGrid3D
@@ -42,6 +43,7 @@ from opengeodeweb_back.typed_route import parse_params, raw_route, typed_route
42
43
  if TYPE_CHECKING:
43
44
  from werkzeug.datastructures import FileStorage
44
45
 
46
+
45
47
  ComponentMesh = (
46
48
  og.Corner2D,
47
49
  og.Corner3D,
@@ -62,11 +64,14 @@ routes = flask.Blueprint("routes", __name__, url_prefix="/opengeodeweb_back")
62
64
 
63
65
  @typed_route(routes, schemas.allowed_files_route)
64
66
  def allowed_files(_params: schemas.AllowedFiles) -> schemas.AllowedFilesResponse:
65
- extensions: set[str] = set()
67
+ data_extensions: set[str] = set()
66
68
  for geode_object in geode_objects.values():
67
- for extension in geode_object.input_extensions():
68
- extensions.add(extension)
69
- return schemas.AllowedFilesResponse(extensions=list(extensions))
69
+ data_extensions.update(geode_object.input_extensions())
70
+ # Time series files are applied on an existing BRep, not imported as data
71
+ return schemas.AllowedFilesResponse(
72
+ data_extensions=list(data_extensions),
73
+ time_series_extensions=GeodeBRep.time_series_input_extensions(),
74
+ )
70
75
 
71
76
 
72
77
  def _write_stream(path: Path, stream: typing.IO[bytes], mode: str = "wb") -> None:
@@ -101,21 +106,23 @@ def upload_file() -> flask.Response:
101
106
  file = flask.request.files["file"]
102
107
  if file.filename is None:
103
108
  flask.abort(400, "Filename is required")
104
- filename = werkzeug.utils.secure_filename(Path(file.filename).name)
105
- file_path = Path(upload_folder_path) / filename
109
+ # Only the name is kept: some multipart clients send their full local path.
110
+ # Relative paths (folder uploads) go through the raw upload below.
111
+ file_path = Path(geode_functions.upload_file_path(Path(file.filename).name))
112
+ file_path.parent.mkdir(parents=True, exist_ok=True)
106
113
  file.save(file_path)
107
- return _finalize_upload(filename)
114
+ return _finalize_upload(file_path.name)
108
115
 
109
116
  raw_filename = flask.request.args.get("filename")
110
117
  if not raw_filename:
111
118
  flask.abort(400, "Filename is required")
112
- filename = werkzeug.utils.secure_filename(Path(raw_filename).name)
113
- file_path = Path(upload_folder_path) / filename
119
+ file_path = Path(geode_functions.upload_file_path(raw_filename))
120
+ file_path.parent.mkdir(parents=True, exist_ok=True)
114
121
 
115
122
  total_chunks = flask.request.args.get("total_chunks", type=int)
116
123
  if total_chunks is None:
117
124
  _write_stream(file_path, flask.request.stream)
118
- return _finalize_upload(filename)
125
+ return _finalize_upload(file_path.name)
119
126
 
120
127
  chunk_index = flask.request.args.get("chunk_index", type=int)
121
128
  if chunk_index is None or not 0 <= chunk_index < total_chunks:
@@ -127,7 +134,18 @@ def upload_file() -> flask.Response:
127
134
  return _upload_response("Chunk received", 200)
128
135
 
129
136
  part_path.replace(file_path)
130
- return _finalize_upload(filename)
137
+ return _finalize_upload(file_path.name)
138
+
139
+
140
+ @typed_route(routes, schemas.time_series_allowed_objects_route)
141
+ def time_series_allowed_objects(
142
+ params: schemas.TimeSeriesAllowedObjects,
143
+ ) -> schemas.TimeSeriesAllowedObjectsResponse:
144
+ extension = utils_functions.extension_from_filename(params.filename)
145
+ is_readable = extension in GeodeBRep.time_series_input_extensions()
146
+ return schemas.TimeSeriesAllowedObjectsResponse(
147
+ allowed_objects=[GeodeBRep.geode_object_type()] if is_readable else []
148
+ )
131
149
 
132
150
 
133
151
  @typed_route(routes, schemas.allowed_objects_route)
@@ -149,28 +167,69 @@ def allowed_objects(
149
167
  return schemas.AllowedObjectsResponse(allowed_objects=allowed_objects)
150
168
 
151
169
 
152
- @typed_route(routes, schemas.missing_files_route)
153
- def missing_files(params: schemas.MissingFiles) -> schemas.MissingFilesResponse:
154
- file_path = geode_functions.upload_file_path(params.filename)
170
+ def _check_time_series_extension(filename: str) -> None:
171
+ extension = utils_functions.extension_from_filename(filename)
172
+ if extension not in GeodeBRep.time_series_input_extensions():
173
+ flask.abort(400, f"No time series reader for .{extension}")
174
+
175
+
176
+ def _resolved_path(file_path: str, main_file: Path) -> Path:
177
+ path = Path(file_path)
178
+ if not path.is_absolute():
179
+ path = main_file.parent / path
180
+ return path.resolve()
181
+
182
+
183
+ def _path_relative_to_main_file(file_path: str, main_file: Path) -> str:
184
+ path = _resolved_path(file_path, main_file)
185
+ if path.is_relative_to(main_file.parent):
186
+ return path.relative_to(main_file.parent).as_posix()
187
+ return path.name
188
+
189
+
190
+ def _missing_files(additional_files: og.AdditionalFiles, main_file: Path) -> dict[str, typing.Any]:
191
+ return {
192
+ "has_missing_files": any(
193
+ file.is_missing
194
+ for file in additional_files.mandatory_files + additional_files.optional_files
195
+ ),
196
+ "mandatory_files": [
197
+ _path_relative_to_main_file(file.filename, main_file)
198
+ for file in additional_files.mandatory_files
199
+ if file.is_missing
200
+ ],
201
+ "additional_files": [
202
+ _path_relative_to_main_file(file.filename, main_file)
203
+ for file in additional_files.optional_files
204
+ if file.is_missing
205
+ ],
206
+ }
207
+
208
+
209
+ @typed_route(routes, schemas.data_missing_files_route)
210
+ def data_missing_files(params: schemas.DataMissingFiles) -> schemas.DataMissingFilesResponse:
211
+ file_path = Path(geode_functions.upload_file_path(params.filename))
155
212
  geode_object = geode_functions.geode_object_from_string(params.geode_object_type)
156
- additional_files = geode_object.additional_files(
157
- file_path,
158
- )
159
- has_missing_files = any(
160
- file.is_missing
213
+ additional_files = geode_object.additional_files(str(file_path))
214
+ return schemas.DataMissingFilesResponse.from_dict(_missing_files(additional_files, file_path))
215
+
216
+
217
+ @typed_route(routes, schemas.time_series_missing_files_route)
218
+ def time_series_missing_files(
219
+ params: schemas.TimeSeriesMissingFiles,
220
+ ) -> schemas.TimeSeriesMissingFilesResponse:
221
+ file_path = Path(geode_functions.upload_file_path(params.filename))
222
+ _check_time_series_extension(file_path.name)
223
+ additional_files = GeodeBRep.time_series_additional_files(str(file_path))
224
+ # Uploads always land under the time series file's folder:
225
+ # files referenced outside of it could never be provided
226
+ if any(
227
+ not _resolved_path(file.filename, file_path).is_relative_to(file_path.parent)
161
228
  for file in additional_files.mandatory_files + additional_files.optional_files
162
- )
163
- mandatory_files = [
164
- Path(file.filename).name for file in additional_files.mandatory_files if file.is_missing
165
- ]
166
- additional_files_array = [
167
- Path(file.filename).name for file in additional_files.optional_files if file.is_missing
168
- ]
169
-
170
- return schemas.MissingFilesResponse(
171
- has_missing_files=has_missing_files,
172
- mandatory_files=mandatory_files,
173
- additional_files=additional_files_array,
229
+ ):
230
+ flask.abort(400, f"{file_path.name} references files outside its folder")
231
+ return schemas.TimeSeriesMissingFilesResponse.from_dict(
232
+ _missing_files(additional_files, file_path)
174
233
  )
175
234
 
176
235
 
@@ -235,6 +294,23 @@ def save_viewable_file(
235
294
  )
236
295
 
237
296
 
297
+ @typed_route(routes, schemas.apply_time_series_route)
298
+ def apply_time_series(
299
+ params: schemas.ApplyTimeSeries,
300
+ ) -> schemas.ApplyTimeSeriesResponse:
301
+ data = geode_functions.get_data_info(params.id)
302
+ _check_time_series_extension(params.filename)
303
+ if data.geode_object != GeodeBRep.geode_object_type():
304
+ flask.abort(400, f"{data.geode_object} is not a BRep: time series cannot be applied")
305
+ geode_object = GeodeBRep.load(geode_functions.data_file_path(params.id, data.native_file))
306
+ # Applied in memory first: if OpenGeode fails, nothing is written.
307
+ geode_object.load_time_series(geode_functions.upload_file_path(params.filename))
308
+ utils_functions.save_all_viewables_and_return_info(
309
+ geode_object, data, geode_functions.data_file_path(params.id)
310
+ )
311
+ return schemas.ApplyTimeSeriesResponse()
312
+
313
+
238
314
  @typed_route(routes, schemas.texture_coordinates_route)
239
315
  def texture_coordinates(
240
316
  params: schemas.TextureCoordinates,
@@ -1,6 +1,8 @@
1
1
  from .vertex_attribute_names import *
2
2
  from .validate import *
3
3
  from .upload_file import *
4
+ from .time_series_missing_files import *
5
+ from .time_series_allowed_objects import *
4
6
  from .texture_coordinates import *
5
7
  from .save_viewable_file import *
6
8
  from .polyhedron_attribute_names import *
@@ -10,7 +12,6 @@ from .model_component_vertex_attribute_names import *
10
12
  from .model_component_polyhedron_attribute_names import *
11
13
  from .model_component_polygon_attribute_names import *
12
14
  from .model_component_edge_attribute_names import *
13
- from .missing_files import *
14
15
  from .kill import *
15
16
  from .import_project import *
16
17
  from .geographic_coordinate_systems import *
@@ -18,6 +19,8 @@ from .geode_objects_and_output_extensions import *
18
19
  from .geode_object_inheritance import *
19
20
  from .export_project import *
20
21
  from .edge_attribute_names import *
22
+ from .data_missing_files import *
21
23
  from .cell_attribute_names import *
24
+ from .apply_time_series import *
22
25
  from .allowed_objects import *
23
26
  from .allowed_files import *
@@ -10,7 +10,13 @@
10
10
  "response": {
11
11
  "type": "object",
12
12
  "properties": {
13
- "extensions": {
13
+ "data_extensions": {
14
+ "type": "array",
15
+ "items": {
16
+ "type": "string"
17
+ }
18
+ },
19
+ "time_series_extensions": {
14
20
  "type": "array",
15
21
  "items": {
16
22
  "type": "string"
@@ -18,8 +24,9 @@
18
24
  }
19
25
  },
20
26
  "required": [
21
- "extensions"
27
+ "data_extensions",
28
+ "time_series_extensions"
22
29
  ],
23
30
  "additionalProperties": false
24
31
  }
25
- }
32
+ }
@@ -19,7 +19,8 @@ class AllowedFilesResponse(DataClassJsonMixin):
19
19
  def __post_init__(self) -> None:
20
20
  print_dataclass(self)
21
21
 
22
- extensions: List[str]
22
+ data_extensions: List[str]
23
+ time_series_extensions: List[str]
23
24
 
24
25
 
25
26
  allowed_files_route = Route(
@@ -0,0 +1,29 @@
1
+ {
2
+ "route": "/apply_time_series",
3
+ "methods": [
4
+ "POST"
5
+ ],
6
+ "type": "object",
7
+ "properties": {
8
+ "id": {
9
+ "type": "string",
10
+ "minLength": 32,
11
+ "maxLength": 32
12
+ },
13
+ "filename": {
14
+ "type": "string",
15
+ "minLength": 1
16
+ }
17
+ },
18
+ "required": [
19
+ "id",
20
+ "filename"
21
+ ],
22
+ "additionalProperties": false,
23
+ "response": {
24
+ "type": "object",
25
+ "properties": {},
26
+ "required": [],
27
+ "additionalProperties": false
28
+ }
29
+ }
@@ -0,0 +1,31 @@
1
+ from pathlib import Path
2
+ from opengeodeweb_microservice.schemas import Route, load_schema
3
+ from dataclasses_json import DataClassJsonMixin
4
+ from opengeodeweb_microservice.schemas import print_dataclass
5
+ from dataclasses import dataclass
6
+
7
+
8
+ @dataclass
9
+ class ApplyTimeSeries(DataClassJsonMixin):
10
+ def __post_init__(self) -> None:
11
+ print_dataclass(self)
12
+
13
+ filename: str
14
+ id: str
15
+
16
+
17
+ @dataclass
18
+ class ApplyTimeSeriesResponse(DataClassJsonMixin):
19
+ def __post_init__(self) -> None:
20
+ print_dataclass(self)
21
+
22
+ pass
23
+
24
+
25
+ apply_time_series_route = Route(
26
+ schema=load_schema(Path(__file__)),
27
+ params=ApplyTimeSeries,
28
+ response=ApplyTimeSeriesResponse,
29
+ )
30
+
31
+ __all__ = ["ApplyTimeSeries", "ApplyTimeSeriesResponse", "apply_time_series_route"]
@@ -1,5 +1,5 @@
1
1
  {
2
- "route": "/missing_files",
2
+ "route": "/data_missing_files",
3
3
  "methods": [
4
4
  "POST"
5
5
  ],
@@ -45,4 +45,4 @@
45
45
  ],
46
46
  "additionalProperties": false
47
47
  }
48
- }
48
+ }
@@ -7,7 +7,7 @@ from dataclasses import dataclass
7
7
 
8
8
 
9
9
  @dataclass
10
- class MissingFiles(DataClassJsonMixin):
10
+ class DataMissingFiles(DataClassJsonMixin):
11
11
  def __post_init__(self) -> None:
12
12
  print_dataclass(self)
13
13
 
@@ -16,7 +16,7 @@ class MissingFiles(DataClassJsonMixin):
16
16
 
17
17
 
18
18
  @dataclass
19
- class MissingFilesResponse(DataClassJsonMixin):
19
+ class DataMissingFilesResponse(DataClassJsonMixin):
20
20
  def __post_init__(self) -> None:
21
21
  print_dataclass(self)
22
22
 
@@ -25,10 +25,10 @@ class MissingFilesResponse(DataClassJsonMixin):
25
25
  mandatory_files: List[str]
26
26
 
27
27
 
28
- missing_files_route = Route(
28
+ data_missing_files_route = Route(
29
29
  schema=load_schema(Path(__file__)),
30
- params=MissingFiles,
31
- response=MissingFilesResponse,
30
+ params=DataMissingFiles,
31
+ response=DataMissingFilesResponse,
32
32
  )
33
33
 
34
- __all__ = ["MissingFiles", "MissingFilesResponse", "missing_files_route"]
34
+ __all__ = ["DataMissingFiles", "DataMissingFilesResponse", "data_missing_files_route"]
@@ -0,0 +1,32 @@
1
+ {
2
+ "route": "/time_series_allowed_objects",
3
+ "methods": [
4
+ "POST"
5
+ ],
6
+ "type": "object",
7
+ "properties": {
8
+ "filename": {
9
+ "type": "string",
10
+ "minLength": 1
11
+ }
12
+ },
13
+ "required": [
14
+ "filename"
15
+ ],
16
+ "additionalProperties": false,
17
+ "response": {
18
+ "type": "object",
19
+ "properties": {
20
+ "allowed_objects": {
21
+ "type": "array",
22
+ "items": {
23
+ "type": "string"
24
+ }
25
+ }
26
+ },
27
+ "required": [
28
+ "allowed_objects"
29
+ ],
30
+ "additionalProperties": false
31
+ }
32
+ }
@@ -0,0 +1,31 @@
1
+ from pathlib import Path
2
+ from opengeodeweb_microservice.schemas import Route, load_schema
3
+ from typing import List
4
+ from dataclasses_json import DataClassJsonMixin
5
+ from opengeodeweb_microservice.schemas import print_dataclass
6
+ from dataclasses import dataclass
7
+
8
+
9
+ @dataclass
10
+ class TimeSeriesAllowedObjects(DataClassJsonMixin):
11
+ def __post_init__(self) -> None:
12
+ print_dataclass(self)
13
+
14
+ filename: str
15
+
16
+
17
+ @dataclass
18
+ class TimeSeriesAllowedObjectsResponse(DataClassJsonMixin):
19
+ def __post_init__(self) -> None:
20
+ print_dataclass(self)
21
+
22
+ allowed_objects: List[str]
23
+
24
+
25
+ time_series_allowed_objects_route = Route(
26
+ schema=load_schema(Path(__file__)),
27
+ params=TimeSeriesAllowedObjects,
28
+ response=TimeSeriesAllowedObjectsResponse,
29
+ )
30
+
31
+ __all__ = ["TimeSeriesAllowedObjects", "TimeSeriesAllowedObjectsResponse", "time_series_allowed_objects_route"]
@@ -0,0 +1,43 @@
1
+ {
2
+ "route": "/time_series_missing_files",
3
+ "methods": [
4
+ "POST"
5
+ ],
6
+ "type": "object",
7
+ "properties": {
8
+ "filename": {
9
+ "type": "string",
10
+ "minLength": 1
11
+ }
12
+ },
13
+ "required": [
14
+ "filename"
15
+ ],
16
+ "additionalProperties": false,
17
+ "response": {
18
+ "type": "object",
19
+ "properties": {
20
+ "has_missing_files": {
21
+ "type": "boolean"
22
+ },
23
+ "mandatory_files": {
24
+ "type": "array",
25
+ "items": {
26
+ "type": "string"
27
+ }
28
+ },
29
+ "additional_files": {
30
+ "type": "array",
31
+ "items": {
32
+ "type": "string"
33
+ }
34
+ }
35
+ },
36
+ "required": [
37
+ "has_missing_files",
38
+ "mandatory_files",
39
+ "additional_files"
40
+ ],
41
+ "additionalProperties": false
42
+ }
43
+ }
@@ -0,0 +1,33 @@
1
+ from pathlib import Path
2
+ from opengeodeweb_microservice.schemas import Route, load_schema
3
+ from typing import List
4
+ from dataclasses_json import DataClassJsonMixin
5
+ from opengeodeweb_microservice.schemas import print_dataclass
6
+ from dataclasses import dataclass
7
+
8
+
9
+ @dataclass
10
+ class TimeSeriesMissingFiles(DataClassJsonMixin):
11
+ def __post_init__(self) -> None:
12
+ print_dataclass(self)
13
+
14
+ filename: str
15
+
16
+
17
+ @dataclass
18
+ class TimeSeriesMissingFilesResponse(DataClassJsonMixin):
19
+ def __post_init__(self) -> None:
20
+ print_dataclass(self)
21
+
22
+ additional_files: List[str]
23
+ has_missing_files: bool
24
+ mandatory_files: List[str]
25
+
26
+
27
+ time_series_missing_files_route = Route(
28
+ schema=load_schema(Path(__file__)),
29
+ params=TimeSeriesMissingFiles,
30
+ response=TimeSeriesMissingFilesResponse,
31
+ )
32
+
33
+ __all__ = ["TimeSeriesMissingFiles", "TimeSeriesMissingFilesResponse", "time_series_missing_files_route"]
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
  # Standard library imports
4
4
  import json
5
5
  import os
6
+ import shutil
6
7
  import sqlite3
7
8
  import zipfile
8
9
  from pathlib import Path
@@ -50,10 +51,13 @@ def test_allowed_files(client: FlaskClient) -> None:
50
51
  json = get_full_data()
51
52
  response = client.post(route, json=json)
52
53
  assert response.status_code == 200
53
- extensions = response.get_json()["extensions"]
54
+ extensions = response.get_json()["data_extensions"]
54
55
  assert type(extensions) is list
55
56
  for extension in extensions:
56
57
  assert type(extension) is str
58
+ time_series_extensions = response.get_json()["time_series_extensions"]
59
+ assert "pvd" in time_series_extensions
60
+ assert "pvd" not in extensions
57
61
 
58
62
  # Test all params
59
63
  test_utils.test_route_wrong_params(client, route, get_full_data)
@@ -166,8 +170,8 @@ def test_upload_file_chunked_invalid_chunk_index(client: FlaskClient) -> None:
166
170
  assert response.status_code == 400
167
171
 
168
172
 
169
- def test_missing_files(client: FlaskClient) -> None:
170
- route = "/opengeodeweb_back/missing_files"
173
+ def test_data_missing_files(client: FlaskClient) -> None:
174
+ route = "/opengeodeweb_back/data_missing_files"
171
175
 
172
176
  def get_full_data() -> test_utils.JsonData:
173
177
  return {
@@ -886,3 +890,113 @@ def test_vertex_attribute_names_time_series(client: FlaskClient) -> None:
886
890
  ]
887
891
  assert len(temperature) == 1
888
892
  assert temperature[0]["time_steps"] == [0.5, 1.0, 2.0]
893
+
894
+
895
+ def test_upload_file_relative_path(client: FlaskClient) -> None:
896
+ upload_folder = Path(client.application.config["UPLOAD_FOLDER_PATH"]).resolve()
897
+ try:
898
+ for filename, expected in [
899
+ ("nested_upload/sub/file.txt", upload_folder / "nested_upload/sub/file.txt"),
900
+ ("../nested_upload/escape.txt", upload_folder / "nested_upload/escape.txt"),
901
+ ]:
902
+ response = client.put(
903
+ "/opengeodeweb_back/upload_file",
904
+ query_string={"filename": filename},
905
+ data=b"content",
906
+ )
907
+ assert response.status_code == 201
908
+ assert expected.is_file()
909
+ finally:
910
+ shutil.rmtree(upload_folder / "nested_upload", ignore_errors=True)
911
+
912
+
913
+ def test_time_series_missing_files(client: FlaskClient) -> None:
914
+ upload_folder = Path(client.application.config["UPLOAD_FOLDER_PATH"]).resolve()
915
+ try:
916
+ response = client.put(
917
+ "/opengeodeweb_back/upload_file",
918
+ query_string={"filename": "lonely_time_series/time_series.pvd"},
919
+ data=(data_dir / "time_series" / "time_series.pvd").read_bytes(),
920
+ )
921
+ assert response.status_code == 201
922
+ response = client.post(
923
+ "/opengeodeweb_back/time_series_missing_files",
924
+ json={"filename": "lonely_time_series/time_series.pvd"},
925
+ )
926
+ assert response.status_code == 200
927
+ assert response.get_json()["has_missing_files"] is True
928
+ assert "vtkOutput/000000.vtm" in response.get_json()["mandatory_files"]
929
+ finally:
930
+ shutil.rmtree(upload_folder / "lonely_time_series", ignore_errors=True)
931
+
932
+
933
+ def test_time_series_missing_files_outside_folder(client: FlaskClient) -> None:
934
+ upload_folder = Path(client.application.config["UPLOAD_FOLDER_PATH"]).resolve()
935
+ pvd = (
936
+ '<?xml version="1.0"?>\n<VTKFile type="Collection" version="0.1"><Collection>'
937
+ '<DataSet timestep="0" file="../000000.vtm" /></Collection></VTKFile>\n'
938
+ )
939
+ try:
940
+ response = client.put(
941
+ "/opengeodeweb_back/upload_file",
942
+ query_string={"filename": "outside_time_series/sub/time_series.pvd"},
943
+ data=pvd.encode(),
944
+ )
945
+ assert response.status_code == 201
946
+ response = client.post(
947
+ "/opengeodeweb_back/time_series_missing_files",
948
+ json={"filename": "outside_time_series/sub/time_series.pvd"},
949
+ )
950
+ assert response.status_code == 400
951
+ finally:
952
+ shutil.rmtree(upload_folder / "outside_time_series", ignore_errors=True)
953
+
954
+
955
+ def test_time_series_allowed_objects(client: FlaskClient) -> None:
956
+ route = "/opengeodeweb_back/time_series_allowed_objects"
957
+
958
+ def get_full_data() -> test_utils.JsonData:
959
+ return {"filename": "time_series/time_series.pvd"}
960
+
961
+ response = client.post(route, json=get_full_data())
962
+ assert response.status_code == 200
963
+ allowed_objects = response.get_json()["allowed_objects"]
964
+ assert allowed_objects == ["BRep"]
965
+
966
+ test_utils.test_route_wrong_params(client, route, get_full_data)
967
+
968
+
969
+ def test_apply_time_series(client: FlaskClient) -> None:
970
+ route = "/opengeodeweb_back/apply_time_series"
971
+ model_id, by_type = _load_brep_components(client)
972
+
973
+ def get_full_data() -> test_utils.JsonData:
974
+ return {"id": model_id, "filename": "time_series/time_series.pvd"}
975
+
976
+ response = client.post(route, json=get_full_data())
977
+ assert response.status_code == 200
978
+
979
+ response = client.post(
980
+ "/opengeodeweb_back/model_component_polyhedron_attribute_names",
981
+ json={"id": model_id, "component_ids": by_type["Block"]},
982
+ )
983
+ pressure = [
984
+ attribute
985
+ for attribute in response.get_json()["attributes"]
986
+ if attribute["attribute_name"] == "pressure"
987
+ ]
988
+ assert len(pressure) == 1
989
+ assert pressure[0]["time_steps"] == [0.0, 1.0]
990
+
991
+ test_utils.test_route_wrong_params(client, route, get_full_data)
992
+
993
+
994
+ def test_apply_time_series_errors(client: FlaskClient) -> None:
995
+ route = "/opengeodeweb_back/apply_time_series"
996
+ filename = "time_series/time_series.pvd"
997
+ response = client.post(route, json={"id": "0" * 32, "filename": filename})
998
+ assert response.status_code == 404
999
+
1000
+ point_set_id = check_save_viewable_file(client, "PointSet3D", "test.og_pts3d").get_json()["id"]
1001
+ response = client.post(route, json={"id": point_set_id, "filename": filename})
1002
+ assert response.status_code == 400