Skip to content

Commit 03087e1

Browse files
committed
refactor(bias-audit): simplify based on code review
- Run PDF generation on a threadpool via asyncio.to_thread() so ReportLab does not block the FastAPI event loop during concurrent downloads. - Drop the duplicated metadata fields (aedt_name, description, data_source_description) in the run request. The modal already writes the top-level systemName/systemDescription/dataSource fields, and the report generator reads those directly. The metadata dict now only carries distribution_date, which has no top-level counterpart. - Reuse the existing triggerBrowserDownload helper from browserDownload.utils instead of rolling a new blob-download pattern in BiasAuditDetail.tsx. Applies to both JSON and PDF downloads.
1 parent 95f958d commit 03087e1

File tree

4 files changed

+12
-28
lines changed

4 files changed

+12
-28
lines changed

Clients/src/presentation/pages/EvalsDashboard/BiasAuditDetail.tsx

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { StatCard } from "../../components/Cards/StatCard";
66
import { CustomizableButton } from "../../components/button/customizable-button";
77
import { getStatusChip, getModeChip } from "./biasAuditHelpers";
88
import ConfirmationModal from "../../components/Dialogs/ConfirmationModal";
9+
import { triggerBrowserDownload } from "../../utils/browserDownload.utils";
910
import { palette } from "../../themes/palette";
1011
import {
1112
getBiasAuditResults,
@@ -311,14 +312,7 @@ export default function BiasAuditDetail({ auditId, onBack }: BiasAuditDetailProp
311312
try {
312313
const json = JSON.stringify(audit.results, null, 2);
313314
const blob = new Blob([json], { type: "application/json" });
314-
const url = URL.createObjectURL(blob);
315-
const a = document.createElement("a");
316-
a.href = url;
317-
a.download = `bias-audit-${auditId}.json`;
318-
document.body.appendChild(a);
319-
a.click();
320-
document.body.removeChild(a);
321-
URL.revokeObjectURL(url);
315+
triggerBrowserDownload(blob, `bias-audit-${auditId}.json`);
322316
} catch (err) {
323317
console.error("Failed to download results:", err);
324318
}
@@ -329,14 +323,7 @@ export default function BiasAuditDetail({ auditId, onBack }: BiasAuditDetailProp
329323
setIsDownloadingPdf(true);
330324
try {
331325
const blob = await downloadBiasAuditReport(auditId);
332-
const url = URL.createObjectURL(blob);
333-
const a = document.createElement("a");
334-
a.href = url;
335-
a.download = `bias-audit-${auditId}.pdf`;
336-
document.body.appendChild(a);
337-
a.click();
338-
document.body.removeChild(a);
339-
URL.revokeObjectURL(url);
326+
triggerBrowserDownload(blob, `bias-audit-${auditId}.pdf`);
340327
} catch (err) {
341328
console.error("Failed to download PDF report:", err);
342329
} finally {
@@ -368,7 +355,7 @@ export default function BiasAuditDetail({ auditId, onBack }: BiasAuditDetailProp
368355
<ArrowLeft size={18} color={theme.palette.text.secondary} strokeWidth={1.5} />
369356
</Box>
370357
<Stack spacing={0.5} flex={1}>
371-
<Stack direction="row" alignItems="center" spacing={1.5}>
358+
<Stack direction="row" alignItems="center" sx={{ gap: "8px" }}>
372359
<Typography sx={{ fontSize: 15, fontWeight: 600, color: theme.palette.text.primary }}>
373360
{audit?.presetName || "Bias audit"}
374361
</Typography>
@@ -381,7 +368,7 @@ export default function BiasAuditDetail({ auditId, onBack }: BiasAuditDetailProp
381368
</Typography>
382369
)}
383370
</Stack>
384-
<Stack direction="row" spacing={1}>
371+
<Stack direction="row" sx={{ gap: "8px" }}>
385372
{audit?.results && status === "completed" && (
386373
<CustomizableButton
387374
variant="contained"

Clients/src/presentation/pages/EvalsDashboard/NewBiasAuditModal.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -343,10 +343,7 @@ const NewBiasAuditModal: React.FC<NewBiasAuditModalProps> = ({
343343
systemDescription: aedtDescription,
344344
dataSource: dataSourceDescription,
345345
metadata: {
346-
aedt_name: aedtName,
347-
description: aedtDescription,
348346
distribution_date: distributionDate,
349-
data_source_description: dataSourceDescription,
350347
},
351348
};
352349
const result = await runBiasAudit(csvFile, config);

EvalServer/src/controllers/bias_audits.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
status polling, and result retrieval.
66
"""
77

8+
import asyncio
89
import json
910
import logging
1011
import traceback
@@ -419,7 +420,8 @@ async def get_bias_audit_report_controller(
419420

420421
from engines.bias_audit.report_generator import generate_pdf_report
421422
try:
422-
pdf_bytes = generate_pdf_report(audit)
423+
# PDF rendering is CPU-bound and holds the GIL; run off the event loop
424+
pdf_bytes = await asyncio.to_thread(generate_pdf_report, audit)
423425
except Exception as e:
424426
logger.error(f"[BiasAudit] Failed to generate report for {audit_id}: {e}")
425427
logger.error(traceback.format_exc())

EvalServer/src/engines/bias_audit/report_generator.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ def _cover_page(
159159
config = audit.get("config", {}) or {}
160160
results = audit.get("results", {}) or {}
161161

162-
system_name = config.get("systemName") or config.get("metadata", {}).get("aedt_name") or "AI system"
162+
system_name = config.get("systemName") or "AI system"
163163
system_version = config.get("systemVersion") or ""
164164
preset_name = audit.get("presetName", "Custom")
165165

@@ -234,13 +234,12 @@ def _system_description(
234234
story: list, styles: Dict[str, ParagraphStyle], audit: Dict[str, Any]
235235
) -> None:
236236
config = audit.get("config", {}) or {}
237-
meta = config.get("metadata", {}) or {}
238237

239238
story.append(Paragraph("System description", styles["h1"]))
240239
story.append(_key_value_table([
241-
("Name", config.get("systemName") or meta.get("aedt_name", "")),
240+
("Name", config.get("systemName", "")),
242241
("Version", config.get("systemVersion", "")),
243-
("Description", config.get("systemDescription") or meta.get("description", "")),
242+
("Description", config.get("systemDescription", "")),
244243
("Deployment context", config.get("deploymentContext", "")),
245244
]))
246245

@@ -249,7 +248,6 @@ def _data_description(
249248
story: list, styles: Dict[str, ParagraphStyle], audit: Dict[str, Any]
250249
) -> None:
251250
config = audit.get("config", {}) or {}
252-
meta = config.get("metadata", {}) or {}
253251
results = audit.get("results", {}) or {}
254252

255253
story.append(Paragraph("Data description", styles["h1"]))
@@ -265,7 +263,7 @@ def _data_description(
265263
date_range = f"up to {end}"
266264

267265
story.append(_key_value_table([
268-
("Source", config.get("dataSource") or meta.get("data_source_description", "")),
266+
("Source", config.get("dataSource", "")),
269267
("Date range", date_range),
270268
("Total records", _count(results.get("total_applicants"))),
271269
("Records with missing data", _count(results.get("unknown_count"))),

0 commit comments

Comments
 (0)