Coverage for docs/utilities/plugindiagram/template_mermaid.py: 0%
123 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-12 17:09 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-12 17:09 +0000
1"""Generate mermaid code via templetes respectively template-functions."""
3import textwrap
4from pathlib import Path
5from bim2sim.plugins import load_plugin
7def split_string_50(text, max_width=50):
8 """Split the text after max. 50 characters."""
9 wraped_text = textwrap.wrap(text, max_width)
10 wraped_text = '\n'.join(wraped_text)
11 return wraped_text
14def generate_task_code(taskname: str = "bim2simtask", # pylint: disable=too-many-arguments
15 module_path: str = "module_path",
16 reads: str = "reads",
17 touches: str = "touches",
18 doc: str = "docstring",
19 reads_touches_vis: bool = True) -> str:
20 """Generate mermaid code representing a bim2sim task.
22 WIP: so the some structure stuff like tpye of diagram is added here, later
23 it should move to def generate_diagram".
25 Args:
26 taskname: name of the bim2sim taks of the plugin, no space allowed
27 module_path: path to the module/task definition
28 reads: input the task uses (from the central state)
29 touches: output the task reply (to the central state)
30 reads_touches_vis: enable/disable the subgraph showing
31 the reads and touches
33 Returns:
34 Mermaid code of an task.
35 WIP: check how to pipe. now it will printed
37 Attention:
38 - Tasknames should be unique, when merge different mermaid
39 templates instances.
41 """
42 # optional reads and touches subgraph, must defined before the templete
43 if reads_touches_vis:
44 code_reads_touches = """
45state{taskname}[("state
46 (reads/touches)")]
47 """
48 code_rt_state = code_reads_touches.format(
49 taskname=taskname)
50 code_rt = code_rt_state
51 # check for reads
52 if reads != ' - ':
53 code_rt_reads = """
54state{taskname} -- {reads} --> t{taskname}
55"""
56 code_rt_reads = code_rt_reads.format(
57 reads=reads,
58 taskname=taskname)
59 code_rt = code_rt + code_rt_reads
60 # check for touches
61 if touches != ' - ':
62 code_rt_touches = """
63t{taskname} -- {touches} --> state{taskname}
64"""
65 code_rt_touches = code_rt_touches.format(
66 touches=touches,
67 taskname=taskname)
68 code_rt = code_rt + code_rt_touches
69 else:
70 code_rt_touches = """direction RL"""
71 code_rt = code_rt + code_rt_touches
72 else:
73 code_rt = ""
75 code_template = """
76subgraph task{taskname}["task {taskname}"]
77 subgraph "" \n
78 t{taskname}["{module_path} \n {taskname}"]
79 ext{taskname}(" {doc} " )
80 end
81{code_rt}
82end
83 """
84 code = code_template.format(taskname=taskname,
85 module_path=module_path,
86 doc=doc,
87 code_rt=code_rt)
88 return code
91def generate_diagram(plugin_infos: list, tasks_infos: list, # pylint: disable=too-many-locals
92 central_state: bool = False) -> str:
93 """Print mermaid code of the whole task structure of one bim2sim plugin.
95 The plugin structure is fix: next plugin is connected to the plugin before.
97 Args:
98 plugin_infos: information about the whole plugin [name, module, .. ]
99 tasks: list of infos of tasks [{name, reads, touches ...}, {...}]
100 central_state: bool
101 True: reads and touches connected the the central state
102 False: reads and touches includes in task element
103 """
104 # header of the mermaid diagram
105 plugin_name = plugin_infos['name']
106 digram_header = """---
107title: plugin {plugin_name}
108---
109flowchart TB
110 """
111 mermaid_code = digram_header.format(plugin_name=plugin_name)
113 # task elements of the mermaid diagram
114 if central_state:
116 state_code = """
117state[("state:
118project
119data storage")]
120"""
121 mermaid_code = mermaid_code + state_code
123 for task_infos in tasks_infos:
124 taskname = task_infos['name']
125 reads = task_infos['reads']
126 touches = task_infos['touches']
127 task_code = generate_task_code(taskname=taskname,
128 module_path=task_infos['module_path'],
129 doc=task_infos['doc_first_sentence'],
130 reads_touches_vis=False)
131 mermaid_code = mermaid_code + task_code
132 # connetion reads and touches of the task to the state
133 code_connection_state = ''
134 if touches != ' - ':
135 code_connection_state_touches = """
136t{taskname} -- {touches} --> state\n"""
137 code_connection_state_touches = code_connection_state_touches.format(
138 taskname=taskname,
139 touches=touches)
140 code_connection_state += code_connection_state_touches
142 if reads != ' - ':
143 code_connection_state_reads = """
144state -- {reads} --> t{taskname} \n"""
145 code_connection_state_reads = code_connection_state_reads.format(
146 taskname=taskname,
147 reads=reads)
148 code_connection_state += code_connection_state_reads
150 mermaid_code = mermaid_code + code_connection_state
151 # if every task has their own state visualisation
152 else:
153 for task_infos in tasks_infos:
154 task_code = generate_task_code(taskname=task_infos['name'],
155 module_path=task_infos['module_path'],
156 reads=task_infos['reads'],
157 touches=task_infos['touches'],
158 doc=task_infos['doc_first_sentence'],
159 reads_touches_vis=True)
160 mermaid_code = mermaid_code + task_code
161 # state element
163 # connections of the task elements of the mermaid diagram
164 code_connection_templ = """task{taskname_from} --> task{taskname_to} \n"""
166 code_connections = ''
167 for i in range(len(tasks_infos) - 1):
168 code_connection = code_connection_templ.format(
169 taskname_from=tasks_infos[i]['name'],
170 taskname_to=tasks_infos[i+1]['name'])
171 code_connections = code_connections + code_connection
173 mermaid_code = mermaid_code + code_connections
175 return mermaid_code
178def write_file(mermaid_code: str, filename: str):
179 """Create a file including mermaid code.
181 Args:
182 mermaid_code: complete mermaid code which represents the diagram
183 filename: name of the source code file of the figure
184 """
185 with open(filename, "w", encoding="utf-8") as f:
186 f.write(mermaid_code)
189def get_plugin_infos(plugin) -> dict:
190 """Get plugin infos, like name.
192 Args:
193 project: Project object
195 Return:
196 dict (name, module)
197 - name of the plugin
198 - module the plugin is integrated
199 """
200 plugin_name = plugin.name
201 plugin_module = plugin.__module__
203 plugin_info = {'name': plugin_name, 'module': plugin_module}
204 return plugin_info
207def get_task_infos(plugin) -> list:
208 """Get information of the task of the plugin.
210 Args:
211 project: Project object
213 Return:
214 list of task names
215 """
216 tasks = plugin.default_tasks
217 task_infos = []
218 for task in tasks:
219 name = task.__name__
221 if len(task.reads) == 0:
222 reads = ' - '
223 else:
224 reads = ', '.join(task.reads)
226 if len(task.touches) == 0:
227 touches = ' - '
228 else:
229 touches = ', '.join(task.touches)
231 doc = task.__doc__
232 doc_first_sentence = str(doc).replace("\n", "").replace(" ", " ")
233 doc_first_sentence = doc_first_sentence.split(".", maxsplit=1)[0]
234 doc_first_sentence = doc_first_sentence + '.'
235 doc_first_sentence = split_string_50(doc_first_sentence)
237 module = task.__module__
238 module_list = str(module).split('.')
239 path_list = module_list[:-1]
240 path_list_arrow = [str(item) + ' > ' for item in path_list]
241 # add line break after third item
242 if len(path_list_arrow) > 3:
243 path_list_arrow[2] = str(path_list_arrow[2]) + '\n'
244 # join list items into a string
245 path_list_str = ''.join(path_list_arrow)
247 info = {'name': name, 'reads': reads, 'touches': touches,
248 'doc': doc, 'doc_first_sentence': doc_first_sentence,
249 'module_path': path_list_str}
250 task_infos.append(info)
251 return task_infos
254def generate_plugin_structure_fig(path_file: str,
255 plugin_name: str,
256 central_state: bool = False):
257 """Generate mermaid code visulzing the task structure of a plugin.
259 This generated mermaid code will written into a file (path_name).
260 To generate a figure, pls use:
261 - https://mermaid.live
262 - mmdc (must installed on your system)
263 - https://github.com/mermaid-js/mermaid-cli
264 - eg. mmdc -i input.mmd -o output.png -t dark -b transparent
265 - code can past into github issues (add ```mermaid code ```)
266 - code can used in Sphinx docuemtation (check for howtos)
267 - code can used in org-mode of emacs (check for howtos)
268 Args:
269 path_file: absolute path to the file (saves mermaid code)
270 plugin_name: name of the choosen plugin - string
271 default value: 'teaser'
272 central_state: bool
273 True: reads and touches connected the the central state
274 False: reads and touches includes in task element
276 Return:
277 nothing, code is witten into the defined file
279 """
280 try:
281 plugin = load_plugin(plugin_name)
282 plugin_infos = get_plugin_infos(plugin)
283 task_infos = get_task_infos(plugin)
284 path_name = Path(path_file) # should import the path for all os
285 write_file(generate_diagram(plugin_infos, task_infos,
286 central_state=central_state),
287 path_name)
288 print('run successful: \nmermaid code was save to:\n'
289 + str(path_name))
291 except ModuleNotFoundError as e:
292 print(e)
293 print("Pls, choose a plugin_name like: \n"
294 + " - 'teaser'\n"
295 + " - 'energyplus'\n"
296 + " - 'aixlib'\n"
297 # + " - 'cfd'\n"
298 + " - 'HKESim'\n"
299 + " - 'lca'\n")
301 except FileNotFoundError as e:
302 print(e)
303 print("Pls choose an existing (absolut) path.")
304 print("At the end of the path add the filename.")
307if __name__ == '__main__':
309 # Examples 8
310 # setup simple plugin, here template decentral state
311 # visualisation
312 PATH_NAME = ("/home/cudok/Documents/10_Git/bim2sim/docs/source/img/" +
313 "dynamic/plugindiagram/template_structure_decentral_state.mmd")
314 generate_plugin_structure_fig(PATH_NAME,
315 plugin_name='Template',
316 central_state=False)
317 # Examples 7
318 # setup simple plugin, here lca decentral state
319 # visualisation
320 PATH_NAME = ("/home/cudok/Documents/10_Git/bim2sim/docs/source/img/" +
321 "dynamic/plugindiagram/lca_structure_decentral_state.mmd")
322 generate_plugin_structure_fig(PATH_NAME,
323 plugin_name='lca',
324 central_state=False)
325 # Examples 6
326 # setup simple plugin, here HKESim decentral state
327 # visualisation
328 PATH_NAME = ("/home/cudok/Documents/10_Git/bim2sim/docs/source/img/" +
329 "dynamic/plugindiagram/HKESim_structure_decentral_state.mmd")
330 generate_plugin_structure_fig(PATH_NAME,
331 plugin_name='HKESim',
332 central_state=False)
334 # Examples 5
335 # setup simple plugin, here aixlib decentral state
336 # visualisation
337 PATH_NAME = ("/home/cudok/Documents/10_Git/bim2sim/docs/source/img/" +
338 "dynamic/plugindiagram/aixlib_structure_decentral_state.mmd")
339 generate_plugin_structure_fig(PATH_NAME,
340 plugin_name='aixlib',
341 central_state=False)
342 # Examples 3
343 # setup simple plugin, here EnergyPlus decentral state
344 # visualisation
345 PATH_NAME = ("/home/cudok/Documents/10_Git/bim2sim/docs/source/img/" +
346 "dynamic/plugindiagram/energyplus_structure_decentral_state.mmd")
347 generate_plugin_structure_fig(PATH_NAME,
348 plugin_name='energyplus',
349 central_state=False)
351 # Examples 2
352 # setup simple plugin, here TEASER decentral state
353 # visualisation
354 PATH_NAME = ("/home/cudok/Documents/10_Git/bim2sim/docs/source/img/" +
355 "dynamic/plugindiagram/teaser_structure_decentral_state.mmd")
356 generate_plugin_structure_fig(PATH_NAME,
357 plugin_name='teaser',
358 central_state=False)
360 # Examples 1
361 # setup simple plugin, here TEASER, central state
362 PATH_NAME = ("/home/cudok/Documents/10_Git/bim2sim/docs/source/img/" +
363 "dynamic/plugindiagram/teaser_structure_central_state.mmd")
364 generate_plugin_structure_fig(PATH_NAME,
365 plugin_name='teaser',
366 central_state=True)
368 # # Examples 4 - not working, because cfd plugin not included since 0.2.1
369 # # setup simple plugin, here cfd decentral state
370 # # visualisation
371 # PATH_NAME = ("/home/cudok/Documents/10_Git/bim2sim/docs/source/img/" +
372 # "dynamic/plugindiagram/cfd_structure_decentral_state.mmd")
373 # generate_plugin_structure_fig(PATH_NAME,
374 # plugin_name='cfd',
375 # central_state=False)