Compare commits
62 Commits
server_ins
...
main
Author | SHA1 | Date | |
---|---|---|---|
34779af69a | |||
55638fe147 | |||
a9af75bc3b | |||
7b45317d4b | |||
bb0bd4d31f | |||
ce18c72fe3 | |||
73641f985e | |||
8fbfd4be89 | |||
3be3121922 | |||
d7634a0d45 | |||
5097c69761 | |||
7a5fa1f09b | |||
b49e70c0d2 | |||
f5b48fee4e | |||
921034a884 | |||
d66ad362c7 | |||
1371f3f2e5 | |||
20c7425637 | |||
7b1cc06454 | |||
a322c59787 | |||
6d4c1437a7 | |||
d98674a286 | |||
31ebe48f68 | |||
c414de2635 | |||
3f35ce1c2c | |||
7753d69a54 | |||
8cfd6ebe82 | |||
0623faaafc | |||
8223d6ff2a | |||
d059a75e8d | |||
087845f83f | |||
6f344c8f2c | |||
3d3a20f560 | |||
0661ab21ec | |||
ab4745bca9 | |||
087cc7dc97 | |||
62f0b0bda0 | |||
242b59bfff | |||
31b52f6d37 | |||
e58d8d4cc9 | |||
b96617bb21 | |||
b06a1d3d53 | |||
|
cb9b9bb251 | ||
beed9f4e22 | |||
d9a464e6d9 | |||
fdf3491dc1 | |||
f0438e6776 | |||
aeedbd370c | |||
bc377bc1fb | |||
98a60fa0a1 | |||
1a4a72bc1a | |||
305eca2718 | |||
c30bf275c5 | |||
bc94ea04c5 | |||
20fc9fa6a5 | |||
1a38c83dee | |||
52bb8afa13 | |||
ec90dd1e0d | |||
01281e4b31 | |||
72b90557ff | |||
68ae20f85d | |||
d02b1cd838 |
45
bootstrap.py
45
bootstrap.py
|
@ -9,6 +9,7 @@ import datetime
|
||||||
|
|
||||||
import flask
|
import flask
|
||||||
import yaml
|
import yaml
|
||||||
|
from pathlib import Path
|
||||||
from flasgger import LazyJSONEncoder, Swagger
|
from flasgger import LazyJSONEncoder, Swagger
|
||||||
from flask import Response
|
from flask import Response
|
||||||
from flask_restful import Api
|
from flask_restful import Api
|
||||||
|
@ -17,12 +18,20 @@ import threading
|
||||||
import hub_api.helpers.session_helper as sh
|
import hub_api.helpers.session_helper as sh
|
||||||
from hub_api.control.session import SessionStart, SessionEnd, KeepSessionAlive
|
from hub_api.control.session import SessionStart, SessionEnd, KeepSessionAlive
|
||||||
from hub_api.control.uptime import Uptime
|
from hub_api.control.uptime import Uptime
|
||||||
from hub_api.buildings.meb import Meb
|
from hub_api.persistence.full_retrofit_results import FullRetrofitResults
|
||||||
from hub_api.geolocation.reverse import Reverse
|
from hub_api.persistence.retrofit_results import RetrofitResults
|
||||||
|
from hub_api.workflow.insel_montly_energy_balance import InselMonthlyEnergyBalance
|
||||||
|
from hub_api.workflow.costs import Costs
|
||||||
|
from hub_api.workflow.energy_plus import EnergyPlus
|
||||||
|
from hub_api.workflow.glb import Glb
|
||||||
|
from hub_api.energy_plus.idf_generator import IdfGenerator
|
||||||
|
from hub_api.config import Config
|
||||||
|
|
||||||
|
|
||||||
sh.begin_time = datetime.datetime.now()
|
sh.begin_time = datetime.datetime.now()
|
||||||
app = flask.Flask('cerc_api')
|
app = flask.Flask('cerc_api')
|
||||||
app.json_provider_class = LazyJSONEncoder
|
app.json_provider_class = LazyJSONEncoder
|
||||||
|
app.config['MAX_CONTENT_LENGTH'] = int(Config.max_file_size())
|
||||||
api = Api(app)
|
api = Api(app)
|
||||||
|
|
||||||
api.add_resource(Uptime, '/v1.4/uptime')
|
api.add_resource(Uptime, '/v1.4/uptime')
|
||||||
|
@ -30,27 +39,36 @@ api.add_resource(Uptime, '/v1.4/uptime')
|
||||||
# Session
|
# Session
|
||||||
api.add_resource(SessionStart, '/v1.4/session/start')
|
api.add_resource(SessionStart, '/v1.4/session/start')
|
||||||
api.add_resource(SessionEnd, '/v1.4/session/end')
|
api.add_resource(SessionEnd, '/v1.4/session/end')
|
||||||
api.add_resource(KeepSessionAlive, '/v1.4/session/keep_alive')
|
api.add_resource(KeepSessionAlive, '/v1.4/session/keep-alive')
|
||||||
|
|
||||||
# Buildings
|
# persistence
|
||||||
api.add_resource(Meb, '/v1.4/buildings/meb')
|
api.add_resource(RetrofitResults, '/v1.4/persistence/retrofit-results')
|
||||||
|
api.add_resource(FullRetrofitResults, '/v1.4/persistence/full-retrofit-results')
|
||||||
|
|
||||||
# GeoLocation
|
# energy plus
|
||||||
api.add_resource(Reverse, '/v1.4/geolocation/reverse/<latitude>/<longitude>', endpoint='reverse')
|
api.add_resource(IdfGenerator, '/v1.4/energy-plus/idf-generator')
|
||||||
|
|
||||||
with open("hub_api/docs/openapi-specs.yml", "r") as stream:
|
# workflows
|
||||||
|
api.add_resource(Costs, '/v1.4/workflow/costs')
|
||||||
|
api.add_resource(EnergyPlus, '/v1.4/workflow/energy-plus')
|
||||||
|
api.add_resource(Glb, '/v1.4/workflow/glb')
|
||||||
|
api.add_resource(InselMonthlyEnergyBalance, '/v1.4/workflow/insel-monthly-energy-balance')
|
||||||
|
|
||||||
|
yml_path = Path('./docs/openapi-specs.yml').resolve()
|
||||||
|
|
||||||
|
with open(yml_path, "r") as stream:
|
||||||
swagger_config = {
|
swagger_config = {
|
||||||
"headers": [],
|
"headers": [],
|
||||||
"specs": [
|
"specs": [
|
||||||
{
|
{
|
||||||
"endpoint": 'apispec',
|
"endpoint": '/api/apispec',
|
||||||
"route": '/v1.4/apispec.json',
|
"route": '/api/apispec/apispec.json',
|
||||||
"rule_filter": lambda rule: True, # all in
|
"rule_filter": lambda rule: True, # all in
|
||||||
"model_filter": lambda tag: True, # all in
|
"model_filter": lambda tag: True, # all in
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"static_url_path": "/api/v1.4/static",
|
"static_url_path": "/api/static",
|
||||||
"specs_route": "/v1.4/api-docs/",
|
"specs_route": "/api/api-docs/",
|
||||||
"openapi": "3.0.0"
|
"openapi": "3.0.0"
|
||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
|
@ -63,7 +81,8 @@ with open("hub_api/docs/openapi-specs.yml", "r") as stream:
|
||||||
def home():
|
def home():
|
||||||
return Response(headers={'Access-Control-Allow-Origin': '*'})
|
return Response(headers={'Access-Control-Allow-Origin': '*'})
|
||||||
|
|
||||||
sh.debug_mode = True
|
|
||||||
|
sh.debug_mode = False
|
||||||
|
|
||||||
threading.Thread(target=sh.expired_sessions_collector, daemon=True, args="5").start()
|
threading.Thread(target=sh.expired_sessions_collector, daemon=True, args="5").start()
|
||||||
app.run(port=15789, host="0.0.0.0", debug=sh.debug_mode)
|
app.run(port=15789, host="0.0.0.0", debug=sh.debug_mode)
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
import json
|
|
||||||
|
|
||||||
from flask import Response, request
|
|
||||||
from flask_restful import Resource
|
|
||||||
|
|
||||||
from hub_api.config import Config
|
|
||||||
from hub_api.helpers.session_helper import session, refresh_session
|
|
||||||
|
|
||||||
|
|
||||||
class Meb(Resource, Config):
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
def post(self):
|
|
||||||
"""
|
|
||||||
API call for requesting a specified list of enriched buildings
|
|
||||||
"""
|
|
||||||
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)
|
|
||||||
if _session is None:
|
|
||||||
return Response(json.dumps({'error': 'unauthorized'}), status=403)
|
|
||||||
application_id = session(session_id)['application_id']
|
|
||||||
user_id = session(session_id)['user_id']
|
|
||||||
token = {'token': _session['token']}
|
|
||||||
payload = request.get_json()
|
|
||||||
results = self.export_db_factory.results(user_id, application_id, payload)
|
|
||||||
if results == {}:
|
|
||||||
# no data found for the given parameters
|
|
||||||
return Response(json.dumps({'result': 'succeed', 'results': results}), status=200, headers=token)
|
|
||||||
# deserialize the response to return pure json
|
|
||||||
city_name = next(iter(results))
|
|
||||||
for building_results in results[city_name]:
|
|
||||||
values = []
|
|
||||||
for value in building_results['insel meb']:
|
|
||||||
key = next(iter(value))
|
|
||||||
values.append({key: json.loads(value[key])})
|
|
||||||
building_results['insel meb'] = values
|
|
||||||
return Response(json.dumps({'result': 'succeed', 'results': results}), status=200, headers=token)
|
|
|
@ -3,25 +3,73 @@ Config
|
||||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
Copyright © 2023 Project Peter Yefi peteryefi@gmail.com
|
Copyright © 2023 Project Peter Yefi peteryefi@gmail.com
|
||||||
"""
|
"""
|
||||||
|
import distutils
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from dotenv import load_dotenv
|
||||||
from hub.exports.db_factory import DBFactory as CityExportFactory
|
import hub.helpers.dictionaries
|
||||||
from hub.imports.db_factory import DBFactory
|
from hub.persistence.db_control import DBControl
|
||||||
|
from hub.persistence.repository import Repository
|
||||||
|
from hub.catalog_factories.energy_systems_catalog_factory import EnergySystemsCatalogFactory
|
||||||
|
from pymongo import MongoClient
|
||||||
|
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
dotenv_path = "{}/.env".format(os.path.expanduser('~'))
|
dotenv_path = "{}/.local/etc/hub_api/.env".format(os.path.expanduser('~'))
|
||||||
if platform.system() == 'Linux':
|
if platform.system() == 'Linux':
|
||||||
dotenv_path = Path('/usr/local/etc/hub/.env').resolve()
|
dotenv_path = Path(dotenv_path).resolve()
|
||||||
|
environment = 'PROD'
|
||||||
|
load_dotenv(dotenv_path=dotenv_path)
|
||||||
|
self._database_name = os.getenv(f'{environment}_DB_NAME')
|
||||||
|
self._database = DBControl(db_name=self._database_name, app_env=environment, dotenv_path=dotenv_path)
|
||||||
|
self._repository = Repository(db_name=self._database_name, app_env=environment, dotenv_path=dotenv_path)
|
||||||
|
self._energy_systems_catalog = EnergySystemsCatalogFactory('montreal_custom').catalog
|
||||||
|
self._dictionaries = {
|
||||||
|
'pluto': hub.helpers.dictionaries.Dictionaries().pluto_function_to_hub_function,
|
||||||
|
'htf': hub.helpers.dictionaries.Dictionaries().hft_function_to_hub_function,
|
||||||
|
'montreal': hub.helpers.dictionaries.Dictionaries().montreal_function_to_hub_function,
|
||||||
|
'alkis': hub.helpers.dictionaries.Dictionaries().alkis_function_to_hub_function,
|
||||||
|
'eilat': hub.helpers.dictionaries.Dictionaries().eilat_function_to_hub_function
|
||||||
|
}
|
||||||
|
# mongodb
|
||||||
|
_mongodb = os.getenv(f'{environment}_MONGO_DB')
|
||||||
|
_mongodb_database = os.getenv(f'{environment}_MONGO_DB_DATABASE')
|
||||||
|
self._mongodb_collection_prefix = os.getenv(f'{environment}_MONGO_DB_COLLECTION_PREFIX')
|
||||||
|
_client = MongoClient(_mongodb)
|
||||||
|
self._montreal_retrofit_db = _client[_mongodb_database]
|
||||||
|
|
||||||
environment = 'TEST'
|
|
||||||
database_name = 'persistence_test'
|
|
||||||
|
|
||||||
self.export_db_factory = CityExportFactory(db_name=database_name, app_env=environment,
|
@property
|
||||||
dotenv_path=dotenv_path)
|
def database(self):
|
||||||
self.import_db_factory = DBFactory(db_name=database_name, app_env=environment,
|
return self._database
|
||||||
dotenv_path=dotenv_path)
|
|
||||||
|
@property
|
||||||
|
def repository(self):
|
||||||
|
return self._repository
|
||||||
|
|
||||||
|
@property
|
||||||
|
def energy_systems_catalog(self):
|
||||||
|
return self._energy_systems_catalog
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def max_file_size():
|
||||||
|
return 10 * 1024 * 1024 # 10 MB
|
||||||
|
|
||||||
|
@property
|
||||||
|
def insel(self):
|
||||||
|
return distutils.spawn.find_executable('insel')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def sra(self):
|
||||||
|
return distutils.spawn.find_executable('sra')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def montreal_retrofit_db(self):
|
||||||
|
return self._montreal_retrofit_db
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mongodb_collection_prefix(self):
|
||||||
|
return self._mongodb_collection_prefix
|
||||||
|
|
|
@ -7,7 +7,7 @@ Copyright © 2022 Project Author name guillermo.gutierrezmorote@concordia.ca
|
||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
import uuid
|
import uuid
|
||||||
|
from sqlalchemy.exc import SQLAlchemyError
|
||||||
from flask import request, Response
|
from flask import request, Response
|
||||||
from flask_restful import Resource
|
from flask_restful import Resource
|
||||||
|
|
||||||
|
@ -22,9 +22,13 @@ class SessionStart(Resource, Config):
|
||||||
def put(self):
|
def put(self):
|
||||||
username = request.headers.get('username', None)
|
username = request.headers.get('username', None)
|
||||||
password = request.headers.get('password', None)
|
password = request.headers.get('password', None)
|
||||||
application_uuid = request.headers.get('application_uuid', None)
|
try:
|
||||||
|
application_uuid = uuid.UUID(request.headers.get('application-uuid', None))
|
||||||
|
user_info = self.database.user_login(name=username, password=password, application_uuid=application_uuid)
|
||||||
|
except (ValueError, SQLAlchemyError, TypeError):
|
||||||
|
return Response(json.dumps({'error': 'unauthorized'}), status=403)
|
||||||
ip = request.remote_addr
|
ip = request.remote_addr
|
||||||
user_info = self.export_db_factory.user_login(name=username, password=password, application_uuid=application_uuid)
|
print(f'received valid login from {ip} {user_info}')
|
||||||
if user_info:
|
if user_info:
|
||||||
session_id = str(uuid.uuid4())
|
session_id = str(uuid.uuid4())
|
||||||
token = str(uuid.uuid4())
|
token = str(uuid.uuid4())
|
||||||
|
@ -36,45 +40,45 @@ class SessionStart(Resource, Config):
|
||||||
'application_id': user_info.application_id,
|
'application_id': user_info.application_id,
|
||||||
'application_uuid': application_uuid,
|
'application_uuid': application_uuid,
|
||||||
'ip': ip,
|
'ip': ip,
|
||||||
'cities': []
|
'scenarios': []
|
||||||
}
|
}
|
||||||
cities = self.export_db_factory.cities_by_user_and_application(user_info.id, user_info.application_id)
|
cities = self.database.cities_by_user_and_application(user_info.id, user_info.application_id)
|
||||||
for city in cities:
|
for city in cities:
|
||||||
session['cities'].append({
|
if city.scenario not in session['scenarios']:
|
||||||
"name": city.name,
|
session['scenarios'].append(city.scenario)
|
||||||
"geometric_level_of_detail": city.level_of_detail
|
|
||||||
})
|
|
||||||
sessions[session_id] = session
|
sessions[session_id] = session
|
||||||
response = Response(json.dumps({'cities': session['cities'], 'result': 'OK'}), status=200)
|
response = Response(json.dumps({'scenarios': session['scenarios'], 'result': 'OK'}), status=200)
|
||||||
response.headers['session_id'] = session_id
|
response.headers['session_id'] = session_id
|
||||||
response.headers['token'] = token
|
response.headers['token'] = token
|
||||||
return response
|
return response
|
||||||
|
|
||||||
return Response(json.dumps({'error': 'unauthorized'}), status=403)
|
return Response(json.dumps({'error': 'unauthorized'}), status=403)
|
||||||
|
|
||||||
|
|
||||||
class SessionEnd(Resource):
|
class SessionEnd(Resource):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def put():
|
def put():
|
||||||
session_id = request.headers.get('session_id', None)
|
session_id = request.headers.get('session-id', None)
|
||||||
token = request.headers.get('token', None)
|
token = request.headers.get('token', None)
|
||||||
application_uuid = request.headers.get('application_uuid', None)
|
application_uuid = request.headers.get('application-uuid', None)
|
||||||
|
|
||||||
if remove_session(session_id, token, application_uuid):
|
if remove_session(session_id, token, application_uuid):
|
||||||
return Response(json.dumps({'result': 'succeed'}), status=200)
|
return Response(json.dumps({'result': 'succeed'}), status=200)
|
||||||
return Response(json.dumps({'error': 'unauthorized'}), status=403)
|
return Response(json.dumps({'error': 'unauthorized'}), status=403)
|
||||||
|
|
||||||
|
|
||||||
class KeepSessionAlive(Resource):
|
class KeepSessionAlive(Resource):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def put():
|
def put():
|
||||||
session_id = request.headers.get('session_id', None)
|
session_id = request.headers.get('session-id', None)
|
||||||
token = request.headers.get('token', None)
|
token = request.headers.get('token', None)
|
||||||
application_uuid = request.headers.get('application_uuid', None)
|
application_uuid = request.headers.get('application-uuid', None)
|
||||||
_session = refresh_session(session_id, token, application_uuid)
|
_session = refresh_session(session_id, token, application_uuid)
|
||||||
|
|
||||||
if _session is None:
|
if _session is None:
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
54
hub_api/energy_plus/idf_generator.py
Normal file
54
hub_api/energy_plus/idf_generator.py
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
import datetime
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from flask import request, Response, make_response, send_file
|
||||||
|
from flask_restful import Resource
|
||||||
|
from hub.city_model_structure.city import City
|
||||||
|
from hub.exports.energy_building_exports_factory import EnergyBuildingsExportsFactory
|
||||||
|
from hub.version import __version__ as version
|
||||||
|
|
||||||
|
from hub_api.config import Config
|
||||||
|
from hub_api.helpers.session_helper import refresh_session, session
|
||||||
|
|
||||||
|
|
||||||
|
class IdfGenerator(Resource, Config):
|
||||||
|
def __init__(self):
|
||||||
|
self._tmp_path = (Path(__file__).parent / 'tmp').resolve()
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
"""
|
||||||
|
API call generate the IDF file for the input data
|
||||||
|
"""
|
||||||
|
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)
|
||||||
|
if _session is None:
|
||||||
|
return Response(json.dumps({'error': 'unauthorized'}), status=403)
|
||||||
|
else:
|
||||||
|
token = _session['token']
|
||||||
|
application_id = session(session_id)['application_id']
|
||||||
|
user_id = session(session_id)['user_id']
|
||||||
|
|
||||||
|
tmp_path = (self._tmp_path / token).resolve()
|
||||||
|
try:
|
||||||
|
os.mkdir(tmp_path)
|
||||||
|
except FileExistsError:
|
||||||
|
pass
|
||||||
|
payload = request.get_json()
|
||||||
|
for key, value in payload.items():
|
||||||
|
db_city = self.database.get_city(self.database.building(value, user_id, application_id, key).city_id)
|
||||||
|
|
||||||
|
if version != db_city.hub_release:
|
||||||
|
return Response(json.dumps({
|
||||||
|
'error': 'The selected building belongs to an old hub release and cannot be loaded.'
|
||||||
|
}), status=422)
|
||||||
|
idf_file = tmp_path/db_city.name
|
||||||
|
city = City.load_compressed(db_city.pickle_path, idf_file)
|
||||||
|
EnergyBuildingsExportsFactory('idf', city, tmp_path, target_buildings=[value]).export()
|
||||||
|
response = make_response(send_file(idf_file))
|
||||||
|
response.headers['token'] = token
|
||||||
|
return response
|
2
hub_api/energy_plus/tmp/.gitignore
vendored
Normal file
2
hub_api/energy_plus/tmp/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
*
|
||||||
|
!.gitignore
|
|
@ -1,35 +0,0 @@
|
||||||
import json
|
|
||||||
import math
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from flask import Response
|
|
||||||
from flask.views import MethodView
|
|
||||||
|
|
||||||
from hub_api.config import Config
|
|
||||||
|
|
||||||
|
|
||||||
class Reverse(MethodView, Config):
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__()
|
|
||||||
self._reverse_path = Path(Path(__file__).parent.parent / 'data/cities15000.txt').resolve()
|
|
||||||
|
|
||||||
def get(self, latitude: float, longitude: float):
|
|
||||||
latitude = float(latitude)
|
|
||||||
longitude = float(longitude)
|
|
||||||
distance = math.inf
|
|
||||||
country = 'unknown'
|
|
||||||
city = 'unknown'
|
|
||||||
with open(self._reverse_path, 'r') as f:
|
|
||||||
for line_number, line in enumerate(f):
|
|
||||||
fields = line.split('\t')
|
|
||||||
file_city_name = fields[1]
|
|
||||||
file_latitude = float(fields[4])
|
|
||||||
file_longitude = float(fields[5])
|
|
||||||
file_country_code = fields[8]
|
|
||||||
new_distance = math.sqrt(pow((latitude-file_latitude),2) + pow((longitude-file_longitude),2))
|
|
||||||
if distance > new_distance:
|
|
||||||
distance = new_distance
|
|
||||||
country = file_country_code
|
|
||||||
city = file_city_name
|
|
||||||
return Response(json.dumps({'country': country, 'city':city}), status=200)
|
|
||||||
|
|
|
@ -3,9 +3,13 @@ Session helper
|
||||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
Copyright © 2022 Project Author name guillermo.gutierrezmorote@concordia.ca
|
Copyright © 2022 Project Author name guillermo.gutierrezmorote@concordia.ca
|
||||||
"""
|
"""
|
||||||
import uuid
|
|
||||||
import datetime
|
import datetime
|
||||||
|
import shutil
|
||||||
import time
|
import time
|
||||||
|
import uuid
|
||||||
|
from copy import deepcopy
|
||||||
|
from glob import glob
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
sessions = {}
|
sessions = {}
|
||||||
begin_time = None
|
begin_time = None
|
||||||
|
@ -16,17 +20,26 @@ construction_catalog = None
|
||||||
usage_catalog = None
|
usage_catalog = None
|
||||||
debug_mode = False
|
debug_mode = False
|
||||||
|
|
||||||
|
|
||||||
def expired_sessions_collector(session_timeout_duration):
|
def expired_sessions_collector(session_timeout_duration):
|
||||||
"""
|
"""
|
||||||
Goes through each session in sessions and removes expired session(s)
|
Goes through each session in sessions and removes expired session(s)
|
||||||
"""
|
"""
|
||||||
while True:
|
while True:
|
||||||
if bool(sessions):
|
if bool(sessions):
|
||||||
for _session in list(sessions):
|
_sessions = deepcopy(sessions)
|
||||||
_expire = datetime.datetime.strptime(sessions[session]['expire'], '%Y-%m-%d %H:%M:%S.%f')
|
for session_uuid in _sessions:
|
||||||
|
_expire = datetime.datetime.strptime(_sessions[session_uuid]['expire'], '%Y-%m-%d %H:%M:%S.%f')
|
||||||
if _expire < datetime.datetime.now():
|
if _expire < datetime.datetime.now():
|
||||||
print("session with session_id: ", session, "expired.")
|
print("session for user: ", _sessions[session_uuid]['user'], "expired.")
|
||||||
del sessions[session]
|
response_path = (Path(__file__).parent.parent / f'response_files/{session_uuid}').resolve()
|
||||||
|
if response_path.exists():
|
||||||
|
shutil.rmtree(response_path)
|
||||||
|
del sessions[session_uuid]
|
||||||
|
existing_directories = glob(f'{Path(__file__).parent.parent.resolve()}/response_files/*')
|
||||||
|
for directory in existing_directories:
|
||||||
|
if directory not in _sessions.keys():
|
||||||
|
shutil.rmtree(directory)
|
||||||
time.sleep(60 * int(session_timeout_duration))
|
time.sleep(60 * int(session_timeout_duration))
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,7 +51,7 @@ def _validate_session(session_id, token, application_uuid):
|
||||||
_session = session(session_id)
|
_session = session(session_id)
|
||||||
if debug_mode:
|
if debug_mode:
|
||||||
token = _session['token']
|
token = _session['token']
|
||||||
return bool(_session) and (_session['token'] == token) and _session['application_uuid'] == application_uuid
|
return bool(_session) and (_session['token'] == token) and str(_session['application_uuid']) == application_uuid
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
231
hub_api/mockup/building.py
Normal file
231
hub_api/mockup/building.py
Normal file
|
@ -0,0 +1,231 @@
|
||||||
|
"""
|
||||||
|
Mockup Building module
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2023 Concordia CERC group
|
||||||
|
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
import hub.helpers.constants as cte
|
||||||
|
from hub.helpers.dictionaries import Dictionaries
|
||||||
|
|
||||||
|
|
||||||
|
from hub_api.mockup.properties import *
|
||||||
|
|
||||||
|
|
||||||
|
class Building:
|
||||||
|
"""
|
||||||
|
Building class
|
||||||
|
"""
|
||||||
|
def __init__(self, building_info, results, catalog_archetype):
|
||||||
|
self._function = building_info.function
|
||||||
|
self._area = building_info.area
|
||||||
|
self._volume = building_info.volume
|
||||||
|
self._total_heating_area = building_info.total_heating_area
|
||||||
|
self._wall_area = building_info.wall_area
|
||||||
|
self._windows_area = building_info.windows_area
|
||||||
|
self._roof_area = building_info.roof_area
|
||||||
|
self._total_pv_area = building_info.total_pv_area
|
||||||
|
self._energy_systems_archetype_name = building_info.system_name
|
||||||
|
self._heating_consumption = {
|
||||||
|
cte.YEAR: results['insel meb']['yearly_heating_consumption'],
|
||||||
|
cte.MONTH: results['insel meb']['monthly_heating_consumption']
|
||||||
|
}
|
||||||
|
self._cooling_consumption = {
|
||||||
|
cte.YEAR: results['insel meb']['yearly_cooling_consumption'],
|
||||||
|
cte.MONTH: results['insel meb']['monthly_cooling_consumption']
|
||||||
|
}
|
||||||
|
self._domestic_hot_water_consumption = {
|
||||||
|
cte.YEAR: results['insel meb']['yearly_domestic_hot_water_consumption'],
|
||||||
|
cte.MONTH: results['insel meb']['monthly_domestic_hot_water_consumption']
|
||||||
|
}
|
||||||
|
self._lighting_electrical_demand = {
|
||||||
|
cte.YEAR: results['insel meb']['yearly_lighting_electrical_demand'],
|
||||||
|
cte.MONTH: results['insel meb']['monthly_lighting_electrical_demand']
|
||||||
|
}
|
||||||
|
self._appliances_electrical_demand = {
|
||||||
|
cte.YEAR: results['insel meb']['yearly_appliances_electrical_demand'],
|
||||||
|
cte.MONTH: results['insel meb']['monthly_appliances_electrical_demand']
|
||||||
|
}
|
||||||
|
self._heating_peak_load = {
|
||||||
|
cte.YEAR: results['insel meb']['yearly_heating_peak_load'],
|
||||||
|
cte.MONTH: results['insel meb']['monthly_heating_peak_load']
|
||||||
|
}
|
||||||
|
self._cooling_peak_load = {
|
||||||
|
cte.YEAR: results['insel meb']['yearly_cooling_peak_load'],
|
||||||
|
cte.MONTH: results['insel meb']['monthly_cooling_peak_load']
|
||||||
|
}
|
||||||
|
self._lighting_peak_load = {
|
||||||
|
cte.YEAR: results['insel meb']['yearly_lighting_peak_load'],
|
||||||
|
cte.MONTH: results['insel meb']['monthly_lighting_peak_load']
|
||||||
|
}
|
||||||
|
self._appliances_peak_load = {
|
||||||
|
cte.YEAR: results['insel meb']['yearly_appliances_peak_load'],
|
||||||
|
cte.MONTH: results['insel meb']['monthly_appliances_peak_load']
|
||||||
|
}
|
||||||
|
self._onsite_electrical_production = {
|
||||||
|
cte.YEAR: results['insel meb']['yearly_on_site_electrical_production'],
|
||||||
|
cte.MONTH: results['insel meb']['monthly_on_site_electrical_production']
|
||||||
|
}
|
||||||
|
self._catalog_archetype = catalog_archetype
|
||||||
|
|
||||||
|
@property
|
||||||
|
def function(self):
|
||||||
|
"""
|
||||||
|
Get building function
|
||||||
|
:return: str
|
||||||
|
"""
|
||||||
|
return self._function
|
||||||
|
|
||||||
|
@property
|
||||||
|
def volume(self):
|
||||||
|
"""
|
||||||
|
Get building volume in m3
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._volume
|
||||||
|
|
||||||
|
@property
|
||||||
|
def thermal_zones_from_internal_zones(self) -> [ThermalZone]:
|
||||||
|
"""
|
||||||
|
Get building thermal zones
|
||||||
|
:return: [ThermalZone]
|
||||||
|
"""
|
||||||
|
ground = ThermalBoundary()
|
||||||
|
ground.type = 'Ground'
|
||||||
|
ground.opaque_area = self._area
|
||||||
|
roof = ThermalBoundary()
|
||||||
|
roof.type = 'Roof'
|
||||||
|
roof.opaque_area = self._roof_area
|
||||||
|
wall = ThermalBoundary()
|
||||||
|
wall.type = 'Wall'
|
||||||
|
wall.opaque_area = self._wall_area
|
||||||
|
wall.window_ratio = self._windows_area / (self._windows_area + self._wall_area)
|
||||||
|
thermal_zone = ThermalZone()
|
||||||
|
thermal_zone.total_floor_area = self._total_heating_area
|
||||||
|
thermal_zone.thermal_boundaries = [roof, wall, ground]
|
||||||
|
return [thermal_zone]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def roofs(self) -> [Roof]:
|
||||||
|
"""
|
||||||
|
Get building roofs
|
||||||
|
:return: [Roof]
|
||||||
|
"""
|
||||||
|
polygon = Polygon()
|
||||||
|
polygon.area = self._roof_area
|
||||||
|
roof = Roof()
|
||||||
|
roof.solid_polygon = polygon
|
||||||
|
roof.solar_collectors_area_reduction_factor = self._total_pv_area / self._roof_area
|
||||||
|
return [roof]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def heating_consumption(self):
|
||||||
|
"""
|
||||||
|
Get building heating consumption in J
|
||||||
|
:return: dict
|
||||||
|
"""
|
||||||
|
return self._heating_consumption
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cooling_consumption(self):
|
||||||
|
"""
|
||||||
|
Get building cooling consumption in J
|
||||||
|
:return: dict
|
||||||
|
"""
|
||||||
|
return self._cooling_consumption
|
||||||
|
|
||||||
|
@property
|
||||||
|
def domestic_hot_water_consumption(self):
|
||||||
|
"""
|
||||||
|
Get building domestic hot water consumption in J
|
||||||
|
:return: dict
|
||||||
|
"""
|
||||||
|
return self._domestic_hot_water_consumption
|
||||||
|
|
||||||
|
@property
|
||||||
|
def lighting_electrical_demand(self):
|
||||||
|
"""
|
||||||
|
Get building lighting demand in J
|
||||||
|
:return: dict
|
||||||
|
"""
|
||||||
|
return self._lighting_electrical_demand
|
||||||
|
|
||||||
|
@property
|
||||||
|
def appliances_electrical_demand(self):
|
||||||
|
"""
|
||||||
|
Get building appliances electrical demand in J
|
||||||
|
:return: dict
|
||||||
|
"""
|
||||||
|
return self._appliances_electrical_demand
|
||||||
|
|
||||||
|
@property
|
||||||
|
def heating_peak_load(self):
|
||||||
|
"""
|
||||||
|
Get building heating peak load in W
|
||||||
|
:return: dict
|
||||||
|
"""
|
||||||
|
return self._heating_peak_load
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cooling_peak_load(self):
|
||||||
|
"""
|
||||||
|
Get building cooling peak load in W
|
||||||
|
:return: dict
|
||||||
|
"""
|
||||||
|
return self._cooling_peak_load
|
||||||
|
|
||||||
|
@property
|
||||||
|
def lighting_peak_load(self):
|
||||||
|
"""
|
||||||
|
Get building lighting peak load in W
|
||||||
|
:return: dict
|
||||||
|
"""
|
||||||
|
return self._lighting_peak_load
|
||||||
|
|
||||||
|
@property
|
||||||
|
def appliances_peak_load(self):
|
||||||
|
"""
|
||||||
|
Get building appliances peak load in W
|
||||||
|
:return: dict
|
||||||
|
"""
|
||||||
|
return self._appliances_peak_load
|
||||||
|
|
||||||
|
@property
|
||||||
|
def onsite_electrical_production(self):
|
||||||
|
"""
|
||||||
|
Get building onsite electrical production in J
|
||||||
|
:return: dict
|
||||||
|
"""
|
||||||
|
return self._onsite_electrical_production
|
||||||
|
|
||||||
|
@property
|
||||||
|
def energy_systems_archetype_name(self):
|
||||||
|
"""
|
||||||
|
Get energy systems archetype name
|
||||||
|
:return: dict
|
||||||
|
"""
|
||||||
|
return self._energy_systems_archetype_name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def energy_systems(self) -> [EnergySystem]:
|
||||||
|
"""
|
||||||
|
Get building energy systems
|
||||||
|
:return: [EnergySystem]
|
||||||
|
"""
|
||||||
|
_energy_systems = []
|
||||||
|
for system in self._catalog_archetype.systems:
|
||||||
|
_hub_demand_types = []
|
||||||
|
for demand_type in system.demand_types:
|
||||||
|
# todo: generalize this when we have more catalogs
|
||||||
|
_hub_demand_types.append(Dictionaries().montreal_demand_type_to_hub_energy_demand_type[demand_type])
|
||||||
|
demands = _hub_demand_types
|
||||||
|
fuel_type = Dictionaries().montreal_custom_fuel_to_hub_fuel[system.generation_systems[0].fuel_type]
|
||||||
|
generic_generation_system = GenericGenerationSystem()
|
||||||
|
generic_generation_system.fuel_type = fuel_type
|
||||||
|
generation_system = GenerationSystem()
|
||||||
|
generation_system.generic_generation_system = generic_generation_system
|
||||||
|
energy_system = EnergySystem()
|
||||||
|
energy_system.generation_system = generation_system
|
||||||
|
energy_system.demand_types = demands
|
||||||
|
_energy_systems.append(energy_system)
|
||||||
|
return _energy_systems
|
267
hub_api/mockup/properties.py
Normal file
267
hub_api/mockup/properties.py
Normal file
|
@ -0,0 +1,267 @@
|
||||||
|
"""
|
||||||
|
Properties module to be used by mockup building
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2023 Concordia CERC group
|
||||||
|
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
|
||||||
|
class EnergySystem:
|
||||||
|
"""
|
||||||
|
EnergySystem class
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
self._generation_system = None
|
||||||
|
self._demand_types = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def generation_system(self) -> GenerationSystem:
|
||||||
|
"""
|
||||||
|
Get building generation system
|
||||||
|
:return: GenerationSystem
|
||||||
|
"""
|
||||||
|
return self._generation_system
|
||||||
|
|
||||||
|
@generation_system.setter
|
||||||
|
def generation_system(self, value):
|
||||||
|
"""
|
||||||
|
Set building generation system
|
||||||
|
:param value: GenerationSystem
|
||||||
|
"""
|
||||||
|
self._generation_system = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def demand_types(self):
|
||||||
|
"""
|
||||||
|
Get demand types covered by an energy system
|
||||||
|
:return: [str]
|
||||||
|
"""
|
||||||
|
return self._demand_types
|
||||||
|
|
||||||
|
@demand_types.setter
|
||||||
|
def demand_types(self, value):
|
||||||
|
"""
|
||||||
|
Set demand types covered by an energy system
|
||||||
|
:param value: [str]
|
||||||
|
"""
|
||||||
|
self._demand_types = value
|
||||||
|
|
||||||
|
|
||||||
|
class GenerationSystem:
|
||||||
|
"""
|
||||||
|
GenerationSystem class
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._generic_generation_system = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def generic_generation_system(self) -> GenericGenerationSystem:
|
||||||
|
"""
|
||||||
|
Get generic generation system
|
||||||
|
:return: GenericGenerationSystem
|
||||||
|
"""
|
||||||
|
return self._generic_generation_system
|
||||||
|
|
||||||
|
@generic_generation_system.setter
|
||||||
|
def generic_generation_system(self, value):
|
||||||
|
"""
|
||||||
|
Set generic generation system
|
||||||
|
:param value: GenericGenerationSystem
|
||||||
|
"""
|
||||||
|
self._generic_generation_system = value
|
||||||
|
|
||||||
|
|
||||||
|
class GenericGenerationSystem:
|
||||||
|
"""
|
||||||
|
GenericGenerationSystem class
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._fuel_type = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fuel_type(self):
|
||||||
|
"""
|
||||||
|
Get fuel type of generic generation system
|
||||||
|
:return: str
|
||||||
|
"""
|
||||||
|
return self._fuel_type
|
||||||
|
|
||||||
|
@fuel_type.setter
|
||||||
|
def fuel_type(self, value):
|
||||||
|
"""
|
||||||
|
Set fuel type of generic generation system
|
||||||
|
:param value: str
|
||||||
|
"""
|
||||||
|
self._fuel_type = value
|
||||||
|
|
||||||
|
|
||||||
|
class ThermalZone:
|
||||||
|
"""
|
||||||
|
ThermalZone class
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._thermal_boundaries = None
|
||||||
|
self._total_floor_area = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def thermal_boundaries(self) -> [ThermalBoundary]:
|
||||||
|
"""
|
||||||
|
Get thermal boundaries
|
||||||
|
:return: [ThermalBoundary]
|
||||||
|
"""
|
||||||
|
return self._thermal_boundaries
|
||||||
|
|
||||||
|
@thermal_boundaries.setter
|
||||||
|
def thermal_boundaries(self, value):
|
||||||
|
"""
|
||||||
|
Set thermal boundaries
|
||||||
|
:param value: [ThermalBoundary]
|
||||||
|
"""
|
||||||
|
self._thermal_boundaries = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def total_floor_area(self):
|
||||||
|
"""
|
||||||
|
Get total floor area in m2
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._total_floor_area
|
||||||
|
|
||||||
|
@total_floor_area.setter
|
||||||
|
def total_floor_area(self, value):
|
||||||
|
"""
|
||||||
|
Get total floor area in m2
|
||||||
|
:param value: float
|
||||||
|
"""
|
||||||
|
self._total_floor_area = value
|
||||||
|
|
||||||
|
|
||||||
|
class ThermalBoundary:
|
||||||
|
"""
|
||||||
|
ThermalBoundary class
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._type = None
|
||||||
|
self._opaque_area = None
|
||||||
|
self._window_ratio = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self):
|
||||||
|
"""
|
||||||
|
Get thermal boundary type
|
||||||
|
:return: str
|
||||||
|
"""
|
||||||
|
return self._type
|
||||||
|
|
||||||
|
@type.setter
|
||||||
|
def type(self, value):
|
||||||
|
"""
|
||||||
|
Set thermal boundary type
|
||||||
|
:param value: str
|
||||||
|
"""
|
||||||
|
self._type = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def opaque_area(self):
|
||||||
|
"""
|
||||||
|
Get thermal boundary opaque area in m2
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._opaque_area
|
||||||
|
|
||||||
|
@opaque_area.setter
|
||||||
|
def opaque_area(self, value):
|
||||||
|
"""
|
||||||
|
Set thermal boundary opaque area in m2
|
||||||
|
:param value: float
|
||||||
|
"""
|
||||||
|
self._opaque_area = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def window_ratio(self):
|
||||||
|
"""
|
||||||
|
Get thermal boundary window ratio
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._window_ratio
|
||||||
|
|
||||||
|
@window_ratio.setter
|
||||||
|
def window_ratio(self, value):
|
||||||
|
"""
|
||||||
|
Set thermal boundary window ratio
|
||||||
|
:param value: float
|
||||||
|
"""
|
||||||
|
self._window_ratio = value
|
||||||
|
|
||||||
|
|
||||||
|
class Roof:
|
||||||
|
"""
|
||||||
|
Roof class
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._solid_polygon = None
|
||||||
|
self._solar_collectors_area_reduction_factor = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def solid_polygon(self) -> Polygon:
|
||||||
|
"""
|
||||||
|
Get solid polygon
|
||||||
|
:return: Polygon
|
||||||
|
"""
|
||||||
|
return self._solid_polygon
|
||||||
|
|
||||||
|
@solid_polygon.setter
|
||||||
|
def solid_polygon(self, value):
|
||||||
|
"""
|
||||||
|
Set solid polygon
|
||||||
|
:param value: Polygon
|
||||||
|
"""
|
||||||
|
self._solid_polygon = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def solar_collectors_area_reduction_factor(self):
|
||||||
|
"""
|
||||||
|
Get solar-collectors-area reduction factor
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._solar_collectors_area_reduction_factor
|
||||||
|
|
||||||
|
@solar_collectors_area_reduction_factor.setter
|
||||||
|
def solar_collectors_area_reduction_factor(self, value):
|
||||||
|
"""
|
||||||
|
Set solar-collectors-area reduction factor
|
||||||
|
:param value: float
|
||||||
|
"""
|
||||||
|
self._solar_collectors_area_reduction_factor = value
|
||||||
|
|
||||||
|
|
||||||
|
class Polygon:
|
||||||
|
"""
|
||||||
|
Polygon class
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._area = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def area(self):
|
||||||
|
"""
|
||||||
|
Get polygon area in m2
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._area
|
||||||
|
|
||||||
|
@area.setter
|
||||||
|
def area(self, value):
|
||||||
|
"""
|
||||||
|
Set polygon area in m2
|
||||||
|
:param value: float
|
||||||
|
"""
|
||||||
|
self._area = value
|
45
hub_api/persistence/full_retrofit_results.py
Normal file
45
hub_api/persistence/full_retrofit_results.py
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
import json
|
||||||
|
|
||||||
|
from flask import Response, request
|
||||||
|
from flask_restful import Resource
|
||||||
|
|
||||||
|
from hub_api.config import Config
|
||||||
|
from hub_api.helpers.session_helper import refresh_session
|
||||||
|
from hub_api.persistence.mock import dic
|
||||||
|
|
||||||
|
|
||||||
|
class FullRetrofitResults(Resource, Config):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
session_id = request.headers.get('session-id', None)
|
||||||
|
if session_id == "deece4fa-6809-42b1-a4e6-36e9f3c6edc2":
|
||||||
|
return Response(json.dumps(dic), status=200)
|
||||||
|
token = request.headers.get('token', None)
|
||||||
|
application_uuid = request.headers.get('application-uuid', None)
|
||||||
|
_session = refresh_session(session_id, token, application_uuid)
|
||||||
|
results = {'current status': [],
|
||||||
|
'skin retrofit': [],
|
||||||
|
'system retrofit and pv': [],
|
||||||
|
'skin and system retrofit with pv': []
|
||||||
|
}
|
||||||
|
if _session is None:
|
||||||
|
return Response(json.dumps({'error': 'unauthorized'}), status=403)
|
||||||
|
else:
|
||||||
|
response_token = {'token': _session['token']}
|
||||||
|
json_request = request.get_json()
|
||||||
|
for scenario in json_request['scenarios']:
|
||||||
|
for key, buildings in scenario.items():
|
||||||
|
mongodb_collection = f'{self.mongodb_collection_prefix}{key.replace(" ", "_")}'
|
||||||
|
building_query = ''
|
||||||
|
for building in buildings:
|
||||||
|
building_query = f'{building_query} {{"alias": "{building}"}},'
|
||||||
|
query = f'{{"$or": [{building_query[:-1]}]}}'
|
||||||
|
cursor = self.montreal_retrofit_db[mongodb_collection].find(json.loads(query))
|
||||||
|
for result in cursor:
|
||||||
|
del result['_id']
|
||||||
|
result['building'] = result['alias']
|
||||||
|
results[key].append(result)
|
||||||
|
return Response(json.dumps({'result': 'succeed', 'results': results}), status=200, headers=response_token)
|
1
hub_api/persistence/mock.py
Normal file
1
hub_api/persistence/mock.py
Normal file
File diff suppressed because one or more lines are too long
142
hub_api/persistence/retrofit_results.py
Normal file
142
hub_api/persistence/retrofit_results.py
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
import json
|
||||||
|
import threading
|
||||||
|
|
||||||
|
from co2_emission.co2_emission import Co2Emission
|
||||||
|
from costs.cost import Cost
|
||||||
|
from flask import Response, request
|
||||||
|
from flask_restful import Resource
|
||||||
|
|
||||||
|
from hub_api.config import Config
|
||||||
|
from hub_api.helpers.session_helper import session, refresh_session
|
||||||
|
from hub_api.mockup.building import Building
|
||||||
|
from hub_api.persistence.mock import dic
|
||||||
|
|
||||||
|
|
||||||
|
class RetrofitResults(Resource, Config):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self._scenario_ids = {'current status': 0,
|
||||||
|
'skin retrofit': 1,
|
||||||
|
'system retrofit and pv': 2,
|
||||||
|
'skin and system retrofit with pv': 3
|
||||||
|
}
|
||||||
|
|
||||||
|
def _calculate_building(self, building_results, user_id, application_id, scenario, scenario_id):
|
||||||
|
building_info = self.database.building(building_results['building'], user_id, application_id, scenario)
|
||||||
|
archetype = self.energy_systems_catalog.get_entry(building_info.system_name)
|
||||||
|
mockup_building = Building(building_info, building_results, archetype)
|
||||||
|
life_cycle = Cost(mockup_building, retrofit_scenario=scenario_id).life_cycle
|
||||||
|
operational_co2 = Co2Emission(mockup_building).operational_co2
|
||||||
|
global_capital_costs = life_cycle[f'Scenario {scenario_id}']['global_capital_costs']
|
||||||
|
global_operational_costs = life_cycle[f'Scenario {scenario_id}']['global_operational_costs']
|
||||||
|
global_capital_incomes = life_cycle[f'Scenario {scenario_id}']['global_capital_incomes']
|
||||||
|
global_maintenance_costs = life_cycle[f'Scenario {scenario_id}']['global_maintenance_costs']
|
||||||
|
building_results['total_heating_area'] = building_info.total_heating_area
|
||||||
|
building_results['year_of_construction'] = building_info.year_of_construction
|
||||||
|
building_results['function'] = building_info.function
|
||||||
|
building_results['costs'] = {
|
||||||
|
'total_capital_costs_skin': life_cycle[f'Scenario {scenario_id}']['total_capital_costs_skin'],
|
||||||
|
'total_capital_costs_systems': life_cycle[f'Scenario {scenario_id}']['total_capital_costs_systems'],
|
||||||
|
'end_of_life_costs': life_cycle[f'Scenario {scenario_id}']['end_of_life_costs'],
|
||||||
|
'total_operational_costs': life_cycle[f'Scenario {scenario_id}']['total_operational_costs'],
|
||||||
|
'total_maintenance_costs': life_cycle[f'Scenario {scenario_id}']['total_maintenance_costs'],
|
||||||
|
'operational_incomes': life_cycle[f'Scenario {scenario_id}']['operational_incomes'],
|
||||||
|
'capital_incomes': life_cycle[f'Scenario {scenario_id}']['capital_incomes'],
|
||||||
|
'global_capital_costs': {
|
||||||
|
'B2010_opaque_walls': global_capital_costs['B2010_opaque_walls'].tolist(),
|
||||||
|
'B2020_transparent': global_capital_costs['B2020_transparent'].tolist(),
|
||||||
|
'B3010_opaque_roof': global_capital_costs['B3010_opaque_roof'].tolist(),
|
||||||
|
'B10_superstructure': global_capital_costs['B10_superstructure'].tolist(),
|
||||||
|
'D3020_heat_generating_systems': global_capital_costs['D3020_heat_generating_systems'].tolist(),
|
||||||
|
'D3030_cooling_generation_systems': global_capital_costs['D3030_cooling_generation_systems'].tolist(),
|
||||||
|
'D3080_other_hvac_ahu': global_capital_costs['D3080_other_hvac_ahu'].tolist(),
|
||||||
|
'D5020_lighting_and_branch_wiring': global_capital_costs['D5020_lighting_and_branch_wiring'].tolist(),
|
||||||
|
'D301010_photovoltaic_system': global_capital_costs['D301010_photovoltaic_system'].tolist(),
|
||||||
|
},
|
||||||
|
'global_end_of_life_costs': life_cycle[f'Scenario {scenario_id}']['global_end_of_life_costs'][
|
||||||
|
'End_of_life_costs'].tolist(),
|
||||||
|
'global_operational_costs': {
|
||||||
|
'fixed_costs_electricity_peak': global_operational_costs['Fixed_costs_electricity_peak'].tolist(),
|
||||||
|
'fixed_costs_electricity_monthly': global_operational_costs['Fixed_costs_electricity_monthly'].tolist(),
|
||||||
|
'variable_costs_electricity': global_operational_costs['Variable_costs_electricity'].tolist(),
|
||||||
|
'fixed_costs_gas': global_operational_costs['Fixed_costs_gas'].tolist(),
|
||||||
|
'variable_costs_gas': global_operational_costs['Variable_costs_gas'].tolist()
|
||||||
|
},
|
||||||
|
'global_maintenance_costs': {
|
||||||
|
'heating_maintenance': global_maintenance_costs['Heating_maintenance'].tolist(),
|
||||||
|
'cooling_maintenance': global_maintenance_costs['Cooling_maintenance'].tolist(),
|
||||||
|
'pv_maintenance': global_maintenance_costs['PV_maintenance'].tolist(),
|
||||||
|
},
|
||||||
|
'global_operational_incomes': life_cycle[f'Scenario {scenario_id}']['global_operational_incomes'][
|
||||||
|
'Incomes electricity'].tolist(),
|
||||||
|
'global_capital_incomes': {
|
||||||
|
'subsidies_construction': global_capital_incomes['Subsidies construction'].tolist(),
|
||||||
|
'subsidies_hvac': global_capital_incomes['Subsidies HVAC'].tolist(),
|
||||||
|
'subsidies_pv': global_capital_incomes['Subsidies PV'].tolist()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
building_results['operational_co2'] = operational_co2
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
"""
|
||||||
|
API call for requesting a specified list of enriched persistence
|
||||||
|
"""
|
||||||
|
# todo: cost and co2 libs are using default canadians values, in the future need to be optionally API configurable
|
||||||
|
session_id = request.headers.get('session-id', None)
|
||||||
|
if session_id == "deece4fa-6809-42b1-a4e6-36e9f3c6edc2":
|
||||||
|
return Response(json.dumps(dic), status=200)
|
||||||
|
token = request.headers.get('token', None)
|
||||||
|
application_uuid = request.headers.get('application-uuid', None)
|
||||||
|
_session = refresh_session(session_id, token, application_uuid)
|
||||||
|
if _session is None:
|
||||||
|
return Response(json.dumps({'error': 'unauthorized'}), status=403)
|
||||||
|
else:
|
||||||
|
token = {'token': _session['token']}
|
||||||
|
application_id = session(session_id)['application_id']
|
||||||
|
user_id = session(session_id)['user_id']
|
||||||
|
payload = request.get_json()
|
||||||
|
if 'scenarios' not in payload:
|
||||||
|
return Response(json.dumps({'error': 'Bad request'}), status=400, headers=token)
|
||||||
|
results = self.database.results(user_id, application_id, payload)
|
||||||
|
if results == {}:
|
||||||
|
# no data found for the given parameters
|
||||||
|
return Response(json.dumps({'result': 'succeed', 'results': results}), status=200, headers=token)
|
||||||
|
# deserialize the response to return pure json
|
||||||
|
t = []
|
||||||
|
for scenario in results:
|
||||||
|
scenario_id = self._scenario_ids[scenario]
|
||||||
|
for building_results in results[scenario]:
|
||||||
|
f = threading.Thread(
|
||||||
|
target=self._calculate_building,
|
||||||
|
args=(building_results, user_id, application_id, scenario, scenario_id)
|
||||||
|
)
|
||||||
|
t.append(f)
|
||||||
|
f.start()
|
||||||
|
for f in t:
|
||||||
|
f.join()
|
||||||
|
return Response(json.dumps({'result': 'succeed', 'results': results}), status=200, headers=token)
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
session_id = request.headers.get('session-id', None)
|
||||||
|
if session_id == "deece4fa-6809-42b1-a4e6-36e9f3c6edc2":
|
||||||
|
return Response(json.dumps(dic), status=200)
|
||||||
|
token = request.headers.get('token', None)
|
||||||
|
application_uuid = request.headers.get('application-uuid', None)
|
||||||
|
_session = refresh_session(session_id, token, application_uuid)
|
||||||
|
results = {'meb': []}
|
||||||
|
if _session is None:
|
||||||
|
return Response(json.dumps({'error': 'unauthorized'}), status=403)
|
||||||
|
else:
|
||||||
|
response_token = {'token': _session['token']}
|
||||||
|
buildings = request.get_json()['buildings']
|
||||||
|
building_query = ''
|
||||||
|
for building in buildings:
|
||||||
|
building_query = f'{building_query} {{"alias": "{building}"}},'
|
||||||
|
query = f'{{"$or": [{building_query[:-1]}]}}'
|
||||||
|
cursor = self.mongodb_meb.find(json.loads(query))
|
||||||
|
for result in cursor:
|
||||||
|
del result['_id']
|
||||||
|
results['meb'].append(result)
|
||||||
|
|
||||||
|
return Response(json.dumps({'result': 'succeed', 'results': results}), status=200, headers=response_token)
|
2
hub_api/response_files/.gitignore
vendored
Normal file
2
hub_api/response_files/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
*
|
||||||
|
!.gitignore
|
16
hub_api/tests/test_api.py
Normal file
16
hub_api/tests/test_api.py
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
from unittest import TestCase
|
||||||
|
|
||||||
|
from hub_api.config import Config
|
||||||
|
from hub.persistence.models import City, Application, CityObject, SimulationResults, User
|
||||||
|
|
||||||
|
|
||||||
|
class TestApi(TestCase):
|
||||||
|
def test_create_database(self):
|
||||||
|
config = Config()
|
||||||
|
Application.__table__.create(bind=config.repository.engine, checkfirst=True)
|
||||||
|
User.__table__.create(bind=config.repository.engine, checkfirst=True)
|
||||||
|
City.__table__.create(bind=config.repository.engine, checkfirst=True)
|
||||||
|
CityObject.__table__.create(bind=config.repository.engine, checkfirst=True)
|
||||||
|
SimulationResults.__table__.create(bind=config.repository.engine, checkfirst=True)
|
||||||
|
config.database.create_user('Administrator', )
|
||||||
|
self.assertTrue(True)
|
14
hub_api/workflow/costs.py
Normal file
14
hub_api/workflow/costs.py
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
from flask_restful import Resource
|
||||||
|
|
||||||
|
from hub_api.config import Config
|
||||||
|
|
||||||
|
|
||||||
|
class Costs(Resource, Config):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
"""
|
||||||
|
API call for performing the cost workflow
|
||||||
|
"""
|
||||||
|
raise NotImplementedError()
|
117
hub_api/workflow/energy_plus.py
Normal file
117
hub_api/workflow/energy_plus.py
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import zipfile
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from flask import Response, request, send_file, make_response
|
||||||
|
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', '.gml']
|
||||||
|
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_to_hub')]
|
||||||
|
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 monthly energy balance workflow
|
||||||
|
"""
|
||||||
|
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)
|
||||||
|
if _session is None:
|
||||||
|
return Response(json.dumps({'error': 'unauthorized'}), status=403)
|
||||||
|
else:
|
||||||
|
response_token = {'token': _session['token']}
|
||||||
|
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 zf:
|
||||||
|
for result_file in result_files:
|
||||||
|
zf.write(result_file, Path(result_file).name)
|
||||||
|
shutil.rmtree(tmp_path)
|
||||||
|
response = make_response(send_file(result_zip))
|
||||||
|
response.headers['token'] = token
|
||||||
|
return response
|
105
hub_api/workflow/glb.py
Normal file
105
hub_api/workflow/glb.py
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
import glob
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import zipfile
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from flask import Response, request, send_file, make_response
|
||||||
|
from flask_restful import Resource
|
||||||
|
from hub.exports.exports_factory import ExportsFactory
|
||||||
|
from hub.imports.geometry_factory import GeometryFactory
|
||||||
|
from werkzeug.utils import secure_filename
|
||||||
|
|
||||||
|
from hub_api.config import Config
|
||||||
|
from hub_api.helpers.session_helper import refresh_session
|
||||||
|
|
||||||
|
|
||||||
|
class Glb(Resource, Config):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self._extensions = ['.geojson', '.gml']
|
||||||
|
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_to_hub')]
|
||||||
|
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 monthly energy balance workflow
|
||||||
|
"""
|
||||||
|
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)
|
||||||
|
if _session is None:
|
||||||
|
return Response(json.dumps({'error': 'unauthorized'}), status=403)
|
||||||
|
else:
|
||||||
|
response_token = {'token': _session['token']}
|
||||||
|
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)
|
||||||
|
ExportsFactory('glb', self._city, tmp_path).export()
|
||||||
|
result_files = glob.glob(f'{tmp_path}/*.glb')
|
||||||
|
result_zip = (response_path / f'{token}.zip').resolve()
|
||||||
|
with zipfile.ZipFile(result_zip, 'w') as zf:
|
||||||
|
for result_file in result_files:
|
||||||
|
zf.write(result_file, Path(result_file).name)
|
||||||
|
shutil.rmtree(tmp_path)
|
||||||
|
response = make_response(send_file(result_zip))
|
||||||
|
response.headers['token'] = token
|
||||||
|
return response
|
137
hub_api/workflow/insel_montly_energy_balance.py
Normal file
137
hub_api/workflow/insel_montly_energy_balance.py
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import hub.helpers.constants as cte
|
||||||
|
from flask import Response, request
|
||||||
|
from flask_restful import Resource
|
||||||
|
from hub.exports.energy_building_exports_factory import EnergyBuildingsExportsFactory
|
||||||
|
from hub.exports.exports_factory import ExportsFactory
|
||||||
|
from hub.imports.construction_factory import ConstructionFactory
|
||||||
|
from hub.imports.geometry_factory import GeometryFactory
|
||||||
|
from hub.imports.results_factory import ResultFactory
|
||||||
|
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 InselMonthlyEnergyBalance(Resource, Config):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self._extensions = ['.geojson', '.gml']
|
||||||
|
self._tmp_path = (Path(__file__).parent / 'tmp').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_to_hub')]
|
||||||
|
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')
|
||||||
|
if year_of_construction_field == '':
|
||||||
|
year_of_construction_field = None
|
||||||
|
function_field = request.form.get('function_field')
|
||||||
|
if function_field == '':
|
||||||
|
function_field = None
|
||||||
|
function_dictionary = self._dictionaries[request.form.get('function_to_hub')]
|
||||||
|
return GeometryFactory('citygml',
|
||||||
|
path=file_path,
|
||||||
|
year_of_construction_field=year_of_construction_field,
|
||||||
|
function_field=function_field,
|
||||||
|
function_to_hub=function_dictionary).city
|
||||||
|
# except KeyError:
|
||||||
|
# return None
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
"""
|
||||||
|
API call for performing the monthly energy balance workflow
|
||||||
|
"""
|
||||||
|
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)
|
||||||
|
if _session is None:
|
||||||
|
return Response(json.dumps({'error': 'unauthorized'}), status=403)
|
||||||
|
else:
|
||||||
|
response_token = {'token': _session['token']}
|
||||||
|
tmp_path = (self._tmp_path / token).resolve()
|
||||||
|
try:
|
||||||
|
os.mkdir(tmp_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()
|
||||||
|
ExportsFactory('sra', self._city, tmp_path).export()
|
||||||
|
sra_file = str((tmp_path / f'{self._city.name}_sra.xml').resolve())
|
||||||
|
subprocess.run([self.sra, sra_file], stdout=subprocess.DEVNULL)
|
||||||
|
ResultFactory('sra', self._city, tmp_path).enrich()
|
||||||
|
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)], stdout=subprocess.DEVNULL)
|
||||||
|
ResultFactory('insel_monthly_energy_balance', self._city, tmp_path).enrich()
|
||||||
|
results = {}
|
||||||
|
for building in self._city.buildings:
|
||||||
|
results[building.name] = {
|
||||||
|
'total_heating_area': building.floor_area * building.storeys_above_ground,
|
||||||
|
'year_of_construction': building.year_of_construction,
|
||||||
|
'function': building.function,
|
||||||
|
'monthly_heating_demand': building.heating_demand[cte.MONTH],
|
||||||
|
'yearly_heating_demand': building.heating_demand[cte.YEAR],
|
||||||
|
'monthly_cooling_demand': building.cooling_demand[cte.MONTH],
|
||||||
|
'yearly_cooling_demand': building.cooling_demand[cte.YEAR],
|
||||||
|
'monthly_lighting_peak_load': building.lighting_peak_load[cte.MONTH],
|
||||||
|
'yearly_lighting_peak_load': building.lighting_peak_load[cte.YEAR],
|
||||||
|
'monthly_appliances_peak_load': building.appliances_peak_load[cte.MONTH],
|
||||||
|
'yearly_appliances_peak_load': building.appliances_peak_load[cte.YEAR],
|
||||||
|
'monthly_cooling_peak_load': building.cooling_peak_load[cte.MONTH],
|
||||||
|
'yearly_cooling_peak_load': building.cooling_peak_load[cte.YEAR],
|
||||||
|
'monthly_heating_peak_load': building.heating_peak_load[cte.MONTH],
|
||||||
|
'yearly_heating_peak_load': building.heating_peak_load[cte.YEAR],
|
||||||
|
'monthly_lighting_electrical_demand': building.lighting_electrical_demand[cte.MONTH],
|
||||||
|
'yearly_lighting_electrical_demand': building.lighting_electrical_demand[cte.YEAR],
|
||||||
|
'monthly_appliances_electrical_demand': building.appliances_electrical_demand[cte.MONTH],
|
||||||
|
'yearly_appliances_electrical_demand': building.appliances_electrical_demand[cte.YEAR],
|
||||||
|
'monthly_domestic_hot_water_heat_demand': building.domestic_hot_water_heat_demand[cte.MONTH],
|
||||||
|
'yearly_domestic_hot_water_heat_demand': building.domestic_hot_water_heat_demand[cte.YEAR],
|
||||||
|
}
|
||||||
|
shutil.rmtree(tmp_path)
|
||||||
|
return Response(json.dumps({'result': 'succeed', 'results': results}), status=200, headers=response_token)
|
2
hub_api/workflow/tmp/.gitignore
vendored
Normal file
2
hub_api/workflow/tmp/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
.gitignore
|
||||||
|
!.gitignore
|
|
@ -27,3 +27,9 @@ flasgger
|
||||||
cerc-hub
|
cerc-hub
|
||||||
python-dotenv
|
python-dotenv
|
||||||
mapbox_earcut
|
mapbox_earcut
|
||||||
|
cerc-costs
|
||||||
|
cerc-co2-emission
|
||||||
|
werkzeug
|
||||||
|
sqlalchemy
|
||||||
|
pathlib
|
||||||
|
pymongo
|
Loading…
Reference in New Issue
Block a user