osbot-utils 1.7.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (260) hide show
  1. osbot_utils/__init__.py +1 -0
  2. osbot_utils/base_classes/Cache_Pickle.py +129 -0
  3. osbot_utils/base_classes/Kwargs_To_Disk.py +27 -0
  4. osbot_utils/base_classes/Kwargs_To_Self.py +308 -0
  5. osbot_utils/base_classes/Type_Safe__List.py +14 -0
  6. osbot_utils/base_classes/__init__.py +0 -0
  7. osbot_utils/context_managers/__init__.py +0 -0
  8. osbot_utils/context_managers/capture_duration.py +33 -0
  9. osbot_utils/decorators/__init__.py +0 -0
  10. osbot_utils/decorators/classes/__init__.py +0 -0
  11. osbot_utils/decorators/classes/singleton.py +9 -0
  12. osbot_utils/decorators/lists/__init__.py +0 -0
  13. osbot_utils/decorators/lists/filter_list.py +12 -0
  14. osbot_utils/decorators/lists/group_by.py +21 -0
  15. osbot_utils/decorators/lists/index_by.py +27 -0
  16. osbot_utils/decorators/methods/__init__.py +0 -0
  17. osbot_utils/decorators/methods/cache.py +19 -0
  18. osbot_utils/decorators/methods/cache_on_function.py +56 -0
  19. osbot_utils/decorators/methods/cache_on_self.py +78 -0
  20. osbot_utils/decorators/methods/cache_on_tmp.py +71 -0
  21. osbot_utils/decorators/methods/capture_exception.py +37 -0
  22. osbot_utils/decorators/methods/capture_status.py +20 -0
  23. osbot_utils/decorators/methods/catch.py +13 -0
  24. osbot_utils/decorators/methods/context.py +11 -0
  25. osbot_utils/decorators/methods/depreciated.py +79 -0
  26. osbot_utils/decorators/methods/function_type_check.py +62 -0
  27. osbot_utils/decorators/methods/obj_as_context.py +6 -0
  28. osbot_utils/decorators/methods/remove_return_value.py +22 -0
  29. osbot_utils/decorators/methods/required_fields.py +19 -0
  30. osbot_utils/fluent/Fluent_Dict.py +19 -0
  31. osbot_utils/fluent/Fluent_List.py +44 -0
  32. osbot_utils/fluent/__init__.py +1 -0
  33. osbot_utils/graphs/__init__.py +0 -0
  34. osbot_utils/graphs/mermaid/Mermaid.py +75 -0
  35. osbot_utils/graphs/mermaid/Mermaid__Edge.py +49 -0
  36. osbot_utils/graphs/mermaid/Mermaid__Graph.py +93 -0
  37. osbot_utils/graphs/mermaid/Mermaid__Node.py +69 -0
  38. osbot_utils/graphs/mermaid/Mermaid__Renderer.py +54 -0
  39. osbot_utils/graphs/mermaid/configs/Mermaid__Edge__Config.py +7 -0
  40. osbot_utils/graphs/mermaid/configs/Mermaid__Node__Config.py +9 -0
  41. osbot_utils/graphs/mermaid/configs/Mermaid__Render__Config.py +7 -0
  42. osbot_utils/graphs/mermaid/examples/Mermaid_Examples__FlowChart.py +98 -0
  43. osbot_utils/graphs/mermaid/models/Mermaid__Diagram_Direction.py +9 -0
  44. osbot_utils/graphs/mermaid/models/Mermaid__Diagram__Type.py +17 -0
  45. osbot_utils/graphs/mermaid/models/Mermaid__Node__Shape.py +30 -0
  46. osbot_utils/graphs/mgraph/MGraph.py +53 -0
  47. osbot_utils/graphs/mgraph/MGraph__Config.py +7 -0
  48. osbot_utils/graphs/mgraph/MGraph__Data.py +139 -0
  49. osbot_utils/graphs/mgraph/MGraph__Edge.py +27 -0
  50. osbot_utils/graphs/mgraph/MGraph__Node.py +33 -0
  51. osbot_utils/graphs/mgraph/MGraph__Random_Graphs.py +27 -0
  52. osbot_utils/graphs/mgraph/MGraph__Serializer.py +43 -0
  53. osbot_utils/graphs/mgraph/MGraphs.py +17 -0
  54. osbot_utils/graphs/mgraph/__init__.py +0 -0
  55. osbot_utils/helpers/CPrint.py +98 -0
  56. osbot_utils/helpers/Dict_To_Attr.py +7 -0
  57. osbot_utils/helpers/Local_Cache.py +111 -0
  58. osbot_utils/helpers/Local_Caches.py +54 -0
  59. osbot_utils/helpers/Print_Table.py +369 -0
  60. osbot_utils/helpers/Python_Audit.py +45 -0
  61. osbot_utils/helpers/Random_Seed.py +27 -0
  62. osbot_utils/helpers/SCP.py +58 -0
  63. osbot_utils/helpers/SSH.py +151 -0
  64. osbot_utils/helpers/Type_Registry.py +16 -0
  65. osbot_utils/helpers/__init__.py +0 -0
  66. osbot_utils/helpers/ast/Ast.py +35 -0
  67. osbot_utils/helpers/ast/Ast_Base.py +124 -0
  68. osbot_utils/helpers/ast/Ast_Data.py +28 -0
  69. osbot_utils/helpers/ast/Ast_Load.py +62 -0
  70. osbot_utils/helpers/ast/Ast_Merge.py +26 -0
  71. osbot_utils/helpers/ast/Ast_Node.py +117 -0
  72. osbot_utils/helpers/ast/Ast_Visit.py +85 -0
  73. osbot_utils/helpers/ast/Call_Tree.py +38 -0
  74. osbot_utils/helpers/ast/__init__.py +145 -0
  75. osbot_utils/helpers/ast/nodes/Ast_Add.py +6 -0
  76. osbot_utils/helpers/ast/nodes/Ast_Alias.py +6 -0
  77. osbot_utils/helpers/ast/nodes/Ast_And.py +6 -0
  78. osbot_utils/helpers/ast/nodes/Ast_Argument.py +7 -0
  79. osbot_utils/helpers/ast/nodes/Ast_Arguments.py +10 -0
  80. osbot_utils/helpers/ast/nodes/Ast_Assert.py +7 -0
  81. osbot_utils/helpers/ast/nodes/Ast_Assign.py +8 -0
  82. osbot_utils/helpers/ast/nodes/Ast_Attribute.py +9 -0
  83. osbot_utils/helpers/ast/nodes/Ast_Aug_Assign.py +9 -0
  84. osbot_utils/helpers/ast/nodes/Ast_Bin_Op.py +8 -0
  85. osbot_utils/helpers/ast/nodes/Ast_Bool_Op.py +7 -0
  86. osbot_utils/helpers/ast/nodes/Ast_Break.py +7 -0
  87. osbot_utils/helpers/ast/nodes/Ast_Call.py +17 -0
  88. osbot_utils/helpers/ast/nodes/Ast_Class_Def.py +9 -0
  89. osbot_utils/helpers/ast/nodes/Ast_Compare.py +9 -0
  90. osbot_utils/helpers/ast/nodes/Ast_Comprehension.py +10 -0
  91. osbot_utils/helpers/ast/nodes/Ast_Constant.py +6 -0
  92. osbot_utils/helpers/ast/nodes/Ast_Continue.py +7 -0
  93. osbot_utils/helpers/ast/nodes/Ast_Dict.py +8 -0
  94. osbot_utils/helpers/ast/nodes/Ast_Eq.py +6 -0
  95. osbot_utils/helpers/ast/nodes/Ast_Except_Handler.py +9 -0
  96. osbot_utils/helpers/ast/nodes/Ast_Expr.py +7 -0
  97. osbot_utils/helpers/ast/nodes/Ast_For.py +10 -0
  98. osbot_utils/helpers/ast/nodes/Ast_Function_Def.py +17 -0
  99. osbot_utils/helpers/ast/nodes/Ast_Generator_Exp.py +8 -0
  100. osbot_utils/helpers/ast/nodes/Ast_Gt.py +7 -0
  101. osbot_utils/helpers/ast/nodes/Ast_GtE.py +7 -0
  102. osbot_utils/helpers/ast/nodes/Ast_If.py +9 -0
  103. osbot_utils/helpers/ast/nodes/Ast_If_Exp.py +9 -0
  104. osbot_utils/helpers/ast/nodes/Ast_Import.py +7 -0
  105. osbot_utils/helpers/ast/nodes/Ast_Import_From.py +7 -0
  106. osbot_utils/helpers/ast/nodes/Ast_In.py +6 -0
  107. osbot_utils/helpers/ast/nodes/Ast_Is.py +6 -0
  108. osbot_utils/helpers/ast/nodes/Ast_Is_Not.py +7 -0
  109. osbot_utils/helpers/ast/nodes/Ast_Keyword.py +8 -0
  110. osbot_utils/helpers/ast/nodes/Ast_Lambda.py +8 -0
  111. osbot_utils/helpers/ast/nodes/Ast_List.py +8 -0
  112. osbot_utils/helpers/ast/nodes/Ast_List_Comp.py +8 -0
  113. osbot_utils/helpers/ast/nodes/Ast_Load.py +6 -0
  114. osbot_utils/helpers/ast/nodes/Ast_Lt.py +7 -0
  115. osbot_utils/helpers/ast/nodes/Ast_LtE.py +7 -0
  116. osbot_utils/helpers/ast/nodes/Ast_Mod.py +6 -0
  117. osbot_utils/helpers/ast/nodes/Ast_Module.py +20 -0
  118. osbot_utils/helpers/ast/nodes/Ast_Mult.py +6 -0
  119. osbot_utils/helpers/ast/nodes/Ast_Name.py +6 -0
  120. osbot_utils/helpers/ast/nodes/Ast_Not.py +7 -0
  121. osbot_utils/helpers/ast/nodes/Ast_Not_Eq.py +7 -0
  122. osbot_utils/helpers/ast/nodes/Ast_Not_In.py +6 -0
  123. osbot_utils/helpers/ast/nodes/Ast_Or.py +7 -0
  124. osbot_utils/helpers/ast/nodes/Ast_Pass.py +7 -0
  125. osbot_utils/helpers/ast/nodes/Ast_Pow.py +7 -0
  126. osbot_utils/helpers/ast/nodes/Ast_Raise.py +8 -0
  127. osbot_utils/helpers/ast/nodes/Ast_Return.py +7 -0
  128. osbot_utils/helpers/ast/nodes/Ast_Set.py +7 -0
  129. osbot_utils/helpers/ast/nodes/Ast_Slice.py +8 -0
  130. osbot_utils/helpers/ast/nodes/Ast_Starred.py +8 -0
  131. osbot_utils/helpers/ast/nodes/Ast_Store.py +7 -0
  132. osbot_utils/helpers/ast/nodes/Ast_Sub.py +7 -0
  133. osbot_utils/helpers/ast/nodes/Ast_Subscript.py +8 -0
  134. osbot_utils/helpers/ast/nodes/Ast_Try.py +9 -0
  135. osbot_utils/helpers/ast/nodes/Ast_Tuple.py +9 -0
  136. osbot_utils/helpers/ast/nodes/Ast_Unary_Op.py +7 -0
  137. osbot_utils/helpers/ast/nodes/Ast_While.py +8 -0
  138. osbot_utils/helpers/ast/nodes/Ast_With.py +7 -0
  139. osbot_utils/helpers/ast/nodes/Ast_With_Item.py +7 -0
  140. osbot_utils/helpers/ast/nodes/Ast_Yield.py +7 -0
  141. osbot_utils/helpers/ast/nodes/__init__.py +0 -0
  142. osbot_utils/helpers/html/Dict_To_Css.py +20 -0
  143. osbot_utils/helpers/html/Dict_To_Html.py +59 -0
  144. osbot_utils/helpers/html/Dict_To_Tags.py +88 -0
  145. osbot_utils/helpers/html/Html_To_Dict.py +75 -0
  146. osbot_utils/helpers/html/Html_To_Tag.py +20 -0
  147. osbot_utils/helpers/html/Tag__Base.py +91 -0
  148. osbot_utils/helpers/html/Tag__Body.py +5 -0
  149. osbot_utils/helpers/html/Tag__Div.py +5 -0
  150. osbot_utils/helpers/html/Tag__H.py +9 -0
  151. osbot_utils/helpers/html/Tag__HR.py +5 -0
  152. osbot_utils/helpers/html/Tag__Head.py +32 -0
  153. osbot_utils/helpers/html/Tag__Html.py +42 -0
  154. osbot_utils/helpers/html/Tag__Link.py +17 -0
  155. osbot_utils/helpers/html/Tag__Style.py +25 -0
  156. osbot_utils/helpers/html/__init__.py +0 -0
  157. osbot_utils/helpers/pubsub/Event__Queue.py +95 -0
  158. osbot_utils/helpers/pubsub/PubSub__Client.py +53 -0
  159. osbot_utils/helpers/pubsub/PubSub__Room.py +13 -0
  160. osbot_utils/helpers/pubsub/PubSub__Server.py +94 -0
  161. osbot_utils/helpers/pubsub/PubSub__Sqlite.py +24 -0
  162. osbot_utils/helpers/pubsub/__init__.py +0 -0
  163. osbot_utils/helpers/pubsub/schemas/Schema__Event.py +15 -0
  164. osbot_utils/helpers/pubsub/schemas/Schema__Event__Connect.py +7 -0
  165. osbot_utils/helpers/pubsub/schemas/Schema__Event__Disconnect.py +7 -0
  166. osbot_utils/helpers/pubsub/schemas/Schema__Event__Join_Room.py +8 -0
  167. osbot_utils/helpers/pubsub/schemas/Schema__Event__Leave_Room.py +8 -0
  168. osbot_utils/helpers/pubsub/schemas/Schema__Event__Message.py +7 -0
  169. osbot_utils/helpers/pubsub/schemas/Schema__PubSub__Client.py +8 -0
  170. osbot_utils/helpers/pubsub/schemas/__init__.py +0 -0
  171. osbot_utils/helpers/sqlite/Capture_Sqlite_Error.py +51 -0
  172. osbot_utils/helpers/sqlite/Sqlite__Cursor.py +87 -0
  173. osbot_utils/helpers/sqlite/Sqlite__Database.py +137 -0
  174. osbot_utils/helpers/sqlite/Sqlite__Field.py +70 -0
  175. osbot_utils/helpers/sqlite/Sqlite__Globals.py +5 -0
  176. osbot_utils/helpers/sqlite/Sqlite__Table.py +293 -0
  177. osbot_utils/helpers/sqlite/Sqlite__Table__Create.py +96 -0
  178. osbot_utils/helpers/sqlite/Temp_Sqlite__Database__Disk.py +17 -0
  179. osbot_utils/helpers/sqlite/Temp_Sqlite__Table.py +23 -0
  180. osbot_utils/helpers/sqlite/__init__.py +0 -0
  181. osbot_utils/helpers/sqlite/domains/Sqlite__Cache__Requests.py +214 -0
  182. osbot_utils/helpers/sqlite/domains/Sqlite__Cache__Requests__Patch.py +63 -0
  183. osbot_utils/helpers/sqlite/domains/Sqlite__DB__Files.py +23 -0
  184. osbot_utils/helpers/sqlite/domains/Sqlite__DB__Graph.py +47 -0
  185. osbot_utils/helpers/sqlite/domains/Sqlite__DB__Json.py +83 -0
  186. osbot_utils/helpers/sqlite/domains/Sqlite__DB__Local.py +20 -0
  187. osbot_utils/helpers/sqlite/domains/Sqlite__DB__Requests.py +39 -0
  188. osbot_utils/helpers/sqlite/domains/__init__.py +0 -0
  189. osbot_utils/helpers/sqlite/domains/schemas/Schema__Table__Requests.py +12 -0
  190. osbot_utils/helpers/sqlite/domains/schemas/__init__.py +0 -0
  191. osbot_utils/helpers/sqlite/models/Sqlite__Field__Type.py +37 -0
  192. osbot_utils/helpers/sqlite/models/__init__.py +0 -0
  193. osbot_utils/helpers/sqlite/sample_data/Sqlite__Sample_Data__Chinook.py +116 -0
  194. osbot_utils/helpers/sqlite/sample_data/__init__.py +0 -0
  195. osbot_utils/helpers/sqlite/sql_builder/SQL_Builder.py +159 -0
  196. osbot_utils/helpers/sqlite/sql_builder/SQL_Builder__Select.py +12 -0
  197. osbot_utils/helpers/sqlite/sql_builder/__init__.py +0 -0
  198. osbot_utils/helpers/sqlite/tables/Sqlite__Table__Config.py +63 -0
  199. osbot_utils/helpers/sqlite/tables/Sqlite__Table__Edges.py +46 -0
  200. osbot_utils/helpers/sqlite/tables/Sqlite__Table__Files.py +45 -0
  201. osbot_utils/helpers/sqlite/tables/Sqlite__Table__Nodes.py +52 -0
  202. osbot_utils/helpers/sqlite/tables/__init__.py +0 -0
  203. osbot_utils/helpers/trace/Trace_Call.py +120 -0
  204. osbot_utils/helpers/trace/Trace_Call__Config.py +94 -0
  205. osbot_utils/helpers/trace/Trace_Call__Graph.py +26 -0
  206. osbot_utils/helpers/trace/Trace_Call__Handler.py +215 -0
  207. osbot_utils/helpers/trace/Trace_Call__Print_Lines.py +85 -0
  208. osbot_utils/helpers/trace/Trace_Call__Print_Traces.py +170 -0
  209. osbot_utils/helpers/trace/Trace_Call__Stack.py +166 -0
  210. osbot_utils/helpers/trace/Trace_Call__Stack_Node.py +59 -0
  211. osbot_utils/helpers/trace/Trace_Call__Stats.py +71 -0
  212. osbot_utils/helpers/trace/Trace_Call__View_Model.py +75 -0
  213. osbot_utils/helpers/trace/Trace_Files.py +33 -0
  214. osbot_utils/helpers/trace/__init__.py +0 -0
  215. osbot_utils/testing/Catch.py +54 -0
  216. osbot_utils/testing/Duration.py +69 -0
  217. osbot_utils/testing/Hook_Method.py +118 -0
  218. osbot_utils/testing/Log_To_Queue.py +46 -0
  219. osbot_utils/testing/Log_To_String.py +37 -0
  220. osbot_utils/testing/Logging.py +81 -0
  221. osbot_utils/testing/Patch_Print.py +52 -0
  222. osbot_utils/testing/Profiler.py +89 -0
  223. osbot_utils/testing/Stderr.py +19 -0
  224. osbot_utils/testing/Stdout.py +19 -0
  225. osbot_utils/testing/Temp_File.py +46 -0
  226. osbot_utils/testing/Temp_Folder.py +114 -0
  227. osbot_utils/testing/Temp_Sys_Path.py +13 -0
  228. osbot_utils/testing/Temp_Web_Server.py +83 -0
  229. osbot_utils/testing/Temp_Zip.py +45 -0
  230. osbot_utils/testing/Temp_Zip_In_Memory.py +90 -0
  231. osbot_utils/testing/Unit_Test.py +34 -0
  232. osbot_utils/testing/Unzip_File.py +30 -0
  233. osbot_utils/testing/__init__.py +0 -0
  234. osbot_utils/utils/Assert.py +52 -0
  235. osbot_utils/utils/Call_Stack.py +187 -0
  236. osbot_utils/utils/Csv.py +32 -0
  237. osbot_utils/utils/Dev.py +47 -0
  238. osbot_utils/utils/Exceptions.py +7 -0
  239. osbot_utils/utils/Files.py +528 -0
  240. osbot_utils/utils/Functions.py +113 -0
  241. osbot_utils/utils/Http.py +136 -0
  242. osbot_utils/utils/Int.py +6 -0
  243. osbot_utils/utils/Json.py +171 -0
  244. osbot_utils/utils/Json_Cache.py +59 -0
  245. osbot_utils/utils/Lists.py +198 -0
  246. osbot_utils/utils/Misc.py +496 -0
  247. osbot_utils/utils/Objects.py +341 -0
  248. osbot_utils/utils/Png.py +29 -0
  249. osbot_utils/utils/Process.py +73 -0
  250. osbot_utils/utils/Python_Logger.py +301 -0
  251. osbot_utils/utils/Status.py +79 -0
  252. osbot_utils/utils/Str.py +63 -0
  253. osbot_utils/utils/Version.py +16 -0
  254. osbot_utils/utils/Zip.py +97 -0
  255. osbot_utils/utils/__init__.py +16 -0
  256. osbot_utils/version +1 -0
  257. osbot_utils-1.7.7.dist-info/LICENSE +201 -0
  258. osbot_utils-1.7.7.dist-info/METADATA +46 -0
  259. osbot_utils-1.7.7.dist-info/RECORD +260 -0
  260. osbot_utils-1.7.7.dist-info/WHEEL +4 -0
@@ -0,0 +1,49 @@
1
+ from osbot_utils.graphs.mermaid.Mermaid__Node import LINE_PADDING, Mermaid__Node
2
+ from osbot_utils.graphs.mermaid.configs.Mermaid__Edge__Config import Mermaid__Edge__Config
3
+ from osbot_utils.graphs.mgraph.MGraph__Edge import MGraph__Edge
4
+ #from osbot_utils.graphs.mgraph.views.mermaid.Mermaid__Node import Mermaid__Node
5
+ from osbot_utils.utils.Str import safe_str
6
+
7
+
8
+ class Mermaid__Edge(MGraph__Edge):
9
+ config : Mermaid__Edge__Config
10
+ from_node : Mermaid__Node
11
+ to_node : Mermaid__Node
12
+
13
+ # def __init__(self, **kwargs):
14
+ # super().__init__(**kwargs)
15
+
16
+ # def __init__(self, **kwargs):
17
+ # super().__init__(**kwargs)
18
+ # self.convert_nodes()
19
+ #
20
+ def edge_mode(self, edge_mode):
21
+ self.config.edge_mode = edge_mode
22
+ return self
23
+
24
+ def edge_mode__lr_using_pipe(self):
25
+ return self.edge_mode('lr_using_pipe')
26
+
27
+ def output_node_from(self, value=True):
28
+ self.config.output_node_from = value
29
+ return self
30
+
31
+ def output_node_to(self, value=True):
32
+ self.config.output_node_to = value
33
+ return self
34
+
35
+ def render_edge(self):
36
+ from_node_key = safe_str(self.from_node.key)
37
+ to_node_key = safe_str(self.to_node .key)
38
+ if self.config.output_node_from:
39
+ from_node_key = self.from_node.render_node(include_padding=False) #f'{edge.from_node.key}["{edge.from_node.label}"]'
40
+ if self.config.output_node_to:
41
+ to_node_key = self.to_node.render_node(include_padding=False ) #f'{edge.to_node .key}["{edge.to_node .label}"]'
42
+ if self.config.edge_mode == 'lr_using_pipe':
43
+ link_code = f'-->|{self.label}|'
44
+ elif self.label:
45
+ link_code = f'--"{self.label}"-->'
46
+ else:
47
+ link_code = '-->'
48
+ edge_code = f'{LINE_PADDING}{from_node_key} {link_code} {to_node_key}'
49
+ return edge_code
@@ -0,0 +1,93 @@
1
+ from osbot_utils.graphs.mgraph.MGraph__Edge import MGraph__Edge
2
+ from osbot_utils.graphs.mgraph.MGraph__Node import MGraph__Node
3
+ from osbot_utils.graphs.mermaid.Mermaid__Edge import Mermaid__Edge
4
+ from osbot_utils.graphs.mermaid.Mermaid__Node import Mermaid__Node
5
+ from osbot_utils.graphs.mgraph.MGraph import MGraph
6
+
7
+
8
+ class Mermaid__Graph(MGraph):
9
+ edges : list[Mermaid__Edge]
10
+ mermaid_code : list
11
+ nodes : list[Mermaid__Node]
12
+
13
+ # def __init__(self, mgraph=None):
14
+ # super().__init__()
15
+ # if mgraph is None:
16
+ # mgraph = MGraph()
17
+ # self.__dict__ = mgraph.__dict__
18
+ # self.convert_nodes().convert_edges()
19
+
20
+ # def cast(self, source):
21
+ # self.__dict__ = source.__dict__
22
+ # return self
23
+
24
+ # def add_edge(self, **kwargs):
25
+ # #new_edge = super().add_edge(*args, **kwargs)
26
+ # mermaid_edge = Mermaid__Edge(**kwargs)
27
+ # self.edges.append(mermaid_edge)
28
+ # return mermaid_edge
29
+ #
30
+ def add_node(self, **kwargs):
31
+ new_node = MGraph__Node(**kwargs)
32
+ mermaid_node = Mermaid__Node().merge_with(new_node)
33
+ self.nodes.append(mermaid_node)
34
+ return mermaid_node
35
+
36
+ #
37
+ #
38
+ # def add_line(self, line):
39
+ # self.mermaid_code.append(line)
40
+ # return line
41
+ #
42
+ # def code(self):
43
+ # self.code_create()
44
+ # return '\n'.join(self.mermaid_code)
45
+ #
46
+ # def code_create(self):
47
+ # with self as _:
48
+ # _.reset_code()
49
+ # _.add_line('graph LR')
50
+ # for node in _.nodes:
51
+ # _.add_line(node.code())
52
+ # _.add_line('')
53
+ # for edge in _.edges:
54
+ # _.add_line(edge.code())
55
+ # return self
56
+ #
57
+ # def code_markdown(self):
58
+ # self.code_create()
59
+ # markdown = ['# Mermaid Graph',
60
+ # "```mermaid" ,
61
+ # *self.mermaid_code,
62
+ # "```"]
63
+ #
64
+ # return '\n'.join(markdown)
65
+ #
66
+ # def convert_edges(self):
67
+ # new_edges = []
68
+ # for edge in self.edges:
69
+ # new_edges.append(Mermaid__Edge().cast(edge))
70
+ # self.edges = new_edges
71
+ # return self
72
+ #
73
+ # def convert_nodes(self):
74
+ # new_nodes = []
75
+ # for node in self.nodes:
76
+ # mermaid_node = Mermaid__Node().cast(node)
77
+ # new_nodes.append(mermaid_node)
78
+ # self.nodes = new_nodes
79
+ # return self
80
+ #
81
+ # def reset_code(self):
82
+ # self.mermaid_code = []
83
+ # return self
84
+ #
85
+ # def save(self, target_file=None):
86
+ # file_path = target_file or '/tmp/mermaid.md'
87
+ #
88
+ # with open(file_path, 'w') as file:
89
+ # file.write(self.code_markdown())
90
+ # return file_path
91
+ #
92
+ # def print_code(self):
93
+ # print(self.code())
@@ -0,0 +1,69 @@
1
+ from enum import Enum
2
+
3
+ from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
4
+ from osbot_utils.graphs.mermaid.configs.Mermaid__Node__Config import Mermaid__Node__Config
5
+ from osbot_utils.graphs.mermaid.models.Mermaid__Node__Shape import Mermaid__Node__Shape
6
+ from osbot_utils.graphs.mgraph.MGraph__Node import MGraph__Node
7
+ from osbot_utils.utils.Str import safe_str
8
+
9
+ LINE_PADDING = ' '
10
+
11
+ class Mermaid__Node(MGraph__Node):
12
+
13
+ config : Mermaid__Node__Config
14
+
15
+ def render_node(self, include_padding=True):
16
+ left_char, right_char = self.config.node_shape.value
17
+
18
+ if self.config.markdown:
19
+ label = f'`{self.label}`'
20
+ else:
21
+ label = self.label
22
+
23
+
24
+ if self.config.show_label is False:
25
+ node_code = f'{self.key}'
26
+ else:
27
+ if self.config.wrap_with_quotes is False:
28
+ node_code = f'{self.key}{left_char}{label}{right_char}'
29
+ else:
30
+ node_code = f'{self.key}{left_char}"{label}"{right_char}'
31
+
32
+ if include_padding:
33
+ node_code = f'{LINE_PADDING}{node_code}'
34
+ return node_code
35
+
36
+ def markdown(self, value=True):
37
+ self.config.markdown = value
38
+ return self
39
+
40
+ def shape(self, shape=None):
41
+ self.config.node_shape = Mermaid__Node__Shape.get_shape(shape)
42
+ return self
43
+
44
+
45
+ def shape_asymmetric (self): self.config.node_shape = Mermaid__Node__Shape.asymmetric ; return self
46
+ def shape_circle (self): self.config.node_shape = Mermaid__Node__Shape.circle ; return self
47
+ def shape_cylindrical (self): self.config.node_shape = Mermaid__Node__Shape.cylindrical ; return self
48
+ def shape_default (self): self.config.node_shape = Mermaid__Node__Shape.default ; return self
49
+ def shape_double_circle (self): self.config.node_shape = Mermaid__Node__Shape.double_circle ; return self
50
+ def shape_hexagon (self): self.config.node_shape = Mermaid__Node__Shape.hexagon ; return self
51
+ def shape_parallelogram (self): self.config.node_shape = Mermaid__Node__Shape.parallelogram ; return self
52
+ def shape_parallelogram_alt (self): self.config.node_shape = Mermaid__Node__Shape.parallelogram_alt ; return self
53
+ def shape_stadium (self): self.config.node_shape = Mermaid__Node__Shape.stadium ; return self
54
+ def shape_subroutine (self): self.config.node_shape = Mermaid__Node__Shape.subroutine ; return self
55
+ def shape_rectangle (self): self.config.node_shape = Mermaid__Node__Shape.rectangle ; return self
56
+ def shape_rhombus (self): self.config.node_shape = Mermaid__Node__Shape.rhombus ; return self
57
+ def shape_round_edges (self): self.config.node_shape = Mermaid__Node__Shape.round_edges ; return self
58
+ def shape_trapezoid (self): self.config.node_shape = Mermaid__Node__Shape.trapezoid ; return self
59
+ def shape_trapezoid_alt (self): self.config.node_shape = Mermaid__Node__Shape.trapezoid_alt ; return self
60
+
61
+
62
+
63
+ def wrap_with_quotes(self, value=True):
64
+ self.config.wrap_with_quotes = value
65
+ return self
66
+
67
+ def show_label(self, value=True):
68
+ self.config.show_label = value
69
+ return self
@@ -0,0 +1,54 @@
1
+ from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
2
+ from osbot_utils.graphs.mermaid.configs.Mermaid__Render__Config import Mermaid__Render__Config
3
+ from osbot_utils.graphs.mermaid.models.Mermaid__Diagram_Direction import Diagram__Direction
4
+ from osbot_utils.graphs.mermaid.models.Mermaid__Diagram__Type import Diagram__Type
5
+
6
+
7
+ class Mermaid__Renderer(Kwargs_To_Self):
8
+ config : Mermaid__Render__Config
9
+ mermaid_code : list
10
+ diagram_direction : Diagram__Direction = Diagram__Direction.LR
11
+ diagram_type : Diagram__Type = Diagram__Type.graph
12
+
13
+
14
+ def add_line(self, line):
15
+ self.mermaid_code.append(line)
16
+ return line
17
+
18
+ def code(self, nodes, edges):
19
+ self.code_create(nodes, edges)
20
+ return '\n'.join(self.mermaid_code)
21
+
22
+ def code_create(self, nodes, edges, recreate=False):
23
+ with self as _:
24
+ if recreate: # if recreate is True, reset the code
25
+ _.reset_code()
26
+ elif self.mermaid_code: # if the code has already been created, don't create it
27
+ return self # todo: find a better way to do this, namely around the concept of auto detecting (on change) when the recreation needs to be done (vs being able to use the previously calculated data)
28
+ for directive in _.config.directives:
29
+ _.add_line(f'%%{{{directive}}}%%')
30
+ _.add_line(self.graph_header())
31
+ if self.config.add_nodes:
32
+ for node in nodes:
33
+ node_code = node.render_node()
34
+ _.add_line(node_code)
35
+ if self.config.line_before_edges:
36
+ _.add_line('')
37
+ for edge in edges:
38
+ edge_code = edge.render_edge()
39
+ _.add_line(edge_code)
40
+ return self
41
+
42
+
43
+
44
+ def graph_header(self):
45
+ # if type(self.diagram_type.value) is str:
46
+ # value = self.diagram_type.value
47
+ # else:
48
+ # value = self.diagram_type.name
49
+ value = self.diagram_type.name
50
+ return f'{value} {self.diagram_direction.name}'
51
+
52
+ def reset_code(self):
53
+ self.mermaid_code = []
54
+ return self
@@ -0,0 +1,7 @@
1
+ from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
2
+
3
+
4
+ class Mermaid__Edge__Config(Kwargs_To_Self):
5
+ edge_mode : str
6
+ output_node_from : bool = False
7
+ output_node_to : bool = False
@@ -0,0 +1,9 @@
1
+ from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
2
+ from osbot_utils.graphs.mermaid.models.Mermaid__Node__Shape import Mermaid__Node__Shape
3
+
4
+
5
+ class Mermaid__Node__Config(Kwargs_To_Self):
6
+ markdown : bool
7
+ node_shape : Mermaid__Node__Shape = Mermaid__Node__Shape.default
8
+ show_label : bool = True
9
+ wrap_with_quotes : bool = True # todo: add support for only using quotes when needed
@@ -0,0 +1,7 @@
1
+ from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
2
+
3
+
4
+ class Mermaid__Render__Config(Kwargs_To_Self):
5
+ add_nodes : bool = True
6
+ directives : list
7
+ line_before_edges : bool = True
@@ -0,0 +1,98 @@
1
+ # from https://mermaid.js.org/syntax/flowchart.html
2
+ class Mermain_Examples__FlowChart:
3
+ example_1__a_node_default = """
4
+ flowchart LR
5
+ id
6
+ """
7
+ example_2__a_node_with_text = """
8
+ flowchart LR
9
+ id1[This is the text in the box]
10
+ """
11
+
12
+ example_3__a_node_with_unicode_text = """
13
+ flowchart LR
14
+ id["This ❤ Unicode"]
15
+ """
16
+ example_4__a_node_with_markdown_formatting = """
17
+ %%{init: {"flowchart": {"htmlLabels": false}} }%%
18
+ flowchart LR
19
+ markdown["`This **is** _Markdown_`"]
20
+ newLines["`Line1
21
+ Line 2
22
+ Line 3`"]
23
+ markdown --> newLines
24
+ """
25
+
26
+ example_5__direction__from_top_to_bottom = """
27
+ flowchart TD
28
+ Start --> Stop
29
+ """
30
+
31
+ example_6__direction__from_left_to_right = """
32
+ flowchart LR
33
+ Start --> Stop
34
+ """
35
+ example_7__node_shapes_a_node_with_round_edges = """
36
+ flowchart LR
37
+ id1(This is the text in the box)
38
+ """
39
+
40
+ example_8__node_shapes_a_stadium_shaped_node = """
41
+ flowchart LR
42
+ id1([This is the text in the box])
43
+ """
44
+
45
+ example_9__node_shapes_a_node_in_a_subroutine ="""
46
+ flowchart LR
47
+ id1[[This is the text in the box]]
48
+ """
49
+
50
+ example_10__node_shapes_a_node_in_a_cylindrical_shape ="""
51
+ flowchart LR
52
+ id1[(Database)]
53
+ """
54
+
55
+ example_11__node_shapes_a_node_in_the_form_of_a_circle = """
56
+ flowchart LR
57
+ id1((This is the text in the circle))
58
+ """
59
+
60
+ example_12__node_shapes_a_node_in_an_asymmetric_shape = """
61
+ flowchart LR
62
+ id1>This is the text in the box]
63
+ """
64
+
65
+ example_13__node_shapes_a_node_rhombus = """
66
+ flowchart LR
67
+ id1{This is the text in the box}
68
+ """
69
+
70
+ example_14__node_shapes_a_hexagon_node = """
71
+ flowchart LR
72
+ id1{{This is the text in the box}}
73
+ """
74
+
75
+ example_15__node_shapes_parallelogram = """
76
+ flowchart TD
77
+ id1[/This is the text in the box/]
78
+ """
79
+
80
+ example_16__node_shapes_parallelogram_alt = r"""
81
+ flowchart TD
82
+ id1[\This is the text in the box\]
83
+ """
84
+
85
+ example_17__node_shapes_trapezoid = r"""
86
+ flowchart TD
87
+ A[/Christmas\]
88
+ """
89
+
90
+ example_18__node_shapes_trapezoid_alt = r"""
91
+ flowchart TD
92
+ B[\Go shopping/]
93
+ """
94
+
95
+ example_19__node_shapes_double_circle = """
96
+ flowchart TD
97
+ id1(((This is the text in the circle)))
98
+ """
@@ -0,0 +1,9 @@
1
+ from enum import Enum, auto
2
+
3
+
4
+ class Diagram__Direction(Enum):
5
+ BT = auto()
6
+ LR = auto()
7
+ TB = auto()
8
+ TD = auto()
9
+ RL = auto()
@@ -0,0 +1,17 @@
1
+ from enum import auto, Enum
2
+
3
+
4
+ class Diagram__Type(Enum):
5
+ class_diagram = auto()
6
+ entity_relationship_diagram = auto()
7
+ flowchart = auto()
8
+ gantt = auto()
9
+ git_graph = auto()
10
+ graph = auto()
11
+ mermaid_map = auto()
12
+ mindmap = auto()
13
+ pie_chart = auto()
14
+ requirement_diagram = auto()
15
+ sequence_diagram = "sequenceDiagram"
16
+ state_diagram = 'stateDiagram-v2'
17
+ user_journey = auto()
@@ -0,0 +1,30 @@
1
+ from __future__ import annotations
2
+ from enum import Enum
3
+
4
+
5
+ class Mermaid__Node__Shape(Enum):
6
+ asymmetric = ('>' , ']' ) # Asymmetric
7
+ circle = ('((' , '))' ) # Circle, used for endpoints or start/end points
8
+ cylindrical = ('[(' , ')]' ) # Cylindrical
9
+ default = ('[' , ']' ) # Rectangle
10
+ double_circle = ('(((', ')))') # Double Circle
11
+ round_edges = ('(' , ')' ) # Stadium shape, used for processes or operations
12
+ rhombus = ('{' , '}' ) # Rhombus, often synonymous with diamond in diagramming contexts
13
+ hexagon = ('{{' , '}}' ) # Hexagon, used for preparation or complex processing
14
+ parallelogram = ('[/' , '/]' ) # Parallelogram, used for input/output
15
+ parallelogram_alt = ('[\\', '\\]') # Alternative parallelogram, also used for input/output
16
+ rectangle = ('[' , ']' ) # Rectangle, used for process
17
+ stadium = ('([' , '])' ) # Stadium
18
+ subroutine = ('[[', ']]' ) # Subroutine
19
+ trapezoid = ('[/' , r'\]' ) # Trapezoid, used for manual operations # todo: figure out why this line is throwing the compile error of: SyntaxWarning: invalid escape sequence '\]'
20
+ trapezoid_alt = ('[\\', '/]' ) # Inverted trapezoid, also used for manual operations
21
+
22
+ @staticmethod
23
+ def get_shape(value = None) -> Mermaid__Node__Shape:
24
+ if isinstance(value, Mermaid__Node__Shape):
25
+ return value
26
+ if type(value) is str:
27
+ for shape in Mermaid__Node__Shape:
28
+ if value == shape.name:
29
+ return shape
30
+ return Mermaid__Node__Shape.default
@@ -0,0 +1,53 @@
1
+ from osbot_utils.utils.Misc import random_text, lower
2
+ from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
3
+ from osbot_utils.graphs.mgraph.MGraph__Config import MGraph__Config
4
+ from osbot_utils.graphs.mgraph.MGraph__Edge import MGraph__Edge
5
+ from osbot_utils.graphs.mgraph.MGraph__Node import MGraph__Node
6
+
7
+
8
+ # todo add support for storing the data in sqlite so that we get the ability to search nodes and edges
9
+ class MGraph(Kwargs_To_Self):
10
+ config : MGraph__Config
11
+ edges : list[MGraph__Edge]
12
+ key : str
13
+ nodes : list[MGraph__Node]
14
+
15
+
16
+ def __init__(self, **kwargs):
17
+ super().__init__(**kwargs)
18
+ if not self.key:
19
+ self.key = random_text("mgraph", lowercase=True) # make sure there is always a key
20
+
21
+ def add_edge(self, from_node, to_node, label=None,attributes=None):
22
+ if self.config.allow_circle_edges is False:
23
+ if from_node == to_node:
24
+ return None
25
+ if self.config.allow_duplicate_edges is False: # todo: figure out if there is a more efficient way to do this
26
+ for edge in self.edges:
27
+ if edge.from_node == from_node and edge.to_node == to_node:
28
+ return None
29
+ new_edge = MGraph__Edge(from_node=from_node, to_node=to_node, label=label, attributes=attributes)
30
+ self.edges.append(new_edge)
31
+ return new_edge
32
+
33
+ def add_node(self, key=None, label=None, attributes=None):
34
+ new_node = MGraph__Node(key=key, label=label, attributes=attributes)
35
+ self.nodes.append(new_node)
36
+ return new_node
37
+
38
+ def data(self):
39
+ from osbot_utils.graphs.mgraph.MGraph__Data import MGraph__Data
40
+ return MGraph__Data(mgraph=self)
41
+
42
+ # def save(self, format='pickle'):
43
+ # if format == 'pickle':
44
+ # return pickle_save_to_file(self)
45
+
46
+ #todo: add save that return saved object
47
+ # def save(self):
48
+ # from osbot_utils.graphs.mgraph.MGraph__Serializer import MGraph__Serializer # due to circular dependency
49
+ # return MGraph__Serializer(mgraph=self).save()
50
+
51
+ def print(self):
52
+ print()
53
+ return self.data().print()
@@ -0,0 +1,7 @@
1
+ from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
2
+
3
+ class MGraph__Config(Kwargs_To_Self):
4
+
5
+ allow_circle_edges : bool
6
+ allow_duplicate_edges : bool
7
+ graph_title : str
@@ -0,0 +1,139 @@
1
+ from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
2
+ from osbot_utils.graphs.mgraph.MGraph import MGraph
3
+ from osbot_utils.helpers.Print_Table import Print_Table
4
+
5
+
6
+ class MGraph__Data(Kwargs_To_Self):
7
+
8
+ mgraph : MGraph
9
+
10
+ def graph_data(self):
11
+ nodes_data = self.nodes_data()
12
+ edges_data = self.edges_data()
13
+ graph_data = {'nodes': nodes_data, 'edges': edges_data}
14
+ return graph_data
15
+
16
+ def edges(self):
17
+ return self.mgraph.edges
18
+
19
+ def edges_data(self):
20
+ edges_data = []
21
+ for edge in self.edges():
22
+ edges_data.append(edge.data())
23
+ return edges_data
24
+
25
+ def nodes(self):
26
+ return self.mgraph.nodes
27
+
28
+ def nodes_data(self):
29
+ nodes_data = []
30
+ for node in self.nodes():
31
+ nodes_data.append(node.data())
32
+ return nodes_data
33
+
34
+
35
+ def nodes__by_key(self):
36
+ by_key = {}
37
+ for node in self.nodes():
38
+ by_key[node.key] = node
39
+ return by_key
40
+
41
+ def nodes__keys(self):
42
+ return [node.key for node in self.nodes()]
43
+
44
+ def nodes_edges(self):
45
+ nodes__edges = {}
46
+ for node in self.nodes():
47
+ nodes__edges[node.key] = []
48
+ for edge in self.edges():
49
+ from_key = edge.from_node.key
50
+ if from_key in nodes__edges: # todo: add a better way to handle this, which is a weird situation, look also at a better way to do this assigment
51
+ nodes__edges[from_key].append(edge.to_node.key)
52
+ for node_key, edges_keys in nodes__edges.items():
53
+ nodes__edges[node_key] = sorted(edges_keys)
54
+ return nodes__edges
55
+
56
+ # def map_paths(self, key, paths, all_paths, nodes_edges):
57
+ # key_edges = nodes_edges[key]
58
+ # new_paths = []
59
+ #
60
+ # for edge_key in key_edges:
61
+ # for path in paths:
62
+ # if edge_key in path:
63
+ # if path not in all_paths:
64
+ # all_paths.append(path)
65
+ # else:
66
+ # new_path = [*path, edge_key]
67
+ # new_paths.append(new_path)
68
+ # self.map_paths(edge_key, new_paths, all_paths, nodes_edges)
69
+ # if new_path not in all_paths:
70
+ # all_paths.append(new_path)
71
+ # if new_paths:
72
+ # return new_paths
73
+
74
+ # for edge_key in key_edges:
75
+ # self.map_paths(edge_key, paths, nodes_edges)
76
+ return paths
77
+
78
+ # def nodes__find_all_paths(self):
79
+ # key = self.nodes__keys()[0]
80
+ # nodes_edges = self.nodes_edges()
81
+ # #for key in self.nodes__keys():
82
+ # all_paths = []
83
+ # paths = [[key]]
84
+ # self.map_paths(key, paths,all_paths, nodes_edges)
85
+ # pprint(all_paths)
86
+
87
+ def print(self):
88
+ with Print_Table() as _:
89
+ _.set_title(self.mgraph.config.graph_title)
90
+ for node_key, edges_keys in self.nodes_edges().items():
91
+ row = {'key': node_key, 'edges': edges_keys}
92
+ _.add_data(row)
93
+ _.set_order('key', 'edges')
94
+ _.print()
95
+
96
+ def print_adjacency_matrix(self):
97
+ adjacency_matrix = self.nodes_edges__adjacency_matrix()
98
+ node_keys = sorted(self.nodes__keys())
99
+ with Print_Table() as _:
100
+ for row in adjacency_matrix:
101
+ _.add_data(row)
102
+ _.set_order('key', *node_keys)
103
+ _.print()
104
+
105
+
106
+ def node_edges__to_from(self):
107
+ # Initialize a dictionary to hold the edges to and from for each node
108
+ node_connections = { node_key: {'edges_to': [], 'edges_from': []} for node_key in self.nodes_edges().keys() }
109
+
110
+
111
+ for node_key, edges_keys in self.nodes_edges().items(): # Fill 'edges_to' and 'edges_from' for each node
112
+ node_connections[node_key]['edges_from'].extend(edges_keys) # 'edges_from' are the outgoing edges from 'node_key'
113
+
114
+ for edge_key in edges_keys: # 'edges_to' are the incoming edges to the nodes in 'edges_keys'
115
+ if edge_key in node_connections: # Check if the edge_key represents a valid node
116
+ node_connections[edge_key]['edges_to'].append(node_key)
117
+
118
+ return node_connections
119
+
120
+ def nodes_edges__adjacency_matrix(self):
121
+ nodes_edges = self.nodes_edges() # Retrieve the nodes and their edges
122
+ node_keys = sorted(nodes_edges.keys()) # Get a sorted list of unique node keys
123
+ node_indices = {node_key: index for index, node_key in enumerate(node_keys)} # Create a mapping of node keys to their respective indices
124
+ size = len(node_keys) # Initialize a square matrix with empty strings
125
+ matrix = [['' for _ in range(size)] for _ in range(size)]
126
+
127
+ for node_key, edges_keys in nodes_edges.items(): # Fill the matrix with 'X' if there is an edge between two nodes
128
+ for edge_key in edges_keys: # Find the matrix positions based on node indices
129
+ row_index = node_indices[node_key]
130
+ col_index = node_indices[edge_key]
131
+ matrix[row_index][col_index] = 'X'
132
+
133
+ table_data = []
134
+ for i, row in enumerate(matrix):
135
+ row_data = {'key': node_keys[i]}
136
+ row_data.update({node_keys[j]: row[j] for j in range(size)})
137
+ table_data.append(row_data)
138
+ return table_data
139
+