fix(report):

This commit is contained in:
Alexander Myasoedov
2024-08-16 21:32:47 +03:00
parent 8d6618487f
commit b3cccc75f5
2 changed files with 128 additions and 45 deletions
+125 -42
View File
@@ -1,74 +1,157 @@
from io import BytesIO
from textwrap import wrap
import io
import math
import string
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from matplotlib.cm import ScalarMappable
from matplotlib.colors import LinearSegmentedColormap, Normalize
def plot_security_report(table):
# Data preprocessing
data = pd.DataFrame(table)
# Sorting by failureRate for a meaningful arrangement
data_sorted = data.sort_values("failureRate", ascending=False)
# Sort by failure rate and reset index
data = data.sort_values("failureRate", ascending=False).reset_index(drop=True)
data["identifier"] = generate_identifiers(data)
# Values for the plot
angles = np.linspace(0, 2 * np.pi, len(data_sorted), endpoint=False)
failure_rate = data_sorted["failureRate"]
tokens = data_sorted["tokens"]
# Plot setup
fig, ax = plt.subplots(figsize=(12, 10), subplot_kw={"projection": "polar"})
fig.set_facecolor("#f0f0f0")
ax.set_facecolor("#f0f0f0")
# Styling parameters
COLORS = ["#6C5B7B", "#C06C84", "#F67280", "#F8B195"]
cmap = mpl.colors.LinearSegmentedColormap.from_list("custom", COLORS, N=256)
norm = mpl.colors.Normalize(vmin=tokens.min(), vmax=tokens.max())
colors = ["#6C5B7B", "#C06C84", "#F67280", "#F8B195"][::-1] # Pastel palette
# colors = ["#440154", "#3b528b", "#21908c", "#5dc863"] # Viridis-inspired palette
cmap = LinearSegmentedColormap.from_list("custom", colors, N=256)
norm = Normalize(vmin=data["tokens"].min(), vmax=data["tokens"].max())
# Polar plot setup
fig, ax = plt.subplots(figsize=(10, 8), subplot_kw={"projection": "polar"})
ax.set_theta_offset(np.pi / 2)
ax.set_theta_direction(-1)
ax.set_facecolor("white")
# Bars for failureRate with colors based on 'tokens'
# Compute angles for the polar plot
angles = np.linspace(0, 2 * np.pi, len(data), endpoint=False)
# Plot bars
bars = ax.bar(
angles,
failure_rate,
width=0.3,
color=[cmap(norm(t)) for t in tokens],
alpha=0.75,
data["failureRate"],
width=0.5,
color=[cmap(norm(t)) for t in data["tokens"]],
alpha=0.8,
label="Failure Rate %",
)
# Add labels for the modules
module_labels = ["\n".join(wrap(m, 10)) for m in data_sorted["module"]]
# Customize polar plot
ax.set_theta_offset(np.pi / 2)
ax.set_theta_direction(-1)
ax.set_ylim(0, max(data["failureRate"]) * 1.1) # Add some headroom
# Add labels (now using identifiers)
ax.set_xticks(angles)
ax.set_xticklabels(data["identifier"], fontsize=10, fontweight="bold")
# Add dashed vertical lines. These are just references
# Add circular grid lines
ax.yaxis.grid(True, color="gray", linestyle=":", alpha=0.5)
ax.set_yticks(np.arange(0, max(data["failureRate"]), 20))
ax.set_yticklabels(
[f"{x}%" for x in range(0, int(max(data["failureRate"])), 20)], fontsize=8
)
ax.set_xticklabels(module_labels, fontsize=7, color="#333")
# Add radial lines
ax.vlines(
angles,
0,
max(data["failureRate"]) * 1.1,
color="gray",
linestyle=":",
alpha=0.5,
)
# Color bar for the tokens
# Color bar for token count
sm = ScalarMappable(cmap=cmap, norm=norm)
sm.set_array([])
cbar = plt.colorbar(sm, ax=ax, orientation="horizontal", pad=0.1)
cbar.set_label("Token Count (k)", fontsize=12, color="#444")
# Grid and legend
ax.grid(True, color="gray", linestyle=":", linewidth=0.5)
plt.legend(loc="upper right", bbox_to_anchor=(1.1, 1.1))
ax.vlines(angles, 0, 100, color="#444", ls=(0, (4, 4)), zorder=11)
# Title and subtitle
title = "Security Report for Different Modules"
# fig.suptitle(title, fontsize=18, weight="bold", ha="center", va="top")
cbar = fig.colorbar(sm, ax=ax, orientation="horizontal", pad=0.08, aspect=30)
cbar.set_label("Token Count (k)", fontsize=10, fontweight="bold")
# Title and caption
fig.suptitle(
"Security Report for Different Modules", fontsize=16, fontweight="bold", y=1.02
)
caption = "Report generated by https://github.com/msoedov/agentic_security"
fig.text(
0.5,
0.02,
caption,
fontsize=8,
ha="center",
va="bottom",
alpha=0.7,
fontweight="bold",
)
fig.text(0.5, 0.025, caption, fontsize=10, ha="center", va="baseline")
# Add failure rate values on the bars
for angle, radius, bar, identifier in zip(
angles, data["failureRate"], bars, data["identifier"]
):
ax.text(
angle,
radius,
f"{identifier}: {radius:.1f}%",
ha="center",
va="bottom",
rotation=angle * 180 / np.pi - 90,
rotation_mode="anchor",
fontsize=7,
fontweight="bold",
color="black",
)
buf = BytesIO()
plt.savefig(buf, format="jpeg")
# Add a table with identifiers and dataset names
table_data = [["Threat"]] + [
[f"{identifier}: {module} ({fr:.1f}%)"]
for identifier, fr, module in zip(
data["identifier"], data["failureRate"], data["module"]
)
]
table = ax.table(
cellText=table_data,
loc="right",
cellLoc="left",
)
table.auto_set_font_size(False)
table.set_fontsize(8)
# Adjust table style
table.scale(1, 0.7)
for (row, col), cell in table.get_celld().items():
cell.set_edgecolor("none")
cell.set_facecolor("#f0f0f0" if row % 2 == 0 else "#e0e0e0")
cell.set_alpha(0.8)
cell.set_text_props(wrap=True)
if row == 0:
cell.set_text_props(fontweight="bold")
# Adjust layout and save
plt.tight_layout()
buf = io.BytesIO()
plt.savefig(buf, format="png", dpi=300, bbox_inches="tight")
plt.close(fig)
buf.seek(0)
return buf
def generate_identifiers(data):
data_length = len(data)
alphabet = string.ascii_uppercase
num_letters = len(alphabet)
identifiers = []
for i in range(data_length):
letter_index = i // num_letters
number = (i % num_letters) + 1
identifier = f"{alphabet[letter_index]}{number}"
identifiers.append(identifier)
return identifiers
+3 -3
View File
@@ -294,9 +294,7 @@
<div id="progress"
class="w-24 bg-earth-1 rounded-full h-2 overflow-hidden"
v-bind:style="{width: progressWidth}">
</div>
<img :src="imageUrl" alt="Generated Plot">
<div
class="rounded-lg border bg-card text-card-foreground shadow-sm"
data-v0-t="card">
@@ -356,6 +354,7 @@
class="inline-flex text-gray-100 items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-earth-1 text-earth-foreground hover:bg-earth-1/90 h-10 px-4 py-2">
Download failures
</button>
<img :src="reportImageUrl" alt="Generated Plot" v-if="reportImageUrl">
</main>
@@ -556,6 +555,7 @@ Content-Type: application/json
console.log('New event');
// { "module": "Module 49", "tokens": 480, "cost": 4.800000000000001, "progress": 9.8 }
let progress = event.progress;
progress = progress % 100;
this.progressWidth = `${progress}%`;
if (this.mainTable.length < 1) {
@@ -574,7 +574,7 @@ Content-Type: application/json
last.last = false;
this.mainTable.push(event);
event.last = true;
// this.newRow()
this.newRow()
}
this.okMsg = `New event: ${event.module}: ${event.progress}%`;