Source code for epyt_flow.rest_api.scenario.handlers

  1"""
  2This module provides REST API handlers for some requests concerning scenarios.
  3"""
  4import warnings
  5import os
  6import falcon
  7
  8from ..base_handler import BaseHandler
  9from ..res_manager import ResourceManager
 10from ...utils import get_temp_folder, pack_zip_archive
 11from ...simulation import ScenarioSimulator, SensorConfig
 12
 13
[docs] 14class ScenarioManager(ResourceManager): 15 """ 16 Class for managing all scenarios that are currently used by the REST API. 17 """
[docs] 18 def create(self, **kwds) -> str: 19 """ 20 Creates a new scenario -- e.g. loading a given .inp file or 21 using a given scenario configuration. 22 23 Returns 24 ------- 25 `str` 26 UUID of the new scenario. 27 """ 28 return self.create_new_item(ScenarioSimulator(**kwds))
29
[docs] 30 def close_item(self, item: ScenarioSimulator) -> None: 31 item.close()
32 33
[docs] 34class ScenarioBaseHandler(BaseHandler): 35 """ 36 Base class for all handlers concerning scenarios. 37 38 Parameters 39 ---------- 40 scenario_mgr : :class:`~epyt_flow.rest_api.scenario.handlers.ScenarioManager` 41 Instance for managing all scenarios. 42 """ 43 def __init__(self, scenario_mgr: ScenarioManager): 44 self.scenario_mgr = scenario_mgr
45 46
[docs] 47class ScenarioRemoveHandler(ScenarioBaseHandler): 48 """ 49 Class for handling a DELETE request for a given scenario. 50 """
[docs] 51 def on_delete(self, _, resp: falcon.Response, scenario_id: str) -> None: 52 """ 53 Deletes a given scenario. 54 55 Parameters 56 ---------- 57 resp : `falcon.Response <https://falcon.readthedocs.io/en/stable/api/request_and_response_asgi.html#response>`_ 58 Response instance. 59 scenario_id : `str` 60 UUID of the scenario. 61 """ 62 try: 63 if self.scenario_mgr.validate_uuid(scenario_id) is False: 64 self.send_invalid_resource_id_error(resp) 65 return 66 67 self.scenario_mgr.remove(scenario_id) 68 except Exception as ex: 69 warnings.warn(str(ex)) 70 resp.status = falcon.HTTP_INTERNAL_SERVER_ERROR
71 72
[docs] 73class ScenarioExportHandler(ScenarioBaseHandler): 74 """ 75 Class for handling GET requests for exporting a given scenario to EPANET files 76 -- i.e. .inp and (otpionally) .msx files. 77 """ 78 def __create_temp_file_path(self, scenario_id: str, file_ext: str) -> None: 79 """ 80 Returns a path to a temporary file for storing the scenario. 81 82 Parameters 83 ---------- 84 scenario_id : `str` 85 UUID of the scenario. 86 file_ext : `str` 87 File extension. 88 """ 89 return os.path.join(get_temp_folder(), f"{scenario_id}.{file_ext}") 90 91 def __send_temp_file(self, resp: falcon.Response, tmp_file: str, 92 content_type: str = "application/octet-stream") -> None: 93 """ 94 Sends a given file (`tmp_file`) to the the client. 95 96 Parameters 97 ---------- 98 resp : `falcon.Response <https://falcon.readthedocs.io/en/stable/api/request_and_response_asgi.html#response>`_ 99 Response instance. 100 tmp_file : `str` 101 Path to the temporary file to be send. 102 """ 103 resp.status = falcon.HTTP_200 104 resp.content_type = content_type 105 with open(tmp_file, 'rb') as f: 106 resp.text = f.read() 107
[docs] 108 def on_get(self, _, resp: falcon.Response, scenario_id: str) -> None: 109 """ 110 Exports a given scenario to an .inp and (optionally) .msx file. 111 112 Parameters 113 ---------- 114 resp : `falcon.Response <https://falcon.readthedocs.io/en/stable/api/request_and_response_asgi.html#response>`_ 115 Response instance. 116 scenario_id : `str` 117 UUID of the scenario. 118 """ 119 try: 120 if self.scenario_mgr.validate_uuid(scenario_id) is False: 121 self.send_invalid_resource_id_error(resp) 122 return 123 124 my_scenario = self.scenario_mgr.get(scenario_id) 125 126 f_inp_out = self.__create_temp_file_path(scenario_id, "inp") 127 f_msx_out = self.__create_temp_file_path(scenario_id, "msx") 128 my_scenario.save_to_epanet_file(f_inp_out, f_msx_out) 129 130 if os.path.isfile(f_msx_out): 131 f_out = self.__create_temp_file_path(scenario_id, "zip") 132 pack_zip_archive([f_inp_out, f_msx_out], f_out) 133 134 self.__send_temp_file(resp, f_out) 135 os.remove(f_out) 136 os.remove(f_msx_out) 137 else: 138 self.__send_temp_file(resp, f_inp_out) 139 os.remove(f_inp_out) 140 except Exception as ex: 141 warnings.warn(str(ex)) 142 resp.status = falcon.HTTP_INTERNAL_SERVER_ERROR
143 144
[docs] 145class ScenarioConfigHandler(ScenarioBaseHandler): 146 """ 147 Class for handling a GET request for getting the scenario configuration of a given scenario. 148 """
[docs] 149 def on_get(self, _, resp: falcon.Response, scenario_id: str) -> None: 150 """ 151 Gets the scenario configuration of a given scenario. 152 153 Parameters 154 ---------- 155 resp : `falcon.Response <https://falcon.readthedocs.io/en/stable/api/request_and_response_asgi.html#response>`_ 156 Response instance. 157 scenario_id : `str` 158 UUID of the scenario. 159 """ 160 try: 161 if self.scenario_mgr.validate_uuid(scenario_id) is False: 162 self.send_invalid_resource_id_error(resp) 163 return 164 165 my_sceanrio_config = self.scenario_mgr.get(scenario_id).get_scenario_config() 166 self.send_json_response(resp, my_sceanrio_config) 167 except Exception as ex: 168 warnings.warn(str(ex)) 169 resp.status = falcon.HTTP_INTERNAL_SERVER_ERROR
170 171
[docs] 172class ScenarioNewHandler(ScenarioBaseHandler): 173 """ 174 Class for handling POST requests for creating a new scenario. 175 """
[docs] 176 def on_post(self, req: falcon.Request, resp: falcon.Response) -> None: 177 """ 178 Creates/Loads a new scenario. 179 180 Parameters 181 ---------- 182 req : `falcon.Request <https://falcon.readthedocs.io/en/stable/api/request_and_response_asgi.html#request>`_ 183 Request instance. 184 resp : `falcon.Response <https://falcon.readthedocs.io/en/stable/api/request_and_response_asgi.html#response>`_ 185 Response instance. 186 scenario_id : `str` 187 UUID of the scenario. 188 """ 189 try: 190 args = self.load_json_data_from_request(req) 191 scenario_id = self.scenario_mgr.create(**args) 192 self.send_json_response(resp, {"scenario_id": scenario_id}) 193 except Exception as ex: 194 warnings.warn(str(ex)) 195 resp.status = falcon.HTTP_INTERNAL_SERVER_ERROR
196 197
[docs] 198class ScenarioTopologyHandler(ScenarioBaseHandler): 199 """ 200 Class for handling GET requests for getting the topology of a given scenario. 201 """
[docs] 202 def on_get(self, _, resp: falcon.Response, scenario_id: str) -> None: 203 """ 204 Gets the topology of a given scenario. 205 206 Parameters 207 ---------- 208 resp : `falcon.Response <https://falcon.readthedocs.io/en/stable/api/request_and_response_asgi.html#response>`_ 209 Response instance. 210 scenario_id : `str` 211 UUID of the scenario. 212 """ 213 try: 214 if self.scenario_mgr.validate_uuid(scenario_id) is False: 215 self.send_invalid_resource_id_error(resp) 216 return 217 218 my_topology = self.scenario_mgr.get(scenario_id).get_topology() 219 self.send_json_response(resp, my_topology) 220 except Exception as ex: 221 warnings.warn(str(ex)) 222 resp.status = falcon.HTTP_INTERNAL_SERVER_ERROR
223 224
[docs] 225class ScenarioGeneralParamsHandler(ScenarioBaseHandler): 226 """ 227 Class for handling GET and POST requests for the general parameters of a given scenario. 228 """
[docs] 229 def on_get(self, _, resp: falcon.Response, scenario_id: str) -> None: 230 """ 231 Gets the general parameters (e.g. simulation duration, etc.) of a given scenario. 232 233 Parameters 234 ---------- 235 resp : `falcon.Response <https://falcon.readthedocs.io/en/stable/api/request_and_response_asgi.html#response>`_ 236 Response instance. 237 scenario_id : `str` 238 UUID of the scenario. 239 """ 240 try: 241 if self.scenario_mgr.validate_uuid(scenario_id) is False: 242 self.send_invalid_resource_id_error(resp) 243 return 244 245 my_general_params = self.scenario_mgr.get(scenario_id).get_scenario_config().\ 246 general_params 247 self.send_json_response(resp, my_general_params) 248 except Exception as ex: 249 warnings.warn(str(ex)) 250 resp.status = falcon.HTTP_INTERNAL_SERVER_ERROR
251
[docs] 252 def on_post(self, req: falcon.Request, resp: falcon.Response, scenario_id: str) -> None: 253 """ 254 Sets the general parameters of a given scenario. 255 256 Parameters 257 ---------- 258 req : `falcon.Request <https://falcon.readthedocs.io/en/stable/api/request_and_response_asgi.html#request>`_ 259 Request instance. 260 resp : `falcon.Response <https://falcon.readthedocs.io/en/stable/api/request_and_response_asgi.html#response>`_ 261 Request instance. 262 scenario_id : `str` 263 UUID of the scenario. 264 """ 265 try: 266 if self.scenario_mgr.validate_uuid(scenario_id) is False: 267 self.send_invalid_resource_id_error(resp) 268 return 269 270 general_params = self.load_json_data_from_request(req) 271 if not isinstance(general_params, dict): 272 self.send_json_parsing_error(resp) 273 return 274 275 self.scenario_mgr.get(scenario_id).set_general_parameters(**general_params) 276 except Exception as ex: 277 warnings.warn(str(ex)) 278 resp.status = falcon.HTTP_INTERNAL_SERVER_ERROR
279 280
[docs] 281class ScenarioQualityParamsHandler(ScenarioBaseHandler): 282 """ 283 Class for handling POST requests for specifying (EPANET) quality parameters of a given scenario. 284 """
[docs] 285 def on_post(self, req: falcon.Request, resp: falcon.Response, scenario_id: str) -> None: 286 """ 287 Specifies the (EPANET) quality parameters of a given scenario. 288 289 Parameters 290 ---------- 291 req : `falcon.Request <https://falcon.readthedocs.io/en/stable/api/request_and_response_asgi.html#request>`_ 292 Request instance. 293 resp : `falcon.Response <https://falcon.readthedocs.io/en/stable/api/request_and_response_asgi.html#response>`_ 294 Request instance. 295 scenario_id : `str` 296 UUID of the scenario. 297 """ 298 try: 299 if self.scenario_mgr.validate_uuid(scenario_id) is False: 300 self.send_invalid_resource_id_error(resp) 301 return 302 303 quality_params = self.load_json_data_from_request(req) 304 if not isinstance(quality_params, dict): 305 self.send_json_parsing_error(resp) 306 return 307 308 self.scenario_mgr.get(scenario_id).set_quality_parameters(**quality_params) 309 except Exception as ex: 310 warnings.warn(str(ex)) 311 resp.status = falcon.HTTP_INTERNAL_SERVER_ERROR
312 313
[docs] 314class ScenarioSensorConfigHandler(ScenarioBaseHandler): 315 """ 316 Class for handling GET and POST requests for the sensor configuration of a given scenario. 317 """
[docs] 318 def on_get(self, _, resp: falcon.Response, scenario_id: str) -> None: 319 """ 320 Gets the sensor configuration of a given scenario. 321 322 Parameters 323 ---------- 324 resp : `falcon.Response <https://falcon.readthedocs.io/en/stable/api/request_and_response_asgi.html#response>`_ 325 Response instance. 326 scenario_id : `str` 327 UUID of the scenario. 328 """ 329 try: 330 if self.scenario_mgr.validate_uuid(scenario_id) is False: 331 self.send_invalid_resource_id_error(resp) 332 return 333 334 my_sensor_config = self.scenario_mgr.get(scenario_id).sensor_config 335 self.send_json_response(resp, my_sensor_config) 336 except Exception as ex: 337 warnings.warn(str(ex)) 338 resp.status = falcon.HTTP_INTERNAL_SERVER_ERROR
339
[docs] 340 def on_post(self, req: falcon.Request, resp: falcon.Response, scenario_id: str) -> None: 341 """ 342 Sets the sensor configuration of a given scenario. 343 344 Parameters 345 ---------- 346 req : `falcon.Request <https://falcon.readthedocs.io/en/stable/api/request_and_response_asgi.html#request>`_ 347 Request instance. 348 resp : `falcon.Response <https://falcon.readthedocs.io/en/stable/api/request_and_response_asgi.html#response>`_ 349 Request instance. 350 scenario_id : `str` 351 UUID of the scenario. 352 """ 353 try: 354 if self.scenario_mgr.validate_uuid(scenario_id) is False: 355 self.send_invalid_resource_id_error(resp) 356 return 357 358 sensor_config = self.load_json_data_from_request(req) 359 if not isinstance(sensor_config, SensorConfig): 360 self.send_json_parsing_error(resp) 361 return 362 363 my_scenario = self.scenario_mgr.get(scenario_id) 364 my_scenario.sensor_config = sensor_config 365 except Exception as ex: 366 warnings.warn(str(ex)) 367 resp.status = falcon.HTTP_INTERNAL_SERVER_ERROR
368 369
[docs] 370class ScenarioNodeDemandPatternHandler(ScenarioBaseHandler): 371 """ 372 Class for handling POST requests for node demand patterns of a given scenario. 373 """
[docs] 374 def on_post(self, req: falcon.Request, resp: falcon.Response, scenario_id: str, 375 node_id: str) -> None: 376 """ 377 Sets the demand pattern of a specific node in a given scenario. 378 379 Parameters 380 ---------- 381 req : `falcon.Request <https://falcon.readthedocs.io/en/stable/api/request_and_response_asgi.html#request>`_ 382 Request instance. 383 resp : `falcon.Response <https://falcon.readthedocs.io/en/stable/api/request_and_response_asgi.html#response>`_ 384 Response instance. 385 scenario_id : `str` 386 UUID of the scenario. 387 node_id : `str` 388 ID of the node. 389 """ 390 try: 391 if self.scenario_mgr.validate_uuid(scenario_id) is False: 392 self.send_invalid_resource_id_error(resp) 393 return 394 395 params = self.load_json_data_from_request(req) 396 397 my_scenario = self.scenario_mgr.get(scenario_id) 398 my_scenario.set_node_demand_pattern(node_id, params["base_demand"], 399 params["demand_pattern_id"], 400 params["demand_pattern"]) 401 except Exception as ex: 402 warnings.warn(str(ex)) 403 resp.data = str(ex) 404 resp.status = falcon.HTTP_INTERNAL_SERVER_ERROR