From a322c59787b8886220aa8c1d7b8226b718302ccd Mon Sep 17 00:00:00 2001 From: Guille Date: Thu, 2 May 2024 17:28:53 +0200 Subject: [PATCH] ep workflow completed --- hub_api/config.py | 6 +- hub_api/helpers/session_helper.py | 6 +- hub_api/workflow/energy_plus.py | 109 +++++++++++++++++- .../workflow/insel_montly_energy_balance.py | 2 +- 4 files changed, 118 insertions(+), 5 deletions(-) diff --git a/hub_api/config.py b/hub_api/config.py index 167d3af..88a201e 100644 --- a/hub_api/config.py +++ b/hub_api/config.py @@ -23,7 +23,7 @@ class Config: environment = 'PROD' database_name = 'montreal_retrofit' - + self._base_uri = 'https://nextgenerations-cities.encs.concordia.ca/api-results' self._database = DBControl(db_name=database_name, app_env=environment, dotenv_path=dotenv_path) self._repository = Repository(db_name=database_name, app_env=environment, dotenv_path=dotenv_path) self._energy_systems_catalog = EnergySystemsCatalogFactory('montreal_custom').catalog @@ -62,3 +62,7 @@ class Config: @property def sra(self): return distutils.spawn.find_executable('sra') + + @property + def base_uri(self): + return self._base_uri diff --git a/hub_api/helpers/session_helper.py b/hub_api/helpers/session_helper.py index 038506d..580da42 100644 --- a/hub_api/helpers/session_helper.py +++ b/hub_api/helpers/session_helper.py @@ -3,9 +3,12 @@ Session helper SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2022 Project Author name guillermo.gutierrezmorote@concordia.ca """ +import os +import shutil import uuid import datetime import time +from pathlib import Path sessions = {} begin_time = None @@ -27,7 +30,8 @@ def expired_sessions_collector(session_timeout_duration): _expire = datetime.datetime.strptime(sessions[session_uuid]['expire'], '%Y-%m-%d %H:%M:%S.%f') if _expire < datetime.datetime.now(): print("session for user: ", sessions[session_uuid]['user'], "expired.") - + response_path = (Path(__file__).parent / f'response_files/{session_uuid}').resolve() + shutil.rmtree(response_path) del sessions[session_uuid] time.sleep(60 * int(session_timeout_duration)) diff --git a/hub_api/workflow/energy_plus.py b/hub_api/workflow/energy_plus.py index cdb9b8c..37ad821 100644 --- a/hub_api/workflow/energy_plus.py +++ b/hub_api/workflow/energy_plus.py @@ -1,14 +1,119 @@ +import json +import os +import shutil +import zipfile +from pathlib import Path + +from flask import Response, request, send_file from flask_restful import Resource +from hub.exports.energy_building_exports_factory import EnergyBuildingsExportsFactory +from hub.imports.construction_factory import ConstructionFactory +from hub.imports.geometry_factory import GeometryFactory +from hub.imports.usage_factory import UsageFactory +from hub.imports.weather_factory import WeatherFactory +from werkzeug.utils import secure_filename from hub_api.config import Config +from hub_api.helpers.session_helper import refresh_session class EnergyPlus(Resource, Config): def __init__(self): super().__init__() + self._extensions = ['.geojson', '.citygml'] + self._tmp_path = (Path(__file__).parent / 'tmp').resolve() + self._response_path = (Path(__file__).parent.parent / 'response_files').resolve() + self._city = None + + def _allowed_extensions(self, filename): + self._file_extension = Path(filename).suffix + return self._file_extension in self._extensions + + def _geojson(self, file_path): + try: + height_field = request.form.get('height_field') + year_of_construction_field = request.form.get('year_of_construction_field') + function_field = request.form.get('function_field') + function_dictionary = self._dictionaries[request.form.get('function_dictionary_name')] + return GeometryFactory('geojson', + path=file_path, + height_field=height_field, + year_of_construction_field=year_of_construction_field, + function_field=function_field, + function_to_hub=function_dictionary).city + except KeyError: + return None + + def _citygml(self, file_path): + try: + year_of_construction_field = request.form.get('year_of_construction_field') + function_field = request.form.get('function_field') + function_dictionary = self._dictionaries[request.form.get('function_dictionary_name')] + hub_crs = request.form.get('hub_crs') + return GeometryFactory('citygml', + path=file_path, + year_of_construction_field=year_of_construction_field, + function_field=function_field, + function_to_hub=function_dictionary, + hub_crs=hub_crs).city + except KeyError: + return None def post(self): """ - API call for performing the energy plus workflow + API call for performing the monthly energy balance workflow """ - raise NotImplementedError() + session_id = request.headers.get('session-id', None) + token = request.headers.get('token', None) + application_uuid = request.headers.get('application-uuid', None) + _session = refresh_session(session_id, token, application_uuid) + _session = {'token': token} + response_token = {'token': _session['token']} + if _session is None: + return Response(json.dumps({'error': 'unauthorized'}), status=403) + else: + tmp_path = (self._tmp_path / token).resolve() + response_path = (self._response_path / session_id).resolve() + try: + os.mkdir(tmp_path) + except FileExistsError: + pass + try: + os.mkdir(response_path) + except FileExistsError: + pass + geometry_file = request.files['geometry_file'] + if not self._allowed_extensions(geometry_file.filename): + shutil.rmtree(tmp_path) + return Response(json.dumps({'error': 'Unsupported media type'}), status=415, headers=response_token) + filename = secure_filename(geometry_file.filename) + file_path = os.path.join(tmp_path, filename) + geometry_file.save(file_path) + if self._file_extension == '.geojson': + self._city = self._geojson(file_path) + else: + self._city = self._citygml(file_path) + if self._city is None: + shutil.rmtree(tmp_path) + return Response(json.dumps({'error': 'Bad request'}), status=400, headers=response_token) + construction_handler = request.form.get('construction_handler') + usage_handler = request.form.get('usage_handler') + WeatherFactory('epw', self._city).enrich() + ConstructionFactory(construction_handler, self._city).enrich() + UsageFactory(usage_handler, self._city).enrich() + _idf = EnergyBuildingsExportsFactory('idf', self._city, tmp_path).export() + _idf.run() + result_files = [ + str((tmp_path / f'{self._city.name}_out.csv').resolve()), + str((tmp_path / f'{self._city.name}_out.eso').resolve()), + str((tmp_path / f'{self._city.name}.idf').resolve()), + ] + result_zip = (response_path / f'{token}.zip').resolve() + with zipfile.ZipFile(result_zip, 'w') as zip: + for result_file in result_files: + zip.write(result_file, Path(result_file).name) + shutil.rmtree(tmp_path) + return Response(json.dumps({ + 'result': 'succeed', + 'file': f'{self.base_uri}/{session_id}/{token}.zip' + }), status=200, headers=response_token) diff --git a/hub_api/workflow/insel_montly_energy_balance.py b/hub_api/workflow/insel_montly_energy_balance.py index 7bb24f0..45bc53b 100644 --- a/hub_api/workflow/insel_montly_energy_balance.py +++ b/hub_api/workflow/insel_montly_energy_balance.py @@ -105,7 +105,7 @@ class InselMonthlyEnergyBalance(Resource, Config): EnergyBuildingsExportsFactory('insel_monthly_energy_balance', self._city, tmp_path).export() for building in self._city.buildings: insel_path = (tmp_path / f'{building.name}.insel') - subprocess.run([self.insel, str(insel_path)]) + subprocess.run([self.insel, str(insel_path)], stdout=subprocess.DEVNULL) ResultFactory('insel_monthly_energy_balance', self._city, tmp_path).enrich() results = {} for building in self._city.buildings: