Skip to content

Commit 0911352

Browse files
committed
fix(run.py): add basic logging support
- Add new logging support (to be deployed throughout) - Update merge_faces to allow user-set tolerance
1 parent 2148f3b commit 0911352

File tree

5 files changed

+170
-42
lines changed

5 files changed

+170
-42
lines changed

PHX/from_HBJSON/cleanup_merge_faces.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from copy import copy
77
from collections import defaultdict
8+
import logging
89
import math
910
from typing import List, Sequence, Tuple, Union, TypeVar, Any
1011

@@ -18,6 +19,7 @@
1819
from ladybug_geometry.geometry3d.pointvector import Point3D, Vector3D
1920
import ladybug_geometry.boolean as pb
2021

22+
logger = logging.getLogger()
2123

2224
# -----------------------------------------------------------------------------
2325
# -- Geometry Functions
@@ -397,10 +399,11 @@ def merge_hb_face_polygons(
397399
try:
398400
merged_polygons = Polygon2D.boolean_union_all(polygons_in_ref_space, _tolerance)
399401
except Exception as e:
400-
msg = f"ERROR merging faces: {[f.display_name for f in _faces]} [Tolerance: {_tolerance}]"
402+
msg = f"\nWARNING: merging faces: {[f'{f.display_name}:{f.properties.energy.construction.display_name}' for f in _faces]} [Tolerance: {_tolerance}]"
401403
merged_polygons = poly2ds
404+
logger.warning(msg)
402405
print(msg)
403-
print(e)
406+
print(e, "\n")
404407

405408
if len(poly2ds) > 100:
406409
print(f"merge_hb_face_polygons resulted in: {len(merged_polygons)} faces.")
@@ -412,11 +415,13 @@ def merge_hb_faces(
412415
_faces: List[Face], _tolerance: float, _angle_tolerance_degrees: float
413416
) -> List[Face]:
414417
"""Merge a group of HB-Faces into the fewest number of faces possible."""
415-
418+
416419
if not _faces:
420+
logger.debug("No faces in group. Skipping merge.")
417421
return []
418422

419423
if len(_faces) == 1:
424+
logger.debug(f"Single face: {_faces[0].display_name} in group. Skipping merge.")
420425
return _faces
421426

422427
# -------------------------------------------------------------------------
@@ -427,6 +432,7 @@ def merge_hb_faces(
427432

428433
# -------------------------------------------------------------------------
429434
# -- Merge the Polygons together
435+
logger.debug(f"Merging faces: {[f.display_name for f in _faces]}")
430436
merged_polygons, ref_plane, ref_face = merge_hb_face_polygons(
431437
_faces, _tolerance, _angle_tolerance_degrees
432438
)

PHX/from_HBJSON/create_project.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"""Functions used to convert a standard HBJSON Model over to WUFI Objects"""
55

66
from collections import defaultdict
7-
from typing import Tuple, List
7+
from typing import Tuple, List, Union
88

99
from honeybee import model
1010
from honeybee import room
@@ -82,7 +82,7 @@ def get_hb_apertures(_hb_model: model.Model) -> List[Aperture]:
8282

8383

8484
def convert_hb_model_to_PhxProject(
85-
_hb_model: model.Model, _group_components: bool = True, _merge_faces: bool = False
85+
_hb_model: model.Model, _group_components: bool = True, _merge_faces: Union[bool, float] = False
8686
) -> PhxProject:
8787
"""Return a complete WUFI Project object with values based on the HB Model
8888
@@ -93,8 +93,9 @@ def convert_hb_model_to_PhxProject(
9393
* _group_components (bool): default=True. Set to true to have the converter
9494
group the components by assembly-type.
9595
96-
* _merge_faces (bool): default=False. Set to true to have the converter try and
97-
group together co-planar faces in the output room.
96+
* _merge_faces (bool | float): default=False. Set to true to have the converter try and
97+
group together co-planar faces in the output room using the HB model tolerance.
98+
If a number is given, it will be used as the tolerance for merging faces.
9899
99100
Returns:
100101
--------
@@ -114,9 +115,18 @@ def convert_hb_model_to_PhxProject(
114115
# -- Merge the rooms together by their Building Segment, Add to the Project
115116
# -- then create a new variant from the merged room.
116117
# -- try and weld the vertices too in order to reduce load-time.
117-
for room_group in sort_hb_rooms_by_bldg_segment(_hb_model.rooms):
118+
for room_group in sort_hb_rooms_by_bldg_segment(_hb_model.rooms): # type: ignore
119+
120+
# -- Configure the merge_faces and merge_face_tolerance
121+
if isinstance(_merge_faces, bool):
122+
merge_faces: bool = _merge_faces
123+
merge_face_tolerance: float = _hb_model.tolerance
124+
else:
125+
merge_faces: bool = True
126+
merge_face_tolerance: float = _merge_faces
127+
118128
merged_hb_room = cleanup.merge_rooms(
119-
room_group, _hb_model.tolerance, _hb_model.angle_tolerance, _merge_faces
129+
room_group, merge_face_tolerance, _hb_model.angle_tolerance, merge_faces
120130
)
121131

122132
new_variant = create_variant.from_hb_room(

PHX/from_HBJSON/read_HBJSON_file.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,33 @@
66
# -- Dev Note: Required to import all the base packages to run the __init__ startup routines
77
# -- which ensures that .ph properties slot is added to all HB Objects. This must be done before
88
# -- running read_hb_json to ensure there is a place for all the .ph properties to go.
9-
# -- Dev Note: Do not remove --
9+
10+
# -----------------------------------------------------------------------------
11+
# -- Dev Note: Do not remove v ------------------------------------------------
12+
1013
import honeybee
1114
import honeybee_ph
1215
import honeybee_energy
1316
import honeybee_energy_ph
1417

15-
# -- Dev Note: Do not remove --
18+
# -- Dev Note: Do not remove ^ ------------------------------------------------
19+
# -----------------------------------------------------------------------------
1620

1721
import os
1822
import json
23+
import logging
1924
import pathlib
2025
from typing import Dict
2126

2227
from honeybee import model
2328

29+
logger = logging.getLogger()
2430

2531
class HBJSONModelReadError(Exception):
26-
def __init__(self, _in):
32+
def __init__(self, _in) -> None:
2733
self.message = (
28-
f"Error: Can only convert a Honeybee 'Model' to WUFI XML.\n"
29-
"Got a Honeybee object of type: {_in}."
34+
f"HBJSONModelReadError: Can only convert a Honeybee 'Model' to WUFI XML.\n"
35+
f"Got a Honeybee object of type: {_in}."
3036
)
3137

3238
super(HBJSONModelReadError, self).__init__(self.message)
@@ -45,15 +51,18 @@ def read_hb_json_from_file(_file_address: pathlib.Path) -> Dict:
4551
"""
4652

4753
if not os.path.isfile(_file_address):
48-
raise FileNotFoundError(
49-
"Error: {} is not a valid file path?".format(_file_address)
50-
)
54+
msg = f"FileNotFoundError: {_file_address} is not a valid file path?"
55+
e = FileNotFoundError(msg)
56+
logger.critical(e)
57+
raise e
5158

5259
with open(_file_address) as json_file:
5360
data = json.load(json_file)
5461

5562
if data.get("type", None) != "Model":
56-
raise HBJSONModelReadError(data.get("type", None))
63+
e = HBJSONModelReadError(data.get("type", None))
64+
logger.critical(e.message)
65+
raise e
5766
else:
5867
return data
5968

PHX/hbjson_to_wufi_xml.py

Lines changed: 107 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33

44
"""Run script to convert an HBJSON file over to WUFI XML format."""
55

6-
from os import mkdir
7-
from typing import Tuple, List
8-
import sys
6+
from datetime import datetime
7+
import logging
8+
import os
99
import pathlib
10+
import sys
11+
from typing import Tuple, List, Union
1012

1113
from PHX.from_HBJSON import read_HBJSON_file, create_project
1214
from PHX.to_WUFI_XML import xml_builder, xml_txt_to_file
@@ -64,38 +66,129 @@ def group_components(_args: List[str]) -> bool:
6466
return _args[4].lower() == "true"
6567

6668

67-
def merge_faces(_args: List[str]) -> bool:
68-
"""Return the merge_faces boolean from the sys.args Tuple.
69+
def merge_faces(_args: List[str]) -> Union[bool, float]:
70+
"""Return the merge_faces as bool | float from the sys.args Tuple.
71+
72+
Arguments:
73+
----------
74+
* _args (Tuple): sys.args Tuple of inputs.
75+
- [5] (str): "True", "False", or float value as a string.
76+
77+
Returns:
78+
--------
79+
* bool | float: True, False, or a float value for the tolerance.
80+
"""
81+
if _args[5].lower() == "true":
82+
return True
83+
elif _args[5].lower() == "false":
84+
return False
85+
else:
86+
return float(_args[5])
87+
88+
89+
def log_level(_args: List[str]) -> int:
90+
"""Return the log_level from the sys.args Tuple.
6991
7092
Arguments:
7193
----------
7294
* _args (Tuple): sys.args Tuple of inputs.
73-
- [5] (str): "True" or "False" string.
95+
- [6] (str): "0", "10", "22", "30" .....
7496
7597
Returns:
7698
--------
77-
* bool: True if the user wants to merge faces.
99+
* int: The logging level.
78100
"""
79-
return _args[5].lower() == "true"
101+
try:
102+
return int(_args[6])
103+
except Exception:
104+
return 0
80105

81106

107+
def setup_logging_dir(_source_file_path: pathlib.Path) -> pathlib.Path:
108+
"""Return logging directory. Create it if needed.
109+
110+
Arguments:
111+
----------
112+
* _source_file_path (pathlib.Path): The base directory to create the logging directory in.
113+
114+
Returns:
115+
--------
116+
* pathlib.Path: The logging directory.
117+
"""
118+
log_dir = _source_file_path.parent / "PHX_Logs"
119+
120+
if not log_dir.exists():
121+
os.mkdir(log_dir)
122+
123+
return log_dir
124+
125+
126+
def startup_logging(_log_level: int) -> logging.Logger:
127+
"""Setup the logging. Create a new dir if needed..
128+
129+
Arguments:
130+
----------
131+
* _log_level (int): The logging level.
132+
133+
Returns:
134+
--------
135+
* logging.Logger: The root logger.
136+
"""
137+
138+
logger = logging.getLogger() # Root Logger
139+
logger.setLevel(_log_level)
140+
141+
# -- Set Format
142+
current_time = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
143+
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(funcName)s - %(message)s')
144+
145+
# -- Setup the STDERR log stream-handler for any WARNING and above
146+
stream_handler = logging.StreamHandler(stream=sys.stderr)
147+
stream_handler.setLevel(logging.WARNING)
148+
stream_handler.setFormatter(formatter)
149+
logger.addHandler(stream_handler)
150+
151+
# -- Setup the STDOUT log stream-handler for any INFO and above
152+
stream_handler = logging.StreamHandler(stream=sys.stdout)
153+
stream_handler.setLevel(logging.INFO)
154+
stream_handler.setFormatter(formatter)
155+
logger.addHandler(stream_handler)
156+
157+
if _log_level > 0:
158+
# -- Find the right path, create if needed
159+
log_path = setup_logging_dir(SOURCE_FILE)
160+
161+
# -- Setup the log file-handler for DEBUG and above
162+
file_handler = logging.FileHandler(log_path / f'PHX_{current_time}.log')
163+
file_handler.setLevel(logging.DEBUG)
164+
file_handler.setFormatter(formatter)
165+
logger.addHandler(file_handler)
166+
167+
logger.info(f"> LOGGING TO: {log_path / 'PHX.log'}")
168+
169+
return logger
170+
82171
if __name__ == "__main__":
83172
print("- " * 50)
84173

85174
# --- Input / Output file Path
86-
# -----------------------------------------------------------------------------
175+
# -------------------------------------------------------------------------
87176
SOURCE_FILE, TARGET_FILE_XML = resolve_paths(sys.argv)
88177
GROUP_COMPONENTS = group_components(sys.argv)
89178
MERGE_FACES = merge_faces(sys.argv)
179+
LOG_LEVEL = log_level(sys.argv)
180+
181+
## -- Setup the logging
182+
logger = startup_logging(LOG_LEVEL)
90183

91184
# --- Read in the existing HB_JSON and re-build the HB Objects
92-
# -----------------------------------------------------------------------------
93-
print(f"> Reading in the HBJSON file: ./{SOURCE_FILE}")
185+
# -------------------------------------------------------------------------
186+
logger.info(f"> Reading in the HBJSON file: ./{SOURCE_FILE}")
94187
hb_json_dict = read_HBJSON_file.read_hb_json_from_file(SOURCE_FILE)
95188
hb_model = read_HBJSON_file.convert_hbjson_dict_to_hb_model(hb_json_dict)
96189

97190
# --- Generate the WUFI Project file.
98-
print(f'> Generating the PHX-Project from the Honeybee-Model: "{hb_model}"')
191+
logger.info(f'> Generating the PHX-Project from the Honeybee-Model: "{hb_model}"')
99192
phx_Project = create_project.convert_hb_model_to_PhxProject(
100193
hb_model,
101194
_group_components=GROUP_COMPONENTS,
@@ -104,8 +197,8 @@ def merge_faces(_args: List[str]) -> bool:
104197

105198
# --- Output the WUFI Project as an XML Text File
106199
# ---------------------------------------------------------------------------
107-
print(f'> Generating XML Text for the PHX-Project: "{phx_Project}"')
200+
logger.info(f'> Generating XML Text for the PHX-Project: "{phx_Project}"')
108201
xml_txt = xml_builder.generate_WUFI_XML_from_object(phx_Project)
109202

110-
print(f"> Saving the XML file to: ./{TARGET_FILE_XML}")
203+
logger.info(f"> Saving the XML file to: ./{TARGET_FILE_XML}")
111204
xml_txt_to_file.write_XML_text_file(TARGET_FILE_XML, xml_txt)

0 commit comments

Comments
 (0)