Skip to content

Commit e3f0cf9

Browse files
master report
1 parent 4f807f9 commit e3f0cf9

3 files changed

Lines changed: 234 additions & 0 deletions

File tree

reports/master_report.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
"""Composite master report - combines all generated report PNGs into one image."""
2+
3+
from pathlib import Path
4+
from typing import List
5+
6+
import matplotlib.image as mpimg
7+
import matplotlib.pyplot as plt
8+
9+
10+
def _sort_paths(paths: List[str]) -> List[Path]:
11+
"""Sort paths: core first, then team (overview then by team), risk, fairness, advanced."""
12+
def key(p: Path) -> tuple:
13+
parts = p.parts
14+
if "core" in parts:
15+
return (0, str(p))
16+
if "team" in parts:
17+
if "overview" in parts:
18+
return (1, str(p))
19+
return (2, str(p))
20+
if "risk" in parts:
21+
return (3, str(p))
22+
if "fairness" in parts:
23+
return (4, str(p))
24+
if "advanced" in parts:
25+
return (5, str(p))
26+
return (6, str(p))
27+
28+
return sorted((Path(p) for p in paths if p.endswith(".png")), key=key)
29+
30+
31+
def build_master_report(generated_paths: List[str], output_dir: Path) -> str:
32+
"""
33+
Composite all generated report PNGs into a single master image.
34+
35+
Reuses existing report outputs - no code duplication. Loads each PNG
36+
and arranges in a grid.
37+
38+
Args:
39+
generated_paths: List of paths to generated PNG files
40+
output_dir: Directory to save master PNG (e.g. reports/)
41+
42+
Returns:
43+
Path to master PNG file
44+
"""
45+
paths = [Path(p) for p in generated_paths if Path(p).exists()]
46+
if not paths:
47+
return ""
48+
49+
paths = _sort_paths([str(p) for p in paths])
50+
n = len(paths)
51+
52+
# Grid: 5 columns, rows as needed
53+
ncols = 5
54+
nrows = (n + ncols - 1) // ncols
55+
56+
# Each cell ~4x2.5 inches, total figure size
57+
fig_w = ncols * 4
58+
fig_h = nrows * 2.5
59+
fig, axes = plt.subplots(nrows, ncols, figsize=(fig_w, fig_h))
60+
flat_axes = (
61+
axes.flatten()
62+
if hasattr(axes, "flatten")
63+
else ([axes] if not hasattr(axes, "__len__") else list(axes))
64+
)
65+
66+
for idx, ax in enumerate(flat_axes):
67+
ax.set_axis_off()
68+
if idx < n:
69+
try:
70+
img = mpimg.imread(paths[idx])
71+
ax.imshow(img, aspect="auto")
72+
except Exception:
73+
pass
74+
75+
fig.suptitle("Engineering Intelligence Reports — Master Summary", fontsize=14, y=1.002)
76+
fig.tight_layout()
77+
out = Path(output_dir) / "master-all-reports.png"
78+
fig.savefig(out, dpi=150, bbox_inches="tight")
79+
plt.close(fig)
80+
return str(out)

reports/runner.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,4 +137,12 @@ def run_one(item: tuple) -> Optional[Union[str, List[str]]]:
137137
else:
138138
generated.append(result)
139139

140+
# Build master composite from all generated PNGs (reuses outputs, no code duplication)
141+
if generated:
142+
from reports.master_report import build_master_report
143+
144+
master_path = build_master_report(generated, output_dir)
145+
if master_path:
146+
generated.append(master_path)
147+
140148
return generated
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Generate a diagram explaining the core principles of the complexity analyzer engine.
4+
"""
5+
6+
import matplotlib.pyplot as plt
7+
import matplotlib.patches as mpatches
8+
from matplotlib.patches import FancyBboxPatch, FancyArrowPatch, Rectangle
9+
import matplotlib.lines as mlines
10+
11+
# Colors - modern palette
12+
COLORS = {
13+
"input": "#E3F2FD", # Light blue
14+
"process": "#FFF3E0", # Light orange
15+
"llm": "#E8F5E9", # Light green
16+
"output": "#F3E5F5", # Light purple
17+
"accent": "#1976D2", # Blue
18+
"text": "#212121",
19+
"border": "#BDBDBD",
20+
}
21+
22+
23+
def add_box(ax, x, y, w, h, text, color, fontsize=9):
24+
"""Add a rounded box with text."""
25+
box = FancyBboxPatch((x, y), w, h, boxstyle="round,pad=0.02",
26+
facecolor=color, edgecolor=COLORS["border"], linewidth=1)
27+
ax.add_patch(box)
28+
ax.text(x + w/2, y + h/2, text, ha="center", va="center", fontsize=fontsize,
29+
wrap=True, color=COLORS["text"])
30+
31+
32+
def add_arrow(ax, start, end, color="#757575"):
33+
"""Add an arrow between two points."""
34+
ax.annotate("", xy=end, xytext=start,
35+
arrowprops=dict(arrowstyle="->", color=color, lw=2))
36+
37+
38+
def main():
39+
fig, ax = plt.subplots(1, 1, figsize=(14, 12))
40+
ax.set_xlim(0, 14)
41+
ax.set_ylim(0, 12)
42+
ax.set_aspect("equal")
43+
ax.axis("off")
44+
45+
# Title
46+
ax.text(7, 11.5, "Complexity Analyzer — Core Engine", fontsize=18, ha="center",
47+
fontweight="bold", color=COLORS["text"])
48+
ax.text(7, 11, "Implementation effort (1–10), not line count or operational risk",
49+
fontsize=10, ha="center", color="#616161")
50+
51+
# === PIPELINE FLOW ===
52+
# Row 1: Input
53+
add_box(ax, 0.5, 9.2, 2.2, 1.2, "PR URL", COLORS["input"])
54+
add_box(ax, 3.2, 9.2, 2.2, 1.2, "GitHub API\nFetch", COLORS["process"])
55+
add_box(ax, 5.9, 9.2, 2.2, 1.2, "Raw Diff\n+ Metadata", COLORS["input"])
56+
57+
add_arrow(ax, (2.7, 9.8), (3.2, 9.8))
58+
add_arrow(ax, (5.4, 9.8), (5.9, 9.8))
59+
60+
# Row 2: Preprocess
61+
add_box(ax, 5.9, 7.2, 2.5, 1.5, "Preprocess\n(redact, filter,\nchunk, truncate)", COLORS["process"])
62+
add_arrow(ax, (6.05, 9.2), (6.05, 8.7))
63+
64+
add_box(ax, 5.9, 5.4, 2.5, 1.5, "Build Prompt\n(URL, title, stats,\ndiff excerpt)", COLORS["process"])
65+
add_arrow(ax, (7.15, 7.2), (7.15, 6.9))
66+
67+
# Row 3: LLM
68+
add_box(ax, 2.5, 3.2, 4.5, 2.0, "LLM Analysis\n(OpenAI / Anthropic / Bedrock)\n\nSystem prompt + diff → JSON", COLORS["llm"], fontsize=10)
69+
add_arrow(ax, (7.15, 6.1), (7, 5.2))
70+
add_arrow(ax, (7, 5.2), (5.25, 5.2))
71+
add_arrow(ax, (5.25, 5.2), (5.25, 5.2))
72+
73+
# Row 4: Output
74+
add_box(ax, 2.5, 1.2, 2.0, 1.5, "Parse & Validate\n(clamp 1–10)", COLORS["output"])
75+
add_box(ax, 5.0, 1.2, 2.0, 1.5, "Score + Explanation", COLORS["output"])
76+
add_arrow(ax, (4.25, 4.2), (3.5, 2.7))
77+
add_arrow(ax, (3.5, 1.95), (5.0, 1.95))
78+
79+
# === PRINCIPLES PANEL ===
80+
principles_y = 0.3
81+
ax.add_patch(Rectangle((8.5, 1.5), 5.2, 8.5, facecolor="#FAFAFA",
82+
edgecolor=COLORS["border"], linewidth=1, linestyle="-"))
83+
ax.text(11.1, 9.7, "Core Principles", fontsize=12, ha="center", fontweight="bold")
84+
85+
principles = [
86+
("What Complexity Means", [
87+
"Implementation effort",
88+
"Design + code + testing",
89+
"Developer velocity proxy",
90+
]),
91+
("LLM Factors", [
92+
"Scope (files/modules)",
93+
"Logic (control flow, abstractions)",
94+
"Testing effort implied",
95+
"Data (migrations, mappings)",
96+
]),
97+
("Score Bands", [
98+
"1–2: Almost trivial",
99+
"3–4: Small but non-trivial",
100+
"5–6: Medium (multi-module)",
101+
"7–8: Large/sophisticated",
102+
"9–10: Very complex",
103+
]),
104+
("Avoid Conflating", [
105+
"Line count ≠ complexity",
106+
"Lockfile churn ≠ high score",
107+
"Operational risk ≠ impl. difficulty",
108+
]),
109+
]
110+
111+
y = 9.2
112+
for title, items in principles:
113+
ax.text(8.7, y, title, fontsize=9, fontweight="bold", color=COLORS["accent"])
114+
y -= 0.35
115+
for item in items:
116+
ax.text(8.9, y, f"• {item}", fontsize=8, color=COLORS["text"])
117+
y -= 0.3
118+
y -= 0.2
119+
120+
# === PREPROCESS DETAILS ===
121+
ax.add_patch(Rectangle((0.5, 1.5), 4.5, 3.2, facecolor="#FFFDE7",
122+
edgecolor=COLORS["border"], linewidth=1))
123+
ax.text(2.75, 4.5, "Preprocess Steps", fontsize=10, ha="center", fontweight="bold")
124+
preprocess_steps = [
125+
"Redact: secrets, emails, keys",
126+
"Filter: lockfiles, binary, vendor/",
127+
"Chunk: max hunks per file (default 2)",
128+
"Truncate: tiktoken, max 50k tokens",
129+
"Stats: additions, deletions, byExt, byLang",
130+
]
131+
for i, step in enumerate(preprocess_steps):
132+
ax.text(0.7, 4.2 - i * 0.5, step, fontsize=8)
133+
134+
# File layout legend
135+
ax.text(7, 0.5, "Key files: cli/analyze.py (orchestration) • cli/preprocess.py • cli/prompt/default.txt • cli/scoring.py • cli/llm*.py",
136+
fontsize=8, ha="center", style="italic", color="#757575")
137+
138+
plt.tight_layout()
139+
out_path = "reports/complexity-engine-principles.png"
140+
fig.savefig(out_path, dpi=150, bbox_inches="tight", facecolor="white")
141+
plt.close(fig)
142+
print(f"Saved: {out_path}")
143+
144+
145+
if __name__ == "__main__":
146+
main()

0 commit comments

Comments
 (0)