OpenGeodeWeb-Back 6.12.6__tar.gz → 6.13.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.
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/PKG-INFO +1 -1
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/pyproject.toml +1 -1
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/OpenGeodeWeb_Back.egg-info/PKG-INFO +1 -1
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/blueprint_routes.py +96 -33
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/tests/test_routes.py +152 -7
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/LICENSE +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/README.md +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/requirements.txt +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/setup.cfg +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/OpenGeodeWeb_Back.egg-info/SOURCES.txt +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/OpenGeodeWeb_Back.egg-info/dependency_links.txt +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/OpenGeodeWeb_Back.egg-info/entry_points.txt +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/OpenGeodeWeb_Back.egg-info/requires.txt +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/OpenGeodeWeb_Back.egg-info/top_level.txt +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/__init__.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/app.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/app_config.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_functions.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/__init__.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/geode_brep.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/geode_cross_section.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/geode_edged_curve2d.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/geode_edged_curve3d.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/geode_graph.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/geode_grid2d.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/geode_grid3d.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/geode_horizon_stack3d.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/geode_hybrid_solid3d.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/geode_implicit_cross_section.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/geode_implicit_structural_model.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/geode_light_regular_grid2d.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/geode_light_regular_grid3d.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/geode_mesh.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/geode_model.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/geode_object.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/geode_point_set2d.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/geode_point_set3d.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/geode_polygonal_surface2d.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/geode_polygonal_surface3d.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/geode_polyhedral_solid3d.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/geode_raster_image2d.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/geode_raster_image3d.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/geode_regular_grid2d.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/geode_regular_grid3d.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/geode_section.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/geode_solid_mesh3d.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/geode_structural_model.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/geode_surface_mesh2d.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/geode_surface_mesh3d.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/geode_tetrahedral_solid3d.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/geode_triangulated_surface2d.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/geode_triangulated_surface3d.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_objects/geode_vertex_set.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/py.typed +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/create/blueprint_create.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/create/schemas/__init__.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/create/schemas/edged_curve.json +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/create/schemas/edged_curve.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/create/schemas/point_set.json +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/create/schemas/point_set.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/create/schemas/polygonal_surface.json +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/create/schemas/polygonal_surface.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/__init__.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/allowed_files.json +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/allowed_files.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/allowed_objects.json +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/allowed_objects.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/cell_attribute_names.json +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/cell_attribute_names.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/edge_attribute_names.json +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/edge_attribute_names.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/export_project.json +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/export_project.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/geode_object_inheritance.json +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/geode_object_inheritance.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/geode_objects_and_output_extensions.json +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/geode_objects_and_output_extensions.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/geographic_coordinate_systems.json +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/geographic_coordinate_systems.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/import_project.json +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/import_project.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/kill.json +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/kill.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/missing_files.json +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/missing_files.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/model_component_edge_attribute_names.json +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/model_component_edge_attribute_names.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/model_component_polygon_attribute_names.json +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/model_component_polygon_attribute_names.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/model_component_polyhedron_attribute_names.json +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/model_component_polyhedron_attribute_names.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/model_component_vertex_attribute_names.json +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/model_component_vertex_attribute_names.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/ping.json +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/ping.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/polygon_attribute_names.json +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/polygon_attribute_names.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/polyhedron_attribute_names.json +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/polyhedron_attribute_names.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/save_viewable_file.json +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/save_viewable_file.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/texture_coordinates.json +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/texture_coordinates.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/upload_file.json +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/upload_file.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/validate.json +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/validate.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/vertex_attribute_names.json +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/vertex_attribute_names.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/test_utils.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/utils_functions.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/tests/test_create_routes.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/tests/test_geode_functions.py +0 -0
- {opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.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.
|
|
3
|
+
Version: 6.13.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.
|
|
7
|
+
version = "6.13.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"
|
{opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/OpenGeodeWeb_Back.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: OpenGeodeWeb-Back
|
|
3
|
-
Version: 6.
|
|
3
|
+
Version: 6.13.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,6 +4,7 @@ import os
|
|
|
4
4
|
import time
|
|
5
5
|
import shutil
|
|
6
6
|
import math
|
|
7
|
+
import typing
|
|
7
8
|
from threading import Timer
|
|
8
9
|
|
|
9
10
|
# Third party imports
|
|
@@ -63,6 +64,18 @@ def allowed_files() -> flask.Response:
|
|
|
63
64
|
return flask.make_response({"extensions": list(extensions)}, 200)
|
|
64
65
|
|
|
65
66
|
|
|
67
|
+
def _write_stream(path: str, stream: typing.IO[bytes], mode: str = "wb") -> None:
|
|
68
|
+
read_buffer_size = 1024 * 1024
|
|
69
|
+
with open(path, mode) as destination:
|
|
70
|
+
while chunk := stream.read(read_buffer_size):
|
|
71
|
+
destination.write(chunk)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _finalize_upload(filename: str) -> flask.Response:
|
|
75
|
+
print(f"{filename=}", flush=True)
|
|
76
|
+
return flask.make_response({"message": "File uploaded"}, 201)
|
|
77
|
+
|
|
78
|
+
|
|
66
79
|
@routes.route(
|
|
67
80
|
schemas_dict["upload_file"]["route"],
|
|
68
81
|
methods=schemas_dict["upload_file"]["methods"],
|
|
@@ -73,18 +86,43 @@ def upload_file() -> flask.Response:
|
|
|
73
86
|
if not os.path.exists(UPLOAD_FOLDER_PATH):
|
|
74
87
|
os.makedirs(UPLOAD_FOLDER_PATH, exist_ok=True)
|
|
75
88
|
|
|
76
|
-
|
|
77
|
-
|
|
89
|
+
# Multipart callers (e.g. Vease) still send the whole file as a "file" form
|
|
90
|
+
# part. Everything else PUTs raw bytes with ?filename= as a query param:
|
|
91
|
+
# either the whole file in one request, or one of several chunks (when
|
|
92
|
+
# ?chunk_index=/?total_chunks= are also present) that get appended in
|
|
93
|
+
# order and assembled into the final file once the last one arrives. This
|
|
94
|
+
# keeps every request under cloud hosting's hard request-size limit.
|
|
95
|
+
if flask.request.mimetype == "multipart/form-data":
|
|
96
|
+
file = flask.request.files["file"]
|
|
97
|
+
if file.filename is None:
|
|
98
|
+
flask.abort(400, "Filename is required")
|
|
99
|
+
filename = werkzeug.utils.secure_filename(os.path.basename(file.filename))
|
|
100
|
+
file_path = os.path.join(UPLOAD_FOLDER_PATH, filename)
|
|
101
|
+
file.save(file_path)
|
|
102
|
+
return _finalize_upload(filename)
|
|
103
|
+
|
|
104
|
+
raw_filename = flask.request.args.get("filename")
|
|
105
|
+
if not raw_filename:
|
|
78
106
|
flask.abort(400, "Filename is required")
|
|
79
|
-
filename = werkzeug.utils.secure_filename(os.path.basename(
|
|
80
|
-
print(f"{filename=}", flush=True)
|
|
107
|
+
filename = werkzeug.utils.secure_filename(os.path.basename(raw_filename))
|
|
81
108
|
file_path = os.path.join(UPLOAD_FOLDER_PATH, filename)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
)
|
|
87
|
-
|
|
109
|
+
|
|
110
|
+
total_chunks = flask.request.args.get("total_chunks", type=int)
|
|
111
|
+
if total_chunks is None:
|
|
112
|
+
_write_stream(file_path, flask.request.stream)
|
|
113
|
+
return _finalize_upload(filename)
|
|
114
|
+
|
|
115
|
+
chunk_index = flask.request.args.get("chunk_index", type=int)
|
|
116
|
+
if chunk_index is None or not 0 <= chunk_index < total_chunks:
|
|
117
|
+
flask.abort(400, "Invalid chunk_index")
|
|
118
|
+
|
|
119
|
+
part_path = f"{file_path}.part"
|
|
120
|
+
_write_stream(part_path, flask.request.stream, "wb" if chunk_index == 0 else "ab")
|
|
121
|
+
if chunk_index < total_chunks - 1:
|
|
122
|
+
return flask.make_response({"message": "Chunk received"}, 200)
|
|
123
|
+
|
|
124
|
+
os.replace(part_path, file_path)
|
|
125
|
+
return _finalize_upload(filename)
|
|
88
126
|
|
|
89
127
|
|
|
90
128
|
@routes.route(
|
|
@@ -256,32 +294,54 @@ def extract_valid_attribute_values(
|
|
|
256
294
|
attribute_manager: og.AttributeManager,
|
|
257
295
|
attribute_name: str,
|
|
258
296
|
item_index: int,
|
|
259
|
-
) -> list[float]:
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
return []
|
|
270
|
-
|
|
271
|
-
|
|
297
|
+
) -> tuple[list[float], bool]:
|
|
298
|
+
attribute_ids = attribute_manager.attribute_ids_matching_name(attribute_name)
|
|
299
|
+
if not isinstance(attribute_ids, list):
|
|
300
|
+
return [], False
|
|
301
|
+
attribute = attribute_manager.find_generic_attribute(attribute_ids[0])
|
|
302
|
+
if (
|
|
303
|
+
attribute is None
|
|
304
|
+
or not attribute.is_genericable()
|
|
305
|
+
or not attribute.properties().transferable
|
|
306
|
+
):
|
|
307
|
+
return [], False
|
|
308
|
+
|
|
309
|
+
nb_items = attribute.nb_items()
|
|
310
|
+
default_values = getattr(attribute, "default_values", None)
|
|
311
|
+
no_value = default_values().no_value if default_values else None
|
|
312
|
+
if no_value is None and (
|
|
313
|
+
"Point" in attribute.type() or "Vector" in attribute.type()
|
|
314
|
+
):
|
|
315
|
+
no_value = [0.0] * nb_items
|
|
316
|
+
|
|
317
|
+
value_getter = getattr(
|
|
318
|
+
attribute,
|
|
319
|
+
"value",
|
|
320
|
+
lambda element_index: [
|
|
321
|
+
attribute.generic_item_value(element_index, i) for i in range(nb_items)
|
|
322
|
+
],
|
|
323
|
+
)
|
|
324
|
+
|
|
272
325
|
valid_values: list[float] = []
|
|
326
|
+
has_nan = False
|
|
273
327
|
for element_index in range(attribute_manager.nb_elements()):
|
|
274
|
-
value =
|
|
275
|
-
if
|
|
328
|
+
value = attribute.generic_item_value(element_index, item_index)
|
|
329
|
+
if (
|
|
330
|
+
value is None
|
|
331
|
+
or math.isnan(value)
|
|
332
|
+
or (no_value is not None and value_getter(element_index) == no_value)
|
|
333
|
+
):
|
|
334
|
+
has_nan = True
|
|
335
|
+
else:
|
|
276
336
|
valid_values.append(value)
|
|
277
|
-
return valid_values
|
|
337
|
+
return valid_values, has_nan
|
|
278
338
|
|
|
279
339
|
|
|
280
340
|
def attributes_metadata(
|
|
281
341
|
manager: og.AttributeManager | list[og.AttributeManager],
|
|
282
|
-
) -> list[dict[str, str | int | float | list[float]]]:
|
|
342
|
+
) -> list[dict[str, str | int | float | bool | list[float]]]:
|
|
283
343
|
attribute_managers = manager if isinstance(manager, list) else [manager]
|
|
284
|
-
attributes: list[dict[str, str | int | float | list[float]]] = []
|
|
344
|
+
attributes: list[dict[str, str | int | float | bool | list[float]]] = []
|
|
285
345
|
first_manager = attribute_managers[0]
|
|
286
346
|
for id in first_manager.attribute_ids():
|
|
287
347
|
attribute = first_manager.find_generic_attribute(id)
|
|
@@ -290,18 +350,20 @@ def attributes_metadata(
|
|
|
290
350
|
attribute_name = attribute.name()
|
|
291
351
|
if attribute_name is None:
|
|
292
352
|
continue
|
|
293
|
-
if not attribute.is_genericable():
|
|
353
|
+
if not attribute.is_genericable() or not attribute.properties().transferable:
|
|
294
354
|
continue
|
|
295
355
|
nb_items = attribute.nb_items()
|
|
296
356
|
min_values, max_values = [], []
|
|
357
|
+
attribute_has_nan = False
|
|
297
358
|
for item_index in range(nb_items):
|
|
298
359
|
valid_values: list[float] = []
|
|
299
360
|
for attribute_manager in attribute_managers:
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
attribute_manager, attribute_name, item_index
|
|
303
|
-
)
|
|
361
|
+
extracted_values, has_nan = extract_valid_attribute_values(
|
|
362
|
+
attribute_manager, attribute_name, item_index
|
|
304
363
|
)
|
|
364
|
+
valid_values.extend(extracted_values)
|
|
365
|
+
if has_nan:
|
|
366
|
+
attribute_has_nan = True
|
|
305
367
|
if valid_values:
|
|
306
368
|
min_values.append(min(valid_values))
|
|
307
369
|
max_values.append(max(valid_values))
|
|
@@ -316,6 +378,7 @@ def attributes_metadata(
|
|
|
316
378
|
"max_value": max(max_values),
|
|
317
379
|
"min_values": min_values,
|
|
318
380
|
"max_values": max_values,
|
|
381
|
+
"no_data": attribute_has_nan,
|
|
319
382
|
}
|
|
320
383
|
)
|
|
321
384
|
return attributes
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import os
|
|
3
3
|
|
|
4
4
|
# Third party imports
|
|
5
|
+
import opengeode as og
|
|
5
6
|
from werkzeug.datastructures import FileStorage
|
|
6
7
|
from flask.testing import FlaskClient
|
|
7
8
|
from werkzeug.test import TestResponse
|
|
@@ -13,6 +14,7 @@ import zipfile
|
|
|
13
14
|
from opengeodeweb_microservice.database.data import Data
|
|
14
15
|
from opengeodeweb_microservice.database.connection import get_session
|
|
15
16
|
from opengeodeweb_back import geode_functions, test_utils
|
|
17
|
+
from opengeodeweb_back.routes.blueprint_routes import extract_valid_attribute_values
|
|
16
18
|
from opengeodeweb_back.geode_objects.geode_polygonal_surface3d import (
|
|
17
19
|
GeodePolygonalSurface3D,
|
|
18
20
|
)
|
|
@@ -79,6 +81,90 @@ def test_upload_file(client: FlaskClient, filename: str = "test.og_brep") -> Non
|
|
|
79
81
|
assert response.status_code == 201
|
|
80
82
|
|
|
81
83
|
|
|
84
|
+
def test_upload_file_raw(client: FlaskClient, filename: str = "test.og_brep") -> None:
|
|
85
|
+
file = os.path.join(data_dir, filename)
|
|
86
|
+
with open(file, "rb") as opened_file:
|
|
87
|
+
file_bytes = opened_file.read()
|
|
88
|
+
|
|
89
|
+
raw_filename = "raw_upload_test.og_brep"
|
|
90
|
+
response = client.put(
|
|
91
|
+
f"/opengeodeweb_back/upload_file?filename={raw_filename}",
|
|
92
|
+
data=file_bytes,
|
|
93
|
+
content_type="application/octet-stream",
|
|
94
|
+
)
|
|
95
|
+
assert response.status_code == 201
|
|
96
|
+
|
|
97
|
+
uploaded_path = os.path.join(data_dir, raw_filename)
|
|
98
|
+
try:
|
|
99
|
+
with open(uploaded_path, "rb") as uploaded_file:
|
|
100
|
+
assert uploaded_file.read() == file_bytes
|
|
101
|
+
finally:
|
|
102
|
+
os.remove(uploaded_path)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def test_upload_file_raw_missing_filename(client: FlaskClient) -> None:
|
|
106
|
+
response = client.put(
|
|
107
|
+
f"/opengeodeweb_back/upload_file",
|
|
108
|
+
data=b"some raw bytes",
|
|
109
|
+
content_type="application/octet-stream",
|
|
110
|
+
)
|
|
111
|
+
assert response.status_code == 400
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def test_upload_file_chunked(
|
|
115
|
+
client: FlaskClient, filename: str = "test.og_brep"
|
|
116
|
+
) -> None:
|
|
117
|
+
file = os.path.join(data_dir, filename)
|
|
118
|
+
with open(file, "rb") as opened_file:
|
|
119
|
+
file_bytes = opened_file.read()
|
|
120
|
+
|
|
121
|
+
chunk_filename = "chunked_upload_test.og_brep"
|
|
122
|
+
chunk_size = max(1, len(file_bytes) // 3)
|
|
123
|
+
chunks = [
|
|
124
|
+
file_bytes[index : index + chunk_size]
|
|
125
|
+
for index in range(0, len(file_bytes), chunk_size)
|
|
126
|
+
]
|
|
127
|
+
total_chunks = len(chunks)
|
|
128
|
+
|
|
129
|
+
uploaded_path = os.path.join(data_dir, chunk_filename)
|
|
130
|
+
try:
|
|
131
|
+
for chunk_index, chunk in enumerate(chunks):
|
|
132
|
+
response = client.put(
|
|
133
|
+
f"/opengeodeweb_back/upload_file"
|
|
134
|
+
f"?filename={chunk_filename}"
|
|
135
|
+
f"&chunk_index={chunk_index}"
|
|
136
|
+
f"&total_chunks={total_chunks}",
|
|
137
|
+
data=chunk,
|
|
138
|
+
content_type="application/octet-stream",
|
|
139
|
+
)
|
|
140
|
+
if chunk_index < total_chunks - 1:
|
|
141
|
+
assert response.status_code == 200
|
|
142
|
+
assert not os.path.exists(uploaded_path)
|
|
143
|
+
else:
|
|
144
|
+
assert response.status_code == 201
|
|
145
|
+
|
|
146
|
+
with open(uploaded_path, "rb") as uploaded_file:
|
|
147
|
+
assert uploaded_file.read() == file_bytes
|
|
148
|
+
finally:
|
|
149
|
+
if os.path.exists(uploaded_path):
|
|
150
|
+
os.remove(uploaded_path)
|
|
151
|
+
part_path = f"{uploaded_path}.part"
|
|
152
|
+
if os.path.exists(part_path):
|
|
153
|
+
os.remove(part_path)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def test_upload_file_chunked_invalid_chunk_index(client: FlaskClient) -> None:
|
|
157
|
+
response = client.put(
|
|
158
|
+
"/opengeodeweb_back/upload_file"
|
|
159
|
+
"?filename=invalid_chunk_index.og_brep"
|
|
160
|
+
"&chunk_index=2"
|
|
161
|
+
"&total_chunks=2",
|
|
162
|
+
data=b"some raw bytes",
|
|
163
|
+
content_type="application/octet-stream",
|
|
164
|
+
)
|
|
165
|
+
assert response.status_code == 400
|
|
166
|
+
|
|
167
|
+
|
|
82
168
|
def test_missing_files(client: FlaskClient) -> None:
|
|
83
169
|
route = f"/opengeodeweb_back/missing_files"
|
|
84
170
|
|
|
@@ -261,6 +347,8 @@ def test_vertex_attribute_names(client: FlaskClient, test_id: str) -> None:
|
|
|
261
347
|
assert "nb_items" in attribute
|
|
262
348
|
assert "min_values" in attribute
|
|
263
349
|
assert "max_values" in attribute
|
|
350
|
+
assert "no_data" in attribute
|
|
351
|
+
assert isinstance(attribute["no_data"], bool)
|
|
264
352
|
print(
|
|
265
353
|
f"[ATTRIBUTES]: ",
|
|
266
354
|
[attribute["nb_items"] for attribute in attributes],
|
|
@@ -297,6 +385,8 @@ def test_cell_attribute_names(client: FlaskClient, test_id: str) -> None:
|
|
|
297
385
|
assert "nb_items" in attribute
|
|
298
386
|
assert "min_values" in attribute
|
|
299
387
|
assert "max_values" in attribute
|
|
388
|
+
assert "no_data" in attribute
|
|
389
|
+
assert isinstance(attribute["no_data"], bool)
|
|
300
390
|
print(
|
|
301
391
|
f"[ATTRIBUTES]: ",
|
|
302
392
|
[attribute["nb_items"] for attribute in attributes],
|
|
@@ -333,6 +423,8 @@ def test_polygon_attribute_names(client: FlaskClient, test_id: str) -> None:
|
|
|
333
423
|
assert "nb_items" in attribute
|
|
334
424
|
assert "min_values" in attribute
|
|
335
425
|
assert "max_values" in attribute
|
|
426
|
+
assert "no_data" in attribute
|
|
427
|
+
assert isinstance(attribute["no_data"], bool)
|
|
336
428
|
print(
|
|
337
429
|
f"[ATTRIBUTES]: ",
|
|
338
430
|
[attribute["nb_items"] for attribute in attributes],
|
|
@@ -370,6 +462,8 @@ def test_polyhedron_attribute_names(client: FlaskClient, test_id: str) -> None:
|
|
|
370
462
|
assert "nb_items" in attribute
|
|
371
463
|
assert "min_values" in attribute
|
|
372
464
|
assert "max_values" in attribute
|
|
465
|
+
assert "no_data" in attribute
|
|
466
|
+
assert isinstance(attribute["no_data"], bool)
|
|
373
467
|
if attribute["attribute_name"] == "Range":
|
|
374
468
|
assert attribute["min_value"] == 0.0
|
|
375
469
|
assert attribute["max_value"] == 579.0
|
|
@@ -411,6 +505,8 @@ def test_edge_attribute_names(client: FlaskClient, test_id: str) -> None:
|
|
|
411
505
|
assert "nb_items" in attribute
|
|
412
506
|
assert "min_values" in attribute
|
|
413
507
|
assert "max_values" in attribute
|
|
508
|
+
assert "no_data" in attribute
|
|
509
|
+
assert isinstance(attribute["no_data"], bool)
|
|
414
510
|
print(
|
|
415
511
|
f"[ATTRIBUTES]: ",
|
|
416
512
|
[attribute["nb_items"] for attribute in attributes],
|
|
@@ -485,7 +581,7 @@ def test_model_components(client: FlaskClient) -> None:
|
|
|
485
581
|
assert isinstance(mesh_components, list)
|
|
486
582
|
assert len(mesh_components) > 0
|
|
487
583
|
for mesh_component in mesh_components:
|
|
488
|
-
assert isinstance(mesh_component,
|
|
584
|
+
assert isinstance(mesh_component, dict)
|
|
489
585
|
assert isinstance(mesh_component["geode_id"], str)
|
|
490
586
|
assert isinstance(mesh_component["viewer_id"], int)
|
|
491
587
|
assert isinstance(mesh_component["name"], str)
|
|
@@ -501,7 +597,7 @@ def test_model_components(client: FlaskClient) -> None:
|
|
|
501
597
|
collection_components = response.get_json()["collection_components"]
|
|
502
598
|
assert isinstance(collection_components, list)
|
|
503
599
|
for collection_component in collection_components:
|
|
504
|
-
assert isinstance(collection_component,
|
|
600
|
+
assert isinstance(collection_component, dict)
|
|
505
601
|
assert isinstance(collection_component["geode_id"], str)
|
|
506
602
|
assert isinstance(collection_component["name"], str)
|
|
507
603
|
assert isinstance(collection_component["items"], list)
|
|
@@ -649,15 +745,18 @@ def _load_brep_components(client: FlaskClient) -> tuple[str, dict[str, list[str]
|
|
|
649
745
|
response = test_save_viewable_file(client, "BRep", "cube.og_brep")
|
|
650
746
|
assert response.status_code == 200
|
|
651
747
|
model_id: str = response.get_json()["id"]
|
|
652
|
-
mesh_components: list[dict] = response.get_json()["mesh_components"]
|
|
748
|
+
mesh_components: list[dict[str, object]] = response.get_json()["mesh_components"]
|
|
653
749
|
by_type: dict[str, list[str]] = {}
|
|
654
|
-
for
|
|
655
|
-
component_type =
|
|
656
|
-
|
|
750
|
+
for mesh_component in mesh_components:
|
|
751
|
+
component_type = mesh_component["type"]
|
|
752
|
+
geode_id = mesh_component["geode_id"]
|
|
753
|
+
assert isinstance(component_type, str)
|
|
754
|
+
assert isinstance(geode_id, str)
|
|
755
|
+
by_type.setdefault(component_type, []).append(geode_id)
|
|
657
756
|
return model_id, by_type
|
|
658
757
|
|
|
659
758
|
|
|
660
|
-
def _assert_attributes_response(response) -> None:
|
|
759
|
+
def _assert_attributes_response(response: TestResponse) -> None:
|
|
661
760
|
assert response.status_code == 200
|
|
662
761
|
attributes = response.get_json()["attributes"]
|
|
663
762
|
assert isinstance(attributes, list)
|
|
@@ -668,6 +767,8 @@ def _assert_attributes_response(response) -> None:
|
|
|
668
767
|
assert "nb_items" in attribute
|
|
669
768
|
assert "min_values" in attribute
|
|
670
769
|
assert "max_values" in attribute
|
|
770
|
+
assert "no_data" in attribute
|
|
771
|
+
assert isinstance(attribute["no_data"], bool)
|
|
671
772
|
|
|
672
773
|
|
|
673
774
|
def test_model_component_vertex_attribute_names(client: FlaskClient) -> None:
|
|
@@ -732,3 +833,47 @@ def test_model_component_polyhedron_attribute_names(client: FlaskClient) -> None
|
|
|
732
833
|
return {"id": model_id, "component_ids": block_ids}
|
|
733
834
|
|
|
734
835
|
test_utils.test_route_wrong_params(client, route, get_full_data)
|
|
836
|
+
|
|
837
|
+
|
|
838
|
+
def test_extract_valid_attribute_values_with_sentinel_no_value() -> None:
|
|
839
|
+
mesh = og.TriangulatedSurface3D.create()
|
|
840
|
+
builder = og.TriangulatedSurfaceBuilder3D.create(mesh)
|
|
841
|
+
vertex_0 = builder.create_point(og.Point3D([0, 0, 0]))
|
|
842
|
+
vertex_1 = builder.create_point(og.Point3D([1, 0, 0]))
|
|
843
|
+
builder.create_triangle([vertex_0, vertex_1, vertex_0])
|
|
844
|
+
|
|
845
|
+
attribute_manager = mesh.vertex_attribute_manager()
|
|
846
|
+
values_config = og.AttributeValuesDouble()
|
|
847
|
+
values_config.no_value = -999.0
|
|
848
|
+
attribute_id = attribute_manager.create_attribute_variable_double(
|
|
849
|
+
"variable_double", values_config, og.AttributeProperties()
|
|
850
|
+
)
|
|
851
|
+
attribute = attribute_manager.find_attribute_variable_double(attribute_id)
|
|
852
|
+
attribute.set_value(vertex_0, -999.0)
|
|
853
|
+
attribute.set_value(vertex_1, 42.0)
|
|
854
|
+
|
|
855
|
+
valid_values, has_nan = extract_valid_attribute_values(
|
|
856
|
+
attribute_manager, "variable_double", 0
|
|
857
|
+
)
|
|
858
|
+
assert has_nan is True
|
|
859
|
+
assert valid_values == [42.0]
|
|
860
|
+
|
|
861
|
+
|
|
862
|
+
def test_extract_valid_attribute_values_with_non_transferable_attribute() -> None:
|
|
863
|
+
mesh = og.TriangulatedSurface3D.create()
|
|
864
|
+
builder = og.TriangulatedSurfaceBuilder3D.create(mesh)
|
|
865
|
+
vertex_0 = builder.create_point(og.Point3D([0, 0, 0]))
|
|
866
|
+
builder.create_triangle([vertex_0, vertex_0, vertex_0])
|
|
867
|
+
|
|
868
|
+
attribute_manager = mesh.vertex_attribute_manager()
|
|
869
|
+
properties = og.AttributeProperties()
|
|
870
|
+
properties.transferable = False
|
|
871
|
+
attribute_manager.create_attribute_variable_double(
|
|
872
|
+
"internal_attribute", og.AttributeValuesDouble(), properties
|
|
873
|
+
)
|
|
874
|
+
|
|
875
|
+
valid_values, has_nan = extract_valid_attribute_values(
|
|
876
|
+
attribute_manager, "internal_attribute", 0
|
|
877
|
+
)
|
|
878
|
+
assert has_nan is False
|
|
879
|
+
assert valid_values == []
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/OpenGeodeWeb_Back.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/OpenGeodeWeb_Back.egg-info/requires.txt
RENAMED
|
File without changes
|
{opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/OpenGeodeWeb_Back.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/geode_functions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/kill.json
RENAMED
|
File without changes
|
{opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/kill.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/ping.json
RENAMED
|
File without changes
|
{opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/routes/schemas/ping.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{opengeodeweb_back-6.12.6 → opengeodeweb_back-6.13.0}/src/opengeodeweb_back/utils_functions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|