Skip to content

Commit

Permalink
Add export function to REST API
Browse files Browse the repository at this point in the history
  • Loading branch information
andreArtelt committed May 24, 2024
1 parent 1f75e2c commit 7963406
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 1 deletion.
2 changes: 2 additions & 0 deletions docs/tut.restapi.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ and is implemented in the class :class:`~epyt_flow.rest_api.server.RestApiServic
+-----------+-------------------------------------------------------+------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------+
| DELETE | /scenario/{scenario_id} | :class:`~epyt_flow.rest_api.scenario_handler.ScenarioRemoveHandler` | Deletes a scenario. |
+-----------+-------------------------------------------------------+------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------+
| GET | /scenario/{scenario_id}/export | :class:`~epyt_flow.rest_api.scenario_handler.ScenarioExportHandler` | Exports a given scenario to an .inp and (optionally) .msx file. |
+-----------+-------------------------------------------------------+------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------+
| GET | /scenario/{scenario_id}/topology | :class:`~epyt_flow.rest_api.scenario_handler.ScenarioTopologyHandler` | Gets the topology of a given scenario. |
+-----------+-------------------------------------------------------+------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------+
| GET | /scenario/{scenario_id}/scenario_config | :class:`~epyt_flow.rest_api.scenario_handler.ScenarioConfigHandler` | Gets the entire configuration/specification of a given scenario. |
Expand Down
74 changes: 74 additions & 0 deletions epyt_flow/rest_api/scenario_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
This module provides all handlers for requests concerning scenarios.
"""
import warnings
import os
import falcon

from .base_handler import BaseHandler
from .res_manager import ResourceManager
from ..utils import get_temp_folder, pack_zip_archive
from .scada_data_handler import ScadaDataManager
from ..simulation import ScenarioSimulator, Leakage, SensorConfig, SensorFault

Expand Down Expand Up @@ -69,6 +71,78 @@ def on_delete(self, _, resp: falcon.Response, scenario_id: str) -> None:
resp.status = falcon.HTTP_INTERNAL_SERVER_ERROR


class ScenarioExportHandler(ScenarioBaseHandler):
"""
Class for handling GET requests for exporting a given scenario to EPANET files
-- i.e. .inp and (otpionally) .msx files.
"""
def __create_temp_file_path(self, scenario_id: str, file_ext: str) -> None:
"""
Returns a path to a temporary file for storing the scenario.
Parameters
----------
scenario_id : `str`
UUID of the scenario.
file_ext : `str`
File extension.
"""
return os.path.join(get_temp_folder(), f"{scenario_id}.{file_ext}")

def __send_temp_file(self, resp: falcon.Response, tmp_file: str,
content_type: str = "application/octet-stream") -> None:
"""
Sends a given file (`tmp_file`) to the the client.
Parameters
----------
resp : `falcon.Response`
Response instance.
tmp_file : `str`
Path to the temporary file to be send.
"""
resp.status = falcon.HTTP_200
resp.content_type = content_type
with open(tmp_file, 'rb') as f:
resp.text = f.read()

def on_get(self, _, resp: falcon.Response, scenario_id: str) -> None:
"""
Exports a given scenario to an .inp and (optionally) .msx file.
Parameters
----------
resp : `falcon.Response`
Response instance.
scenario_id : `str`
UUID of the scenario.
"""
try:
if self.scenario_mgr.validate_uuid(scenario_id) is False:
self.send_invalid_resource_id_error(resp)
return

my_scenario = self.scenario_mgr.get(scenario_id)

f_inp_out = self.__create_temp_file_path(scenario_id, "inp")
f_msx_out = self.__create_temp_file_path(scenario_id, "msx")
my_scenario.save_to_epanet_file(f_inp_out, f_msx_out)

if os.path.isfile(f_msx_out):
f_out = self.__create_temp_file_path(scenario_id, "zip")
pack_zip_archive([f_inp_out, f_msx_out], f_out)

self.__send_temp_file(resp, f_out)
os.remove(f_out)
os.remove(f_msx_out)
else:
self.__send_temp_file(resp, f_inp_out)
os.remove(f_inp_out)
except Exception as ex:
warnings.warn(str(ex))
resp.status = falcon.HTTP_INTERNAL_SERVER_ERROR


class ScenarioConfigHandler(ScenarioBaseHandler):
"""
Class for handling a GET request for getting the scenario configuration of a given scenario.
Expand Down
4 changes: 3 additions & 1 deletion epyt_flow/rest_api/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
ScenarioGeneralParamsHandler, ScenarioSensorConfigHandler, ScenarioSimulationHandler, \
ScenarioTopologyHandler, ScenarioConfigHandler, ScenarioLeakageHandler, \
ScenarioBasicQualitySimulationHandler, ScenarioAdvancedQualitySimulationHandler, \
ScenarioNodeDemandPatternHandler, ScenarioSensorFaultHandler
ScenarioNodeDemandPatternHandler, ScenarioSensorFaultHandler, ScenarioExportHandler
from .scada_data_handler import ScadaDataManager, ScadaDataSensorConfigHandler, \
ScadaDataPressuresHandler, ScadaDataDemandsHandler, ScadaDataFlowsHandler, \
ScadaDataLinksQualityHandler, ScadaDataNodesQualityHandler, ScadaDataRemoveHandler, \
Expand Down Expand Up @@ -40,6 +40,8 @@ def __init__(self, port: int = 8080):
ScenarioNewHandler(self.scenario_mgr))
self.app.add_route("/scenario/{scenario_id}",
ScenarioRemoveHandler(self.scenario_mgr))
self.app.add_route("/scenario/{scenario_id}/export",
ScenarioExportHandler(self.scenario_mgr))
self.app.add_route("/scenario/{scenario_id}/topology",
ScenarioTopologyHandler(self.scenario_mgr))
self.app.add_route("/scenario/{scenario_id}/scenario_config",
Expand Down
16 changes: 16 additions & 0 deletions epyt_flow/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,22 @@ def create_path_if_not_exist(path_in: str) -> None:
Path(path_in).mkdir(parents=True, exist_ok=True)


def pack_zip_archive(f_in: list[str], f_out: str) -> None:
"""
Compresses a given list of files into a .zip archive.
Parameters
----------
f_in : `list[str]`
List of files to be compressed into the .zip archive.
f_out : `str`
Path to the final .zip file.
"""
with zipfile.ZipFile(f_out, "w") as f_zip_out:
for f_cur_in in f_in:
f_zip_out.write(f_cur_in, compress_type=zipfile.ZIP_DEFLATED)


def unpack_zip_archive(f_in: str, folder_out: str) -> None:
"""
Unpacks a .zip archive.
Expand Down

0 comments on commit 7963406

Please sign in to comment.