Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: create dot and svg from EBDs with no tables #296

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions src/rebdhuhn/graph_conversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
EbdGraphMetaData,
EbdGraphNode,
EbdTable,
EbdTableMetaData,
EbdTableRow,
EbdTableSubRow,
EndNode,
Expand All @@ -21,6 +22,7 @@
ToNoEdge,
ToYesEdge,
)
from rebdhuhn.models.ebd_graph import EmptyNode
from rebdhuhn.models.errors import (
EbdCrossReferenceNotSupportedError,
EndeInWrongColumnError,
Expand Down Expand Up @@ -156,6 +158,8 @@ def convert_table_to_graph(table: EbdTable) -> EbdGraph:
"""
if table is None:
raise ValueError("table must not be None")
if not any(table.rows):
return convert_empty_table_to_graph(table.metadata)
graph = convert_table_to_digraph(table)
graph_metadata = EbdGraphMetaData(
ebd_code=table.metadata.ebd_code,
Expand All @@ -165,3 +169,22 @@ def convert_table_to_graph(table: EbdTable) -> EbdGraph:
role=table.metadata.role,
)
return EbdGraph(metadata=graph_metadata, graph=graph, multi_step_instructions=table.multi_step_instructions)


def convert_empty_table_to_graph(metadata: EbdTableMetaData) -> EbdGraph:
"""
Converts an ebd section with no table to a graph to capture hints.
E.g. E_0534 -> Es ist das EBD E_0527 zu nutzen.
"""
empty_digraph: DiGraph = DiGraph()
empty_digraph.add_nodes_from([(EmptyNode().get_key(), {"node": EmptyNode()})])

graph_metadata = EbdGraphMetaData(
ebd_code=metadata.ebd_code,
chapter=metadata.chapter,
section=metadata.section,
ebd_name=metadata.ebd_name,
role=metadata.role,
remark=metadata.remark,
)
return EbdGraph(metadata=graph_metadata, graph=empty_digraph, multi_step_instructions=None)
22 changes: 20 additions & 2 deletions src/rebdhuhn/graphviz.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from rebdhuhn.graph_utils import _mark_last_common_ancestors
from rebdhuhn.kroki import DotToSvgConverter
from rebdhuhn.models import DecisionNode, EbdGraph, EbdGraphEdge, EndNode, OutcomeNode, StartNode, ToNoEdge, ToYesEdge
from rebdhuhn.models.ebd_graph import EmptyNode
from rebdhuhn.utils import add_line_breaks

ADD_INDENT = " " #: This is just for style purposes to make the plantuml files human-readable.
Expand Down Expand Up @@ -43,6 +44,20 @@ def _convert_start_node_to_dot(ebd_graph: EbdGraph, node: str, indent: str) -> s
)


def _convert_empty_node_to_dot(ebd_graph: EbdGraph, node: str, indent: str) -> str:
"""
Convert a StartNode to dot code
"""
formatted_label = (
f'<B>{ebd_graph.metadata.ebd_code}</B><BR align="center"/>'
f'<FONT point-size="12">{ebd_graph.metadata.remark}</FONT><BR align="center"/>'
)
return (
f'{indent}"{node}" '
f'[margin="0.2,0.12", shape=box, style=filled, fillcolor="#7a8da1", label=<{formatted_label}>];'
)


def _convert_end_node_to_dot(node: str, indent: str) -> str:
"""
Convert an EndNode to dot code
Expand Down Expand Up @@ -95,6 +110,8 @@ def _convert_node_to_dot(ebd_graph: EbdGraph, node: str, indent: str) -> str:
return _convert_end_node_to_dot(node, indent)
case StartNode():
return _convert_start_node_to_dot(ebd_graph, node, indent)
case EmptyNode():
return _convert_empty_node_to_dot(ebd_graph, node, indent)
case _:
raise ValueError(f"Unknown node type: {ebd_graph.graph.nodes[node]['node']}")

Expand Down Expand Up @@ -179,9 +196,10 @@ def convert_graph_to_dot(ebd_graph: EbdGraph) -> str:
dot_code = "digraph D {\n"
for dot_attr_key, dot_attr_value in dot_attributes.items():
dot_code += f"{ADD_INDENT}{dot_attr_key}={dot_attr_value};\n"
assert len(nx_graph["Start"]) == 1, "Start node must have exactly one outgoing edge."
dot_code += _convert_nodes_to_dot(ebd_graph, ADD_INDENT) + "\n\n"
dot_code += "\n".join(_convert_edges_to_dot(ebd_graph, ADD_INDENT)) + "\n"
if "Start" in nx_graph:
assert len(nx_graph["Start"]) == 1, "Start node must have exactly one outgoing edge."
dot_code += "\n".join(_convert_edges_to_dot(ebd_graph, ADD_INDENT)) + "\n"
dot_code += '\n bgcolor="transparent";\n'
return dot_code + "}"

Expand Down
18 changes: 18 additions & 0 deletions src/rebdhuhn/models/ebd_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ class EbdGraphMetaData:
"""
e.g. 'BIKO' for "Prüfende Rolle: 'BIKO'"
"""
remark: Optional[str] = attrs.field(
default=None, validator=attrs.validators.optional(attrs.validators.instance_of(str))
)
"""
remark for empty ebd sections, e.g. 'Derzeit ist für diese Entscheidung kein Entscheidungsbaum notwendig,
da keine Antwort gegeben wird und ausschließlich die Liste versandt wird.'
"""


class EbdGraphNode(ABC):
Expand Down Expand Up @@ -127,6 +134,17 @@ def get_key(self) -> str:
return "Start"


@attrs.define(auto_attribs=True, kw_only=True, frozen=True) # networkx requirement: nodes are hashable (frozen=True)
class EmptyNode(EbdGraphNode):
"""
This is a node which will contain the hints for the cases where a EBD key has no table.
E.g. E_0534 -> Es ist das EBD E_0527 zu nutzen.
"""

def get_key(self) -> str:
return "Empty"


@attrs.define(auto_attribs=True, kw_only=True)
class EbdGraphEdge:
"""
Expand Down
26 changes: 26 additions & 0 deletions unittests/__snapshots__/test_table_to_graph.ambr
Original file line number Diff line number Diff line change
@@ -1,4 +1,30 @@
# serializer version: 1
# name: TestEbdTableModels.test_empty_table_to_graph[metadata_only0][empty_E_0590]
'''
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 9.0.0 (20230911.1827)
-->
<!-- Title: D Pages: 1 -->
<svg width="677pt" height="149pt"
viewBox="0.00 0.00 676.50 149.28" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 145.28)">
<title>D</title>
<text text-anchor="start" x="305.38" y="-121.18" font-family="Times,serif" font-weight="bold" font-size="18.00">GPKE</text>
<text text-anchor="start" x="8" y="-91.08" font-family="Times,serif" font-weight="bold" font-size="16.00">6.24.3: AD: Bestellung zur Stammdatenänderung an LF (verantwortlich)</text>
<!-- Empty -->
<g id="node1" class="node">
<title>Empty</title>
<polygon fill="#7a8da1" stroke="black" points="661.4,-43.28 7.1,-43.28 7.1,0 661.4,0 661.4,-43.28"/>
<text text-anchor="start" x="306.12" y="-22.34" font-family="Times,serif" font-weight="bold" font-size="14.00">E_0590</text>
<text text-anchor="start" x="21.5" y="-10.24" font-family="Times,serif" font-size="12.00">Derzeit ist für diese Entscheidung kein Entscheidungsbaum notwendig, da keine Antwort gegeben wird.</text>
</g>
</g>
</svg>

'''
# ---
# name: TestEbdTableModels.test_table_to_digraph[table0-DiGraph with 6 nodes and 5 edges][test_table_to_digraph_E_0003]
'<?xml version="1.0" encoding="us-ascii" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" height="445px" preserveAspectRatio="none" style="width:855px;height:445px;background:#FFFFFF;" version="1.1" viewBox="0 0 855 445" width="855px" zoomAndPan="magnify"><defs/><g><text fill="#888888" font-family="DejaVu Serif Condensed" font-size="10" font-weight="bold" lengthAdjust="spacing" textLength="43" x="799" y="19.2822">FV2210</text><text fill="#888888" font-family="DejaVu Serif Condensed" font-size="10" lengthAdjust="spacing" textLength="56" x="786" y="30.9229">2022-12-12</text><text fill="#000000" font-family="DejaVu Serif Condensed" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="812" x="20" y="57.2764">7.39 AD: Bestellung der Aggregationsebene der Bilanzkreissummenzeitreihe auf Ebene der Regelzone</text><text fill="#000000" font-family="DejaVu Serif Condensed" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="5" x="423.5" y="73.5732">&#160;</text><text fill="#000000" font-family="DejaVu Serif Condensed" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="478" x="187" y="89.8701">7.39.1 E_0003_Bestellung der Aggregationsebene RZ pr&#252;fen</text><text fill="#000000" font-family="DejaVu Serif Condensed" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="5" x="423.5" y="106.167">&#160;</text><text fill="#000000" font-family="DejaVu Serif Condensed" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="5" x="423.5" y="122.4639">&#160;</text><text fill="#000000" font-family="DejaVu Serif Condensed" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="5" x="423.5" y="138.7607">&#160;</text><path d="M478.5,161.3906 L478.5,169.375 L458.5,173.375 L478.5,177.375 L478.5,185.3594 A0,0 0 0 0 478.5,185.3594 L641.5,185.3594 A0,0 0 0 0 641.5,185.3594 L641.5,171.3906 L631.5,161.3906 L478.5,161.3906 A0,0 0 0 0 478.5,161.3906 " fill="#F3F1F6" style="stroke:#F3F1F6;stroke-width:0.5;"/><path d="M631.5,161.3906 L631.5,171.3906 L641.5,171.3906 L631.5,161.3906 " fill="#F3F1F6" style="stroke:#F3F1F6;stroke-width:0.5;"/><text fill="#000000" font-family="DejaVu Serif Condensed" font-size="12" font-style="italic" font-weight="bold" lengthAdjust="spacing" textLength="142" x="484.5" y="177.5293">Pr&#252;fende Rolle: &#220;NB</text><rect fill="#7A8DA1" height="38.625" rx="12.5" ry="12.5" style="stroke:none;stroke-width:0.5;" width="82" x="376.5" y="154.0625"/><text fill="#000000" font-family="DejaVu Serif Condensed" font-size="16" font-weight="bold" lengthAdjust="spacing" textLength="62" x="386.5" y="178.9141">E_0003</text><path d="M228,271.3281 L228,295.2969 A0,0 0 0 0 228,295.2969 L372,295.2969 A0,0 0 0 0 372,295.2969 L372,289.3281 L392,283.3125 L372,281.3281 L372,281.3281 L362,271.3281 L228,271.3281 A0,0 0 0 0 228,271.3281 " fill="#F3F1F6" style="stroke:#F3F1F6;stroke-width:0.5;"/><path d="M362,271.3281 L362,281.3281 L372,281.3281 L362,271.3281 " fill="#F3F1F6" style="stroke:#F3F1F6;stroke-width:0.5;"/><text fill="#000000" font-family="DejaVu Serif Condensed" font-size="12" lengthAdjust="spacing" textLength="123" x="234" y="287.4668">Frist&#252;berschreitung</text><rect fill="#7A8DA1" height="38.625" rx="12.5" ry="12.5" style="stroke:none;stroke-width:0.5;" width="51" x="392" y="264"/><text fill="#000000" font-family="DejaVu Serif Condensed" font-size="16" lengthAdjust="spacing" textLength="31" x="402" y="288.8516">A01</text><polygon fill="#7AAB8A" points="190,212.6875,645,212.6875,657,224.6875,645,236.6875,190,236.6875,178,224.6875,190,212.6875" style="stroke:#7AAB8A;stroke-width:0.5;"/><text fill="#000000" font-family="DejaVu Serif Condensed" font-size="16" lengthAdjust="spacing" textLength="32" x="421.5" y="251.5391">nein</text><text fill="#000000" font-family="DejaVu Serif Condensed" font-size="18" font-weight="bold" lengthAdjust="spacing" textLength="20" x="190" y="230.9189">1:</text><text fill="#000000" font-family="DejaVu Serif Condensed" font-size="18" lengthAdjust="spacing" textLength="423" x="222" y="230.9189">Erfolgt der Eingang der Bestellung fristgerecht?</text><text fill="#000000" font-family="DejaVu Serif Condensed" font-size="16" lengthAdjust="spacing" textLength="12" x="657" y="220.9141">ja</text><path d="M135,403.2656 L135,427.2344 A0,0 0 0 0 135,427.2344 L372,427.2344 A0,0 0 0 0 372,427.2344 L372,421.2656 L392,415.25 L372,413.2656 L372,413.2656 L362,403.2656 L135,403.2656 A0,0 0 0 0 135,403.2656 " fill="#F3F1F6" style="stroke:#F3F1F6;stroke-width:0.5;"/><path d="M362,403.2656 L362,413.2656 L372,413.2656 L362,403.2656 " fill="#F3F1F6" style="stroke:#F3F1F6;stroke-width:0.5;"/><text fill="#000000" font-family="DejaVu Serif Condensed" font-size="12" lengthAdjust="spacing" textLength="216" x="141" y="419.4043">Gew&#228;hlter Zeitpunkt nicht zul&#228;ssig</text><rect fill="#7A8DA1" height="38.625" rx="12.5" ry="12.5" style="stroke:none;stroke-width:0.5;" width="51" x="392" y="395.9375"/><text fill="#000000" font-family="DejaVu Serif Condensed" font-size="16" lengthAdjust="spacing" textLength="31" x="402" y="420.7891">A02</text><polygon fill="#7AAB8A" points="170,344.625,665,344.625,677,356.625,665,368.625,170,368.625,158,356.625,170,344.625" style="stroke:#7AAB8A;stroke-width:0.5;"/><text fill="#000000" font-family="DejaVu Serif Condensed" font-size="16" lengthAdjust="spacing" textLength="32" x="421.5" y="383.4766">nein</text><text fill="#000000" font-family="DejaVu Serif Condensed" font-size="18" font-weight="bold" lengthAdjust="spacing" textLength="20" x="170" y="362.8564">2:</text><text fill="#000000" font-family="DejaVu Serif Condensed" font-size="18" lengthAdjust="spacing" textLength="463" x="202" y="362.8564">Erfolgt die Bestellung zum Monatsersten 00:00 Uhr?</text><text fill="#000000" font-family="DejaVu Serif Condensed" font-size="16" lengthAdjust="spacing" textLength="12" x="677" y="352.8516">ja</text><ellipse cx="709" cy="356.625" fill="none" rx="10" ry="10" style="stroke:#669580;stroke-width:1.5;"/><line style="stroke:#669580;stroke-width:2.5;" x1="702.8128" x2="715.1872" y1="350.4378" y2="362.8122"/><line style="stroke:#669580;stroke-width:2.5;" x1="715.1872" x2="702.8128" y1="350.4378" y2="362.8122"/><line style="stroke:#7AAB8A;stroke-width:1.0;" x1="417.5" x2="417.5" y1="236.6875" y2="264"/><polygon fill="#7AAB8A" points="413.5,254,417.5,264,421.5,254,417.5,258" style="stroke:#7AAB8A;stroke-width:1.0;"/><line style="stroke:#7AAB8A;stroke-width:1.0;" x1="657" x2="669" y1="224.6875" y2="224.6875"/><polygon fill="#7AAB8A" points="665,270.3125,669,280.3125,673,270.3125,669,274.3125" style="stroke:#7AAB8A;stroke-width:1.0;"/><line style="stroke:#7AAB8A;stroke-width:1.0;" x1="669" x2="669" y1="224.6875" y2="324.625"/><line style="stroke:#7AAB8A;stroke-width:1.0;" x1="669" x2="417.5" y1="324.625" y2="324.625"/><line style="stroke:#7AAB8A;stroke-width:1.0;" x1="417.5" x2="417.5" y1="324.625" y2="344.625"/><polygon fill="#7AAB8A" points="413.5,334.625,417.5,344.625,421.5,334.625,417.5,338.625" style="stroke:#7AAB8A;stroke-width:1.0;"/><line style="stroke:#7AAB8A;stroke-width:1.0;" x1="417.5" x2="417.5" y1="192.6875" y2="212.6875"/><polygon fill="#7AAB8A" points="413.5,202.6875,417.5,212.6875,421.5,202.6875,417.5,206.6875" style="stroke:#7AAB8A;stroke-width:1.0;"/><line style="stroke:#7AAB8A;stroke-width:1.0;" x1="417.5" x2="417.5" y1="368.625" y2="395.9375"/><polygon fill="#7AAB8A" points="413.5,385.9375,417.5,395.9375,421.5,385.9375,417.5,389.9375" style="stroke:#7AAB8A;stroke-width:1.0;"/><line style="stroke:#7AAB8A;stroke-width:1.0;" x1="677" x2="699" y1="356.625" y2="356.625"/><polygon fill="#7AAB8A" points="689,352.625,699,356.625,689,360.625,693,356.625" style="stroke:#7AAB8A;stroke-width:1.0;"/><!--SRC=[XPF1Rk8m48RlVegHzWA7xIP1Isb0L51WJukgeksXbygGSUBYsCXs5YtFiiS-Hc-ys4u2IzE8jbOEIUQV__yyYTrQwWstF8T5nXEpbJe5mPL3vawLkV4uDZP1UsEKiV15T4Gekaq5NwrJQmgTVAIQ6UqNSeSGHhNAQENbR-d_r5EqqNXEqeovvBs4XsUKhr5rY96rPdjgvSiURnRVwJkwJYJFZKxEO3Iz6Wtr0NucVaNerKG92bueNnRdF4UOu1E_Bs21LWgueJRK3fCphjCJPRTxzRqNC9OX9nesM0vdzr4K1YmAekXhM5uCTN8iCo-zGdPvqRc2qII6CJgFIXNqZr0PHcbgCULU6ifTeiRgxLWghdThYzAv8izHxr1wUieGU25WUXBUOOfgLtxbAk4YXEbZ40ITnqzZxXvWO_Sl0ZMZHRnm08rfFmUFmRVbiCyq_N1WPPhvSgS3ERmzT2LaOPJ26FP_v-FAe9GoHdDiaJACeJI0gHL6fRwYcDB9u4UY6g2mqlaKBQuoVzq6dw66rXDlCwGp12sDKhSPq8f7GTYlxYegXS9NJ-MQbIRxboLQjyfeL9wSgoAHLM3b_LegrIzPfVZF6JKv9TRPTaKEFutctf6n9x0WY8C0VcNs3Uanw1nnz1xn3zpkdpFbQHWFnBifzDg3bZG4IbNxP-Vanplu1m00]--></g></svg>'
# ---
Expand Down
25 changes: 1 addition & 24 deletions unittests/output/E_0003.dot.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading