praca_magisterska/pytania/generate_pattern_diagrams.py

298 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
Generate pattern cataloguing diagrams for PYTANIE 14 (AIS):
1. Pattern Template Structure — the standard fields every pattern has
2. Catalog Classification Map — catalogs arranged by scope & domain
3. Pattern Language Network — how patterns reference each other
All: A4-compatible, B&W, 300 DPI, laser-printer-friendly.
"""
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.patches import FancyBboxPatch, FancyArrowPatch
import numpy as np
import os
DPI = 300
BG = 'white'
LN = 'black'
FS = 9
FS_TITLE = 13
FS_SMALL = 7.5
OUTPUT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'img')
os.makedirs(OUTPUT_DIR, exist_ok=True)
GRAY1 = '#E8E8E8'
GRAY2 = '#D0D0D0'
GRAY3 = '#B8B8B8'
GRAY4 = '#F5F5F5'
GRAY5 = '#C0C0C0'
def draw_box(ax, x, y, w, h, text, fill='white', lw=1.2, fontsize=FS,
fontweight='normal', ha='center', va='center', rounded=True):
if rounded:
rect = FancyBboxPatch((x, y), w, h, boxstyle="round,pad=0.08",
lw=lw, edgecolor=LN, facecolor=fill)
else:
rect = mpatches.Rectangle((x, y), w, h, lw=lw, edgecolor=LN, facecolor=fill)
ax.add_patch(rect)
ax.text(x + w / 2, y + h / 2, text, ha=ha, va=va, fontsize=fontsize,
fontweight=fontweight, wrap=True)
def draw_arrow(ax, x1, y1, x2, y2, lw=1.2, style='->', color=LN):
ax.annotate("", xy=(x2, y2), xytext=(x1, y1),
arrowprops=dict(arrowstyle=style, color=color, lw=lw))
# ============================================================
# 1. Pattern Template Structure (NaPSiRoKo mnemonic)
# ============================================================
def generate_pattern_template():
fig, ax = plt.subplots(figsize=(8.27, 6))
ax.set_xlim(0, 10)
ax.set_ylim(0, 8)
ax.set_aspect('equal')
ax.axis('off')
fig.patch.set_facecolor(BG)
ax.set_title('Szablon opisu wzorca \u2014 \u201eNaPSiRoKo\u201d',
fontsize=FS_TITLE, fontweight='bold', pad=15)
# Main card outline
card_x, card_y, card_w, card_h = 1.5, 0.5, 7, 7
card = FancyBboxPatch((card_x, card_y), card_w, card_h,
boxstyle="round,pad=0.15", lw=2.5,
edgecolor=LN, facecolor=GRAY4)
ax.add_patch(card)
# Title of card
ax.text(card_x + card_w / 2, card_y + card_h - 0.35,
"KARTA WZORCA", ha='center', va='center',
fontsize=FS_TITLE, fontweight='bold')
# Fields as horizontal bands
fields = [
("Na", "NAZWA", "Layered, Observer, Microservices", GRAY1),
("P", "PROBLEM / KONTEKST", "Kiedy stosować? Jaki problem rozwiązuje?", 'white'),
("Si", "SIŁY (forces)", "Konkurencyjne wymagania do pogodzenia\n(np. testowalność vs wydajność)", GRAY1),
("Ro", "ROZWIĄZANIE", "Struktura, diagram, zachowanie", 'white'),
("Ko", "KONSEKWENCJE", "Tradeoffs: co zyskujemy, co tracimy", GRAY1),
]
band_x = card_x + 0.3
band_w = card_w - 0.6
band_h = 1.05
start_y = card_y + card_h - 1.1
for i, (abbr, title, desc, fill) in enumerate(fields):
by = start_y - i * (band_h + 0.15)
# Abbreviation circle on the left
circle = plt.Circle((band_x + 0.35, by + band_h / 2), 0.28,
lw=1.5, edgecolor=LN, facecolor=GRAY2)
ax.add_patch(circle)
ax.text(band_x + 0.35, by + band_h / 2, abbr, ha='center', va='center',
fontsize=10, fontweight='bold')
# Field box
fx = band_x + 0.8
fw = band_w - 0.8
rect = FancyBboxPatch((fx, by), fw, band_h,
boxstyle="round,pad=0.06", lw=1,
edgecolor=LN, facecolor=fill)
ax.add_patch(rect)
ax.text(fx + 0.15, by + band_h - 0.25, title, ha='left', va='center',
fontsize=FS, fontweight='bold')
ax.text(fx + 0.15, by + 0.25, desc, ha='left', va='center',
fontsize=FS_SMALL, fontstyle='italic', color='#444444')
# Arrow connecting fields
if i < len(fields) - 1:
draw_arrow(ax, band_x + 0.35, by - 0.02,
band_x + 0.35, by - 0.13, lw=1.0)
# Extra fields note at bottom
ax.text(card_x + card_w / 2, card_y + 0.25,
"+ Powiązane wzorce • Znane zastosowania • Warianty",
ha='center', va='center', fontsize=FS_SMALL, fontstyle='italic')
# Mnemonic reminder on the right
ax.text(9.8, 4, "Mnemonik:\nNaPSiRoKo",
ha='center', va='center', fontsize=10, fontweight='bold',
rotation=90, color='#666666')
fig.tight_layout()
out = os.path.join(OUTPUT_DIR, 'q14_pattern_template.png')
fig.savefig(out, dpi=DPI, bbox_inches='tight', facecolor=BG)
plt.close(fig)
print(f" Saved: {out}")
# ============================================================
# 2. Catalog Classification Map
# ============================================================
def generate_catalog_map():
fig, ax = plt.subplots(figsize=(8.27, 7))
ax.set_xlim(0, 12)
ax.set_ylim(0, 9)
ax.set_aspect('equal')
ax.axis('off')
fig.patch.set_facecolor(BG)
ax.set_title('Mapa katalog\u00f3w wzorc\u00f3w \u2014 \u201ePawe\u0142 Gra\u0142 Efektownie Pod Chmurami\u201d',
fontsize=FS_TITLE, fontweight='bold', pad=15)
# Y-axis: Scale (architectural → design → idiom)
ax.text(0.3, 7.8, "SKALA", fontsize=10, fontweight='bold',
ha='center', va='center', rotation=90)
ax.annotate("", xy=(0.3, 2.0), xytext=(0.3, 7.5),
arrowprops=dict(arrowstyle='->', lw=1.5, color=LN))
scale_labels = [
(7.0, "Architektoniczny\n(cały system)"),
(5.0, "Projektowy\n(klasa/obiekt)"),
(3.0, "Idiomatyczny\n(linia kodu)"),
]
for sy, label in scale_labels:
ax.text(1.0, sy, label, fontsize=FS_SMALL, ha='left', va='center',
fontstyle='italic')
ax.plot([0.15, 0.45], [sy, sy], color=GRAY3, lw=0.8, ls='--')
# X-axis: Domain
ax.text(6.5, 1.2, "DOMENA ZASTOSOWANIA", fontsize=10, fontweight='bold',
ha='center', va='center')
ax.annotate("", xy=(11.5, 1.5), xytext=(2.0, 1.5),
arrowprops=dict(arrowstyle='->', lw=1.5, color=LN))
# Catalog boxes positioned by scale × domain
catalogs = [
# (x, y, w, h, name, subtitle, fill, mnemonic_letter)
(2.5, 6.2, 2.5, 1.4, "POSA", "1996 • Buschmann\nLayers, Broker,\nPipes & Filters, MVC", GRAY1, "P"),
(2.5, 4.2, 2.5, 1.4, "GoF", "1994 • Gamma et al.\n23 wzorce:\n5 kreac. / 7 strukt. / 11 behaw.", GRAY2, "G"),
(5.5, 6.2, 2.5, 1.4, "EIP", "2003 • Hohpe & Woolf\nMessage Channel,\nRouter, Aggregator", GRAY1, "E"),
(5.5, 4.2, 2.5, 1.4, "PoEAA", "2002 • M. Fowler\nRepository, Unit of Work,\nDomain Model", 'white', "P"),
(8.5, 6.2, 2.8, 1.4, "Cloud\nPatterns", "~2015 • Azure/AWS\nCircuit Breaker,\nSaga, Sidecar", GRAY1, "C"),
]
for cx, cy, cw, ch, name, sub, fill, ml in catalogs:
rect = FancyBboxPatch((cx, cy), cw, ch, boxstyle="round,pad=0.1",
lw=1.5, edgecolor=LN, facecolor=fill)
ax.add_patch(rect)
ax.text(cx + cw / 2, cy + ch - 0.3, name, ha='center', va='center',
fontsize=10, fontweight='bold')
ax.text(cx + cw / 2, cy + 0.4, sub, ha='center', va='center',
fontsize=FS_SMALL, linespacing=1.3)
# Mnemonic letter in corner
circle = plt.Circle((cx + 0.25, cy + ch - 0.25), 0.2,
lw=1, edgecolor=LN, facecolor=GRAY5)
ax.add_patch(circle)
ax.text(cx + 0.25, cy + ch - 0.25, ml, ha='center', va='center',
fontsize=8, fontweight='bold')
# Mnemonic bar at bottom
mnem_y = 2.2
ax.text(6.0, mnem_y, "PGEP+C → Paweł Grał Efektownie Pod Chmurami",
ha='center', va='center', fontsize=10, fontweight='bold',
bbox=dict(boxstyle='round,pad=0.3', facecolor=GRAY4, edgecolor=LN, lw=1.5))
# Domain labels along x-axis
domains = [
(3.75, 1.7, "Architektura"),
(6.75, 1.7, "Integracja / Enterprise"),
(9.9, 1.7, "Chmura"),
]
for dx, dy, dlabel in domains:
ax.text(dx, dy, dlabel, ha='center', va='center',
fontsize=FS_SMALL, fontstyle='italic')
fig.tight_layout()
out = os.path.join(OUTPUT_DIR, 'q14_catalog_map.png')
fig.savefig(out, dpi=DPI, bbox_inches='tight', facecolor=BG)
plt.close(fig)
print(f" Saved: {out}")
# ============================================================
# 3. Three Pillars of Cataloguing
# ============================================================
def generate_three_pillars():
fig, ax = plt.subplots(figsize=(8.27, 5.5))
ax.set_xlim(0, 12)
ax.set_ylim(0, 7)
ax.set_aspect('equal')
ax.axis('off')
fig.patch.set_facecolor(BG)
ax.set_title("Jak są katalogowane wzorce? — Trzy filary",
fontsize=FS_TITLE, fontweight='bold', pad=15)
# Roof / banner
roof_pts = np.array([[1, 5.5], [6, 6.8], [11, 5.5]])
roof = plt.Polygon(roof_pts, closed=True, lw=2,
edgecolor=LN, facecolor=GRAY4)
ax.add_patch(roof)
ax.text(6, 6.0, "KATALOGOWANIE WZORCÓW",
ha='center', va='center', fontsize=11, fontweight='bold')
# Three pillars
pillars = [
(1.3, "1. SZABLON\nOPISU",
"Każdy wzorzec ma\nte same pola:\nNazwa → Problem\n→ Siły → Rozwiązanie\n→ Konsekwencje",
"Analogia:\nformatka\nencyklopedii"),
(4.8, "2. KLASYFIKACJA\nWIELOOSIOWA",
"Osie podziału:\n• Skala (arch/proj/idiom)\n• Domena problemu\n• Atrybut jakościowy\n• Domena zastosowania",
"Analogia:\nkategorie\nw bibliotece"),
(8.3, "3. JĘZYK\nWZORCÓW",
"Wzorce referują się\nwzajemnie tworząc\nsieć/graf:\nA → wymaga → B\nB → wariant → C",
"Analogia:\n\u201ezobacz te\u017c\u201d\nw encyklopedii"),
]
for px, title, desc, analogy in pillars:
pw, ph = 2.8, 5.0
py = 0.5
# Pillar rectangle
rect = FancyBboxPatch((px, py), pw, ph, boxstyle="round,pad=0.1",
lw=1.8, edgecolor=LN, facecolor='white')
ax.add_patch(rect)
# Title
ax.text(px + pw / 2, py + ph - 0.55, title, ha='center', va='center',
fontsize=9, fontweight='bold')
# Horizontal line under title
ax.plot([px + 0.2, px + pw - 0.2], [py + ph - 1.0, py + ph - 1.0],
color=LN, lw=0.8)
# Description
ax.text(px + pw / 2, py + ph / 2 - 0.3, desc, ha='center', va='center',
fontsize=FS_SMALL, linespacing=1.4)
# Analogy box at bottom
analogy_rect = FancyBboxPatch((px + 0.2, py + 0.15), pw - 0.4, 1.0,
boxstyle="round,pad=0.06", lw=0.8,
edgecolor=GRAY3, facecolor=GRAY1)
ax.add_patch(analogy_rect)
ax.text(px + pw / 2, py + 0.65, analogy, ha='center', va='center',
fontsize=FS_SMALL, fontstyle='italic', color='#555555')
fig.tight_layout()
out = os.path.join(OUTPUT_DIR, 'q14_three_pillars.png')
fig.savefig(out, dpi=DPI, bbox_inches='tight', facecolor=BG)
plt.close(fig)
print(f" Saved: {out}")
# ============================================================
# Main
# ============================================================
if __name__ == '__main__':
print("Generating PYTANIE 14 diagrams...")
generate_pattern_template()
generate_catalog_map()
generate_three_pillars()
print("Done!")