From 051201667e39b3d7b71e5a69ef7e5f2a6e5adc06 Mon Sep 17 00:00:00 2001 From: pheetah Date: Fri, 14 Jun 2024 02:29:49 +0300 Subject: [PATCH 1/3] feat: architecture --- client.py | 24 ++++++++------ compose.py | 16 ++++++++++ doctree.py | 76 ++++++++++++++++++++++++++++++++++++++++++-- settings/language.py | 6 ++++ 4 files changed, 111 insertions(+), 11 deletions(-) diff --git a/client.py b/client.py index 66b816a..969ad7d 100644 --- a/client.py +++ b/client.py @@ -35,9 +35,7 @@ def _compose_and_draw(self, pygraph: AGraph, flow: TokenSequence): return diagram - def _compose_and_draw_inner( - self, pygraph: AGraph, flow: TokenSequence, name: str - ): + def _compose_and_draw_inner(self, pygraph: AGraph, flow: TokenSequence, name: str): pygraph.add_subgraph( name=name, label=name, cluster=True, labelloc="t", fontcolor="blue" ) @@ -46,18 +44,23 @@ def _compose_and_draw_inner( returned = self._compose_and_draw(pygraph=sg, flow=flow) return returned + def _draw_architectural_connections(self, pygraph: AGraph, diagram: TokenSequence): + diagram.architecture.draw_connections(pygraph=pygraph) + def draw_epc(self, file_format: list[FileFormat]): cluster = Cluster() - cluster.extract_flows( - file_name_list=[file.input_path for file in file_format] - ) + cluster.extract_flows(file_name_list=[file.input_path for file in file_format]) main_flows = [flow.tokens for flow in cluster._main_flows] + ARCHG = AGraph(directed=True, compound=True) for index, main_flow in enumerate(main_flows): G = AGraph(directed=True, compound=True) - current_main_process = self._compose_and_draw( - pygraph=G, flow=main_flow + current_main_process = self._compose_and_draw(pygraph=G, flow=main_flow) + + current_main_process.architecture.draw_architectural( + ARCHG, cluster._main_flows[index].name ) + inner_flows = [ flow for flow in cluster._inner_flows @@ -75,4 +78,7 @@ def draw_epc(self, file_format: list[FileFormat]): prog="dot", ) - log.info(msg=f"{file.output_path} - Done.") + ARCHG.layout() + ARCHG.draw("./_outputs/architecture.png", prog="dot") + + log.info(msg=f"{file.output_path} - Done.") diff --git a/compose.py b/compose.py index 11d911c..2b232e9 100644 --- a/compose.py +++ b/compose.py @@ -8,6 +8,7 @@ from settings.language import ( NODE_KEYWORDS, SYMBOLS, + ArchitecturalKeywords, ClusterKeywords, ContextKeywords, Keywords, @@ -52,6 +53,16 @@ def _split_flow(self, token: str): return flows + def _handle_subscriptions(self, token: str, diagram: EpcDiagram) -> EpcNode: + if ArchitecturalKeywords.SUBSCRIBES in token: + raw_action = self._get_after(token, ArchitecturalKeywords.SUBSCRIBES) + subscriptions = list( + map(lambda x: x.lstrip().rstrip(), raw_action.split(",")) + ) + + for subscription in subscriptions: + diagram.architecture.subscribe(subscription) + def _handle_activity(self, token: str, diagram: EpcDiagram) -> EpcNode: if Keywords.ACTIVITY in token: raw_action = self._get_after(token, Keywords.ACTIVITY) @@ -76,6 +87,10 @@ def _handle_event(self, token: str, diagram: EpcDiagram) -> EpcNode: raw_action = self._get_after(token, Keywords.EVENT) diagram.push(EventNode(description=raw_action)) + if ArchitecturalKeywords.PUBLISHES in token: + raw_action = self._get_after(token, ArchitecturalKeywords.PUBLISHES) + diagram.architecture.publish(raw_action) + def _handle_inner_flow(self, token: str, diagram: EpcDiagram) -> EpcNode: if ClusterKeywords.INNER_FLOW in token: raw_name = self._get_after(token, ClusterKeywords.INNER_FLOW) @@ -84,6 +99,7 @@ def _handle_inner_flow(self, token: str, diagram: EpcDiagram) -> EpcNode: def _handle_flow(self, token: str, diagram: EpcDiagram): self._handle_inner_flow(token=token, diagram=diagram) + self._handle_subscriptions(token=token, diagram=diagram) flows = self._split_flow(token=token) for flow in flows: diff --git a/doctree.py b/doctree.py index 85cd2e1..a57f5b7 100644 --- a/doctree.py +++ b/doctree.py @@ -5,6 +5,70 @@ import pygraphviz as pgv +class ArchitectureManager: + _subscribes: list[str] + _publishes: list[str] + + def __init__(self, name: str) -> None: + self._subscribes = [] + self._publishes = [] + + def subscribe(self, subscribe: str): + self._subscribes.append(subscribe) + + def publish(self, publish: str): + self._publishes.append(publish) + + def draw_architectural(self, pygraph: pgv.AGraph, diagram_name: str): + for subscribe in self._subscribes: + pygraph.add_node( + diagram_name, + color="palegoldenrod", + shape="polygon", + fontcolor="black", + style="filled", + fontsize=16, + width=4, + group="1", + ) + pygraph.add_node( + subscribe, + color="darkred", + shape="octagon", + fontcolor="white", + style="filled", + width=1.0, + height=0.6, + fixedsize=False, + group="1", + ) + pygraph.add_edge(subscribe, diagram_name) + + for publish in self._publishes: + pygraph.add_node( + diagram_name, + color="palegoldenrod", + shape="polygon", + fontcolor="black", + style="filled", + fontsize=16, + width=4, + group="1", + ) + pygraph.add_node( + publish, + color="darkred", + shape="octagon", + fontcolor="white", + style="filled", + width=1.0, + height=0.6, + fixedsize=False, + group="1", + ) + pygraph.add_edge(diagram_name, publish) + + class EpcNode(ABC): _description: str _start: str @@ -53,7 +117,9 @@ def draw_line(self, pygraph: pgv.AGraph, end_id: str = None): name=self._description + self._database, ) db_id = self.add_db_node( - pygraph=graph, description=self._database, group=self._description + pygraph=graph, + description=self._database, + group=self._description, ) graph.add_edge(self._description, db_id) @@ -187,7 +253,10 @@ class IfNode(EpcNode): branches: list[EpcNode] def __init__( - self, description: str, next: Self | None = None, branches: list[EpcNode] = [] + self, + description: str, + next: Self | None = None, + branches: list[EpcNode] = [], ) -> None: super().__init__(description, next) self.branches = branches @@ -230,6 +299,8 @@ class EpcDiagram: tail: EpcNode inner_flow_names: list[str] length: int + name: str + architecture: ArchitectureManager def __init__(self, name: str = "EPC") -> None: self.name = name @@ -237,6 +308,7 @@ def __init__(self, name: str = "EPC") -> None: self.tail = None self.length = 0 self.inner_flow_names = [] + self.architecture = ArchitectureManager(name) def add(self, node: EpcNode): self.head = node diff --git a/settings/language.py b/settings/language.py index ceb6c55..7bdb36c 100644 --- a/settings/language.py +++ b/settings/language.py @@ -28,6 +28,11 @@ class ClusterKeywords(StringEnum): END_MAIN_CLUSTER = "end-diagram-main:" +class ArchitecturalKeywords(StringEnum): + SUBSCRIBES = "subscribes:" + PUBLISHES = "publishes:" + + NODE_KEYWORDS = [ Keywords.ACTIVITY, Keywords.EVENT, @@ -45,4 +50,5 @@ class ClusterKeywords(StringEnum): ContextKeywords.DATABASE, ContextKeywords.API_CALL_OUT, ContextKeywords.API_CALL_IN, + ArchitecturalKeywords.PUBLISHES, ] From 48ec300f0f08a9aec538e2142fe63d9e962fd7dc Mon Sep 17 00:00:00 2001 From: pheetah Date: Fri, 14 Jun 2024 02:54:06 +0300 Subject: [PATCH 2/3] fix: errors --- client.py | 4 +--- clusterer.py | 10 +--------- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/client.py b/client.py index 969ad7d..e931024 100644 --- a/client.py +++ b/client.py @@ -12,7 +12,6 @@ @dataclass class FileFormat: input_path: str - output_path: str # Façade @@ -71,7 +70,6 @@ def draw_epc(self, file_format: list[FileFormat]): pygraph=G, flow=inner_flow.tokens, name=inner_flow.name ) - file = file_format[index] G.layout() G.draw( f"./_outputs/{cluster._main_flows[index].name}.png", @@ -81,4 +79,4 @@ def draw_epc(self, file_format: list[FileFormat]): ARCHG.layout() ARCHG.draw("./_outputs/architecture.png", prog="dot") - log.info(msg=f"{file.output_path} - Done.") + log.info(msg="Done.") diff --git a/clusterer.py b/clusterer.py index 2026d46..2252418 100644 --- a/clusterer.py +++ b/clusterer.py @@ -50,9 +50,7 @@ def _find_main_flows(self, token: str, index: int, flows: Flows): if ClusterKeywords.MAIN_CLUSTER in token: flow = deepcopy(Flow()) flow.name = ( - re.search( - rf"(?<={ClusterKeywords.MAIN_CLUSTER})(.*?)$", token - )[0] + re.search(rf"(?<={ClusterKeywords.MAIN_CLUSTER})(.*?)$", token)[0] .lstrip() .rstrip() ) @@ -90,9 +88,3 @@ def extract_flows(self, file_name_list: list[str]): indexes_processed = self._find_main_flows( str(token_raw), index, main_flows ) - - if indexes_processed: - start_index = main_flows.processed_indexes["start_index"] - end_index = main_flows.processed_indexes["end_index"] + 1 - - del parsed[start_index:end_index] From fded1458f8f563549372c0f2df37150004b79d25 Mon Sep 17 00:00:00 2001 From: pheetah Date: Fri, 14 Jun 2024 04:05:47 +0300 Subject: [PATCH 3/3] feat: package initials --- .gitignore | 4 ++-- client.py | 6 +++--- main.py | 51 ++++++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 1 + setup.py | 16 +++++++++++++++ 5 files changed, 73 insertions(+), 5 deletions(-) create mode 100644 main.py create mode 100644 setup.py diff --git a/.gitignore b/.gitignore index 6f13c73..1c36f5f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,13 +4,13 @@ /_trials # ignore outs folder -/_outputs +/_outputs* # ignore python cache folders *__pycache__ # ignore test files for now -_test_files +_test_files* test_cli.py ## PYTHON diff --git a/client.py b/client.py index e931024..d652730 100644 --- a/client.py +++ b/client.py @@ -46,7 +46,7 @@ def _compose_and_draw_inner(self, pygraph: AGraph, flow: TokenSequence, name: st def _draw_architectural_connections(self, pygraph: AGraph, diagram: TokenSequence): diagram.architecture.draw_connections(pygraph=pygraph) - def draw_epc(self, file_format: list[FileFormat]): + def draw_epc(self, out_path: str, file_format: list[FileFormat]): cluster = Cluster() cluster.extract_flows(file_name_list=[file.input_path for file in file_format]) @@ -72,11 +72,11 @@ def draw_epc(self, file_format: list[FileFormat]): G.layout() G.draw( - f"./_outputs/{cluster._main_flows[index].name}.png", + f"./{out_path}/{cluster._main_flows[index].name}.png", prog="dot", ) ARCHG.layout() - ARCHG.draw("./_outputs/architecture.png", prog="dot") + ARCHG.draw(f"./{out_path}/architecture.png", prog="dot") log.info(msg="Done.") diff --git a/main.py b/main.py new file mode 100644 index 0000000..611163a --- /dev/null +++ b/main.py @@ -0,0 +1,51 @@ +import os +from typing import Optional + +import typer + +from client import DocupytClient, FileFormat + +app = typer.Typer() + +client = DocupytClient() + + +def get_filepaths(directory): + """ + This function will generate the file names in a directory + tree by walking the tree either top-down or bottom-up. For each + directory in the tree rooted at directory top (including top itself), + it yields a 3-tuple (dirpath, dirnames, filenames). + """ + file_paths = [] + + for root, directories, files in os.walk(directory): + for filename in files: + filepath = os.path.join(root, filename) + file_paths.append(filepath) + + return file_paths + + +@app.command() +def docupyt(path: Optional[str] = None, out_path: Optional[str] = "_outputs"): + if not path: + raise ValueError("Path is required") + + if not os.path.exists(out_path): + os.mkdir(out_path) + + formats = [] + + for filepath in get_filepaths(path): + formats.append( + FileFormat( + input_path=filepath, + ) + ) + + client.draw_epc(out_path=out_path, file_format=formats) + + +if __name__ == "__main__": + app() diff --git a/requirements.txt b/requirements.txt index 67a9861..e82d331 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,6 @@ # main code-tokenize==0.2.0 +typer==0.12.3 # test pytest-cov==4.1.0 pytest==7.4.3 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..85cd10f --- /dev/null +++ b/setup.py @@ -0,0 +1,16 @@ +from setuptools import find_packages, setup + +setup( + name="docupyt", + version="0.1.1", + author="Eyup Fatih Ersoy", + author_email="eyupfatih.ersoy@hotmail.com", + description="Docupyt", + packages=find_packages(""), + classifiers=[ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + ], + python_requires=">=3.6", +)