77from matplotlib .cm import ScalarMappable
88from matplotlib .colors import LinearSegmentedColormap , Normalize
99
10+ from agentic_security .logutils import logger
11+
1012from .primitives import Table
1113
1214
1315def 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