mirror of
https://github.com/kuhyx/testsAndMisc-archive.git
synced 2026-07-04 13:43:02 +02:00
refactor(praca/generate_images): fix ruff violations in generate_process_diagrams.py
This commit is contained in:
parent
bd45d60c85
commit
31a41e33d8
@ -5,16 +5,26 @@ all representing the same process: "Obsluga reklamacji" (Complaint Handling).
|
||||
Output: A4-compatible, black & white, laser-printer-friendly PNG files.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import matplotlib as mpl
|
||||
|
||||
mpl.use("Agg")
|
||||
from pathlib import Path
|
||||
|
||||
import matplotlib.patches as mpatches
|
||||
from matplotlib.patches import FancyBboxPatch, Polygon
|
||||
from matplotlib.path import Path as MplPath
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from matplotlib.axes import Axes
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
# --- Common settings ---
|
||||
DPI = 300
|
||||
BG_COLOR = "white"
|
||||
@ -25,7 +35,9 @@ OUTPUT_DIR = str(Path(__file__).resolve().parent / "img")
|
||||
Path(OUTPUT_DIR).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
def draw_arrow(ax, x1, y1, x2, y2) -> None:
|
||||
def draw_arrow(
|
||||
ax: Axes, x1: float, y1: float, x2: float, y2: float,
|
||||
) -> None:
|
||||
"""Draw arrow."""
|
||||
ax.annotate(
|
||||
"",
|
||||
@ -35,13 +47,27 @@ def draw_arrow(ax, x1, y1, x2, y2) -> None:
|
||||
)
|
||||
|
||||
|
||||
def draw_line(ax, x1, y1, x2, y2) -> None:
|
||||
def draw_line(
|
||||
ax: Axes, x1: float, y1: float, x2: float, y2: float,
|
||||
) -> None:
|
||||
"""Draw line."""
|
||||
ax.plot([x1, x2], [y1, y2], color=LINE_COLOR, lw=1.3, solid_capstyle="round")
|
||||
ax.plot(
|
||||
[x1, x2], [y1, y2],
|
||||
color=LINE_COLOR, lw=1.3, solid_capstyle="round",
|
||||
)
|
||||
|
||||
|
||||
def draw_rounded_rect(
|
||||
ax, x, y, w, h, text, fill="white", lw=1.5, fontsize=FONT_SIZE
|
||||
ax: Axes,
|
||||
x: float,
|
||||
y: float,
|
||||
w: float,
|
||||
h: float,
|
||||
text: str,
|
||||
*,
|
||||
fill: str = "white",
|
||||
lw: float = 1.5,
|
||||
fontsize: float = FONT_SIZE,
|
||||
) -> None:
|
||||
"""Draw rounded rect."""
|
||||
rect = FancyBboxPatch(
|
||||
@ -57,7 +83,16 @@ def draw_rounded_rect(
|
||||
ax.text(x, y, text, ha="center", va="center", fontsize=fontsize)
|
||||
|
||||
|
||||
def draw_diamond(ax, x, y, size, text="", fill="white", fontsize=8) -> None:
|
||||
def draw_diamond(
|
||||
ax: Axes,
|
||||
x: float,
|
||||
y: float,
|
||||
size: float,
|
||||
text: str = "",
|
||||
*,
|
||||
fill: str = "white",
|
||||
fontsize: float = 8,
|
||||
) -> None:
|
||||
"""Draw diamond."""
|
||||
s = size
|
||||
diamond = Polygon(
|
||||
@ -70,29 +105,25 @@ def draw_diamond(ax, x, y, size, text="", fill="white", fontsize=8) -> None:
|
||||
ax.add_patch(diamond)
|
||||
if text:
|
||||
ax.text(
|
||||
x, y, text, ha="center", va="center", fontsize=fontsize, fontweight="bold"
|
||||
x,
|
||||
y,
|
||||
text,
|
||||
ha="center",
|
||||
va="center",
|
||||
fontsize=fontsize,
|
||||
fontweight="bold",
|
||||
)
|
||||
|
||||
|
||||
# =========================================================================
|
||||
# 1. BPMN 2.0 Diagram
|
||||
# =========================================================================
|
||||
def generate_bpmn() -> None:
|
||||
"""Generate bpmn."""
|
||||
fig, ax = plt.subplots(figsize=(11, 7.5))
|
||||
ax.set_xlim(0, 110)
|
||||
ax.set_ylim(0, 75)
|
||||
ax.set_aspect("equal")
|
||||
ax.axis("off")
|
||||
fig.patch.set_facecolor(BG_COLOR)
|
||||
ax.set_title(
|
||||
"BPMN 2.0 \u2014 Obs\u0142uga reklamacji",
|
||||
fontsize=TITLE_SIZE,
|
||||
fontweight="bold",
|
||||
pad=12,
|
||||
)
|
||||
|
||||
# --- Pool outline ---
|
||||
|
||||
def _draw_bpmn_pool_and_lanes(
|
||||
ax: Axes,
|
||||
) -> tuple[float, float, float, float]:
|
||||
"""Draw BPMN pool outline and swim lanes, return lane positions."""
|
||||
pool_x, pool_y, pool_w, pool_h = 3, 3, 104, 68
|
||||
ax.add_patch(
|
||||
plt.Rectangle(
|
||||
@ -105,10 +136,12 @@ def generate_bpmn() -> None:
|
||||
)
|
||||
)
|
||||
|
||||
# Pool label
|
||||
label_strip = pool_x + 4
|
||||
ax.plot(
|
||||
[label_strip, label_strip], [pool_y, pool_y + pool_h], color=LINE_COLOR, lw=1.5
|
||||
[label_strip, label_strip],
|
||||
[pool_y, pool_y + pool_h],
|
||||
color=LINE_COLOR,
|
||||
lw=1.5,
|
||||
)
|
||||
ax.text(
|
||||
pool_x + 2,
|
||||
@ -121,16 +154,21 @@ def generate_bpmn() -> None:
|
||||
va="center",
|
||||
)
|
||||
|
||||
# --- Lanes ---
|
||||
lane_top = pool_y + pool_h
|
||||
lane_mid1 = pool_y + pool_h * 2 / 3
|
||||
lane_mid2 = pool_y + pool_h * 1 / 3
|
||||
|
||||
ax.plot(
|
||||
[label_strip, pool_x + pool_w], [lane_mid1, lane_mid1], color=LINE_COLOR, lw=1
|
||||
[label_strip, pool_x + pool_w],
|
||||
[lane_mid1, lane_mid1],
|
||||
color=LINE_COLOR,
|
||||
lw=1,
|
||||
)
|
||||
ax.plot(
|
||||
[label_strip, pool_x + pool_w], [lane_mid2, lane_mid2], color=LINE_COLOR, lw=1
|
||||
[label_strip, pool_x + pool_w],
|
||||
[lane_mid2, lane_mid2],
|
||||
color=LINE_COLOR,
|
||||
lw=1,
|
||||
)
|
||||
|
||||
y_bok = (lane_top + lane_mid1) / 2
|
||||
@ -169,82 +207,124 @@ def generate_bpmn() -> None:
|
||||
)
|
||||
|
||||
content_left = label_strip + 5
|
||||
return y_bok, y_jak, y_mag, content_left
|
||||
|
||||
# --- Elements ---
|
||||
# Start event
|
||||
|
||||
def _draw_bpmn_elements(
|
||||
ax: Axes,
|
||||
y_bok: float,
|
||||
y_jak: float,
|
||||
y_mag: float,
|
||||
content_left: float,
|
||||
) -> None:
|
||||
"""Draw all BPMN tasks, gateways, and events."""
|
||||
sx = content_left + 4
|
||||
ax.add_patch(
|
||||
plt.Circle((sx, y_bok), 2, lw=2, edgecolor=LINE_COLOR, facecolor="white")
|
||||
plt.Circle(
|
||||
(sx, y_bok), 2, lw=2, edgecolor=LINE_COLOR, facecolor="white",
|
||||
)
|
||||
)
|
||||
ax.text(
|
||||
sx, y_bok - 3.5, "Reklamacja\nwp\u0142ywa", fontsize=6, ha="center",
|
||||
)
|
||||
ax.text(sx, y_bok - 3.5, "Reklamacja\nwp\u0142ywa", fontsize=6, ha="center")
|
||||
|
||||
# Task 1: Przyjmij zg\u0142oszenie (BOK)
|
||||
t1x = sx + 14
|
||||
draw_rounded_rect(ax, t1x, y_bok, 14, 6, "Przyjmij\nzg\u0142oszenie")
|
||||
draw_arrow(ax, sx + 2, y_bok, t1x - 7, y_bok)
|
||||
|
||||
# Task 2: Zweryfikuj zasadno\u015b\u0107 (Jako\u015b\u0107) \u2014 elbow routing
|
||||
t2x = t1x + 18
|
||||
draw_rounded_rect(ax, t2x, y_jak, 14, 6, "Zweryfikuj\nzasadno\u015b\u0107")
|
||||
draw_rounded_rect(
|
||||
ax, t2x, y_jak, 14, 6, "Zweryfikuj\nzasadno\u015b\u0107",
|
||||
)
|
||||
elbow_x = t1x + 10
|
||||
draw_line(ax, t1x + 7, y_bok, elbow_x, y_bok)
|
||||
draw_line(ax, elbow_x, y_bok, elbow_x, y_jak)
|
||||
draw_arrow(ax, elbow_x, y_jak, t2x - 7, y_jak)
|
||||
|
||||
# XOR Gateway (split)
|
||||
gx = t2x + 14
|
||||
draw_diamond(ax, gx, y_jak, 3.5, "X")
|
||||
draw_arrow(ax, t2x + 7, y_jak, gx - 3.5, y_jak)
|
||||
|
||||
# YES: down to Magazyn
|
||||
t3x = gx + 14
|
||||
draw_rounded_rect(ax, t3x, y_mag, 14, 6, "Przygotuj\nwymian\u0119/zwrot")
|
||||
draw_rounded_rect(
|
||||
ax, t3x, y_mag, 14, 6, "Przygotuj\nwymian\u0119/zwrot",
|
||||
)
|
||||
draw_line(ax, gx, y_jak - 3.5, gx, y_mag)
|
||||
draw_arrow(ax, gx, y_mag, t3x - 7, y_mag)
|
||||
ax.text(gx + 1.5, y_jak - 6, "Tak", fontsize=7, ha="left")
|
||||
|
||||
# NO: right in Jako\u015b\u0107
|
||||
t4x = gx + 14
|
||||
draw_rounded_rect(ax, t4x, y_jak, 14, 6, "Odrzu\u0107\nreklamacj\u0119")
|
||||
draw_rounded_rect(
|
||||
ax, t4x, y_jak, 14, 6, "Odrzu\u0107\nreklamacj\u0119",
|
||||
)
|
||||
draw_arrow(ax, gx + 3.5, y_jak, t4x - 7, y_jak)
|
||||
ax.text(gx + 4, y_jak + 2, "Nie", fontsize=7, ha="left")
|
||||
|
||||
# XOR merge (in BOK)
|
||||
mx = t4x + 14
|
||||
draw_diamond(ax, mx, y_bok, 3.5, "X")
|
||||
# From Odrzu\u0107 up to merge
|
||||
draw_line(ax, t4x + 7, y_jak, mx, y_jak)
|
||||
draw_arrow(ax, mx, y_jak, mx, y_bok - 3.5)
|
||||
# From Przygotuj wymian\u0119 up to merge (offset to avoid overlap)
|
||||
draw_line(ax, t3x + 7, y_mag, mx - 4, y_mag)
|
||||
draw_line(ax, mx - 4, y_mag, mx - 4, y_bok)
|
||||
draw_arrow(ax, mx - 4, y_bok, mx - 3.5, y_bok)
|
||||
|
||||
# Task 5: Powiadom klienta (BOK)
|
||||
t5x = mx + 13
|
||||
draw_rounded_rect(ax, t5x, y_bok, 14, 6, "Powiadom\nklienta")
|
||||
draw_arrow(ax, mx + 3.5, y_bok, t5x - 7, y_bok)
|
||||
|
||||
# End event
|
||||
ex = t5x + 12
|
||||
ax.add_patch(
|
||||
plt.Circle((ex, y_bok), 2, lw=3, edgecolor=LINE_COLOR, facecolor="white")
|
||||
plt.Circle(
|
||||
(ex, y_bok), 2, lw=3, edgecolor=LINE_COLOR, facecolor="white",
|
||||
)
|
||||
)
|
||||
draw_arrow(ax, t5x + 7, y_bok, ex - 2, y_bok)
|
||||
ax.text(ex, y_bok - 3.5, "Koniec", fontsize=6, ha="center")
|
||||
|
||||
# --- Legend ---
|
||||
|
||||
def _draw_bpmn_legend(ax: Axes) -> None:
|
||||
"""Draw BPMN legend."""
|
||||
ly = 1
|
||||
ax.text(12, ly, "Legenda:", fontsize=7, fontweight="bold", va="center")
|
||||
ax.add_patch(plt.Circle((22, ly), 1, lw=2, edgecolor=LINE_COLOR, facecolor="white"))
|
||||
ax.text(
|
||||
12, ly, "Legenda:", fontsize=7, fontweight="bold", va="center",
|
||||
)
|
||||
ax.add_patch(
|
||||
plt.Circle(
|
||||
(22, ly), 1, lw=2, edgecolor=LINE_COLOR, facecolor="white",
|
||||
)
|
||||
)
|
||||
ax.text(24, ly, "Start", fontsize=6, va="center")
|
||||
ax.add_patch(plt.Circle((30, ly), 1, lw=3, edgecolor=LINE_COLOR, facecolor="white"))
|
||||
ax.add_patch(
|
||||
plt.Circle(
|
||||
(30, ly), 1, lw=3, edgecolor=LINE_COLOR, facecolor="white",
|
||||
)
|
||||
)
|
||||
ax.text(32, ly, "Koniec", fontsize=6, va="center")
|
||||
draw_diamond(ax, 40, ly, 1.5, "X", fontsize=5)
|
||||
ax.text(43, ly, "Bramka XOR", fontsize=6, va="center")
|
||||
draw_rounded_rect(ax, 58, ly, 7, 2.5, "Zadanie", fontsize=6)
|
||||
ax.text(65, ly, "Sequence Flow \u2192", fontsize=6, va="center")
|
||||
|
||||
|
||||
def generate_bpmn() -> None:
|
||||
"""Generate bpmn."""
|
||||
fig, ax = plt.subplots(figsize=(11, 7.5))
|
||||
ax.set_xlim(0, 110)
|
||||
ax.set_ylim(0, 75)
|
||||
ax.set_aspect("equal")
|
||||
ax.axis("off")
|
||||
fig.patch.set_facecolor(BG_COLOR)
|
||||
ax.set_title(
|
||||
"BPMN 2.0 \u2014 Obs\u0142uga reklamacji",
|
||||
fontsize=TITLE_SIZE,
|
||||
fontweight="bold",
|
||||
pad=12,
|
||||
)
|
||||
|
||||
y_bok, y_jak, y_mag, content_left = _draw_bpmn_pool_and_lanes(ax)
|
||||
_draw_bpmn_elements(ax, y_bok, y_jak, y_mag, content_left)
|
||||
_draw_bpmn_legend(ax)
|
||||
|
||||
fig.tight_layout()
|
||||
fig.savefig(
|
||||
str(Path(OUTPUT_DIR) / "bpmn_reklamacja.png"),
|
||||
@ -253,12 +333,112 @@ def generate_bpmn() -> None:
|
||||
bbox_inches="tight",
|
||||
)
|
||||
plt.close(fig)
|
||||
print(" OK BPMN saved")
|
||||
_logger.info(" OK BPMN saved")
|
||||
|
||||
|
||||
# =========================================================================
|
||||
# 2. UML Activity Diagram
|
||||
# =========================================================================
|
||||
|
||||
|
||||
def _draw_uml_elements(ax: Axes) -> None:
|
||||
"""Draw all UML activity diagram elements."""
|
||||
cx = 50
|
||||
y = 93
|
||||
step = 11
|
||||
|
||||
ax.add_patch(
|
||||
plt.Circle((cx, y), 1.8, facecolor="black", edgecolor="black"),
|
||||
)
|
||||
|
||||
y -= step
|
||||
draw_rounded_rect(
|
||||
ax, cx, y, 28, 6, "Przyjmij zg\u0142oszenie reklamacji",
|
||||
)
|
||||
draw_arrow(ax, cx, y + step - 1.8, cx, y + 3)
|
||||
|
||||
y -= step
|
||||
draw_rounded_rect(
|
||||
ax, cx, y, 28, 6, "Zweryfikuj zasadno\u015b\u0107",
|
||||
)
|
||||
draw_arrow(ax, cx, y + step - 3, cx, y + 3)
|
||||
|
||||
y -= step
|
||||
draw_diamond(ax, cx, y, 4)
|
||||
draw_arrow(ax, cx, y + step - 3, cx, y + 4)
|
||||
ax.text(
|
||||
cx + 6, y + 5, "[zasadna?]", fontsize=8, fontstyle="italic",
|
||||
)
|
||||
|
||||
dec_y = y
|
||||
branch_y = dec_y - step
|
||||
|
||||
left_x = cx - 24
|
||||
draw_rounded_rect(
|
||||
ax, left_x, branch_y, 22, 6, "Przygotuj\nwymian\u0119/zwrot",
|
||||
)
|
||||
draw_line(ax, cx - 4, dec_y, left_x, dec_y)
|
||||
draw_arrow(ax, left_x, dec_y, left_x, branch_y + 3)
|
||||
ax.text(
|
||||
left_x + 2, dec_y + 1.5, "[tak]",
|
||||
fontsize=8, fontstyle="italic",
|
||||
)
|
||||
|
||||
right_x = cx + 24
|
||||
draw_rounded_rect(
|
||||
ax, right_x, branch_y, 22, 6, "Odrzu\u0107\nreklamacj\u0119",
|
||||
)
|
||||
draw_line(ax, cx + 4, dec_y, right_x, dec_y)
|
||||
draw_arrow(ax, right_x, dec_y, right_x, branch_y + 3)
|
||||
ax.text(
|
||||
right_x - 12, dec_y + 1.5, "[nie]",
|
||||
fontsize=8, fontstyle="italic",
|
||||
)
|
||||
|
||||
merge_y = branch_y - step
|
||||
draw_diamond(ax, cx, merge_y, 4)
|
||||
draw_line(ax, left_x, branch_y - 3, left_x, merge_y)
|
||||
draw_line(ax, left_x, merge_y, cx - 4, merge_y)
|
||||
draw_line(ax, right_x, branch_y - 3, right_x, merge_y)
|
||||
draw_line(ax, right_x, merge_y, cx + 4, merge_y)
|
||||
|
||||
y = merge_y - step
|
||||
draw_rounded_rect(ax, cx, y, 28, 6, "Powiadom klienta")
|
||||
draw_arrow(ax, cx, merge_y - 4, cx, y + 3)
|
||||
|
||||
ey = y - step
|
||||
ax.add_patch(
|
||||
plt.Circle(
|
||||
(cx, ey), 2.5, lw=2, facecolor="white", edgecolor="black",
|
||||
)
|
||||
)
|
||||
ax.add_patch(
|
||||
plt.Circle((cx, ey), 1.5, facecolor="black", edgecolor="black"),
|
||||
)
|
||||
draw_arrow(ax, cx, y - 3, cx, ey + 2.5)
|
||||
|
||||
|
||||
def _draw_uml_legend(ax: Axes) -> None:
|
||||
"""Draw UML activity diagram legend."""
|
||||
ly = 5
|
||||
ax.add_patch(
|
||||
plt.Circle((12, ly), 1.2, facecolor="black", edgecolor="black"),
|
||||
)
|
||||
ax.text(15, ly, "= Pocz\u0105tek", fontsize=7, va="center")
|
||||
ax.add_patch(
|
||||
plt.Circle(
|
||||
(32, ly), 1.3, lw=2, facecolor="white", edgecolor="black",
|
||||
)
|
||||
)
|
||||
ax.add_patch(
|
||||
plt.Circle((32, ly), 0.8, facecolor="black", edgecolor="black"),
|
||||
)
|
||||
ax.text(35, ly, "= Koniec", fontsize=7, va="center")
|
||||
draw_diamond(ax, 50, ly, 1.5)
|
||||
ax.text(53, ly, "= Decyzja/Merge", fontsize=7, va="center")
|
||||
draw_rounded_rect(ax, 78, ly, 9, 3, "Akcja", fontsize=7)
|
||||
|
||||
|
||||
def generate_uml_activity() -> None:
|
||||
"""Generate uml activity."""
|
||||
fig, ax = plt.subplots(figsize=(8.27, 10))
|
||||
@ -274,75 +454,8 @@ def generate_uml_activity() -> None:
|
||||
pad=12,
|
||||
)
|
||||
|
||||
cx = 50
|
||||
y = 93
|
||||
step = 11
|
||||
|
||||
# Initial node
|
||||
ax.add_patch(plt.Circle((cx, y), 1.8, facecolor="black", edgecolor="black"))
|
||||
|
||||
# Action 1
|
||||
y -= step
|
||||
draw_rounded_rect(ax, cx, y, 28, 6, "Przyjmij zg\u0142oszenie reklamacji")
|
||||
draw_arrow(ax, cx, y + step - 1.8, cx, y + 3)
|
||||
|
||||
# Action 2
|
||||
y -= step
|
||||
draw_rounded_rect(ax, cx, y, 28, 6, "Zweryfikuj zasadno\u015b\u0107")
|
||||
draw_arrow(ax, cx, y + step - 3, cx, y + 3)
|
||||
|
||||
# Decision
|
||||
y -= step
|
||||
draw_diamond(ax, cx, y, 4)
|
||||
draw_arrow(ax, cx, y + step - 3, cx, y + 4)
|
||||
ax.text(cx + 6, y + 5, "[zasadna?]", fontsize=8, fontstyle="italic")
|
||||
|
||||
dec_y = y
|
||||
branch_y = dec_y - step
|
||||
|
||||
# Left [tak]
|
||||
left_x = cx - 24
|
||||
draw_rounded_rect(ax, left_x, branch_y, 22, 6, "Przygotuj\nwymian\u0119/zwrot")
|
||||
draw_line(ax, cx - 4, dec_y, left_x, dec_y)
|
||||
draw_arrow(ax, left_x, dec_y, left_x, branch_y + 3)
|
||||
ax.text(left_x + 2, dec_y + 1.5, "[tak]", fontsize=8, fontstyle="italic")
|
||||
|
||||
# Right [nie]
|
||||
right_x = cx + 24
|
||||
draw_rounded_rect(ax, right_x, branch_y, 22, 6, "Odrzu\u0107\nreklamacj\u0119")
|
||||
draw_line(ax, cx + 4, dec_y, right_x, dec_y)
|
||||
draw_arrow(ax, right_x, dec_y, right_x, branch_y + 3)
|
||||
ax.text(right_x - 12, dec_y + 1.5, "[nie]", fontsize=8, fontstyle="italic")
|
||||
|
||||
# Merge
|
||||
merge_y = branch_y - step
|
||||
draw_diamond(ax, cx, merge_y, 4)
|
||||
draw_line(ax, left_x, branch_y - 3, left_x, merge_y)
|
||||
draw_line(ax, left_x, merge_y, cx - 4, merge_y)
|
||||
draw_line(ax, right_x, branch_y - 3, right_x, merge_y)
|
||||
draw_line(ax, right_x, merge_y, cx + 4, merge_y)
|
||||
|
||||
# Action: Powiadom
|
||||
y = merge_y - step
|
||||
draw_rounded_rect(ax, cx, y, 28, 6, "Powiadom klienta")
|
||||
draw_arrow(ax, cx, merge_y - 4, cx, y + 3)
|
||||
|
||||
# Final node
|
||||
ey = y - step
|
||||
ax.add_patch(plt.Circle((cx, ey), 2.5, lw=2, facecolor="white", edgecolor="black"))
|
||||
ax.add_patch(plt.Circle((cx, ey), 1.5, facecolor="black", edgecolor="black"))
|
||||
draw_arrow(ax, cx, y - 3, cx, ey + 2.5)
|
||||
|
||||
# Legend
|
||||
ly = 5
|
||||
ax.add_patch(plt.Circle((12, ly), 1.2, facecolor="black", edgecolor="black"))
|
||||
ax.text(15, ly, "= Pocz\u0105tek", fontsize=7, va="center")
|
||||
ax.add_patch(plt.Circle((32, ly), 1.3, lw=2, facecolor="white", edgecolor="black"))
|
||||
ax.add_patch(plt.Circle((32, ly), 0.8, facecolor="black", edgecolor="black"))
|
||||
ax.text(35, ly, "= Koniec", fontsize=7, va="center")
|
||||
draw_diamond(ax, 50, ly, 1.5)
|
||||
ax.text(53, ly, "= Decyzja/Merge", fontsize=7, va="center")
|
||||
draw_rounded_rect(ax, 78, ly, 9, 3, "Akcja", fontsize=7)
|
||||
_draw_uml_elements(ax)
|
||||
_draw_uml_legend(ax)
|
||||
|
||||
fig.tight_layout()
|
||||
fig.savefig(
|
||||
@ -352,33 +465,18 @@ def generate_uml_activity() -> None:
|
||||
bbox_inches="tight",
|
||||
)
|
||||
plt.close(fig)
|
||||
print(" OK UML Activity saved")
|
||||
_logger.info(" OK UML Activity saved")
|
||||
|
||||
|
||||
# =========================================================================
|
||||
# 3. EPC (Event-driven Process Chain)
|
||||
# =========================================================================
|
||||
def generate_epc() -> None:
|
||||
"""Generate epc."""
|
||||
fig, ax = plt.subplots(figsize=(8.27, 11))
|
||||
ax.set_xlim(0, 100)
|
||||
ax.set_ylim(0, 120)
|
||||
ax.set_aspect("equal")
|
||||
ax.axis("off")
|
||||
fig.patch.set_facecolor(BG_COLOR)
|
||||
ax.set_title(
|
||||
"EPC (Event-driven Process Chain) \u2014 Obs\u0142uga reklamacji",
|
||||
fontsize=TITLE_SIZE,
|
||||
fontweight="bold",
|
||||
pad=12,
|
||||
)
|
||||
|
||||
cx = 50
|
||||
y = 114
|
||||
step = 9.5
|
||||
|
||||
def draw_epc_event(x, y, text) -> None:
|
||||
"""Draw epc event."""
|
||||
def _draw_epc_event(
|
||||
ax: Axes, x: float, y: float, text: str,
|
||||
) -> None:
|
||||
"""Draw an EPC event shape (rounded grey box)."""
|
||||
w, h = 26, 5.5
|
||||
rect = FancyBboxPatch(
|
||||
(x - w / 2, y - h / 2),
|
||||
@ -392,8 +490,11 @@ def generate_epc() -> None:
|
||||
ax.add_patch(rect)
|
||||
ax.text(x, y, text, ha="center", va="center", fontsize=8)
|
||||
|
||||
def draw_epc_function(x, y, text) -> None:
|
||||
"""Draw epc function."""
|
||||
|
||||
def _draw_epc_function(
|
||||
ax: Axes, x: float, y: float, text: str,
|
||||
) -> None:
|
||||
"""Draw an EPC function shape (rounded white box, bold)."""
|
||||
w, h = 26, 5.5
|
||||
rect = FancyBboxPatch(
|
||||
(x - w / 2, y - h / 2),
|
||||
@ -405,95 +506,140 @@ def generate_epc() -> None:
|
||||
facecolor="white",
|
||||
)
|
||||
ax.add_patch(rect)
|
||||
ax.text(x, y, text, ha="center", va="center", fontsize=8, fontweight="bold")
|
||||
ax.text(
|
||||
x, y, text,
|
||||
ha="center", va="center", fontsize=8, fontweight="bold",
|
||||
)
|
||||
|
||||
def draw_epc_connector(x, y, text) -> None:
|
||||
"""Draw epc connector."""
|
||||
c = plt.Circle((x, y), 2.8, lw=1.5, edgecolor=LINE_COLOR, facecolor="white")
|
||||
ax.add_patch(c)
|
||||
ax.text(x, y, text, ha="center", va="center", fontsize=9, fontweight="bold")
|
||||
|
||||
# E1
|
||||
draw_epc_event(cx, y, "Reklamacja wp\u0142yn\u0119\u0142a")
|
||||
def _draw_epc_connector(
|
||||
ax: Axes, x: float, y: float, text: str,
|
||||
) -> None:
|
||||
"""Draw an EPC logical connector (circle)."""
|
||||
circle = plt.Circle(
|
||||
(x, y), 2.8, lw=1.5, edgecolor=LINE_COLOR, facecolor="white",
|
||||
)
|
||||
ax.add_patch(circle)
|
||||
ax.text(
|
||||
x, y, text,
|
||||
ha="center", va="center", fontsize=9, fontweight="bold",
|
||||
)
|
||||
|
||||
|
||||
def _draw_epc_flow(
|
||||
ax: Axes,
|
||||
) -> tuple[float, float, float]:
|
||||
"""Draw sequential EPC flow from E1 through XOR split."""
|
||||
cx = 50
|
||||
y = 114
|
||||
step = 9.5
|
||||
|
||||
_draw_epc_event(ax, cx, y, "Reklamacja wp\u0142yn\u0119\u0142a")
|
||||
|
||||
# F1
|
||||
y -= step
|
||||
draw_epc_function(cx, y, "Przyjmij zg\u0142oszenie")
|
||||
_draw_epc_function(ax, cx, y, "Przyjmij zg\u0142oszenie")
|
||||
draw_arrow(ax, cx, y + step - 2.8, cx, y + 2.8)
|
||||
|
||||
# E2
|
||||
y -= step
|
||||
draw_epc_event(cx, y, "Zg\u0142oszenie przyj\u0119te")
|
||||
_draw_epc_event(ax, cx, y, "Zg\u0142oszenie przyj\u0119te")
|
||||
draw_arrow(ax, cx, y + step - 2.8, cx, y + 2.8)
|
||||
|
||||
# F2
|
||||
y -= step
|
||||
draw_epc_function(cx, y, "Zweryfikuj zasadno\u015b\u0107")
|
||||
_draw_epc_function(ax, cx, y, "Zweryfikuj zasadno\u015b\u0107")
|
||||
draw_arrow(ax, cx, y + step - 2.8, cx, y + 2.8)
|
||||
|
||||
# E3
|
||||
y -= step
|
||||
draw_epc_event(cx, y, "Zasadno\u015b\u0107 oceniona")
|
||||
_draw_epc_event(ax, cx, y, "Zasadno\u015b\u0107 oceniona")
|
||||
draw_arrow(ax, cx, y + step - 2.8, cx, y + 2.8)
|
||||
|
||||
# XOR split
|
||||
y -= step
|
||||
draw_epc_connector(cx, y, "XOR")
|
||||
_draw_epc_connector(ax, cx, y, "XOR")
|
||||
draw_arrow(ax, cx, y + step - 2.8, cx, y + 2.8)
|
||||
split_y = y
|
||||
|
||||
return cx, y, step
|
||||
|
||||
|
||||
def _draw_epc_branches(
|
||||
ax: Axes,
|
||||
cx: float,
|
||||
split_y: float,
|
||||
step: float,
|
||||
) -> None:
|
||||
"""Draw EPC branches, merge, and post-merge elements."""
|
||||
left_x = cx - 28
|
||||
right_x = cx + 28
|
||||
|
||||
# Left branch
|
||||
by = split_y - step
|
||||
draw_epc_event(left_x, by, "Reklamacja zasadna")
|
||||
_draw_epc_event(ax, left_x, by, "Reklamacja zasadna")
|
||||
draw_line(ax, cx - 2.8, split_y, left_x, split_y)
|
||||
draw_arrow(ax, left_x, split_y, left_x, by + 2.8)
|
||||
|
||||
by2 = by - step
|
||||
draw_epc_function(left_x, by2, "Przygotuj wymian\u0119/zwrot")
|
||||
_draw_epc_function(
|
||||
ax, left_x, by2, "Przygotuj wymian\u0119/zwrot",
|
||||
)
|
||||
draw_arrow(ax, left_x, by - 2.8, left_x, by2 + 2.8)
|
||||
|
||||
by3 = by2 - step
|
||||
draw_epc_event(left_x, by3, "Wymiana przygotowana")
|
||||
_draw_epc_event(ax, left_x, by3, "Wymiana przygotowana")
|
||||
draw_arrow(ax, left_x, by2 - 2.8, left_x, by3 + 2.8)
|
||||
|
||||
# Right branch
|
||||
draw_epc_event(right_x, by, "Reklamacja niezasadna")
|
||||
_draw_epc_event(ax, right_x, by, "Reklamacja niezasadna")
|
||||
draw_line(ax, cx + 2.8, split_y, right_x, split_y)
|
||||
draw_arrow(ax, right_x, split_y, right_x, by + 2.8)
|
||||
|
||||
draw_epc_function(right_x, by2, "Odrzu\u0107 reklamacj\u0119")
|
||||
_draw_epc_function(ax, right_x, by2, "Odrzu\u0107 reklamacj\u0119")
|
||||
draw_arrow(ax, right_x, by - 2.8, right_x, by2 + 2.8)
|
||||
|
||||
draw_epc_event(right_x, by3, "Reklamacja odrzucona")
|
||||
_draw_epc_event(ax, right_x, by3, "Reklamacja odrzucona")
|
||||
draw_arrow(ax, right_x, by2 - 2.8, right_x, by3 + 2.8)
|
||||
|
||||
# XOR merge
|
||||
merge_y = by3 - step
|
||||
draw_epc_connector(cx, merge_y, "XOR")
|
||||
_draw_epc_connector(ax, cx, merge_y, "XOR")
|
||||
draw_line(ax, left_x, by3 - 2.8, left_x, merge_y)
|
||||
draw_line(ax, left_x, merge_y, cx - 2.8, merge_y)
|
||||
draw_line(ax, right_x, by3 - 2.8, right_x, merge_y)
|
||||
draw_line(ax, right_x, merge_y, cx + 2.8, merge_y)
|
||||
|
||||
# F: Powiadom
|
||||
y = merge_y - step
|
||||
draw_epc_function(cx, y, "Powiadom klienta")
|
||||
_draw_epc_function(ax, cx, y, "Powiadom klienta")
|
||||
draw_arrow(ax, cx, merge_y - 2.8, cx, y + 2.8)
|
||||
|
||||
# E: Klient powiadomiony
|
||||
y -= step
|
||||
draw_epc_event(cx, y, "Klient powiadomiony")
|
||||
_draw_epc_event(ax, cx, y, "Klient powiadomiony")
|
||||
draw_arrow(ax, cx, y + step - 2.8, cx, y + 2.8)
|
||||
|
||||
# Legend
|
||||
|
||||
def _draw_epc_legend(ax: Axes) -> None:
|
||||
"""Draw EPC legend."""
|
||||
ly = 3
|
||||
draw_epc_event(16, ly, "Zdarzenie")
|
||||
draw_epc_function(46, ly, "Funkcja")
|
||||
draw_epc_connector(68, ly, "XOR")
|
||||
ax.text(72, ly, "= \u0141\u0105cznik logiczny", fontsize=7, va="center")
|
||||
_draw_epc_event(ax, 16, ly, "Zdarzenie")
|
||||
_draw_epc_function(ax, 46, ly, "Funkcja")
|
||||
_draw_epc_connector(ax, 68, ly, "XOR")
|
||||
ax.text(
|
||||
72, ly, "= \u0141\u0105cznik logiczny", fontsize=7, va="center",
|
||||
)
|
||||
|
||||
|
||||
def generate_epc() -> None:
|
||||
"""Generate epc."""
|
||||
fig, ax = plt.subplots(figsize=(8.27, 11))
|
||||
ax.set_xlim(0, 100)
|
||||
ax.set_ylim(0, 120)
|
||||
ax.set_aspect("equal")
|
||||
ax.axis("off")
|
||||
fig.patch.set_facecolor(BG_COLOR)
|
||||
ax.set_title(
|
||||
"EPC (Event-driven Process Chain)"
|
||||
" \u2014 Obs\u0142uga reklamacji",
|
||||
fontsize=TITLE_SIZE,
|
||||
fontweight="bold",
|
||||
pad=12,
|
||||
)
|
||||
|
||||
cx, split_y, step = _draw_epc_flow(ax)
|
||||
_draw_epc_branches(ax, cx, split_y, step)
|
||||
_draw_epc_legend(ax)
|
||||
|
||||
fig.tight_layout()
|
||||
fig.savefig(
|
||||
@ -503,33 +649,18 @@ def generate_epc() -> None:
|
||||
bbox_inches="tight",
|
||||
)
|
||||
plt.close(fig)
|
||||
print(" OK EPC saved")
|
||||
_logger.info(" OK EPC saved")
|
||||
|
||||
|
||||
# =========================================================================
|
||||
# 4. Classic Flowchart
|
||||
# =========================================================================
|
||||
def generate_flowchart() -> None:
|
||||
"""Generate flowchart."""
|
||||
fig, ax = plt.subplots(figsize=(8.27, 11))
|
||||
ax.set_xlim(0, 100)
|
||||
ax.set_ylim(0, 110)
|
||||
ax.set_aspect("equal")
|
||||
ax.axis("off")
|
||||
fig.patch.set_facecolor(BG_COLOR)
|
||||
ax.set_title(
|
||||
"Schemat blokowy (Flowchart) \u2014 Obs\u0142uga reklamacji",
|
||||
fontsize=TITLE_SIZE,
|
||||
fontweight="bold",
|
||||
pad=12,
|
||||
)
|
||||
|
||||
cx = 50
|
||||
y = 103
|
||||
step = 11
|
||||
|
||||
def draw_terminal(x, y, text) -> None:
|
||||
"""Draw terminal."""
|
||||
def _draw_fc_terminal(
|
||||
ax: Axes, x: float, y: float, text: str,
|
||||
) -> None:
|
||||
"""Draw a flowchart terminal (rounded) shape."""
|
||||
w, h = 20, 5.5
|
||||
rect = FancyBboxPatch(
|
||||
(x - w / 2, y - h / 2),
|
||||
@ -542,11 +673,20 @@ def generate_flowchart() -> None:
|
||||
)
|
||||
ax.add_patch(rect)
|
||||
ax.text(
|
||||
x, y, text, ha="center", va="center", fontsize=FONT_SIZE, fontweight="bold"
|
||||
x,
|
||||
y,
|
||||
text,
|
||||
ha="center",
|
||||
va="center",
|
||||
fontsize=FONT_SIZE,
|
||||
fontweight="bold",
|
||||
)
|
||||
|
||||
def draw_process_box(x, y, text) -> None:
|
||||
"""Draw process box."""
|
||||
|
||||
def _draw_fc_process_box(
|
||||
ax: Axes, x: float, y: float, text: str,
|
||||
) -> None:
|
||||
"""Draw a flowchart process box (rectangle)."""
|
||||
w, h = 26, 6
|
||||
rect = plt.Rectangle(
|
||||
(x - w / 2, y - h / 2),
|
||||
@ -557,10 +697,15 @@ def generate_flowchart() -> None:
|
||||
facecolor="white",
|
||||
)
|
||||
ax.add_patch(rect)
|
||||
ax.text(x, y, text, ha="center", va="center", fontsize=FONT_SIZE)
|
||||
ax.text(
|
||||
x, y, text, ha="center", va="center", fontsize=FONT_SIZE,
|
||||
)
|
||||
|
||||
def draw_io_shape(x, y, text) -> None:
|
||||
"""Draw io shape."""
|
||||
|
||||
def _draw_fc_io_shape(
|
||||
ax: Axes, x: float, y: float, text: str,
|
||||
) -> None:
|
||||
"""Draw a flowchart I/O parallelogram."""
|
||||
w, h = 26, 5.5
|
||||
skew = 3
|
||||
verts = [
|
||||
@ -578,74 +723,96 @@ def generate_flowchart() -> None:
|
||||
MplPath.CLOSEPOLY,
|
||||
]
|
||||
patch = mpatches.PathPatch(
|
||||
MplPath(verts, codes), facecolor="white", edgecolor=LINE_COLOR, lw=1.5
|
||||
MplPath(verts, codes),
|
||||
facecolor="white",
|
||||
edgecolor=LINE_COLOR,
|
||||
lw=1.5,
|
||||
)
|
||||
ax.add_patch(patch)
|
||||
ax.text(x, y, text, ha="center", va="center", fontsize=FONT_SIZE)
|
||||
ax.text(
|
||||
x, y, text, ha="center", va="center", fontsize=FONT_SIZE,
|
||||
)
|
||||
|
||||
# Start
|
||||
draw_terminal(cx, y, "START")
|
||||
|
||||
# I/O
|
||||
def _draw_fc_elements(ax: Axes) -> None:
|
||||
"""Draw all flowchart elements."""
|
||||
cx = 50
|
||||
y = 103
|
||||
step = 11
|
||||
|
||||
_draw_fc_terminal(ax, cx, y, "START")
|
||||
|
||||
y -= step
|
||||
draw_io_shape(cx, y, "Reklamacja od klienta")
|
||||
_draw_fc_io_shape(ax, cx, y, "Reklamacja od klienta")
|
||||
draw_arrow(ax, cx, y + step - 2.8, cx, y + 2.8)
|
||||
|
||||
# Process
|
||||
y -= step
|
||||
draw_process_box(cx, y, "Przyjmij zg\u0142oszenie")
|
||||
_draw_fc_process_box(ax, cx, y, "Przyjmij zg\u0142oszenie")
|
||||
draw_arrow(ax, cx, y + step - 2.8, cx, y + 3)
|
||||
|
||||
# Process
|
||||
y -= step
|
||||
draw_process_box(cx, y, "Zweryfikuj zasadno\u015b\u0107")
|
||||
_draw_fc_process_box(
|
||||
ax, cx, y, "Zweryfikuj zasadno\u015b\u0107",
|
||||
)
|
||||
draw_arrow(ax, cx, y + step - 3, cx, y + 3)
|
||||
|
||||
# Decision
|
||||
y -= step
|
||||
draw_diamond(ax, cx, y, 4.5, "Zasadna?")
|
||||
draw_arrow(ax, cx, y + step - 3, cx, y + 4.5)
|
||||
dec_y = y
|
||||
|
||||
# Left: Tak
|
||||
left_x = cx - 26
|
||||
draw_process_box(left_x, dec_y, "Przygotuj wymian\u0119/zwrot")
|
||||
_draw_fc_process_box(
|
||||
ax, left_x, dec_y, "Przygotuj wymian\u0119/zwrot",
|
||||
)
|
||||
draw_line(ax, cx - 4.5, dec_y, left_x + 13, dec_y)
|
||||
ax.text(cx - 7, dec_y + 2, "Tak", fontsize=8, ha="center", fontweight="bold")
|
||||
ax.text(
|
||||
cx - 7, dec_y + 2, "Tak",
|
||||
fontsize=8, ha="center", fontweight="bold",
|
||||
)
|
||||
|
||||
# Right: Nie
|
||||
right_x = cx + 26
|
||||
draw_process_box(right_x, dec_y, "Odrzu\u0107 reklamacj\u0119")
|
||||
_draw_fc_process_box(
|
||||
ax, right_x, dec_y, "Odrzu\u0107 reklamacj\u0119",
|
||||
)
|
||||
draw_line(ax, cx + 4.5, dec_y, right_x - 13, dec_y)
|
||||
ax.text(cx + 7, dec_y + 2, "Nie", fontsize=8, ha="center", fontweight="bold")
|
||||
ax.text(
|
||||
cx + 7, dec_y + 2, "Nie",
|
||||
fontsize=8, ha="center", fontweight="bold",
|
||||
)
|
||||
|
||||
# Merge
|
||||
merge_y = dec_y - step
|
||||
draw_line(ax, left_x, dec_y - 3, left_x, merge_y)
|
||||
draw_line(ax, right_x, dec_y - 3, right_x, merge_y)
|
||||
draw_line(ax, left_x, merge_y, right_x, merge_y)
|
||||
ax.plot(cx, merge_y, "ko", markersize=4)
|
||||
|
||||
# Process: Powiadom
|
||||
y = merge_y - step + 3
|
||||
draw_process_box(cx, y, "Powiadom klienta")
|
||||
_draw_fc_process_box(ax, cx, y, "Powiadom klienta")
|
||||
draw_arrow(ax, cx, merge_y, cx, y + 3)
|
||||
|
||||
# I/O
|
||||
y -= step
|
||||
draw_io_shape(cx, y, "Odpowied\u017a do klienta")
|
||||
_draw_fc_io_shape(
|
||||
ax, cx, y, "Odpowied\u017a do klienta",
|
||||
)
|
||||
draw_arrow(ax, cx, y + step - 3, cx, y + 2.8)
|
||||
|
||||
# End
|
||||
y -= step
|
||||
draw_terminal(cx, y, "KONIEC")
|
||||
_draw_fc_terminal(ax, cx, y, "KONIEC")
|
||||
draw_arrow(ax, cx, y + step - 2.8, cx, y + 2.8)
|
||||
|
||||
# Legend
|
||||
|
||||
def _draw_fc_legend(ax: Axes) -> None:
|
||||
"""Draw flowchart legend."""
|
||||
ly = 4
|
||||
ax.text(5, ly, "Legenda:", fontsize=7, fontweight="bold", va="center")
|
||||
draw_terminal(18, ly, "")
|
||||
ax.text(18, ly, "Start/\nKoniec", fontsize=5.5, ha="center", va="center")
|
||||
ax.text(
|
||||
5, ly, "Legenda:", fontsize=7, fontweight="bold", va="center",
|
||||
)
|
||||
_draw_fc_terminal(ax, 18, ly, "")
|
||||
ax.text(
|
||||
18, ly, "Start/\nKoniec",
|
||||
fontsize=5.5, ha="center", va="center",
|
||||
)
|
||||
w, h = 9, 3
|
||||
ax.add_patch(
|
||||
plt.Rectangle(
|
||||
@ -678,11 +845,34 @@ def generate_flowchart() -> None:
|
||||
]
|
||||
ax.add_patch(
|
||||
mpatches.PathPatch(
|
||||
MplPath(verts, codes), facecolor="white", edgecolor=LINE_COLOR, lw=1.2
|
||||
MplPath(verts, codes),
|
||||
facecolor="white",
|
||||
edgecolor=LINE_COLOR,
|
||||
lw=1.2,
|
||||
)
|
||||
)
|
||||
ax.text(62, ly, "We/Wy", fontsize=6, ha="center", va="center")
|
||||
|
||||
|
||||
def generate_flowchart() -> None:
|
||||
"""Generate flowchart."""
|
||||
fig, ax = plt.subplots(figsize=(8.27, 11))
|
||||
ax.set_xlim(0, 100)
|
||||
ax.set_ylim(0, 110)
|
||||
ax.set_aspect("equal")
|
||||
ax.axis("off")
|
||||
fig.patch.set_facecolor(BG_COLOR)
|
||||
ax.set_title(
|
||||
"Schemat blokowy (Flowchart)"
|
||||
" \u2014 Obs\u0142uga reklamacji",
|
||||
fontsize=TITLE_SIZE,
|
||||
fontweight="bold",
|
||||
pad=12,
|
||||
)
|
||||
|
||||
_draw_fc_elements(ax)
|
||||
_draw_fc_legend(ax)
|
||||
|
||||
fig.tight_layout()
|
||||
fig.savefig(
|
||||
str(Path(OUTPUT_DIR) / "flowchart_reklamacja.png"),
|
||||
@ -691,18 +881,24 @@ def generate_flowchart() -> None:
|
||||
bbox_inches="tight",
|
||||
)
|
||||
plt.close(fig)
|
||||
print(" OK Flowchart saved")
|
||||
_logger.info(" OK Flowchart saved")
|
||||
|
||||
|
||||
# =========================================================================
|
||||
if __name__ == "__main__":
|
||||
print(f"Generating diagrams to {OUTPUT_DIR}/...")
|
||||
logging.basicConfig(level=logging.INFO, format="%(message)s")
|
||||
_logger.info("Generating diagrams to %s/...", OUTPUT_DIR)
|
||||
generate_bpmn()
|
||||
generate_uml_activity()
|
||||
generate_epc()
|
||||
generate_flowchart()
|
||||
print(f"\nAll 4 diagrams saved to {OUTPUT_DIR}/")
|
||||
for f in sorted([p.name for p in Path(OUTPUT_DIR).iterdir()]):
|
||||
if f.endswith(".png"):
|
||||
size_kb = Path(str(Path(OUTPUT_DIR).stat().st_size / f)) / 1024
|
||||
print(f" {f} ({size_kb:.0f} KB)")
|
||||
_logger.info("\nAll 4 diagrams saved to %s/", OUTPUT_DIR)
|
||||
for fname in sorted(p.name for p in Path(OUTPUT_DIR).iterdir()):
|
||||
if fname.endswith(".png"):
|
||||
size_kb = (
|
||||
Path(
|
||||
str(Path(OUTPUT_DIR).stat().st_size / fname),
|
||||
)
|
||||
/ 1024
|
||||
)
|
||||
_logger.info(" %s (%.0f KB)", fname, size_kb)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user