Spaces:
Sleeping
Sleeping
Upload folder using huggingface_hub
Browse files- __pycache__/app.cpython-311.pyc +2 -2
- app.py +127 -179
- logs/privacy_audit_detailed.log +6 -0
__pycache__/app.cpython-311.pyc
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:64e05fdbfb34e223a6459477a52724bfa95d8ba16076b2527b84a60063adb015
|
| 3 |
+
size 123258
|
app.py
CHANGED
|
@@ -1035,204 +1035,187 @@ def _create_error_figure(result: Dict[str, Any]) -> go.Figure:
|
|
| 1035 |
return fig
|
| 1036 |
|
| 1037 |
def _create_comprehensive_dashboard(result: Dict[str, Any]) -> go.Figure:
|
| 1038 |
-
"""Create
|
| 1039 |
from plotly.subplots import make_subplots
|
| 1040 |
|
| 1041 |
-
#
|
| 1042 |
fig = make_subplots(
|
| 1043 |
-
rows=2, cols=
|
| 1044 |
subplot_titles=(
|
| 1045 |
-
"Distance
|
| 1046 |
-
"Privacy Risk
|
| 1047 |
-
"Data Quality
|
| 1048 |
-
"
|
| 1049 |
-
"Privacy Bounds Comparison",
|
| 1050 |
-
"Processing Pipeline Status"
|
| 1051 |
),
|
| 1052 |
specs=[
|
| 1053 |
-
[{"type": "bar"}, {"type": "indicator"}
|
| 1054 |
-
[{"type": "bar"}, {"type": "
|
| 1055 |
],
|
| 1056 |
-
vertical_spacing=0.
|
| 1057 |
-
horizontal_spacing=0.
|
| 1058 |
)
|
| 1059 |
|
| 1060 |
-
|
| 1061 |
-
|
| 1062 |
-
|
| 1063 |
-
|
| 1064 |
-
_add_privacy_bounds(fig, result)
|
| 1065 |
-
_add_processing_status(fig, result)
|
| 1066 |
|
| 1067 |
-
#
|
| 1068 |
fig.update_layout(
|
| 1069 |
title={
|
| 1070 |
-
"text": "
|
| 1071 |
"x": 0.5,
|
| 1072 |
"xanchor": "center",
|
| 1073 |
-
"font": {"size":
|
| 1074 |
},
|
| 1075 |
-
height=
|
| 1076 |
showlegend=False,
|
| 1077 |
plot_bgcolor="white",
|
| 1078 |
-
paper_bgcolor="
|
| 1079 |
-
font=dict(family="Arial, sans-serif", size=
|
| 1080 |
-
margin=dict(t=
|
| 1081 |
)
|
| 1082 |
|
| 1083 |
return fig
|
| 1084 |
|
| 1085 |
|
| 1086 |
-
def
|
| 1087 |
-
"""Add
|
| 1088 |
stats = result.get("distance_statistics", {})
|
| 1089 |
|
| 1090 |
-
#
|
| 1091 |
-
metrics = ["Mean
|
| 1092 |
values = [
|
| 1093 |
stats.get("mean_nearest_distance", 0),
|
| 1094 |
stats.get("median_nearest_distance", 0),
|
| 1095 |
-
stats.get("
|
| 1096 |
-
stats.get("q25_nearest_distance", 0),
|
| 1097 |
-
stats.get("q75_nearest_distance", 0)
|
| 1098 |
]
|
| 1099 |
|
| 1100 |
-
# Use
|
| 1101 |
-
colors = ['#3498db', '#2ecc71', '#f39c12', '#9b59b6', '#e74c3c']
|
| 1102 |
-
|
| 1103 |
fig.add_trace(
|
| 1104 |
go.Bar(
|
| 1105 |
x=metrics,
|
| 1106 |
y=values,
|
| 1107 |
-
marker_color=
|
| 1108 |
-
|
|
|
|
| 1109 |
textposition='outside',
|
| 1110 |
-
textfont=dict(size=
|
| 1111 |
-
hovertemplate="<b>%{x}</b><br
|
| 1112 |
-
|
| 1113 |
),
|
| 1114 |
row=1, col=1
|
| 1115 |
)
|
| 1116 |
|
| 1117 |
-
|
| 1118 |
-
fig.
|
| 1119 |
-
fig.update_yaxes(title_text="Distance Value", row=1, col=1, tickformat=".2e")
|
| 1120 |
|
| 1121 |
-
def
|
| 1122 |
-
"""Add
|
| 1123 |
risk_level = result.get("privacy_assessment", {}).get("risk_level", "UNKNOWN")
|
| 1124 |
epsilon = result.get("privacy_assessment", {}).get("primary_epsilon", 0)
|
| 1125 |
|
| 1126 |
-
#
|
| 1127 |
risk_colors = {
|
| 1128 |
-
"EXCEPTIONAL": "#
|
| 1129 |
-
"MEDIUM": "#
|
| 1130 |
-
"CRITICAL": "#
|
| 1131 |
}
|
| 1132 |
|
| 1133 |
-
# Determine gauge range based on epsilon value
|
| 1134 |
-
max_range = max(5.0, epsilon * 1.5) if epsilon > 0 else 5.0
|
| 1135 |
-
|
| 1136 |
fig.add_trace(
|
| 1137 |
go.Indicator(
|
| 1138 |
-
mode="
|
| 1139 |
value=epsilon,
|
| 1140 |
title={
|
| 1141 |
-
"text": f"
|
| 1142 |
-
"font": {"size":
|
| 1143 |
},
|
| 1144 |
-
number={"font": {"size":
|
| 1145 |
-
delta={"reference": 1.0, "valueformat": ".6f"},
|
| 1146 |
gauge={
|
| 1147 |
-
"axis": {
|
| 1148 |
-
|
| 1149 |
-
|
| 1150 |
-
|
| 1151 |
-
|
| 1152 |
-
},
|
| 1153 |
-
"bar": {"color": risk_colors.get(risk_level, "#7f8c8d"), "thickness": 0.8},
|
| 1154 |
-
"steps": [
|
| 1155 |
-
{"range": [0, 0.01], "color": "#d5f4e6", "name": "Exceptional"},
|
| 1156 |
-
{"range": [0.01, 0.1], "color": "#a9dfbf", "name": "Very Low"},
|
| 1157 |
-
{"range": [0.1, 0.5], "color": "#fcf3cf", "name": "Low"},
|
| 1158 |
-
{"range": [0.5, 1.0], "color": "#f8c471", "name": "Medium"},
|
| 1159 |
-
{"range": [1.0, 2.0], "color": "#f1948a", "name": "High"},
|
| 1160 |
-
{"range": [2.0, max_range], "color": "#e8daef", "name": "Critical"}
|
| 1161 |
-
],
|
| 1162 |
-
"threshold": {
|
| 1163 |
-
"line": {"color": "#2c3e50", "width": 3},
|
| 1164 |
-
"thickness": 0.9,
|
| 1165 |
-
"value": 1.0 # Reference line at ε = 1.0
|
| 1166 |
-
}
|
| 1167 |
}
|
| 1168 |
),
|
| 1169 |
row=1, col=2
|
| 1170 |
)
|
| 1171 |
|
| 1172 |
-
def
|
| 1173 |
-
"""Add
|
| 1174 |
stats = result.get("distance_statistics", {})
|
| 1175 |
|
| 1176 |
-
# Calculate meaningful quality percentages
|
| 1177 |
total_samples = result.get("dataset_info", {}).get("real_samples_used", 1)
|
| 1178 |
zero_distances = stats.get("zero_distance_count", 0)
|
| 1179 |
small_distances = stats.get("small_distance_count", 0)
|
| 1180 |
|
| 1181 |
-
|
| 1182 |
-
|
| 1183 |
-
near_memorization_pct = ((small_distances - zero_distances) / total_samples) * 100 if total_samples > 0 else 0
|
| 1184 |
-
safe_samples_pct = 100 - memorization_pct - near_memorization_pct
|
| 1185 |
-
|
| 1186 |
-
categories = ["Safe Samples", "Near Matches", "Exact Matches"]
|
| 1187 |
-
percentages = [safe_samples_pct, near_memorization_pct, memorization_pct]
|
| 1188 |
-
colors = ['#27ae60', '#f39c12', '#e74c3c']
|
| 1189 |
|
| 1190 |
-
# Create horizontal bar chart for better readability
|
| 1191 |
fig.add_trace(
|
| 1192 |
go.Bar(
|
| 1193 |
-
|
| 1194 |
-
|
| 1195 |
-
|
| 1196 |
-
|
| 1197 |
-
text=[f"{
|
| 1198 |
-
textposition='
|
| 1199 |
-
|
| 1200 |
-
|
| 1201 |
-
|
| 1202 |
),
|
| 1203 |
-
row=
|
| 1204 |
)
|
| 1205 |
|
| 1206 |
-
fig.update_xaxes(title_text="
|
| 1207 |
-
fig.update_yaxes(title_text="
|
| 1208 |
|
| 1209 |
-
def
|
| 1210 |
-
"""Add
|
| 1211 |
dataset_info = result.get("dataset_info", {})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1212 |
|
| 1213 |
-
metrics = ["Real Samples", "Synthetic Samples", "Dimensions", "Common Features"]
|
| 1214 |
values = [
|
| 1215 |
-
dataset_info.get(
|
| 1216 |
-
dataset_info.get(
|
| 1217 |
-
dataset_info.get(
|
| 1218 |
-
|
|
|
|
|
|
|
| 1219 |
]
|
| 1220 |
|
| 1221 |
fig.add_trace(
|
| 1222 |
-
go.
|
| 1223 |
-
|
| 1224 |
-
|
| 1225 |
-
|
| 1226 |
-
|
| 1227 |
-
|
| 1228 |
-
|
| 1229 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1230 |
),
|
| 1231 |
-
row=2, col=
|
| 1232 |
)
|
| 1233 |
-
|
| 1234 |
-
fig.update_xaxes(title_text="Dataset Characteristics", row=2, col=1, tickangle=45)
|
| 1235 |
-
fig.update_yaxes(title_text="Count", row=2, col=1)
|
| 1236 |
|
| 1237 |
def _add_privacy_bounds(fig, result: Dict[str, Any]):
|
| 1238 |
"""Add privacy bounds comparison across confidence levels"""
|
|
@@ -1295,81 +1278,46 @@ def _add_processing_status(fig, result: Dict[str, Any]):
|
|
| 1295 |
fig.update_yaxes(title_text="Processing Completion %", row=2, col=3, range=[0, 100])
|
| 1296 |
|
| 1297 |
def create_safe_epsilon_plot(result: Dict[str, Any]) -> go.Figure:
|
| 1298 |
-
"""Create
|
| 1299 |
try:
|
| 1300 |
if "error" in result:
|
| 1301 |
-
|
| 1302 |
-
fig.add_annotation(
|
| 1303 |
-
text=f"Audit Error: {result.get('error', 'Unknown error')}",
|
| 1304 |
-
x=0.5, y=0.5, showarrow=False,
|
| 1305 |
-
font=dict(size=16, color="red")
|
| 1306 |
-
)
|
| 1307 |
-
return fig
|
| 1308 |
|
| 1309 |
epsilon_bounds = result.get("epsilon_bounds", {})
|
| 1310 |
-
|
| 1311 |
confidence_levels = [90, 95, 99]
|
| 1312 |
epsilon_values = [epsilon_bounds.get(f"eps_lb_{conf}", 0) for conf in confidence_levels]
|
| 1313 |
|
| 1314 |
-
# Risk level colors
|
| 1315 |
-
risk_colors = []
|
| 1316 |
-
risk_levels = []
|
| 1317 |
-
for eps in epsilon_values:
|
| 1318 |
-
risk = auditor.assess_privacy_risk(eps) if auditor else "UNKNOWN"
|
| 1319 |
-
risk_levels.append(risk)
|
| 1320 |
-
color_map = {
|
| 1321 |
-
"EXCEPTIONAL": '#2ca02c', "VERY LOW": '#8dd3c7', "LOW": '#ffd92f',
|
| 1322 |
-
"MEDIUM": '#ff7f0e', "HIGH": '#d62728', "VERY HIGH": '#8b0000',
|
| 1323 |
-
"CRITICAL": '#4b0082', "UNKNOWN": '#gray'
|
| 1324 |
-
}
|
| 1325 |
-
risk_colors.append(color_map.get(risk, '#gray'))
|
| 1326 |
-
|
| 1327 |
fig = go.Figure()
|
| 1328 |
|
| 1329 |
-
#
|
| 1330 |
fig.add_trace(go.Bar(
|
| 1331 |
x=[f"{conf}%" for conf in confidence_levels],
|
| 1332 |
y=epsilon_values,
|
| 1333 |
-
marker_color=
|
| 1334 |
-
|
| 1335 |
-
|
| 1336 |
-
|
| 1337 |
-
|
|
|
|
|
|
|
| 1338 |
))
|
| 1339 |
|
| 1340 |
-
# Add reference lines for risk thresholds
|
| 1341 |
-
fig.add_hline(y=0.1, line_dash="dash", line_color="green",
|
| 1342 |
-
annotation_text="Low Risk Threshold")
|
| 1343 |
-
fig.add_hline(y=1.0, line_dash="dash", line_color="orange",
|
| 1344 |
-
annotation_text="Medium Risk Threshold")
|
| 1345 |
-
fig.add_hline(y=2.0, line_dash="dash", line_color="red",
|
| 1346 |
-
annotation_text="High Risk Threshold")
|
| 1347 |
-
|
| 1348 |
-
# Use log scale if values span multiple orders of magnitude
|
| 1349 |
-
use_log_scale = max(epsilon_values) > 0 and (max(epsilon_values) / max(min(epsilon_values), 1e-10)) > 100
|
| 1350 |
-
|
| 1351 |
fig.update_layout(
|
| 1352 |
-
title="Privacy Budget
|
| 1353 |
xaxis_title="Confidence Level",
|
| 1354 |
-
yaxis_title="
|
| 1355 |
-
|
| 1356 |
-
|
| 1357 |
-
|
| 1358 |
-
|
|
|
|
| 1359 |
)
|
| 1360 |
|
| 1361 |
return fig
|
| 1362 |
|
| 1363 |
except Exception as e:
|
| 1364 |
logger.error(f"Epsilon plot creation failed: {e}")
|
| 1365 |
-
|
| 1366 |
-
fig = go.Figure()
|
| 1367 |
-
fig.add_annotation(
|
| 1368 |
-
text=f"Visualization Error: {str(e)}",
|
| 1369 |
-
x=0.5, y=0.5, showarrow=False,
|
| 1370 |
-
font=dict(size=16, color="red")
|
| 1371 |
-
)
|
| 1372 |
-
return fig
|
| 1373 |
|
| 1374 |
def generate_safe_report(result: Dict[str, Any]) -> str:
|
| 1375 |
"""Generate safe executive report with error handling"""
|
|
@@ -2141,7 +2089,7 @@ Run a privacy audit to generate a comprehensive executive report including:
|
|
| 2141 |
try:
|
| 2142 |
# Update status to running
|
| 2143 |
yield (
|
| 2144 |
-
gr.update(value="
|
| 2145 |
gr.update(), gr.update(), gr.update(), gr.update(), gr.update(visible=False)
|
| 2146 |
)
|
| 2147 |
|
|
@@ -2152,10 +2100,10 @@ Run a privacy audit to generate a comprehensive executive report including:
|
|
| 2152 |
if result[0] and "error" not in result[0]:
|
| 2153 |
risk_level = result[0].get("privacy_assessment", {}).get("risk_level", "UNKNOWN")
|
| 2154 |
epsilon = result[0].get("privacy_assessment", {}).get("primary_epsilon", 0)
|
| 2155 |
-
status_msg = f"
|
| 2156 |
else:
|
| 2157 |
error_msg = result[0].get("error", "Unknown error") if result[0] else "Unknown error"
|
| 2158 |
-
status_msg = f"
|
| 2159 |
|
| 2160 |
# Make export visible if successful
|
| 2161 |
export_visible = result[4] is not None
|
|
@@ -2173,8 +2121,8 @@ Run a privacy audit to generate a comprehensive executive report including:
|
|
| 2173 |
error_msg = f"Interface error: {str(e)}"
|
| 2174 |
logger.error(error_msg)
|
| 2175 |
yield (
|
| 2176 |
-
gr.update(value=f"
|
| 2177 |
-
{"error": error_msg}, None, None, f"
|
| 2178 |
)
|
| 2179 |
|
| 2180 |
# Connect the interface - FIXED: Now returns 6 outputs
|
|
|
|
| 1035 |
return fig
|
| 1036 |
|
| 1037 |
def _create_comprehensive_dashboard(result: Dict[str, Any]) -> go.Figure:
|
| 1038 |
+
"""Create simplified privacy dashboard focused on key metrics"""
|
| 1039 |
from plotly.subplots import make_subplots
|
| 1040 |
|
| 1041 |
+
# Simplified 2x2 layout focusing on essential information
|
| 1042 |
fig = make_subplots(
|
| 1043 |
+
rows=2, cols=2,
|
| 1044 |
subplot_titles=(
|
| 1045 |
+
"Distance Statistics",
|
| 1046 |
+
"Privacy Risk Level",
|
| 1047 |
+
"Data Quality Assessment",
|
| 1048 |
+
"Key Metrics Summary"
|
|
|
|
|
|
|
| 1049 |
),
|
| 1050 |
specs=[
|
| 1051 |
+
[{"type": "bar"}, {"type": "indicator"}],
|
| 1052 |
+
[{"type": "bar"}, {"type": "table"}]
|
| 1053 |
],
|
| 1054 |
+
vertical_spacing=0.2,
|
| 1055 |
+
horizontal_spacing=0.15
|
| 1056 |
)
|
| 1057 |
|
| 1058 |
+
_add_simplified_distance_analysis(fig, result)
|
| 1059 |
+
_add_simplified_risk_assessment(fig, result)
|
| 1060 |
+
_add_simplified_quality_assessment(fig, result)
|
| 1061 |
+
_add_key_metrics_table(fig, result)
|
|
|
|
|
|
|
| 1062 |
|
| 1063 |
+
# Clean, minimal layout
|
| 1064 |
fig.update_layout(
|
| 1065 |
title={
|
| 1066 |
+
"text": "Privacy Audit Results",
|
| 1067 |
"x": 0.5,
|
| 1068 |
"xanchor": "center",
|
| 1069 |
+
"font": {"size": 18, "color": "#000000"}
|
| 1070 |
},
|
| 1071 |
+
height=600,
|
| 1072 |
showlegend=False,
|
| 1073 |
plot_bgcolor="white",
|
| 1074 |
+
paper_bgcolor="white",
|
| 1075 |
+
font=dict(family="Arial, sans-serif", size=12, color="#000000"),
|
| 1076 |
+
margin=dict(t=80, b=50, l=60, r=60)
|
| 1077 |
)
|
| 1078 |
|
| 1079 |
return fig
|
| 1080 |
|
| 1081 |
|
| 1082 |
+
def _add_simplified_distance_analysis(fig, result: Dict[str, Any]):
|
| 1083 |
+
"""Add simplified distance analysis focusing on key metrics"""
|
| 1084 |
stats = result.get("distance_statistics", {})
|
| 1085 |
|
| 1086 |
+
# Focus on most important metrics
|
| 1087 |
+
metrics = ["Mean", "Median", "Max"]
|
| 1088 |
values = [
|
| 1089 |
stats.get("mean_nearest_distance", 0),
|
| 1090 |
stats.get("median_nearest_distance", 0),
|
| 1091 |
+
stats.get("max_nearest_distance", 0)
|
|
|
|
|
|
|
| 1092 |
]
|
| 1093 |
|
| 1094 |
+
# Use simple, accessible colors
|
|
|
|
|
|
|
| 1095 |
fig.add_trace(
|
| 1096 |
go.Bar(
|
| 1097 |
x=metrics,
|
| 1098 |
y=values,
|
| 1099 |
+
marker_color='#2563eb',
|
| 1100 |
+
marker_line=dict(color='#1e40af', width=1),
|
| 1101 |
+
text=[f"{v:.4f}" if v > 0 else "0.0000" for v in values],
|
| 1102 |
textposition='outside',
|
| 1103 |
+
textfont=dict(size=11, color="#000000"),
|
| 1104 |
+
hovertemplate="<b>%{x}</b><br>%{y:.6f}<extra></extra>",
|
| 1105 |
+
showlegend=False
|
| 1106 |
),
|
| 1107 |
row=1, col=1
|
| 1108 |
)
|
| 1109 |
|
| 1110 |
+
fig.update_xaxes(title_text="Distance Metric", row=1, col=1, title_font_size=12)
|
| 1111 |
+
fig.update_yaxes(title_text="Value", row=1, col=1, title_font_size=12)
|
|
|
|
| 1112 |
|
| 1113 |
+
def _add_simplified_risk_assessment(fig, result: Dict[str, Any]):
|
| 1114 |
+
"""Add simplified risk assessment indicator"""
|
| 1115 |
risk_level = result.get("privacy_assessment", {}).get("risk_level", "UNKNOWN")
|
| 1116 |
epsilon = result.get("privacy_assessment", {}).get("primary_epsilon", 0)
|
| 1117 |
|
| 1118 |
+
# Simple risk color mapping
|
| 1119 |
risk_colors = {
|
| 1120 |
+
"EXCEPTIONAL": "#059669", "VERY LOW": "#059669", "LOW": "#0891b2",
|
| 1121 |
+
"MEDIUM": "#ea580c", "HIGH": "#dc2626", "VERY HIGH": "#dc2626",
|
| 1122 |
+
"CRITICAL": "#991b1b", "UNKNOWN": "#6b7280"
|
| 1123 |
}
|
| 1124 |
|
|
|
|
|
|
|
|
|
|
| 1125 |
fig.add_trace(
|
| 1126 |
go.Indicator(
|
| 1127 |
+
mode="number+gauge",
|
| 1128 |
value=epsilon,
|
| 1129 |
title={
|
| 1130 |
+
"text": f"Privacy Risk: {risk_level}<br>Epsilon Value",
|
| 1131 |
+
"font": {"size": 14, "color": "#000000"}
|
| 1132 |
},
|
| 1133 |
+
number={"font": {"size": 20, "color": risk_colors.get(risk_level, "#6b7280")}},
|
|
|
|
| 1134 |
gauge={
|
| 1135 |
+
"axis": {"range": [0, 5], "tickcolor": "#000000"},
|
| 1136 |
+
"bar": {"color": risk_colors.get(risk_level, "#6b7280")},
|
| 1137 |
+
"bgcolor": "white",
|
| 1138 |
+
"bordercolor": "#d1d5db",
|
| 1139 |
+
"borderwidth": 2
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1140 |
}
|
| 1141 |
),
|
| 1142 |
row=1, col=2
|
| 1143 |
)
|
| 1144 |
|
| 1145 |
+
def _add_simplified_quality_assessment(fig, result: Dict[str, Any]):
|
| 1146 |
+
"""Add simplified quality assessment"""
|
| 1147 |
stats = result.get("distance_statistics", {})
|
| 1148 |
|
|
|
|
| 1149 |
total_samples = result.get("dataset_info", {}).get("real_samples_used", 1)
|
| 1150 |
zero_distances = stats.get("zero_distance_count", 0)
|
| 1151 |
small_distances = stats.get("small_distance_count", 0)
|
| 1152 |
|
| 1153 |
+
categories = ["Safe", "Near Match", "Exact Match"]
|
| 1154 |
+
counts = [total_samples - small_distances, small_distances - zero_distances, zero_distances]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1155 |
|
|
|
|
| 1156 |
fig.add_trace(
|
| 1157 |
go.Bar(
|
| 1158 |
+
x=categories,
|
| 1159 |
+
y=counts,
|
| 1160 |
+
marker_color=['#059669', '#ea580c', '#dc2626'],
|
| 1161 |
+
marker_line=dict(color='#000000', width=1),
|
| 1162 |
+
text=[f"{c:,}" for c in counts],
|
| 1163 |
+
textposition='outside',
|
| 1164 |
+
textfont=dict(size=11, color="#000000"),
|
| 1165 |
+
hovertemplate="<b>%{x}</b><br>Count: %{y:,}<extra></extra>",
|
| 1166 |
+
showlegend=False
|
| 1167 |
),
|
| 1168 |
+
row=2, col=1
|
| 1169 |
)
|
| 1170 |
|
| 1171 |
+
fig.update_xaxes(title_text="Sample Type", row=2, col=1, title_font_size=12)
|
| 1172 |
+
fig.update_yaxes(title_text="Count", row=2, col=1, title_font_size=12)
|
| 1173 |
|
| 1174 |
+
def _add_key_metrics_table(fig, result: Dict[str, Any]):
|
| 1175 |
+
"""Add key metrics summary table"""
|
| 1176 |
dataset_info = result.get("dataset_info", {})
|
| 1177 |
+
stats = result.get("distance_statistics", {})
|
| 1178 |
+
risk_level = result.get("privacy_assessment", {}).get("risk_level", "UNKNOWN")
|
| 1179 |
+
epsilon = result.get("privacy_assessment", {}).get("primary_epsilon", 0)
|
| 1180 |
+
|
| 1181 |
+
metrics = [
|
| 1182 |
+
"Real Samples",
|
| 1183 |
+
"Synthetic Samples",
|
| 1184 |
+
"Dimensions",
|
| 1185 |
+
"Risk Level",
|
| 1186 |
+
"Epsilon Value",
|
| 1187 |
+
"Exact Matches"
|
| 1188 |
+
]
|
| 1189 |
|
|
|
|
| 1190 |
values = [
|
| 1191 |
+
f"{dataset_info.get('real_samples_used', 0):,}",
|
| 1192 |
+
f"{dataset_info.get('synthetic_samples', 0):,}",
|
| 1193 |
+
f"{dataset_info.get('dimensions', 0)}",
|
| 1194 |
+
risk_level,
|
| 1195 |
+
f"{epsilon:.6f}",
|
| 1196 |
+
f"{stats.get('zero_distance_count', 0):,}"
|
| 1197 |
]
|
| 1198 |
|
| 1199 |
fig.add_trace(
|
| 1200 |
+
go.Table(
|
| 1201 |
+
header=dict(
|
| 1202 |
+
values=["<b>Metric</b>", "<b>Value</b>"],
|
| 1203 |
+
fill_color="#f3f4f6",
|
| 1204 |
+
font=dict(size=12, color="#000000"),
|
| 1205 |
+
align="left",
|
| 1206 |
+
line_color="#d1d5db"
|
| 1207 |
+
),
|
| 1208 |
+
cells=dict(
|
| 1209 |
+
values=[metrics, values],
|
| 1210 |
+
fill_color="white",
|
| 1211 |
+
font=dict(size=11, color="#000000"),
|
| 1212 |
+
align="left",
|
| 1213 |
+
line_color="#d1d5db",
|
| 1214 |
+
height=30
|
| 1215 |
+
)
|
| 1216 |
),
|
| 1217 |
+
row=2, col=2
|
| 1218 |
)
|
|
|
|
|
|
|
|
|
|
| 1219 |
|
| 1220 |
def _add_privacy_bounds(fig, result: Dict[str, Any]):
|
| 1221 |
"""Add privacy bounds comparison across confidence levels"""
|
|
|
|
| 1278 |
fig.update_yaxes(title_text="Processing Completion %", row=2, col=3, range=[0, 100])
|
| 1279 |
|
| 1280 |
def create_safe_epsilon_plot(result: Dict[str, Any]) -> go.Figure:
|
| 1281 |
+
"""Create simplified epsilon analysis plot"""
|
| 1282 |
try:
|
| 1283 |
if "error" in result:
|
| 1284 |
+
return _create_error_figure(result)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1285 |
|
| 1286 |
epsilon_bounds = result.get("epsilon_bounds", {})
|
|
|
|
| 1287 |
confidence_levels = [90, 95, 99]
|
| 1288 |
epsilon_values = [epsilon_bounds.get(f"eps_lb_{conf}", 0) for conf in confidence_levels]
|
| 1289 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1290 |
fig = go.Figure()
|
| 1291 |
|
| 1292 |
+
# Simple bar chart
|
| 1293 |
fig.add_trace(go.Bar(
|
| 1294 |
x=[f"{conf}%" for conf in confidence_levels],
|
| 1295 |
y=epsilon_values,
|
| 1296 |
+
marker_color='#2563eb',
|
| 1297 |
+
marker_line=dict(color='#1e40af', width=1),
|
| 1298 |
+
text=[f"{eps:.6f}" for eps in epsilon_values],
|
| 1299 |
+
textposition='outside',
|
| 1300 |
+
textfont=dict(size=11, color="#000000"),
|
| 1301 |
+
hovertemplate="<b>%{x} Confidence</b><br>Epsilon: %{y:.6f}<extra></extra>",
|
| 1302 |
+
showlegend=False
|
| 1303 |
))
|
| 1304 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1305 |
fig.update_layout(
|
| 1306 |
+
title="Privacy Budget Analysis",
|
| 1307 |
xaxis_title="Confidence Level",
|
| 1308 |
+
yaxis_title="Epsilon Lower Bound",
|
| 1309 |
+
plot_bgcolor="white",
|
| 1310 |
+
paper_bgcolor="white",
|
| 1311 |
+
font=dict(family="Arial, sans-serif", size=12, color="#000000"),
|
| 1312 |
+
height=400,
|
| 1313 |
+
margin=dict(t=80, b=50, l=60, r=60)
|
| 1314 |
)
|
| 1315 |
|
| 1316 |
return fig
|
| 1317 |
|
| 1318 |
except Exception as e:
|
| 1319 |
logger.error(f"Epsilon plot creation failed: {e}")
|
| 1320 |
+
return _create_error_figure({"error": str(e)})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1321 |
|
| 1322 |
def generate_safe_report(result: Dict[str, Any]) -> str:
|
| 1323 |
"""Generate safe executive report with error handling"""
|
|
|
|
| 2089 |
try:
|
| 2090 |
# Update status to running
|
| 2091 |
yield (
|
| 2092 |
+
gr.update(value="Audit in progress. Processing your datasets and running privacy analysis."),
|
| 2093 |
gr.update(), gr.update(), gr.update(), gr.update(), gr.update(visible=False)
|
| 2094 |
)
|
| 2095 |
|
|
|
|
| 2100 |
if result[0] and "error" not in result[0]:
|
| 2101 |
risk_level = result[0].get("privacy_assessment", {}).get("risk_level", "UNKNOWN")
|
| 2102 |
epsilon = result[0].get("privacy_assessment", {}).get("primary_epsilon", 0)
|
| 2103 |
+
status_msg = f"Audit completed successfully.\n\nRisk Level: {risk_level}\nEpsilon-DP Bound: {epsilon:.6f}"
|
| 2104 |
else:
|
| 2105 |
error_msg = result[0].get("error", "Unknown error") if result[0] else "Unknown error"
|
| 2106 |
+
status_msg = f"Audit failed: {error_msg}"
|
| 2107 |
|
| 2108 |
# Make export visible if successful
|
| 2109 |
export_visible = result[4] is not None
|
|
|
|
| 2121 |
error_msg = f"Interface error: {str(e)}"
|
| 2122 |
logger.error(error_msg)
|
| 2123 |
yield (
|
| 2124 |
+
gr.update(value=f"Interface Error: {error_msg}"),
|
| 2125 |
+
{"error": error_msg}, None, None, f"Error: {error_msg}", gr.update(visible=False)
|
| 2126 |
)
|
| 2127 |
|
| 2128 |
# Connect the interface - FIXED: Now returns 6 outputs
|
logs/privacy_audit_detailed.log
CHANGED
|
@@ -37,3 +37,9 @@
|
|
| 37 |
2025-09-07 02:50:30,255 - app - INFO - __init__:265 - Initialized Privacy Auditor - Session: 2d9998de
|
| 38 |
2025-09-07 02:50:30,255 - app - INFO - __init__:266 - Configuration: {'confidence_level': 0.95, 'subsample_size': None, 'categorical_encoding': 'onehot', 'numerical_scaling': 'standard', 'distance_metric': 'euclidean', 'enable_preprocessing_report': True, 'max_file_size_mb': 500, 'timeout_seconds': 300, 'enable_data_validation': True, 'chunk_size': 10000, 'max_categories_onehot': 50}
|
| 39 |
2025-09-07 02:50:30,255 - app - INFO - <module>:999 - Privacy auditor initialized successfully
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
2025-09-07 02:50:30,255 - app - INFO - __init__:265 - Initialized Privacy Auditor - Session: 2d9998de
|
| 38 |
2025-09-07 02:50:30,255 - app - INFO - __init__:266 - Configuration: {'confidence_level': 0.95, 'subsample_size': None, 'categorical_encoding': 'onehot', 'numerical_scaling': 'standard', 'distance_metric': 'euclidean', 'enable_preprocessing_report': True, 'max_file_size_mb': 500, 'timeout_seconds': 300, 'enable_data_validation': True, 'chunk_size': 10000, 'max_categories_onehot': 50}
|
| 39 |
2025-09-07 02:50:30,255 - app - INFO - <module>:999 - Privacy auditor initialized successfully
|
| 40 |
+
2025-09-07 03:03:38,859 - app - INFO - <module>:51 - Privacy Auditor Starting - 2025-09-07 03:03:38
|
| 41 |
+
2025-09-07 03:03:38,860 - app - INFO - __init__:265 - Initialized Privacy Auditor - Session: 852aeeb3
|
| 42 |
+
2025-09-07 03:03:38,860 - app - INFO - __init__:266 - Configuration: {'confidence_level': 0.95, 'subsample_size': None, 'categorical_encoding': 'onehot', 'numerical_scaling': 'standard', 'distance_metric': 'euclidean', 'enable_preprocessing_report': True, 'max_file_size_mb': 500, 'timeout_seconds': 300, 'enable_data_validation': True, 'chunk_size': 10000, 'max_categories_onehot': 50}
|
| 43 |
+
2025-09-07 03:03:38,860 - app - INFO - <module>:999 - Privacy auditor initialized successfully
|
| 44 |
+
2025-09-07 03:03:39,448 - httpx - INFO - _send_single_request:1038 - HTTP Request: GET https://checkip.amazonaws.com/ "HTTP/1.1 200 "
|
| 45 |
+
2025-09-07 03:03:39,804 - httpx - INFO - _send_single_request:1038 - HTTP Request: GET https://api.gradio.app/pkg-version "HTTP/1.1 200 OK"
|