Skip to content

Commit b4e0aef

Browse files
committed
Merge branch 'main' of github.com:msoedov/agentic_security
2 parents 3a3ac44 + d6ccd8f commit b4e0aef

File tree

1 file changed

+78
-10
lines changed

1 file changed

+78
-10
lines changed

agentic_security/report_chart.py

Lines changed: 78 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,65 @@
77
from matplotlib.cm import ScalarMappable
88
from matplotlib.colors import LinearSegmentedColormap, Normalize
99

10+
from agentic_security.logutils import logger
11+
1012
from .primitives import Table
1113

1214

1315
def plot_security_report(table: Table) -> io.BytesIO:
16+
"""
17+
Generates a polar plot representing the security report based on the given data.
18+
19+
Args:
20+
table (Table): The input data table containing security metrics.
21+
22+
Returns:
23+
io.BytesIO: A buffer containing the generated plot image in PNG format.
24+
Returns an empty buffer in case of an error.
25+
"""
26+
try:
27+
return _plot_security_report(table=table)
28+
except (TypeError, ValueError, OverflowError, IndexError, Exception) as e:
29+
logger.error(f"Error in generating the security report: {e}")
30+
return io.BytesIO()
31+
32+
33+
def generate_identifiers(data: pd.DataFrame) -> list[str]:
34+
"""
35+
Generates unique identifiers for the given dataset.
36+
37+
Args:
38+
data (pd.DataFrame): A pandas DataFrame containing security-related data.
39+
40+
Returns:
41+
list[str]: A list of generated identifiers. Returns a list with an empty string in case of an error.
42+
"""
43+
try:
44+
_generate_identifiers(data=data)
45+
except (TypeError, ValueError, Exception) as e:
46+
logger.error(f"Error in generate_identifiers: {e}")
47+
return [""]
48+
49+
50+
def _plot_security_report(table: Table) -> io.BytesIO:
51+
"""
52+
Generates a polar plot-based security report visualizing the failure rates for different modules.
53+
54+
This function processes the input data, sorts it by failure rate, and generates a polar plot
55+
where each bar represents the failure rate for a specific module. The plot includes identifiers,
56+
color-coding based on token count, failure rate values on the bars, and a table listing the modules
57+
and their corresponding failure rates.
58+
59+
Args:
60+
table (Table): A table-like structure (e.g., pandas DataFrame) containing security report data
61+
with columns for failure rate, tokens, and modules.
62+
63+
Returns:
64+
io.BytesIO: A buffer containing the generated plot image in PNG format.
65+
"""
1466
# Data preprocessing
67+
logger.info("Data preprocessing started.")
68+
1569
data = pd.DataFrame(table)
1670

1771
# Sort by failure rate and reset index
@@ -22,10 +76,10 @@ def plot_security_report(table: Table) -> io.BytesIO:
2276
fig, ax = plt.subplots(figsize=(12, 10), subplot_kw={"projection": "polar"})
2377
fig.set_facecolor("#f0f0f0")
2478
ax.set_facecolor("#f0f0f0")
79+
logger.info("Plot setup complete.")
2580

2681
# Styling parameters
2782
colors = ["#6C5B7B", "#C06C84", "#F67280", "#F8B195"][::-1] # Pastel palette
28-
# colors = ["#440154", "#3b528b", "#21908c", "#5dc863"] # Viridis-inspired palette
2983
cmap = LinearSegmentedColormap.from_list("custom", colors, N=256)
3084
norm = Normalize(vmin=data["tokens"].min(), vmax=data["tokens"].max())
3185

@@ -76,7 +130,10 @@ def plot_security_report(table: Table) -> io.BytesIO:
76130

77131
# Title and caption
78132
fig.suptitle(
79-
"Security Report for Different Modules", fontsize=16, fontweight="bold", y=1.02
133+
"Security Report for Different Modules",
134+
fontsize=16,
135+
fontweight="bold",
136+
y=1.02,
80137
)
81138
caption = "Report generated by https://github.com/msoedov/agentic_security"
82139
fig.text(
@@ -114,17 +171,12 @@ def plot_security_report(table: Table) -> io.BytesIO:
114171
data["identifier"], data["failureRate"], data["module"]
115172
)
116173
]
117-
table = ax.table(
118-
cellText=table_data,
119-
loc="right",
120-
cellLoc="left",
121-
)
174+
table = ax.table(cellText=table_data, loc="right", cellLoc="left")
122175
table.auto_set_font_size(False)
123176
table.set_fontsize(8)
124177

125178
# Adjust table style
126179
table.scale(1, 0.7)
127-
128180
for (row, col), cell in table.get_celld().items():
129181
cell.set_edgecolor("none")
130182
cell.set_facecolor("#f0f0f0" if row % 2 == 0 else "#e0e0e0")
@@ -134,17 +186,33 @@ def plot_security_report(table: Table) -> io.BytesIO:
134186
cell.set_text_props(fontweight="bold")
135187

136188
# Adjust layout and save
137-
138189
plt.tight_layout()
139190
buf = io.BytesIO()
140191
plt.savefig(buf, format="png", dpi=300, bbox_inches="tight")
141192
plt.close(fig)
142193
buf.seek(0)
194+
logger.info("Report successfully generated and saved to buffer.")
143195
return buf
144196

145197

146-
def generate_identifiers(data: pd.DataFrame) -> list[str]:
198+
def _generate_identifiers(data: pd.DataFrame) -> list[str]:
199+
"""
200+
Generates a list of unique identifiers for each row in the given DataFrame.
201+
202+
The identifiers are based on the English alphabet, with each identifier consisting
203+
of a letter followed by a number. The letter represents the "group" of identifiers
204+
(using a letter from A to Z) and the number is a counter within that group. For example:
205+
- A1, A2, ..., A26, B1, B2, ..., Z1, Z2, ...
206+
207+
Args:
208+
data (pd.DataFrame): The input DataFrame containing data for which identifiers
209+
are to be generated.
210+
211+
Returns:
212+
list[str]: A list of unique identifiers as strings, one for each row in the DataFrame.
213+
"""
147214
data_length = len(data)
215+
148216
alphabet = string.ascii_uppercase
149217
num_letters = len(alphabet)
150218

0 commit comments

Comments
 (0)