OpenGeodeWeb-Back 5.10.0rc22__py3-none-any.whl → 5.14.1__py3-none-any.whl

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 (91) hide show
  1. opengeodeweb_back/app.py +173 -0
  2. opengeodeweb_back/app_config.py +1 -10
  3. opengeodeweb_back/geode_functions.py +44 -283
  4. opengeodeweb_back/geode_objects/__init__.py +61 -0
  5. opengeodeweb_back/geode_objects/geode_brep.py +102 -0
  6. opengeodeweb_back/geode_objects/geode_cross_section.py +75 -0
  7. opengeodeweb_back/geode_objects/geode_edged_curve2d.py +107 -0
  8. opengeodeweb_back/geode_objects/geode_edged_curve3d.py +107 -0
  9. opengeodeweb_back/geode_objects/geode_graph.py +76 -0
  10. opengeodeweb_back/geode_objects/geode_grid2d.py +30 -0
  11. opengeodeweb_back/geode_objects/geode_grid3d.py +30 -0
  12. opengeodeweb_back/geode_objects/geode_hybrid_solid3d.py +71 -0
  13. opengeodeweb_back/geode_objects/geode_implicit_cross_section.py +81 -0
  14. opengeodeweb_back/geode_objects/geode_implicit_structural_model.py +85 -0
  15. opengeodeweb_back/geode_objects/geode_light_regular_grid2d.py +72 -0
  16. opengeodeweb_back/geode_objects/geode_light_regular_grid3d.py +72 -0
  17. opengeodeweb_back/geode_objects/geode_mesh.py +19 -0
  18. opengeodeweb_back/geode_objects/geode_model.py +22 -0
  19. opengeodeweb_back/geode_objects/geode_object.py +78 -0
  20. opengeodeweb_back/geode_objects/geode_point_set2d.py +105 -0
  21. opengeodeweb_back/geode_objects/geode_point_set3d.py +105 -0
  22. opengeodeweb_back/geode_objects/geode_polygonal_surface2d.py +73 -0
  23. opengeodeweb_back/geode_objects/geode_polygonal_surface3d.py +73 -0
  24. opengeodeweb_back/geode_objects/geode_polyhedral_solid3d.py +73 -0
  25. opengeodeweb_back/geode_objects/geode_raster_image2d.py +83 -0
  26. opengeodeweb_back/geode_objects/geode_raster_image3d.py +83 -0
  27. opengeodeweb_back/geode_objects/geode_regular_grid2d.py +78 -0
  28. opengeodeweb_back/geode_objects/geode_regular_grid3d.py +78 -0
  29. opengeodeweb_back/geode_objects/geode_section.py +106 -0
  30. opengeodeweb_back/geode_objects/geode_solid_mesh3d.py +61 -0
  31. opengeodeweb_back/geode_objects/geode_structural_model.py +78 -0
  32. opengeodeweb_back/geode_objects/geode_surface_mesh2d.py +66 -0
  33. opengeodeweb_back/geode_objects/geode_surface_mesh3d.py +66 -0
  34. opengeodeweb_back/geode_objects/geode_tetrahedral_solid3d.py +73 -0
  35. opengeodeweb_back/geode_objects/geode_triangulated_surface2d.py +77 -0
  36. opengeodeweb_back/geode_objects/geode_triangulated_surface3d.py +77 -0
  37. opengeodeweb_back/geode_objects/geode_vertex_set.py +81 -0
  38. opengeodeweb_back/geode_objects/types.py +75 -0
  39. opengeodeweb_back/routes/blueprint_routes.py +352 -231
  40. opengeodeweb_back/routes/create/blueprint_create.py +121 -0
  41. opengeodeweb_back/routes/create/schemas/__init__.py +3 -0
  42. opengeodeweb_back/routes/create/schemas/create_aoi.json +45 -0
  43. opengeodeweb_back/routes/create/schemas/create_aoi.py +25 -0
  44. opengeodeweb_back/routes/create/schemas/create_point.json +29 -0
  45. opengeodeweb_back/routes/create/schemas/create_point.py +13 -0
  46. opengeodeweb_back/routes/create/schemas/create_voi.json +36 -0
  47. opengeodeweb_back/routes/create/schemas/create_voi.py +24 -0
  48. opengeodeweb_back/routes/models/blueprint_models.py +27 -27
  49. opengeodeweb_back/routes/models/schemas/__init__.py +2 -0
  50. opengeodeweb_back/routes/models/schemas/mesh_components.py +10 -0
  51. opengeodeweb_back/routes/models/schemas/vtm_component_indices.py +10 -0
  52. opengeodeweb_back/routes/schemas/__init__.py +17 -0
  53. opengeodeweb_back/routes/schemas/allowed_files.json +6 -8
  54. opengeodeweb_back/routes/schemas/allowed_files.py +10 -0
  55. opengeodeweb_back/routes/schemas/allowed_objects.json +1 -8
  56. opengeodeweb_back/routes/schemas/allowed_objects.py +10 -0
  57. opengeodeweb_back/routes/schemas/cell_attribute_names.json +13 -0
  58. opengeodeweb_back/routes/schemas/cell_attribute_names.py +10 -0
  59. opengeodeweb_back/routes/schemas/export_project.json +21 -0
  60. opengeodeweb_back/routes/schemas/export_project.py +12 -0
  61. opengeodeweb_back/routes/schemas/geode_objects_and_output_extensions.json +2 -2
  62. opengeodeweb_back/routes/schemas/geode_objects_and_output_extensions.py +11 -0
  63. opengeodeweb_back/routes/schemas/geographic_coordinate_systems.json +2 -2
  64. opengeodeweb_back/routes/schemas/geographic_coordinate_systems.py +10 -0
  65. opengeodeweb_back/routes/schemas/import_project.json +10 -0
  66. opengeodeweb_back/routes/schemas/import_project.py +10 -0
  67. opengeodeweb_back/routes/schemas/inspect_file.json +2 -2
  68. opengeodeweb_back/routes/schemas/inspect_file.py +11 -0
  69. opengeodeweb_back/routes/schemas/kill.json +10 -0
  70. opengeodeweb_back/routes/schemas/kill.py +10 -0
  71. opengeodeweb_back/routes/schemas/missing_files.json +2 -2
  72. opengeodeweb_back/routes/schemas/missing_files.py +11 -0
  73. opengeodeweb_back/routes/schemas/ping.py +10 -0
  74. opengeodeweb_back/routes/schemas/polygon_attribute_names.py +10 -0
  75. opengeodeweb_back/routes/schemas/polyhedron_attribute_names.py +10 -0
  76. opengeodeweb_back/routes/schemas/save_viewable_file.json +2 -2
  77. opengeodeweb_back/routes/schemas/save_viewable_file.py +11 -0
  78. opengeodeweb_back/routes/schemas/texture_coordinates.py +10 -0
  79. opengeodeweb_back/routes/schemas/upload_file.py +11 -0
  80. opengeodeweb_back/routes/schemas/vertex_attribute_names.py +10 -0
  81. opengeodeweb_back/test_utils.py +9 -3
  82. opengeodeweb_back/utils_functions.py +93 -88
  83. {opengeodeweb_back-5.10.0rc22.dist-info → opengeodeweb_back-5.14.1.dist-info}/METADATA +17 -22
  84. opengeodeweb_back-5.14.1.dist-info/RECORD +98 -0
  85. opengeodeweb_back-5.14.1.dist-info/entry_points.txt +2 -0
  86. opengeodeweb_back/geode_objects.py +0 -570
  87. opengeodeweb_back/routes/schemas/create_point.json +0 -29
  88. opengeodeweb_back-5.10.0rc22.dist-info/RECORD +0 -30
  89. {opengeodeweb_back-5.10.0rc22.dist-info → opengeodeweb_back-5.14.1.dist-info}/WHEEL +0 -0
  90. {opengeodeweb_back-5.10.0rc22.dist-info → opengeodeweb_back-5.14.1.dist-info}/licenses/LICENSE +0 -0
  91. {opengeodeweb_back-5.10.0rc22.dist-info → opengeodeweb_back-5.14.1.dist-info}/top_level.txt +0 -0
@@ -1,143 +1,122 @@
1
1
  # Standard library imports
2
- import json
3
2
  import os
4
3
  import time
4
+ import shutil
5
+ from typing import Any
5
6
 
6
7
  # Third party imports
7
8
  import flask
8
- import opengeode
9
9
  import werkzeug
10
+ import zipfile
11
+ import opengeode_geosciences as og_geosciences
12
+ from opengeodeweb_microservice.schemas import get_schemas_dict
13
+ from opengeodeweb_microservice.database.data import Data
14
+ from opengeodeweb_microservice.database.connection import get_session
15
+ from opengeodeweb_microservice.database import connection
10
16
 
11
17
  # Local application imports
12
- from .. import geode_functions, utils_functions
13
-
14
18
  from .models import blueprint_models
19
+ from . import schemas
20
+ from opengeodeweb_back import geode_functions, utils_functions
21
+ from opengeodeweb_back.geode_objects import geode_objects
22
+ from opengeodeweb_back.geode_objects.types import geode_object_type
23
+ from opengeodeweb_back.geode_objects.geode_mesh import GeodeMesh
24
+ from opengeodeweb_back.geode_objects.geode_grid2d import GeodeGrid2D
25
+ from opengeodeweb_back.geode_objects.geode_grid3d import GeodeGrid3D
26
+ from opengeodeweb_back.geode_objects.geode_surface_mesh2d import GeodeSurfaceMesh2D
27
+ from opengeodeweb_back.geode_objects.geode_surface_mesh3d import GeodeSurfaceMesh3D
28
+ from opengeodeweb_back.geode_objects.geode_solid_mesh3d import GeodeSolidMesh3D
15
29
 
16
30
  routes = flask.Blueprint("routes", __name__, url_prefix="/opengeodeweb_back")
17
31
 
18
32
 
19
- @routes.before_request
20
- def before_request():
21
- if "ping" not in flask.request.path:
22
- utils_functions.increment_request_counter(flask.current_app)
23
-
24
-
25
- @routes.teardown_request
26
- def teardown_request(exception):
27
-
28
- if "ping" not in flask.request.path:
29
- utils_functions.decrement_request_counter(flask.current_app)
30
- utils_functions.update_last_request_time(flask.current_app)
31
-
32
-
33
33
  routes.register_blueprint(
34
34
  blueprint_models.routes,
35
35
  url_prefix=blueprint_models.routes.url_prefix,
36
36
  name=blueprint_models.routes.name,
37
37
  )
38
38
 
39
-
40
- schemas = os.path.join(os.path.dirname(__file__), "schemas")
41
-
42
- with open(
43
- os.path.join(schemas, "allowed_files.json"),
44
- "r",
45
- ) as file:
46
- allowed_files_json = json.load(file)
39
+ schemas_dict = get_schemas_dict(os.path.join(os.path.dirname(__file__), "schemas"))
47
40
 
48
41
 
49
42
  @routes.route(
50
- allowed_files_json["route"],
51
- methods=allowed_files_json["methods"],
43
+ schemas_dict["allowed_files"]["route"],
44
+ methods=schemas_dict["allowed_files"]["methods"],
52
45
  )
53
- def allowed_files():
54
- utils_functions.validate_request(flask.request, allowed_files_json)
55
- extensions = geode_functions.list_input_extensions(
56
- flask.request.get_json()["supported_feature"]
57
- )
58
- return flask.make_response({"extensions": extensions}, 200)
59
-
60
-
61
- with open(
62
- os.path.join(schemas, "upload_file.json"),
63
- "r",
64
- ) as file:
65
- upload_file_json = json.load(file)
46
+ def allowed_files() -> flask.Response:
47
+ utils_functions.validate_request(flask.request, schemas_dict["allowed_files"])
48
+ extensions: set[str] = set()
49
+ for geode_object in geode_objects.values():
50
+ for extension in geode_object.input_extensions():
51
+ extensions.add(extension)
52
+ return flask.make_response({"extensions": list(extensions)}, 200)
66
53
 
67
54
 
68
55
  @routes.route(
69
- upload_file_json["route"],
70
- methods=upload_file_json["methods"],
56
+ schemas_dict["upload_file"]["route"],
57
+ methods=schemas_dict["upload_file"]["methods"],
71
58
  )
72
- def upload_file():
73
- if flask.request.method == "OPTIONS":
74
- return flask.make_response({}, 200)
75
-
59
+ def upload_file() -> flask.Response:
76
60
  UPLOAD_FOLDER = flask.current_app.config["UPLOAD_FOLDER"]
77
61
  if not os.path.exists(UPLOAD_FOLDER):
78
- os.mkdir(UPLOAD_FOLDER)
62
+ os.makedirs(UPLOAD_FOLDER, exist_ok=True)
63
+
79
64
  file = flask.request.files["file"]
80
65
  filename = werkzeug.utils.secure_filename(os.path.basename(file.filename))
81
66
  file.save(os.path.join(UPLOAD_FOLDER, filename))
82
67
  return flask.make_response({"message": "File uploaded"}, 201)
83
68
 
84
69
 
85
- with open(
86
- os.path.join(schemas, "allowed_objects.json"),
87
- "r",
88
- ) as file:
89
- allowed_objects_json = json.load(file)
90
-
91
-
92
70
  @routes.route(
93
- allowed_objects_json["route"],
94
- methods=allowed_objects_json["methods"],
71
+ schemas_dict["allowed_objects"]["route"],
72
+ methods=schemas_dict["allowed_objects"]["methods"],
95
73
  )
96
- def allowed_objects():
97
- if flask.request.method == "OPTIONS":
98
- return flask.make_response({}, 200)
99
-
100
- utils_functions.validate_request(flask.request, allowed_objects_json)
101
- file_absolute_path = geode_functions.upload_file_path(
102
- flask.request.get_json()["filename"]
74
+ def allowed_objects() -> flask.Response:
75
+ json_data = utils_functions.validate_request(
76
+ flask.request, schemas_dict["allowed_objects"]
103
77
  )
104
- allowed_objects = geode_functions.list_geode_objects(
105
- file_absolute_path, flask.request.get_json()["supported_feature"]
78
+ params = schemas.AllowedObjects.from_dict(json_data)
79
+ file_absolute_path = geode_functions.upload_file_path(params.filename)
80
+ file_extension = utils_functions.extension_from_filename(
81
+ os.path.basename(file_absolute_path)
106
82
  )
83
+ allowed_objects = {}
84
+ for geode_object_type, geode_object in geode_objects.items():
85
+ if file_extension not in geode_object.input_extensions():
86
+ continue
87
+ loadability_score = geode_object.is_loadable(file_absolute_path)
88
+ priority_score = geode_object.object_priority(file_absolute_path)
89
+ allowed_objects[geode_object_type] = {
90
+ "is_loadable": loadability_score.value(),
91
+ "object_priority": priority_score,
92
+ }
107
93
  return flask.make_response({"allowed_objects": allowed_objects}, 200)
108
94
 
109
95
 
110
- with open(
111
- os.path.join(schemas, "missing_files.json"),
112
- "r",
113
- ) as file:
114
- missing_files_json = json.load(file)
115
-
116
-
117
96
  @routes.route(
118
- missing_files_json["route"],
119
- methods=missing_files_json["methods"],
97
+ schemas_dict["missing_files"]["route"],
98
+ methods=schemas_dict["missing_files"]["methods"],
120
99
  )
121
- def missing_files():
122
- utils_functions.validate_request(flask.request, missing_files_json)
123
- file_path = geode_functions.upload_file_path(flask.request.get_json()["filename"])
124
-
125
- additional_files = geode_functions.additional_files(
126
- flask.request.get_json()["input_geode_object"],
100
+ def missing_files() -> flask.Response:
101
+ json_data = utils_functions.validate_request(
102
+ flask.request, schemas_dict["missing_files"]
103
+ )
104
+ params = schemas.MissingFiles.from_dict(json_data)
105
+ file_path = geode_functions.upload_file_path(params.filename)
106
+ geode_object = geode_functions.geode_object_from_string(params.geode_object_type)
107
+ additional_files = geode_object.additional_files(
127
108
  file_path,
128
109
  )
129
-
130
110
  has_missing_files = any(
131
111
  file.is_missing
132
112
  for file in additional_files.mandatory_files + additional_files.optional_files
133
113
  )
134
-
135
114
  mandatory_files = [
136
115
  os.path.basename(file.filename)
137
116
  for file in additional_files.mandatory_files
138
117
  if file.is_missing
139
118
  ]
140
- additional_files = [
119
+ additional_files_array = [
141
120
  os.path.basename(file.filename)
142
121
  for file in additional_files.optional_files
143
122
  if file.is_missing
@@ -147,27 +126,26 @@ def missing_files():
147
126
  {
148
127
  "has_missing_files": has_missing_files,
149
128
  "mandatory_files": mandatory_files,
150
- "additional_files": additional_files,
129
+ "additional_files": additional_files_array,
151
130
  },
152
131
  200,
153
132
  )
154
133
 
155
134
 
156
- with open(
157
- os.path.join(schemas, "geographic_coordinate_systems.json"),
158
- "r",
159
- ) as file:
160
- geographic_coordinate_systems_json = json.load(file)
161
-
162
-
163
135
  @routes.route(
164
- geographic_coordinate_systems_json["route"],
165
- methods=geographic_coordinate_systems_json["methods"],
136
+ schemas_dict["geographic_coordinate_systems"]["route"],
137
+ methods=schemas_dict["geographic_coordinate_systems"]["methods"],
166
138
  )
167
- def crs_converter_geographic_coordinate_systems():
168
- utils_functions.validate_request(flask.request, geographic_coordinate_systems_json)
169
- infos = geode_functions.geographic_coordinate_systems(
170
- flask.request.get_json()["input_geode_object"]
139
+ def crs_converter_geographic_coordinate_systems() -> flask.Response:
140
+ json_data = utils_functions.validate_request(
141
+ flask.request, schemas_dict["geographic_coordinate_systems"]
142
+ )
143
+ params = schemas.GeographicCoordinateSystems.from_dict(json_data)
144
+ geode_object = geode_functions.geode_object_from_string(params.geode_object_type)
145
+ infos = (
146
+ og_geosciences.GeographicCoordinateSystem3D.geographic_coordinate_systems()
147
+ if geode_object.is_3D()
148
+ else og_geosciences.GeographicCoordinateSystem2D.geographic_coordinate_systems()
171
149
  )
172
150
  crs_list = []
173
151
  for info in infos:
@@ -176,59 +154,70 @@ def crs_converter_geographic_coordinate_systems():
176
154
  crs["code"] = info.code
177
155
  crs["authority"] = info.authority
178
156
  crs_list.append(crs)
179
-
180
157
  return flask.make_response({"crs_list": crs_list}, 200)
181
158
 
182
159
 
183
- with open(
184
- os.path.join(schemas, "inspect_file.json"),
185
- "r",
186
- ) as file:
187
- inspect_file_json = json.load(file)
188
-
189
-
190
160
  @routes.route(
191
- inspect_file_json["route"],
192
- methods=inspect_file_json["methods"],
161
+ schemas_dict["inspect_file"]["route"],
162
+ methods=schemas_dict["inspect_file"]["methods"],
193
163
  )
194
- def inspect_file():
195
- utils_functions.validate_request(flask.request, inspect_file_json)
196
-
197
- file_path = geode_functions.upload_file_path(flask.request.get_json()["filename"])
198
- data = geode_functions.load(
199
- flask.request.get_json()["input_geode_object"], file_path
200
- )
201
- class_inspector = geode_functions.inspect(
202
- flask.request.get_json()["input_geode_object"], data
164
+ def inspect_file() -> flask.Response:
165
+ json_data = utils_functions.validate_request(
166
+ flask.request, schemas_dict["inspect_file"]
203
167
  )
204
- inspection_result = geode_functions.get_inspector_children(class_inspector)
168
+ params = schemas.InspectFile.from_dict(json_data)
169
+ file_path = geode_functions.upload_file_path(params.filename)
170
+ geode_object = geode_functions.geode_object_from_string(
171
+ params.geode_object_type
172
+ ).load(file_path)
173
+ inspection_data = geode_object.inspect()
174
+ inspection_result = extract_inspector_result(inspection_data)
205
175
  return flask.make_response({"inspection_result": inspection_result}, 200)
206
176
 
207
177
 
208
- with open(
209
- os.path.join(schemas, "geode_objects_and_output_extensions.json"),
210
- "r",
211
- ) as file:
212
- geode_objects_and_output_extensions_json = json.load(file)
178
+ def extract_inspector_result(inspection_data: Any) -> object:
179
+ new_object = {}
180
+
181
+ if hasattr(inspection_data, "inspection_type"):
182
+ new_object["title"] = inspection_data.inspection_type()
183
+ new_object["nb_issues"] = 0
184
+ new_object["children"] = []
185
+ for child in dir(inspection_data):
186
+ if child.startswith("__") or child in [
187
+ "inspection_type",
188
+ "string",
189
+ ]:
190
+ continue
191
+ child_instance = getattr(inspection_data, child)
192
+ child_object = extract_inspector_result(child_instance)
193
+ new_object["children"].append(child_object)
194
+ if hasattr(child_object, "nb_issues"):
195
+ new_object["nb_issues"] += child_object.nb_issues()
196
+ else:
197
+ new_object["title"] = inspection_data.description()
198
+ nb_issues = inspection_data.nb_issues()
199
+ new_object["nb_issues"] = nb_issues
200
+ if nb_issues > 0:
201
+ issues = inspection_data.string().split("\n")
202
+ new_object["issues"] = issues
203
+ return new_object
213
204
 
214
205
 
215
206
  @routes.route(
216
- geode_objects_and_output_extensions_json["route"],
217
- methods=geode_objects_and_output_extensions_json["methods"],
207
+ schemas_dict["geode_objects_and_output_extensions"]["route"],
208
+ methods=schemas_dict["geode_objects_and_output_extensions"]["methods"],
218
209
  )
219
- def geode_objects_and_output_extensions():
220
- utils_functions.validate_request(
221
- flask.request, geode_objects_and_output_extensions_json
222
- )
223
- file_path = geode_functions.upload_file_path(flask.request.get_json()["filename"])
224
- data = geode_functions.load(
225
- flask.request.get_json()["input_geode_object"],
226
- file_path,
210
+ def geode_objects_and_output_extensions() -> flask.Response:
211
+ json_data = utils_functions.validate_request(
212
+ flask.request, schemas_dict["geode_objects_and_output_extensions"]
227
213
  )
214
+ params = schemas.GeodeObjectsAndOutputExtensions.from_dict(json_data)
215
+ file_path = geode_functions.upload_file_path(params.filename)
216
+ geode_object = geode_functions.geode_object_from_string(
217
+ params.geode_object_type
218
+ ).load(file_path)
228
219
  geode_objects_and_output_extensions = (
229
- geode_functions.geode_objects_output_extensions(
230
- flask.request.get_json()["input_geode_object"], data
231
- )
220
+ geode_functions.geode_object_output_extensions(geode_object)
232
221
  )
233
222
  return flask.make_response(
234
223
  {"geode_objects_and_output_extensions": geode_objects_and_output_extensions},
@@ -236,82 +225,53 @@ def geode_objects_and_output_extensions():
236
225
  )
237
226
 
238
227
 
239
- with open(
240
- os.path.join(schemas, "save_viewable_file.json"),
241
- "r",
242
- ) as file:
243
- save_viewable_file_json = json.load(file)
244
-
245
-
246
228
  @routes.route(
247
- save_viewable_file_json["route"],
248
- methods=save_viewable_file_json["methods"],
229
+ schemas_dict["save_viewable_file"]["route"],
230
+ methods=schemas_dict["save_viewable_file"]["methods"],
249
231
  )
250
- def save_viewable_file():
251
- utils_functions.validate_request(flask.request, save_viewable_file_json)
252
- return flask.make_response(
253
- utils_functions.generate_native_viewable_and_light_viewable_from_file(
254
- geode_object=flask.request.get_json()["input_geode_object"],
255
- input_filename=flask.request.get_json()["filename"],
256
- ),
257
- 200,
232
+ def save_viewable_file() -> flask.Response:
233
+ json_data = utils_functions.validate_request(
234
+ flask.request, schemas_dict["save_viewable_file"]
258
235
  )
259
-
260
-
261
- with open(os.path.join(schemas, "create_point.json"), "r") as file:
262
- create_point_json = json.load(file)
263
-
264
-
265
- @routes.route(create_point_json["route"], methods=create_point_json["methods"])
266
- def create_point():
267
- utils_functions.validate_request(flask.request, create_point_json)
268
- title = flask.request.get_json()["title"]
269
- x = flask.request.get_json()["x"]
270
- y = flask.request.get_json()["y"]
271
- z = flask.request.get_json()["z"]
272
- class_ = geode_functions.geode_object_class("PointSet3D")
273
- PointSet3D = class_.create()
274
- builder = geode_functions.create_builder("PointSet3D", PointSet3D)
275
- builder.create_point(opengeode.Point3D([x, y, z]))
276
- builder.set_name(title)
236
+ params = schemas.SaveViewableFile.from_dict(json_data)
277
237
  return flask.make_response(
278
- utils_functions.generate_native_viewable_and_light_viewable_from_object(
279
- "PointSet3D", PointSet3D
238
+ utils_functions.generate_native_viewable_and_light_viewable_from_file(
239
+ geode_object_type=geode_object_type(params.geode_object_type),
240
+ input_file=params.filename,
280
241
  ),
281
242
  200,
282
243
  )
283
244
 
284
245
 
285
- with open(os.path.join(schemas, "texture_coordinates.json"), "r") as file:
286
- texture_coordinates_json = json.load(file)
287
-
288
-
289
246
  @routes.route(
290
- texture_coordinates_json["route"],
291
- methods=texture_coordinates_json["methods"],
247
+ schemas_dict["texture_coordinates"]["route"],
248
+ methods=schemas_dict["texture_coordinates"]["methods"],
292
249
  )
293
- def texture_coordinates():
294
- utils_functions.validate_request(flask.request, texture_coordinates_json)
295
- data = geode_functions.load_data(flask.request.get_json().get("id"))
296
- texture_coordinates = data.texture_manager().texture_names()
250
+ def texture_coordinates() -> flask.Response:
251
+ json_data = utils_functions.validate_request(
252
+ flask.request, schemas_dict["texture_coordinates"]
253
+ )
254
+ params = schemas.TextureCoordinates.from_dict(json_data)
255
+ geode_object = geode_functions.load_geode_object(params.id)
256
+ if not isinstance(geode_object, GeodeSurfaceMesh2D | GeodeSurfaceMesh3D):
257
+ flask.abort(400, f"{params.id} is not a GeodeSurfaceMesh")
258
+ texture_coordinates = geode_object.texture_manager().texture_names()
297
259
  return flask.make_response({"texture_coordinates": texture_coordinates}, 200)
298
260
 
299
261
 
300
- with open(
301
- os.path.join(schemas, "vertex_attribute_names.json"),
302
- "r",
303
- ) as file:
304
- vertex_attribute_names_json = json.load(file)
305
-
306
-
307
262
  @routes.route(
308
- vertex_attribute_names_json["route"],
309
- methods=vertex_attribute_names_json["methods"],
263
+ schemas_dict["vertex_attribute_names"]["route"],
264
+ methods=schemas_dict["vertex_attribute_names"]["methods"],
310
265
  )
311
- def vertex_attribute_names():
312
- utils_functions.validate_request(flask.request, vertex_attribute_names_json)
313
- data = geode_functions.load_data(flask.request.get_json().get("id"))
314
- vertex_attribute_names = data.vertex_attribute_manager().attribute_names()
266
+ def vertex_attribute_names() -> flask.Response:
267
+ json_data = utils_functions.validate_request(
268
+ flask.request, schemas_dict["vertex_attribute_names"]
269
+ )
270
+ params = schemas.VertexAttributeNames.from_dict(json_data)
271
+ geode_object = geode_functions.load_geode_object(params.id)
272
+ if not isinstance(geode_object, GeodeMesh):
273
+ flask.abort(400, f"{params.id} is not a GeodeMesh")
274
+ vertex_attribute_names = geode_object.vertex_attribute_manager().attribute_names()
315
275
  return flask.make_response(
316
276
  {
317
277
  "vertex_attribute_names": vertex_attribute_names,
@@ -320,21 +280,40 @@ def vertex_attribute_names():
320
280
  )
321
281
 
322
282
 
323
- with open(
324
- os.path.join(schemas, "polygon_attribute_names.json"),
325
- "r",
326
- ) as file:
327
- polygon_attribute_names_json = json.load(file)
283
+ @routes.route(
284
+ schemas_dict["cell_attribute_names"]["route"],
285
+ methods=schemas_dict["cell_attribute_names"]["methods"],
286
+ )
287
+ def cell_attribute_names() -> flask.Response:
288
+ json_data = utils_functions.validate_request(
289
+ flask.request, schemas_dict["cell_attribute_names"]
290
+ )
291
+ params = schemas.PolygonAttributeNames.from_dict(json_data)
292
+ geode_object = geode_functions.load_geode_object(params.id)
293
+ if not isinstance(geode_object, GeodeGrid2D | GeodeGrid3D):
294
+ flask.abort(400, f"{params.id} is not a GeodeGrid")
295
+ cell_attribute_names = geode_object.cell_attribute_manager().attribute_names()
296
+ return flask.make_response(
297
+ {
298
+ "cell_attribute_names": cell_attribute_names,
299
+ },
300
+ 200,
301
+ )
328
302
 
329
303
 
330
304
  @routes.route(
331
- polygon_attribute_names_json["route"],
332
- methods=polygon_attribute_names_json["methods"],
305
+ schemas_dict["polygon_attribute_names"]["route"],
306
+ methods=schemas_dict["polygon_attribute_names"]["methods"],
333
307
  )
334
- def polygon_attribute_names():
335
- utils_functions.validate_request(flask.request, polygon_attribute_names_json)
336
- data = geode_functions.load_data(flask.request.get_json().get("id"))
337
- polygon_attribute_names = data.polygon_attribute_manager().attribute_names()
308
+ def polygon_attribute_names() -> flask.Response:
309
+ json_data = utils_functions.validate_request(
310
+ flask.request, schemas_dict["polygon_attribute_names"]
311
+ )
312
+ params = schemas.PolygonAttributeNames.from_dict(json_data)
313
+ geode_object = geode_functions.load_geode_object(params.id)
314
+ if not isinstance(geode_object, GeodeSurfaceMesh2D | GeodeSurfaceMesh3D):
315
+ flask.abort(400, f"{params.id} is not a GeodeSurfaceMesh")
316
+ polygon_attribute_names = geode_object.polygon_attribute_manager().attribute_names()
338
317
  return flask.make_response(
339
318
  {
340
319
  "polygon_attribute_names": polygon_attribute_names,
@@ -343,21 +322,21 @@ def polygon_attribute_names():
343
322
  )
344
323
 
345
324
 
346
- with open(
347
- os.path.join(schemas, "polyhedron_attribute_names.json"),
348
- "r",
349
- ) as file:
350
- polyhedron_attribute_names_json = json.load(file)
351
-
352
-
353
325
  @routes.route(
354
- polyhedron_attribute_names_json["route"],
355
- methods=polyhedron_attribute_names_json["methods"],
326
+ schemas_dict["polyhedron_attribute_names"]["route"],
327
+ methods=schemas_dict["polyhedron_attribute_names"]["methods"],
356
328
  )
357
- def polyhedron_attribute_names():
358
- utils_functions.validate_request(flask.request, polyhedron_attribute_names_json)
359
- data = geode_functions.load_data(flask.request.get_json().get("id"))
360
- polyhedron_attribute_names = data.polyhedron_attribute_manager().attribute_names()
329
+ def polyhedron_attribute_names() -> flask.Response:
330
+ json_data = utils_functions.validate_request(
331
+ flask.request, schemas_dict["polyhedron_attribute_names"]
332
+ )
333
+ params = schemas.PolyhedronAttributeNames.from_dict(json_data)
334
+ geode_object = geode_functions.load_geode_object(params.id)
335
+ if not isinstance(geode_object, GeodeSolidMesh3D):
336
+ flask.abort(400, f"{params.id} is not a GeodeSolidMesh")
337
+ polyhedron_attribute_names = (
338
+ geode_object.polyhedron_attribute_manager().attribute_names()
339
+ )
361
340
  return flask.make_response(
362
341
  {
363
342
  "polyhedron_attribute_names": polyhedron_attribute_names,
@@ -366,18 +345,160 @@ def polyhedron_attribute_names():
366
345
  )
367
346
 
368
347
 
369
- with open(
370
- os.path.join(schemas, "ping.json"),
371
- "r",
372
- ) as file:
373
- ping_json = json.load(file)
374
-
375
-
376
348
  @routes.route(
377
- ping_json["route"],
378
- methods=ping_json["methods"],
349
+ schemas_dict["ping"]["route"],
350
+ methods=schemas_dict["ping"]["methods"],
379
351
  )
380
- def ping():
381
- utils_functions.validate_request(flask.request, ping_json)
352
+ def ping() -> flask.Response:
353
+ utils_functions.validate_request(flask.request, schemas_dict["ping"])
382
354
  flask.current_app.config.update(LAST_PING_TIME=time.time())
383
355
  return flask.make_response({"message": "Flask server is running"}, 200)
356
+
357
+
358
+ @routes.route(schemas_dict["kill"]["route"], methods=schemas_dict["kill"]["methods"])
359
+ def kill() -> flask.Response:
360
+ print("Manual server kill, shutting down...", flush=True)
361
+ os._exit(0)
362
+ return flask.make_response({"message": "Flask server is dead"}, 200)
363
+
364
+
365
+ @routes.route(
366
+ schemas_dict["export_project"]["route"],
367
+ methods=schemas_dict["export_project"]["methods"],
368
+ )
369
+ def export_project() -> flask.Response:
370
+ json_data = utils_functions.validate_request(
371
+ flask.request, schemas_dict["export_project"]
372
+ )
373
+ params = schemas.ExportProject.from_dict(json_data)
374
+
375
+ project_folder: str = flask.current_app.config["DATA_FOLDER_PATH"]
376
+ os.makedirs(project_folder, exist_ok=True)
377
+
378
+ filename: str = werkzeug.utils.secure_filename(os.path.basename(params.filename))
379
+ if not filename.lower().endswith(".vease"):
380
+ flask.abort(400, "Requested filename must end with .vease")
381
+ export_vease_path = os.path.join(project_folder, filename)
382
+
383
+ with get_session() as session:
384
+ rows = session.query(Data.id, Data.input_file, Data.additional_files).all()
385
+
386
+ with zipfile.ZipFile(
387
+ export_vease_path, "w", compression=zipfile.ZIP_DEFLATED
388
+ ) as zip_file:
389
+ database_root_path = os.path.join(project_folder, "project.db")
390
+ if os.path.isfile(database_root_path):
391
+ zip_file.write(database_root_path, "project.db")
392
+
393
+ for data_id, input_file, additional_files in rows:
394
+ base_dir = os.path.join(project_folder, data_id)
395
+
396
+ input_path = os.path.join(base_dir, str(input_file))
397
+ if os.path.isfile(input_path):
398
+ zip_file.write(input_path, os.path.join(data_id, str(input_file)))
399
+
400
+ for relative_path in (
401
+ additional_files if isinstance(additional_files, list) else []
402
+ ):
403
+ additional_path = os.path.join(base_dir, relative_path)
404
+ if os.path.isfile(additional_path):
405
+ zip_file.write(
406
+ additional_path, os.path.join(data_id, relative_path)
407
+ )
408
+
409
+ zip_file.writestr("snapshot.json", flask.json.dumps(params.snapshot))
410
+
411
+ return utils_functions.send_file(project_folder, [export_vease_path], filename)
412
+
413
+
414
+ @routes.route(
415
+ schemas_dict["import_project"]["route"],
416
+ methods=schemas_dict["import_project"]["methods"],
417
+ )
418
+ def import_project() -> flask.Response:
419
+ utils_functions.validate_request(flask.request, schemas_dict["import_project"])
420
+ if "file" not in flask.request.files:
421
+ flask.abort(400, "No .vease file provided under 'file'")
422
+ zip_file = flask.request.files["file"]
423
+ assert zip_file.filename is not None
424
+ filename = werkzeug.utils.secure_filename(os.path.basename(zip_file.filename))
425
+ if not filename.lower().endswith(".vease"):
426
+ flask.abort(400, "Uploaded file must be a .vease")
427
+
428
+ data_folder_path: str = flask.current_app.config["DATA_FOLDER_PATH"]
429
+
430
+ # 423 Locked bypass : remove stopped requests
431
+ if connection.scoped_session_registry:
432
+ connection.scoped_session_registry.remove()
433
+ if connection.engine:
434
+ connection.engine.dispose()
435
+ connection.engine = connection.session_factory = (
436
+ connection.scoped_session_registry
437
+ ) = None
438
+
439
+ try:
440
+ if os.path.exists(data_folder_path):
441
+ shutil.rmtree(data_folder_path)
442
+ os.makedirs(data_folder_path, exist_ok=True)
443
+ except PermissionError:
444
+ flask.abort(423, "Project files are locked; cannot overwrite")
445
+
446
+ zip_file.stream.seek(0)
447
+ with zipfile.ZipFile(zip_file.stream) as zip_archive:
448
+ project_folder = os.path.abspath(data_folder_path)
449
+ for member in zip_archive.namelist():
450
+ target = os.path.abspath(
451
+ os.path.normpath(os.path.join(project_folder, member))
452
+ )
453
+ if not (
454
+ target == project_folder or target.startswith(project_folder + os.sep)
455
+ ):
456
+ flask.abort(400, "Vease file contains unsafe paths")
457
+ zip_archive.extractall(project_folder)
458
+
459
+ database_root_path = os.path.join(project_folder, "project.db")
460
+ if not os.path.isfile(database_root_path):
461
+ flask.abort(400, "Missing project.db at project root")
462
+
463
+ connection.init_database(database_root_path, create_tables=False)
464
+
465
+ try:
466
+ with get_session() as session:
467
+ rows = session.query(Data).all()
468
+ except Exception:
469
+ connection.init_database(database_root_path, create_tables=True)
470
+ with get_session() as session:
471
+ rows = session.query(Data).all()
472
+
473
+ with get_session() as session:
474
+ for data in rows:
475
+ data_path = geode_functions.data_file_path(data.id)
476
+ viewable_name = data.viewable_file
477
+ if viewable_name:
478
+ vpath = geode_functions.data_file_path(data.id, viewable_name)
479
+ if os.path.isfile(vpath):
480
+ continue
481
+
482
+ input_file = str(data.input_file or "")
483
+ if not input_file:
484
+ continue
485
+
486
+ input_full = geode_functions.data_file_path(data.id, input_file)
487
+ if not os.path.isfile(input_full):
488
+ continue
489
+
490
+ geode_object = geode_functions.geode_object_from_string(
491
+ data.geode_object
492
+ ).load(input_full)
493
+ utils_functions.save_all_viewables_and_return_info(
494
+ geode_object, data, data_path
495
+ )
496
+ session.commit()
497
+
498
+ snapshot = {}
499
+ try:
500
+ raw = zip_archive.read("snapshot.json").decode("utf-8")
501
+ snapshot = flask.json.loads(raw)
502
+ except KeyError:
503
+ snapshot = {}
504
+ return flask.make_response({"snapshot": snapshot}, 200)