Coverage for docs/utilities/settings_to_md.py: 0%

88 statements  

« prev     ^ index     » next       coverage.py v7.10.7, created at 2025-10-01 10:24 +0000

1"""Simple script for automatically creating Markdown-tables for the 

2documentation of SimSettings in 

3bim2sim/docs/source/advanced-user-guide/concepts/sim_settings.md . To run the 

4script, please specify a base path for where to save the Markdown table files 

5for each SimSetting class (Base / Building/ PluginEnergyPlus / PluginComfort). 

6""" 

7import importlib 

8import importlib.util 

9import pandas as pd 

10from pathlib import Path 

11import textwrap 

12from typing import Union 

13 

14from bim2sim.sim_settings import (BooleanSetting, ChoiceSetting, 

15 NumberSetting, PathSetting, 

16 GuidListSetting, Setting) 

17 

18 

19def load_module(source: str): 

20 """Load a bim2sim module. 

21 

22 Load a module within bim2sim containing SimSettings either from the 

23 module name or from a file path and return it. 

24 

25 Args: 

26 source: source file or module name as a string 

27 

28 Returns: 

29 module: loaded module 

30 """ 

31 if source.endswith(".py"): 

32 spec = importlib.util.spec_from_file_location("temp_module", source) 

33 module = importlib.util.module_from_spec(spec) 

34 spec.loader.exec_module(module) 

35 return module 

36 else: 

37 return importlib.import_module(source) 

38 

39 

40def simplify_type_name(setting_obj) -> str: 

41 """ Simplify SimSetting types. 

42 

43 Simplify SimSetting types to shorter names as strings for `Type` 

44 column. 

45 

46 Args: 

47 setting_obj: bim2sim SimSetting object 

48 

49 Returns: 

50 string defining the simplified SimSetting type 

51 """ 

52 if isinstance(setting_obj, BooleanSetting): 

53 return "Boolean" 

54 if isinstance(setting_obj, NumberSetting): 

55 return "Number" 

56 if isinstance(setting_obj, ChoiceSetting): 

57 return "Choice" 

58 if isinstance(setting_obj, PathSetting): 

59 return "Path" 

60 if isinstance(setting_obj, GuidListSetting): 

61 return "GuidList" 

62 if isinstance(setting_obj, Setting): 

63 return "Base" 

64 return type(setting_obj).__name__ 

65 

66 

67def wrap_for_md(text: str, width: int) -> str: 

68 """Wrap text sections after specified length. 

69 

70 Wrap longer texts in the Description or Choices column after `width` 

71 characters for better readability. 

72 

73 Args: 

74 text: string to be wrapped 

75 width: number of characters after which text is wrapped 

76 

77 Returns: 

78 reformatted string with automatic linebreaks 

79 """ 

80 if text is None: 

81 return "" 

82 text = str(text).strip() 

83 if not text: 

84 return "" 

85 # automatic linebreaks 

86 paragraphs = text.split("\n\n") 

87 wrapped_paragraphs = [] 

88 for p in paragraphs: 

89 p_single = " ".join(line.strip() for line in p.splitlines()) 

90 wrapped = textwrap.wrap(p_single, width=width) 

91 if not wrapped: 

92 wrapped_paragraphs.append("") 

93 else: 

94 wrapped_paragraphs.append("<br>".join(wrapped)) 

95 return "<br><br>".join(wrapped_paragraphs) 

96 

97 

98def choices_to_string(choices: Union[dict, list, None], wrap_width: int) -> str: 

99 """Convert SimSetting Choices to a string. 

100 

101 Convert a dictionary or list of Choices for a SimSetting to a string 

102 for Markdown-tables. If there are more than 10 settings, the short 

103 description of each choice is not adopted. 

104 

105 Args: 

106 choices: list or dict of Choices for a SimSetting to be converted 

107 wrap_width: number of characters after which text is wrapped 

108 

109 Returns: 

110 formatted string containing Choices as text 

111 """ 

112 s = "" 

113 if not choices: 

114 return "" 

115 if len(choices) > 10 and isinstance(choices, dict): 

116 choices = list(choices.keys()) 

117 if isinstance(choices, dict): 

118 entries = [f"'{k}': {v}" if v else f"{k}" for k, v in choices.items()] 

119 s = "<br>".join(entries) 

120 elif isinstance(choices, (list, tuple)): 

121 entries = [str(f"'{e}'") for e in choices] 

122 s = ", ".join(entries) 

123 s = wrap_for_md(s, wrap_width) 

124 return s 

125 

126 

127def extract_settings_from_class(cls, wrap_width: int = 60): 

128 """Get SimSettings from a bim2sim class. 

129 

130 Extract all Settings of a SimSetting class `cls` and put the 

131 properties Setting Name, Type, Default, Description and Choices in to a 

132 list. 

133 

134 Args: 

135 cls: class to extract Settings from 

136 wrap_width: number of characters after which text is wrapped 

137 

138 Returns: 

139 list of Settings objectssorted in Setting Name, Type, Default, 

140 Description, and Choices columns 

141 """ 

142 rows = [] 

143 for name, value in list(cls.__dict__.items()): 

144 if name.startswith("_"): 

145 continue 

146 if isinstance(value, (BooleanSetting, ChoiceSetting, NumberSetting, 

147 PathSetting, GuidListSetting, Setting)): 

148 stype = simplify_type_name(value) 

149 default = getattr(value, "default", "") 

150 default = f"'{default}'" if isinstance(default, str) else default 

151 description = getattr(value, "description", "") or "" 

152 description = wrap_for_md(description, width=wrap_width) 

153 choices_col = "" 

154 if isinstance(value, ChoiceSetting): 

155 choices_col = choices_to_string(getattr(value, "choices", 

156 None), wrap_width) 

157 rows.append({"Setting Name": name, "Type": stype, "Default": 

158 default, "Description": description, "Choices": 

159 choices_col}) 

160 return rows 

161 

162 

163def settings_to_markdown(source: str, class_name: str, 

164 output_md: str = "settings.md", wrap_width: int = 60): 

165 """Generate Markdown-tables from SimSettings. 

166 

167 Generate a markdown table for SimSettings defined in `class_name` 

168 found in `source`. `source` can be a dotted module name (e.g. 

169 "bim2sim.sim_settings") or a path to a .py file. Save table to output.md 

170 file with text wrapping in the `Description` and `Choices` columns after 

171 wrap_width characters. 

172 

173 Args: 

174 source: name of a bim2sim SimSettings module or a path to a .py file 

175 class_name: name of the class containing the SimSettings objects 

176 output_md: path to output Markdown-tables 

177 wrap_width: number of characters after which text is wrapped 

178 """ 

179 mod = load_module(source) 

180 cls = getattr(mod, class_name) 

181 rows = extract_settings_from_class(cls, wrap_width=wrap_width) 

182 df = pd.DataFrame(rows) 

183 df = df[["Setting Name", "Type", "Default", "Description", "Choices"]] 

184 markdown_str = df.to_markdown(index=False) 

185 Path(output_md).write_text(markdown_str, encoding="utf-8") 

186 print(f"Markdown table written to {output_md}") 

187 

188 

189if __name__ == "__main__": 

190 save_path = "" # Add path to save markdown tables here 

191 settings_to_markdown( 

192 "bim2sim.plugins.PluginComfort.bim2sim_comfort.sim_settings", 

193 "ComfortSimSettings", 

194 save_path + "comfort_settings.md") 

195 settings_to_markdown("bim2sim.sim_settings", 

196 "BaseSimSettings", 

197 save_path + "base_settings.md") 

198 settings_to_markdown("bim2sim.sim_settings", 

199 "BuildingSimSettings", 

200 save_path + "building_settings.md") 

201 settings_to_markdown( 

202 "bim2sim.plugins.PluginEnergyPlus.bim2sim_energyplus.sim_settings", 

203 "EnergyPlusSimSettings", 

204 save_path + "eplus_settings.md")