Merge remote-tracking branch 'origin/main' into main

# Conflicts:
#	hub_api/mockup/building.py
This commit is contained in:
Pilar Monsalvete 2023-08-08 16:01:15 -04:00
commit 31b52f6d37
6 changed files with 155 additions and 56 deletions

View File

@ -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
@ -47,20 +48,21 @@ api.add_resource(Costs, '/v1.4/workflow/costs')
api.add_resource(EnergyPlus, '/v1.4/workflow/energy-plus') api.add_resource(EnergyPlus, '/v1.4/workflow/energy-plus')
api.add_resource(InselMonthlyEnergyBalance, '/v1.4/workflow/insel-monthly-energy-balance') api.add_resource(InselMonthlyEnergyBalance, '/v1.4/workflow/insel-monthly-energy-balance')
yml_path = Path('./docs/openapi-specs.yml').resolve()
with open("hub_api/docs/openapi-specs.yml", "r") as stream: 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:

View File

@ -15,9 +15,9 @@ from hub.catalog_factories.energy_systems_catalog_factory import EnergySystemsCa
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('/home/guille/.local/etc/hub/.env').resolve() dotenv_path = Path(dotenv_path).resolve()
environment = 'TEST' environment = 'TEST'
database_name = 'montreal_retrofit_test' database_name = 'montreal_retrofit_test'

View File

@ -9,7 +9,7 @@ externalDocs:
description: Find out more about Swagger description: Find out more about Swagger
url: http://swagger.io url: http://swagger.io
paths: paths:
/v1.4/uptime: /api/v1.4/uptime:
get: get:
parameters: parameters:
[] []
@ -26,7 +26,7 @@ paths:
schema: schema:
$ref: '#/components/schemas/uptime' $ref: '#/components/schemas/uptime'
/v1.4/session/start: /api/v1.4/session/start:
put: put:
parameters: parameters:
- in: header - in: header
@ -78,7 +78,7 @@ paths:
schema: schema:
$ref: '#/components/schemas/unauthorized' $ref: '#/components/schemas/unauthorized'
/v1.4/session/keep-alive: /api/v1.4/session/keep-alive:
put: put:
security: security:
- session-id: [] - session-id: []
@ -111,7 +111,7 @@ paths:
schema: schema:
$ref: '#/components/schemas/unauthorized' $ref: '#/components/schemas/unauthorized'
/v1.4/session/end: /api/v1.4/session/end:
put: put:
security: security:
- session-id: [] - session-id: []
@ -144,7 +144,7 @@ paths:
schema: schema:
$ref: '#/components/schemas/unauthorized' $ref: '#/components/schemas/unauthorized'
/v1.4/persistence/retrofit-results: /api/v1.4/persistence/retrofit-results:
post: post:
security: security:
- session-id: [ ] - session-id: [ ]
@ -192,7 +192,7 @@ paths:
schema: schema:
$ref: '#/components/schemas/unauthorized' $ref: '#/components/schemas/unauthorized'
/v1.4/workflow/costs: /api/v1.4/workflow/costs:
post: post:
security: security:
- session-id: [ ] - session-id: [ ]
@ -213,7 +213,7 @@ paths:
schema: schema:
$ref: '#/components/schemas/not-implemented-error' $ref: '#/components/schemas/not-implemented-error'
/v1.4/workflow/energy-plus: /api/v1.4/workflow/energy-plus:
post: post:
security: security:
- session-id: [ ] - session-id: [ ]
@ -234,7 +234,7 @@ paths:
schema: schema:
$ref: '#/components/schemas/not-implemented-error' $ref: '#/components/schemas/not-implemented-error'
/v1.4/workflow/insel-monthly-energy-balance: /api/v1.4/workflow/insel-monthly-energy-balance:
post: post:
security: security:
- session-id: [ ] - session-id: [ ]

View File

@ -23,13 +23,14 @@ def expired_sessions_collector(session_timeout_duration):
""" """
while True: while True:
if bool(sessions): if bool(sessions):
for _session in list(sessions): for session_uuid in sessions:
print(_session) print(sessions)
_expire = datetime.datetime.strptime(_session['expire'], '%Y-%m-%d %H:%M:%S.%f') print(sessions[session_uuid]['expire'])
_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 for user: ", _session['username'], "expired.") print("session for user: ", sessions[session_uuid]['user'], "expired.")
del sessions[session] del sessions[session_uuid]
time.sleep(60 * int(session_timeout_duration)) time.sleep(60 * int(session_timeout_duration))

View File

@ -5,14 +5,19 @@ Copyright © 2023 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
""" """
from properties import * import hub.helpers.constants as cte
from hub.helpers.dictionaries import Dictionaries
from hub_api.mockup.properties import *
import pandas as pd
class Building: class Building:
""" """
Building class Building class
""" """
def __init__(self, building_info, catalog_archetype): def __init__(self, building_info, results, catalog_archetype):
self._function = building_info.function self._function = building_info.function
self._area = building_info.area self._area = building_info.area
self._volume = building_info.volume self._volume = building_info.volume
@ -21,14 +26,48 @@ class Building:
self._windows_area = building_info.windows_area self._windows_area = building_info.windows_area
self._roof_area = building_info.roof_area self._roof_area = building_info.roof_area
self._total_pv_area = building_info.total_pv_area self._total_pv_area = building_info.total_pv_area
self._heating_consumption = building_info.heating_consumption self._energy_systems_archetype_name = building_info.system_name
self._cooling_consumption = building_info.cooling_consumption self._heating_consumption = {
self._domestic_hot_water_consumption = building_info.domestic_hot_water_consumption cte.YEAR: results['yearly_heating_consumption'],
self._lighting_electrical_demand = building_info.lighting_electrical_demand cte.MONTH: results['monthly_heating_consumption']
self._appliances_electrical_demand = building_info.appliances_electrical_demand }
self._heating_peak_load = building_info.heating_peak_load print(self._heating_consumption)
self._cooling_peak_load = building_info.cooling_peak_load self._cooling_consumption = {
self._onsite_electrical_production = building_info.onsite_electrical_production cte.YEAR: results['yearly_cooling_consumption'],
cte.MONTH: results['monthly_cooling_consumption']
}
self._domestic_hot_water_consumption = {
cte.YEAR: results['yearly_domestic_hot_water_consumption'],
cte.MONTH: results['monthly_domestic_hot_water_consumption']
}
self._lighting_electrical_demand = {
cte.YEAR: pd.DataFrame(results['yearly_lighting_electrical_demand'], columns=['insel meb']),
cte.MONTH: pd.DataFrame(results['monthly_lighting_electrical_demand'], columns=['insel meb'])
}
self._appliances_electrical_demand = {
cte.YEAR: pd.DataFrame(results['yearly_appliances_electrical_demand'], columns=['insel meb']),
cte.MONTH: pd.DataFrame(results['monthly_appliances_electrical_demand'], columns=['insel meb'])
}
self._heating_peak_load = {
cte.YEAR: results['yearly_heating_peak_load'],
cte.MONTH: results['monthly_heating_peak_load']
}
self._cooling_peak_load = {
cte.YEAR: results['yearly_cooling_peak_load'],
cte.MONTH: results['monthly_cooling_peak_load']
}
self._lighting_peak_load = {
cte.YEAR: results['yearly_lighting_peak_load'],
cte.MONTH: results['monthly_lighting_peak_load']
}
self._appliances_peak_load = {
cte.YEAR: results['yearly_appliances_peak_load'],
cte.MONTH: results['monthly_appliances_peak_load']
}
self._onsite_electrical_production = {
cte.YEAR: results['yearly_on_site_electrical_production'],
cte.MONTH: results['monthly_on_site_electrical_production']
}
self._catalog_archetype = catalog_archetype self._catalog_archetype = catalog_archetype
@property @property
@ -137,6 +176,14 @@ class Building:
""" """
return self._cooling_peak_load return self._cooling_peak_load
@property
def lighting_peak_load(self):
return self._lighting_peak_load
@property
def appliances_peak_load(self):
return self._appliances_peak_load
@property @property
def onsite_electrical_production(self): def onsite_electrical_production(self):
""" """
@ -145,6 +192,10 @@ class Building:
""" """
return self._onsite_electrical_production return self._onsite_electrical_production
@property
def energy_systems_archetype_name(self):
return self._energy_systems_archetype_name
@property @property
def energy_systems(self) -> [EnergySystem]: def energy_systems(self) -> [EnergySystem]:
""" """
@ -154,8 +205,12 @@ class Building:
_energy_systems = [] _energy_systems = []
for system in self._catalog_archetype.systems: for system in self._catalog_archetype.systems:
demands = system.demand_types _hub_demand_types = []
fuel_type = system.generation_system.fuel_type 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_system.fuel_type]
generic_generation_system = GenericGenerationSystem() generic_generation_system = GenericGenerationSystem()
generic_generation_system.fuel_type = fuel_type generic_generation_system.fuel_type = fuel_type
generation_system = GenerationSystem() generation_system = GenerationSystem()

View File

@ -3,6 +3,8 @@ import json
from flask import Response, request from flask import Response, request
from flask_restful import Resource from flask_restful import Resource
from costs.cost import Cost
from co2_emission.co2_emission import Co2Emission
from hub_api.config import Config from hub_api.config import Config
from hub_api.helpers.session_helper import session, refresh_session from hub_api.helpers.session_helper import session, refresh_session
from hub_api.mockup.building import Building from hub_api.mockup.building import Building
@ -16,6 +18,7 @@ class RetrofitResults(Resource, Config):
""" """
API call for requesting a specified list of enriched persistence API call for requesting a specified list of enriched persistence
""" """
# todo: cost and co2 libraries are using default canadians values, in the future need to be optionally API configurable
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)
@ -29,32 +32,70 @@ class RetrofitResults(Resource, Config):
if 'scenarios' not in payload: if 'scenarios' not in payload:
return Response(json.dumps({'error': 'Bad request'}), status=400, headers=token) return Response(json.dumps({'error': 'Bad request'}), status=400, headers=token)
# retrieve the buildings info
buildings = []
buildings_info = []
scenario_name = None
for scenario in payload['scenarios']:
scenario_name = next(iter(scenario))
for name in scenario[scenario_name]:
if name not in buildings:
buildings.append(name)
for building in buildings:
building_info = self.database.building(building, user_id, application_id, scenario_name)
archetype = self.energy_systems_catalog.get_entry(building_info.system_name)
buildings_info.append(building_info)
buildings.append(Building(building_info, archetype))
results = self.database.results(user_id, application_id, payload) results = self.database.results(user_id, application_id, payload)
if results == {}: if results == {}:
# no data found for the given parameters # no data found for the given parameters
return Response(json.dumps({'result': 'succeed', 'results': results}), status=200, headers=token) return Response(json.dumps({'result': 'succeed', 'results': results}), status=200, headers=token)
# deserialize the response to return pure json # deserialize the response to return pure json
scenario = next(iter(results))
for scenario in results:
for building_results in results[scenario]: for building_results in results[scenario]:
values = [] values = []
building_info = self.database.building(building_results['building'], user_id, application_id, scenario)
results_dictionary = {}
archetype = self.energy_systems_catalog.get_entry(building_info.system_name)
for value in building_results['insel meb']: for value in building_results['insel meb']:
key = next(iter(value)) key = next(iter(value))
values.append({key: json.loads(str(value[key]))}) values.append({key: json.loads(str(value[key]))})
results_dictionary[key] = json.loads(str(value[key]))
building_results['insel meb'] = values building_results['insel meb'] = values
mockup_building = Building(building_info, results_dictionary, archetype)
life_cycle = Cost(mockup_building, retrofit_scenario=scenario).life_cycle
operational_co2 = Co2Emission(mockup_building).operational_co2
global_capital_costs = life_cycle[f'Scenario {scenario}']['global_capital_costs']
global_operational_costs = life_cycle[f'Scenario {scenario}']['global_operational_costs']
global_capital_incomes = life_cycle[f'Scenario {scenario}']['global_capital_incomes']
global_maintenance_costs = life_cycle[f'Scenario {scenario}']['global_maintenance_costs']
building_results['costs'] = {
'total_capital_costs_skin': life_cycle[f'Scenario {scenario}']['total_capital_costs_skin'],
'total_capital_costs_systems': life_cycle[f'Scenario {scenario}']['total_capital_costs_systems'],
'end_of_life_costs': life_cycle[f'Scenario {scenario}']['end_of_life_costs'],
'total_operational_costs': life_cycle[f'Scenario {scenario}']['total_operational_costs'],
'total_maintenance_costs': life_cycle[f'Scenario {scenario}']['total_maintenance_costs'],
'operational_incomes': life_cycle[f'Scenario {scenario}']['operational_incomes'],
'capital_incomes': life_cycle[f'Scenario {scenario}']['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}']['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}']['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
return Response(json.dumps({'result': 'succeed', 'results': results}), status=200, headers=token) return Response(json.dumps({'result': 'succeed', 'results': results}), status=200, headers=token)