FAILLED DAIR IN KNEE ARTHROPLASTY
failed DAIR debridement antibiotics implant retention knee arthroplasty periprosthetic infection
PMID: 40480337
PMID: 38529130
PMID: 39223364
PMID: 35360944
PMID: 37714518
failed DAIR periprosthetic infection predictors risk factors two-stage revision
PMID: 39612194
PMID: 36998382
failed DAIR knee arthroplasty management predictors failure 2024 2025
DAIR failure knee arthroplasty Staphylococcus risk factors prediction scoring
PMID: 41566067
PMID: 38797446
PMID: 40746658
| Infection Type | Pooled DAIR Failure Rate |
|---|---|
| Overall (all types) | 35.9% (95% CI: 23.9-48.0%) |
| Acute/early postoperative | 34.2% |
| Acute hematogenous | 39.1% |
| Late/chronic | 73.6% |
| TKA vs THA | TKA significantly worse |
| Risk Factor | Evidence |
|---|---|
| High Charlson Comorbidity Index | OR 1.57 (p=0.003) - independent predictor |
| Total knee (vs hip) | OR 6.08 (p=0.001) - knee far more likely to fail |
| Diabetes mellitus | Highly prevalent in failure cases |
| Cardiovascular disease | Prevalent comorbidity in failures |
| Immunosuppressive therapy | OR 0.13 success (p=0.012) - independent predictor |
| Obesity | Consistently associated with failure |
| Risk Factor | Significance |
|---|---|
| Staphylococcus aureus | Most common organism linked to DAIR failure (biofilm production) |
| Polymicrobial infection | OR significantly elevated; failure predictor for second DAIR and two-stage revision |
| Antibiotic-resistant organisms (MRSA, ESBL) | Strong predictor of failure (p=0.035) |
| Antibiotic mismatch | OR 0.13 success (p=0.007) - critical modifiable factor |
| Positive blood culture (bacteremia) | OR 12 (95% CI 1.1-18, p=0.04) -- new 2026 risk factor for hematogenous TKA infection |
| Marker | OR for Failure |
|---|---|
| Elevated preoperative CRP | OR 1.06 per unit (p=0.014) |
| Elevated synovial WBC count | OR 1.14 (p=0.008) |
| Elevated synovial PMN% | OR 1.05 (p=0.015) |
| Factor | Impact |
|---|---|
| DAIR > 30 days after revision (index surgery) | OR 0.24 success (p=0.008) |
| Repeated DAIR within 90 days | OR 0.37 success (p=0.04) |
| Non-exchange of modular components | Strong predictor of failure (p=0.0038) |
| Non-specialized surgical team | OR significantly worse (p=0.034) |
DAIR Failure confirmed
|
├─ Acute, <3 weeks, sensitive organism, modular components
│ not exchanged previously, specialist team available,
│ no polymicrobial/resistant infection
│ → Consider 2nd DAIR (in carefully selected patients)
│
├─ Good bone stock, well-fixed implants, known sensitive
│ organism, patient fit for surgery
│ → One-stage revision (76.2% success)
│
├─ Chronic infection, biofilm-forming organism (Staph),
│ resistant/polymicrobial, standard approach
│ → Two-stage revision (79.3% success)
│
└─ Poor host, failed multiple revisions, uncontrolled
sepsis
→ Arthrodesis / resection / suppression
| Strategy | Success Rate | Best For | Key Risk Factors for Failure |
|---|---|---|---|
| Repeat DAIR | 54.5% (up to 83.3% selected) | Acute, no resistance, components not previously exchanged | Polymicrobial, resistant organism, no modular exchange |
| One-stage revision | ~76.2% | Known organism, good bone stock | No clear predictors identified |
| Two-stage revision | ~79.3% | All chronic/difficult cases | Polymicrobial, prior revision |
| Arthrodesis/salvage | Variable | Failed all above | Patient factors |
Create a clinical decision-making PDF guide for failed DAIR in TKA
ls -la /tmp/workspace/ && mkdir -p /tmp/workspace/failed-dair-guide/
~/failed-dair-guide/generate_pdf.py
#!/usr/bin/env python3
"""
Clinical Decision-Making PDF Guide: Failed DAIR in Total Knee Arthroplasty
Generated with ReportLab
"""
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import cm, mm
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, PageBreak, KeepTogether
)
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY, TA_RIGHT
from reportlab.platypus.flowables import Flowable
from reportlab.lib.colors import HexColor
import os
OUTPUT_PATH = "/tmp/workspace/failed-dair-guide/Failed_DAIR_TKA_Clinical_Guide.pdf"
# ── Colour palette ────────────────────────────────────────────────────────────
NAVY = HexColor("#1B3A5C")
TEAL = HexColor("#0D7377")
TEAL_LIGHT = HexColor("#E6F4F5")
AMBER = HexColor("#E87722")
AMBER_LIGHT = HexColor("#FFF4E8")
RED = HexColor("#C0392B")
RED_LIGHT = HexColor("#FDECEA")
GREEN = HexColor("#1A7A4A")
GREEN_LIGHT = HexColor("#E8F6EE")
GREY = HexColor("#4A4A4A")
GREY_LIGHT = HexColor("#F5F5F5")
WHITE = colors.white
BLACK = colors.black
GOLD = HexColor("#D4AC0D")
# ── Helper flowable: coloured banner ─────────────────────────────────────────
class ColorBanner(Flowable):
def __init__(self, text, bg_color, text_color=WHITE, height=14*mm, font_size=13):
super().__init__()
self.text = text
self.bg_color = bg_color
self.text_color = text_color
self.height = height
self.font_size = font_size
self.width = 0 # set during wrap
def wrap(self, availWidth, availHeight):
self.width = availWidth
return availWidth, self.height
def draw(self):
c = self.canv
c.setFillColor(self.bg_color)
c.rect(0, 0, self.width, self.height, fill=1, stroke=0)
c.setFillColor(self.text_color)
c.setFont("Helvetica-Bold", self.font_size)
c.drawString(8*mm, self.height/2 - self.font_size*0.35, self.text)
# ── Helper flowable: side-coloured alert box ──────────────────────────────────
class AlertBox(Flowable):
"""A box with a thick left-side accent bar."""
def __init__(self, paragraphs, accent_color, bg_color=GREY_LIGHT, padding=6*mm):
super().__init__()
self.paragraphs = paragraphs # list of Paragraph objects already created
self.accent_color = accent_color
self.bg_color = bg_color
self.padding = padding
self._built_height = 0
self.width = 0
def wrap(self, availWidth, availHeight):
self.width = availWidth
inner_w = availWidth - self.padding*2 - 4*mm # 4mm accent bar
total_h = self.padding
for p in self.paragraphs:
w, h = p.wrap(inner_w, availHeight)
total_h += h + 2*mm
total_h += self.padding
self._built_height = total_h
return availWidth, total_h
def draw(self):
c = self.canv
# background
c.setFillColor(self.bg_color)
c.rect(0, 0, self.width, self._built_height, fill=1, stroke=0)
# accent bar
c.setFillColor(self.accent_color)
c.rect(0, 0, 3.5*mm, self._built_height, fill=1, stroke=0)
# draw paragraphs
y = self._built_height - self.padding
inner_w = self.width - self.padding*2 - 4*mm
for p in self.paragraphs:
w, h = p.wrap(inner_w, 9999)
y -= h
p.drawOn(c, 4*mm + self.padding, y)
y -= 2*mm
# ── Page template with header/footer ─────────────────────────────────────────
def add_header_footer(canvas, doc):
canvas.saveState()
w, h = A4
# Header bar
canvas.setFillColor(NAVY)
canvas.rect(0, h - 22*mm, w, 22*mm, fill=1, stroke=0)
canvas.setFillColor(WHITE)
canvas.setFont("Helvetica-Bold", 10)
canvas.drawString(15*mm, h - 12*mm, "CLINICAL DECISION GUIDE | FAILED DAIR IN TOTAL KNEE ARTHROPLASTY")
canvas.setFont("Helvetica", 8)
canvas.drawRightString(w - 15*mm, h - 12*mm, f"Page {doc.page}")
# Footer
canvas.setFillColor(NAVY)
canvas.rect(0, 0, w, 12*mm, fill=1, stroke=0)
canvas.setFillColor(WHITE)
canvas.setFont("Helvetica", 7)
canvas.drawString(15*mm, 4*mm,
"Based on ICM 2018, Abbaszadeh et al. 2026, Auñón et al. 2025, Ashkenazi et al. 2024, Salman et al. 2024 | For clinical guidance only – not a substitute for specialist judgement")
canvas.restoreState()
# ── Build styles ──────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()
def S(name, **kw):
return ParagraphStyle(name, **kw)
style_title = S("Title2", fontName="Helvetica-Bold", fontSize=28,
textColor=WHITE, alignment=TA_CENTER, spaceAfter=4)
style_subtitle = S("Sub", fontName="Helvetica", fontSize=13,
textColor=HexColor("#B0C4DE"), alignment=TA_CENTER, spaceAfter=2)
style_tagline = S("Tag", fontName="Helvetica-Oblique", fontSize=10,
textColor=HexColor("#90A8C0"), alignment=TA_CENTER, spaceAfter=0)
style_section = S("Section", fontName="Helvetica-Bold", fontSize=12,
textColor=NAVY, spaceBefore=8, spaceAfter=4)
style_body = S("Body2", fontName="Helvetica", fontSize=9,
textColor=GREY, leading=14, spaceAfter=3, alignment=TA_JUSTIFY)
style_body_bold = S("BodyBold", fontName="Helvetica-Bold", fontSize=9,
textColor=GREY, leading=14)
style_small = S("Small", fontName="Helvetica", fontSize=8,
textColor=GREY, leading=12)
style_small_bold = S("SmallBold", fontName="Helvetica-Bold", fontSize=8,
textColor=NAVY)
style_white_bold = S("WBold", fontName="Helvetica-Bold", fontSize=9,
textColor=WHITE)
style_white = S("W", fontName="Helvetica", fontSize=9, textColor=WHITE)
style_amber_bold = S("ABold", fontName="Helvetica-Bold", fontSize=9,
textColor=AMBER)
style_red_bold = S("RBold", fontName="Helvetica-Bold", fontSize=9,
textColor=RED)
style_green_bold = S("GBold", fontName="Helvetica-Bold", fontSize=9,
textColor=GREEN)
style_teal_bold = S("TBold", fontName="Helvetica-Bold", fontSize=10,
textColor=TEAL)
style_center = S("Ctr", fontName="Helvetica", fontSize=9,
textColor=GREY, alignment=TA_CENTER)
style_center_bold = S("CtrBold", fontName="Helvetica-Bold", fontSize=9,
textColor=NAVY, alignment=TA_CENTER)
style_note = S("Note", fontName="Helvetica-Oblique", fontSize=7.5,
textColor=HexColor("#888888"), leading=11)
# ── Cover page content ────────────────────────────────────────────────────────
def cover_page():
items = []
# big navy cover block via Table trick
cover_data = [[
Paragraph("FAILED DAIR", style_title),
],[
Paragraph("IN TOTAL KNEE ARTHROPLASTY", S("sub2", fontName="Helvetica-Bold",
fontSize=16, textColor=HexColor("#B0D4E8"), alignment=TA_CENTER)),
],[
Paragraph("Clinical Decision-Making Guide", style_subtitle),
],[
Paragraph("Evidence-Based Management Framework | August 2026", style_tagline),
]]
cover_table = Table(cover_data, colWidths=[17*cm])
cover_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), NAVY),
("TOPPADDING", (0,0), (-1,0), 18*mm),
("BOTTOMPADDING", (0,-1), (-1,-1), 14*mm),
("LEFTPADDING", (0,0), (-1,-1), 10*mm),
("RIGHTPADDING", (0,0), (-1,-1), 10*mm),
("ALIGN", (0,0), (-1,-1), "CENTER"),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
items.append(cover_table)
items.append(Spacer(1, 5*mm))
# Summary stats row
stats = [
["35.9%", "Overall\nDAIR failure rate", AMBER],
["~42%", "Failure rate\nin TKA specifically", RED],
["6×", "TKA vs THA\nrelative failure risk", NAVY],
["79%", "Two-stage success\nafter failed DAIR", GREEN],
]
stat_cells = []
for val, label, col in stats:
cell = Table([
[Paragraph(val, S("stat", fontName="Helvetica-Bold", fontSize=22,
textColor=col, alignment=TA_CENTER))],
[Paragraph(label, S("statlabel", fontName="Helvetica", fontSize=8,
textColor=GREY, alignment=TA_CENTER, leading=11))],
], colWidths=[3.8*cm])
cell.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), GREY_LIGHT),
("BOX", (0,0), (-1,-1), 0.5, HexColor("#CCCCCC")),
("TOPPADDING", (0,0), (-1,0), 4*mm),
("BOTTOMPADDING", (0,-1), (-1,-1), 4*mm),
]))
stat_cells.append(cell)
stats_row = Table([stat_cells], colWidths=[3.8*cm]*4,
hAlign="CENTER")
stats_row.setStyle(TableStyle([("LEFTPADDING",(0,0),(-1,-1),2*mm),
("RIGHTPADDING",(0,0),(-1,-1),2*mm)]))
items.append(stats_row)
items.append(Spacer(1, 4*mm))
# Scope note
scope_txt = (
"<b>Scope:</b> This guide covers the recognition, risk stratification, and stepwise management of "
"failed Debridement, Antibiotics and Implant Retention (DAIR) procedures following Total Knee "
"Arthroplasty (TKA). It integrates data from the 2018 International Consensus Meeting (ICM) on "
"Musculoskeletal Infection and publications through 2026."
)
scope_box = AlertBox(
[Paragraph(scope_txt, style_body)],
accent_color=TEAL, bg_color=TEAL_LIGHT
)
items.append(scope_box)
items.append(Spacer(1, 3*mm))
# Disclaimer
items.append(Paragraph(
"<i>This guide is intended as a clinical reference tool for qualified orthopaedic surgeons and "
"infectious disease specialists. All treatment decisions must be individualised based on clinical "
"judgement, local microbiology, and patient-specific factors.</i>",
style_note))
return items
# ── Section 1: DAIR Overview ─────────────────────────────────────────────────
def section_dair_overview():
items = []
items.append(ColorBanner("1 | DAIR IN TOTAL KNEE ARTHROPLASTY – OVERVIEW", TEAL))
items.append(Spacer(1, 3*mm))
items.append(Paragraph("What is DAIR?", style_section))
items.append(Paragraph(
"DAIR consists of open surgical debridement of all infected and necrotic tissue, "
"copious pulse-lavage irrigation, mandatory exchange of all modular components "
"(polyethylene tibial insert), and a targeted antibiotic course. Fixed implant components "
"are retained. It is the preferred first-line surgical strategy for <b>acute PJI</b> because "
"it avoids full explantation, preserves bone stock, and carries lower morbidity.",
style_body))
items.append(Spacer(1, 2*mm))
# Indications table
items.append(Paragraph("Classic Indications for DAIR", style_teal_bold))
ind_data = [
[Paragraph("<b>Criterion</b>", style_small_bold), Paragraph("<b>Requirement</b>", style_small_bold)],
["Symptom duration", "< 3-4 weeks from onset (acute infection)"],
["Implant stability", "Well-fixed components on imaging"],
["Skin/soft tissue", "Intact wound, no sinus tract"],
["Organism", "Sensitive pathogen (or unknown pending cultures)"],
["Infection type", "Early postoperative OR acute hematogenous"],
["Component condition", "Modular components exchangeable (liner, insert)"],
]
t = Table(ind_data, colWidths=[5*cm, 11*cm])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), TEAL),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 8.5),
("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, TEAL_LIGHT]),
("GRID", (0,0), (-1,-1), 0.3, HexColor("#CCCCCC")),
("LEFTPADDING", (0,0), (-1,-1), 4*mm),
("RIGHTPADDING", (0,0), (-1,-1), 3*mm),
("TOPPADDING", (0,0), (-1,-1), 2.5*mm),
("BOTTOMPADDING", (0,0), (-1,-1), 2.5*mm),
]))
items.append(t)
items.append(Spacer(1, 3*mm))
# Mandatory steps
items.append(Paragraph("Mandatory Surgical Steps (DAIR Checklist)", style_teal_bold))
steps = [
("1", "Open arthrotomy – avoid minimally invasive approach for infected knee"),
("2", "Complete synovectomy – remove all infected and necrotic tissue"),
("3", "Pulse-lavage irrigation – minimum 6 litres saline; consider povidone-iodine/dilute betadine wash"),
("4", "Exchange ALL modular components – polyethylene tibial insert MUST be removed and replaced"),
("5", "Send multiple intraoperative samples (≥3 tissue, ≥1 synovial fluid) for culture before antibiotics"),
("6", "Retain well-fixed metal components only if no evidence of loosening"),
("7", "Wound closure over drain; consider wound VAC if significant soft tissue compromise"),
]
step_rows = [[Paragraph(n, S("step_n", fontName="Helvetica-Bold", fontSize=10,
textColor=WHITE, alignment=TA_CENTER)),
Paragraph(txt, style_small)] for n, txt in steps]
step_t = Table(step_rows, colWidths=[1*cm, 15*cm])
step_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (0,-1), TEAL),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("ROWBACKGROUNDS", (1,0), (1,-1), [WHITE, GREY_LIGHT]),
("GRID", (0,0), (-1,-1), 0.3, HexColor("#CCCCCC")),
("LEFTPADDING", (0,0), (-1,-1), 3*mm),
("TOPPADDING", (0,0), (-1,-1), 2.5*mm),
("BOTTOMPADDING", (0,0), (-1,-1), 2.5*mm),
]))
items.append(step_t)
return items
# ── Section 2: Failure Rates ──────────────────────────────────────────────────
def section_failure_rates():
items = []
items.append(Spacer(1, 4*mm))
items.append(ColorBanner("2 | DAIR FAILURE RATES IN TKA", RED))
items.append(Spacer(1, 3*mm))
items.append(Paragraph(
"DAIR failure rates in TKA are substantial and consistently higher than in THA. "
"The 2026 meta-analysis by Abbaszadeh et al. (81 studies, PMID 40480337) provides the most "
"comprehensive pooled data to date:", style_body))
items.append(Spacer(1, 2*mm))
rate_data = [
[Paragraph("<b>Infection Type</b>", style_white_bold),
Paragraph("<b>Pooled Failure Rate</b>", style_white_bold),
Paragraph("<b>Clinical Implication</b>", style_white_bold)],
["Overall (all types)", "35.9% (95% CI: 23.9–48.0%)", "One-third of all DAIRs fail"],
["Acute postoperative", "34.2% (95% CI: 28.8–39.6%)", "Best candidate for DAIR"],
["Acute hematogenous", "39.1%", "Higher failure than postoperative"],
["Late / chronic", "73.6%", "DAIR generally NOT indicated"],
["TKA vs THA", "TKA significantly worse (OR ~6×)", "Knee anatomy and biofilm drive excess failure"],
]
rate_t = Table(rate_data, colWidths=[4.5*cm, 5*cm, 6.5*cm])
rate_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), RED),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 8.5),
("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, RED_LIGHT]),
("GRID", (0,0), (-1,-1), 0.3, HexColor("#CCCCCC")),
("LEFTPADDING", (0,0), (-1,-1), 4*mm),
("TOPPADDING", (0,0), (-1,-1), 2.5*mm),
("BOTTOMPADDING", (0,0), (-1,-1), 2.5*mm),
]))
items.append(rate_t)
items.append(Spacer(1, 2*mm))
items.append(Paragraph(
"Most failures (78.4%) occur within the first year (Frear et al., JBJI 2025, PMID 40746658). "
"Success rates across systematic reviews range 55–90%, mean ~71% (Longo et al., J ISAKOS 2024, PMID 37714518).",
style_note))
return items
# ── Section 3: Risk factors ───────────────────────────────────────────────────
def section_risk_factors():
items = []
items.append(Spacer(1, 4*mm))
items.append(ColorBanner("3 | RISK FACTORS FOR DAIR FAILURE", AMBER))
items.append(Spacer(1, 3*mm))
items.append(Paragraph(
"Recognising failure-risk factors before or at the time of DAIR allows better patient selection "
"and may guide the decision to proceed directly to revision.", style_body))
items.append(Spacer(1, 2*mm))
# Three-column risk factor table
risk_data = [
[Paragraph("<b>Patient Factors</b>", style_white_bold),
Paragraph("<b>Microbiological Factors</b>", style_white_bold),
Paragraph("<b>Surgical / Lab Factors</b>", style_white_bold)],
[
Paragraph(
"• High Charlson Comorbidity Index (OR 1.57, p=0.003)\n"
"• TKA vs THA (OR 6.08, p=0.001)\n"
"• Diabetes mellitus\n"
"• Cardiovascular disease\n"
"• Immunosuppressive therapy (OR 0.13 success)\n"
"• Obesity (BMI >30)\n"
"• Renal impairment\n"
"• Rheumatoid arthritis",
style_small),
Paragraph(
"• Staphylococcus aureus (most common organism in failure)\n"
"• MRSA / antibiotic-resistant organisms\n"
"• Polymicrobial infection\n"
"• Positive blood culture (OR 12 for failure in hematogenous TKA, 2026)\n"
"• Antibiotic mismatch (OR 0.13 success)\n"
"• Gram-negative organisms\n"
"• Fungal infection",
style_small),
Paragraph(
"• Elevated preop CRP (OR 1.06/unit)\n"
"• High synovial WBC (OR 1.14)\n"
"• High PMN% in synovial fluid (OR 1.05)\n"
"• DAIR >30 days post-index revision\n"
"• Non-exchange of modular components (p=0.0038)\n"
"• Repeat DAIR within 90 days\n"
"• Non-specialist surgical team (p=0.034)\n"
"• Sinus tract present",
style_small),
]
]
risk_t = Table(risk_data, colWidths=[5.5*cm, 5.5*cm, 5*cm])
risk_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (0,0), AMBER),
("BACKGROUND", (1,0), (1,0), AMBER),
("BACKGROUND", (2,0), (2,0), AMBER),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("FONTSIZE", (0,0), (-1,-1), 8.5),
("BACKGROUND", (0,1), (-1,-1), AMBER_LIGHT),
("GRID", (0,0), (-1,-1), 0.4, HexColor("#CCCCCC")),
("VALIGN", (0,0), (-1,-1), "TOP"),
("LEFTPADDING", (0,0), (-1,-1), 3.5*mm),
("TOPPADDING", (0,0), (-1,-1), 3*mm),
("BOTTOMPADDING", (0,0), (-1,-1), 3*mm),
]))
items.append(risk_t)
items.append(Spacer(1, 3*mm))
# Red alert box: high-risk combinations
alert_paras = [
Paragraph("<b>HIGH-RISK COMBINATION – Consider Proceeding Directly to Revision:</b>", style_red_bold),
Paragraph(
"When ≥2 of the following are present, the probability of DAIR success falls below 30% "
"and direct revision (one- or two-stage) should be strongly considered:",
style_small),
Paragraph(
" \u2022 Staphylococcus aureus or MRSA \u2022 Symptom duration >3 weeks "
"\u2022 Sinus tract \u2022 Positive blood cultures \u2022 Severe immunosuppression "
"\u2022 CCI >4 \u2022 Prior failed DAIR",
style_small),
]
items.append(AlertBox(alert_paras, accent_color=RED, bg_color=RED_LIGHT))
return items
# ── Section 4: Defining Failure ───────────────────────────────────────────────
def section_defining_failure():
items = []
items.append(Spacer(1, 4*mm))
items.append(ColorBanner("4 | DEFINING DAIR FAILURE", NAVY))
items.append(Spacer(1, 3*mm))
items.append(Paragraph("DAIR is considered failed when ANY of the following criteria are met:", style_body))
items.append(Spacer(1, 1*mm))
fail_criteria = [
("Clinical failure", "Persistent or recurrent pain, swelling, warmth, or wound discharge despite antibiotic therapy"),
("Microbiological failure", "Positive cultures on repeat aspiration or culture of synovial fluid after completed antibiotic course"),
("Surgical failure", "Need for additional surgical intervention for infection (repeat debridement, spacer, or revision)"),
("Implant removal", "Explantation of any fixed or modular component due to uncontrolled infection"),
("Suppressive antibiotics", "Requirement for indefinite/long-term suppressive antibiotic therapy to control infection"),
("Infection-related mortality", "Death attributable directly to periprosthetic infection or its complications"),
]
fail_rows = [[Paragraph(f"<b>{t}</b>", style_small_bold), Paragraph(d, style_small)]
for t, d in fail_criteria]
fail_t = Table(fail_rows, colWidths=[4.5*cm, 11.5*cm])
fail_t.setStyle(TableStyle([
("ROWBACKGROUNDS", (0,0), (-1,-1), [GREY_LIGHT, WHITE]),
("GRID", (0,0), (-1,-1), 0.3, HexColor("#CCCCCC")),
("LEFTPADDING", (0,0), (-1,-1), 4*mm),
("TOPPADDING", (0,0), (-1,-1), 2.5*mm),
("BOTTOMPADDING", (0,0), (-1,-1), 2.5*mm),
("VALIGN", (0,0), (-1,-1), "TOP"),
("LINEAFTER", (0,0), (0,-1), 0.8, NAVY),
]))
items.append(fail_t)
items.append(Spacer(1, 2*mm))
items.append(Paragraph(
"<b>Timing of failure assessment:</b> Most DAIR failures occur within 12 months. "
"Formal failure assessment should be performed at 3, 6, and 12 months post-DAIR using "
"clinical examination, CRP/ESR, and joint aspiration if clinically indicated. "
"The MSIS Outcome Reporting Tool (Tiers 1–4) provides a standardised framework.",
style_body))
return items
# ── Section 5: Management Algorithm ──────────────────────────────────────────
def section_algorithm():
items = []
items.append(PageBreak())
items.append(ColorBanner("5 | MANAGEMENT ALGORITHM AFTER FAILED DAIR", NAVY, height=16*mm, font_size=12))
items.append(Spacer(1, 4*mm))
# Flow diagram via nested tables
def flow_box(text, color, text_color=WHITE, w=16*cm, font_size=9):
p = Paragraph(text, S("fb", fontName="Helvetica-Bold", fontSize=font_size,
textColor=text_color, alignment=TA_CENTER))
t = Table([[p]], colWidths=[w])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), color),
("BOX", (0,0), (-1,-1), 0.8, HexColor("#888888")),
("TOPPADDING", (0,0), (-1,-1), 3*mm),
("BOTTOMPADDING", (0,0), (-1,-1), 3*mm),
("LEFTPADDING", (0,0), (-1,-1), 5*mm),
("RIGHTPADDING", (0,0), (-1,-1), 5*mm),
]))
return t
def arrow():
return Paragraph("<b>▼</b>", S("arr", fontName="Helvetica-Bold", fontSize=14,
textColor=GREY, alignment=TA_CENTER))
items.append(flow_box("DAIR FAILURE CONFIRMED", NAVY))
items.append(arrow())
items.append(flow_box("STEP 1: REASSESS – Culture results, implant status, host factors, bone stock", TEAL))
items.append(Spacer(1, 3*mm))
# Three pathways side by side
path_a = Table([
[Paragraph("<b>PATH A</b>\nSecond DAIR", S("ph", fontName="Helvetica-Bold", fontSize=9,
textColor=WHITE, alignment=TA_CENTER))],
[Paragraph(
"<b>Select ONLY if ALL criteria met:</b>\n"
"• First DAIR within past 2-4 weeks\n"
"• Sensitive, non-Staph organism\n"
"• Modular components NOT exchanged at 1st DAIR\n"
"• No sinus tract\n"
"• No polymicrobial / resistant infection\n"
"• Specialist surgical team available\n"
"• No prior history of PJI\n\n"
"<b>Expected success: 54.5%</b>\n"
"(up to 83.3% if all criteria met)\n\n"
"ICM 2018: 85% against routine 2nd DAIR",
S("pa", fontName="Helvetica", fontSize=8, textColor=GREY, leading=12))],
], colWidths=[5.1*cm])
path_a.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), AMBER),
("BACKGROUND", (0,1), (-1,-1), AMBER_LIGHT),
("BOX", (0,0), (-1,-1), 0.8, HexColor("#AAAAAA")),
("TOPPADDING", (0,0), (-1,-1), 3*mm),
("BOTTOMPADDING", (0,0), (-1,-1), 3*mm),
("LEFTPADDING", (0,0), (-1,-1), 3*mm),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
path_b = Table([
[Paragraph("<b>PATH B</b>\nOne-Stage Revision", S("ph", fontName="Helvetica-Bold", fontSize=9,
textColor=WHITE, alignment=TA_CENTER))],
[Paragraph(
"<b>Select if ALL criteria met:</b>\n"
"• Known, sensitive organism\n"
"• Adequate bone stock\n"
"• Good soft tissue coverage\n"
"• Patient medically fit for surgery\n"
"• No resistant organisms\n"
"• Well-equipped centre with experience\n\n"
"<b>Expected success: ~76.2%</b>\n"
"after failed DAIR\n\n"
"No identified independent predictors\n"
"of failure in this cohort",
S("pb", fontName="Helvetica", fontSize=8, textColor=GREY, leading=12))],
], colWidths=[5.1*cm])
path_b.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), GREEN),
("BACKGROUND", (0,1), (-1,-1), GREEN_LIGHT),
("BOX", (0,0), (-1,-1), 0.8, HexColor("#AAAAAA")),
("TOPPADDING", (0,0), (-1,-1), 3*mm),
("BOTTOMPADDING", (0,0), (-1,-1), 3*mm),
("LEFTPADDING", (0,0), (-1,-1), 3*mm),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
path_c = Table([
[Paragraph("<b>PATH C</b>\nTwo-Stage Revision", S("ph", fontName="Helvetica-Bold", fontSize=9,
textColor=WHITE, alignment=TA_CENTER))],
[Paragraph(
"<b>Standard approach – use when:</b>\n"
"• Chronic / late infection\n"
"• Resistant or difficult organism (MRSA)\n"
"• Polymicrobial infection\n"
"• Unknown organism\n"
"• Biofilm-forming bacteria (Staph)\n"
"• Bone loss requiring reconstruction\n\n"
"<b>Expected success: ~79.3%</b>\n"
"after failed DAIR\n\n"
"Failure risk: polymicrobial\n"
"infection, prior revision\n"
"(Auñón et al. 2025)",
S("pc", fontName="Helvetica", fontSize=8, textColor=GREY, leading=12))],
], colWidths=[5.1*cm])
path_c.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), NAVY),
("BACKGROUND", (0,1), (-1,-1), GREY_LIGHT),
("BOX", (0,0), (-1,-1), 0.8, HexColor("#AAAAAA")),
("TOPPADDING", (0,0), (-1,-1), 3*mm),
("BOTTOMPADDING", (0,0), (-1,-1), 3*mm),
("LEFTPADDING", (0,0), (-1,-1), 3*mm),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
paths_row = Table([[path_a, path_b, path_c]], colWidths=[5.5*cm, 5.5*cm, 5.5*cm])
paths_row.setStyle(TableStyle([
("LEFTPADDING", (0,0), (-1,-1), 2*mm),
("RIGHTPADDING", (0,0), (-1,-1), 2*mm),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
items.append(paths_row)
items.append(Spacer(1, 2*mm))
items.append(arrow())
items.append(Spacer(1, 1*mm))
salvage_box = Table([
[Paragraph(
"<b>SALVAGE (when all revision options exhausted or patient unfit):</b> "
"Resection arthroplasty | Knee arthrodesis | Indefinite suppressive antibiotics | Amputation (rare)",
S("sv", fontName="Helvetica-Bold", fontSize=8.5, textColor=WHITE, alignment=TA_CENTER))],
], colWidths=[16*cm])
salvage_box.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), RED),
("TOPPADDING", (0,0), (-1,-1), 3*mm),
("BOTTOMPADDING", (0,0), (-1,-1), 3*mm),
]))
items.append(salvage_box)
return items
# ── Section 6: Two-Stage Revision Detail ─────────────────────────────────────
def section_two_stage():
items = []
items.append(Spacer(1, 4*mm))
items.append(ColorBanner("6 | TWO-STAGE REVISION – OPERATIVE DETAIL", NAVY))
items.append(Spacer(1, 3*mm))
stage_data = [
[Paragraph("<b>Stage 1 – Explantation</b>", style_white_bold),
Paragraph("<b>Interval Phase</b>", style_white_bold),
Paragraph("<b>Stage 2 – Reimplantation</b>", style_white_bold)],
[
Paragraph(
"• Complete removal of all prosthetic components\n"
"• Thorough debridement and pulse lavage\n"
"• Multiple intraop cultures (≥3 samples)\n"
"• Insert antibiotic-loaded cement spacer:\n"
" – Static spacer: simpler, limited mobility\n"
" – Articulating (PROSTALAC): maintains ROM\n"
"• Antibiotics: Vancomycin ± tobramycin in cement\n"
"• Wound closure; drain",
style_small),
Paragraph(
"• IV antibiotics guided by sensitivities:\n"
" – Typically 4-6 weeks IV, then oral\n"
" – Duration: 6-12 weeks total\n"
"• Monitor ESR, CRP to nadir\n"
"• Aspiration at 2-4 weeks off antibiotics\n"
" (confirm eradication before reimplant)\n"
"• Criteria to proceed:\n"
" – CRP normalised, ESR trending down\n"
" – Aspirate WBC <3000, PMN <80%\n"
" – Cultures negative",
style_small),
Paragraph(
"• Minimum 6-week interval (most centres 10-12 weeks)\n"
"• Frozen section intraop: <5 PMN/HPF confirms eradication\n"
"• Revision knee components with:\n"
" – Constrained condylar or hinge if ligament compromise\n"
" – Stems and augments for bone loss\n"
" – Antibiotic-loaded cement recommended\n"
"• Continue oral antibiotics 3 months post-reimplant\n"
"• Failure predictors: polymicrobial, prior revision",
style_small),
]
]
stage_t = Table(stage_data, colWidths=[5.3*cm, 5.3*cm, 5.4*cm])
stage_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), NAVY),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 8.5),
("BACKGROUND", (0,1), (-1,-1), GREY_LIGHT),
("GRID", (0,0), (-1,-1), 0.4, HexColor("#CCCCCC")),
("VALIGN", (0,0), (-1,-1), "TOP"),
("LEFTPADDING", (0,0), (-1,-1), 3.5*mm),
("TOPPADDING", (0,0), (-1,-1), 3*mm),
("BOTTOMPADDING", (0,0), (-1,-1), 3*mm),
]))
items.append(stage_t)
return items
# ── Section 7: Antibiotic Management ─────────────────────────────────────────
def section_antibiotics():
items = []
items.append(Spacer(1, 4*mm))
items.append(ColorBanner("7 | ANTIBIOTIC MANAGEMENT PRINCIPLES", TEAL))
items.append(Spacer(1, 3*mm))
abx_data = [
[Paragraph("<b>Organism</b>", style_white_bold),
Paragraph("<b>First-Line</b>", style_white_bold),
Paragraph("<b>Duration</b>", style_white_bold),
Paragraph("<b>Key Notes</b>", style_white_bold)],
["MSSA", "Flucloxacillin IV + oral Rifampicin", "3 months (TKA)", "Rifampicin NEVER as monotherapy; biofilm penetration"],
["MRSA", "Vancomycin IV + Rifampicin oral", "3-6 months", "Monitor vancomycin levels; linezolid alternative"],
["Streptococcus spp.", "Amoxicillin/Ampicillin IV → oral", "3 months", "Good bioavailability for oral step-down"],
["Gram-negative", "Beta-lactam based on sensitivity", "3 months", "Ciprofloxacin oral has good bone penetration"],
["Coagulase-neg Staph (CoNS)", "Oxacillin or Vancomycin + Rifampicin", "3-6 months", "Common after revision; rifampicin key"],
["Polymicrobial", "Broad spectrum → directed", "≥6 months", "Difficult to treat; poor prognosis with DAIR"],
["Culture-negative", "Empirical: Vanc + ciprofloxacin", "3-6 months", "Review local antibiogram; consider 2-stage"],
]
abx_t = Table(abx_data, colWidths=[3.5*cm, 4.5*cm, 2.5*cm, 5.5*cm])
abx_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), TEAL),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 8),
("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, TEAL_LIGHT]),
("GRID", (0,0), (-1,-1), 0.3, HexColor("#CCCCCC")),
("LEFTPADDING", (0,0), (-1,-1), 3*mm),
("TOPPADDING", (0,0), (-1,-1), 2.5*mm),
("BOTTOMPADDING", (0,0), (-1,-1), 2.5*mm),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
items.append(abx_t)
items.append(Spacer(1, 2*mm))
abx_alert = AlertBox([
Paragraph("<b>ANTIBIOTIC MISMATCH WARNING:</b>", style_amber_bold),
Paragraph(
"Antibiotic mismatch (empirical choice not matching culture sensitivity) "
"reduces DAIR success by 87% (OR 0.13, p=0.007; Veerman et al., Bone Joint J 2022). "
"Always obtain at least 3 intraoperative tissue cultures BEFORE administering antibiotics. "
"Hold empirical antibiotics intraoperatively if clinically feasible to maximise culture yield.",
style_small),
], accent_color=AMBER, bg_color=AMBER_LIGHT)
items.append(abx_alert)
items.append(Spacer(1, 2*mm))
items.append(Paragraph(
"<b>Rifampicin note:</b> Rifampicin is the only antibiotic with proven biofilm-penetrating activity "
"against staphylococci and is a cornerstone of DAIR success. Ongoing RCTs: "
"<b>RiCOTTA</b> (monotherapy vs combination) and <b>RIFAMAB</b> (rifabutin vs rifampicin) "
"will further define optimal rifampicin regimens.",
style_body))
return items
# ── Section 8: Repeat DAIR Decision ──────────────────────────────────────────
def section_repeat_dair():
items = []
items.append(Spacer(1, 4*mm))
items.append(ColorBanner("8 | REPEAT (SECOND) DAIR – DECISION CRITERIA", AMBER))
items.append(Spacer(1, 3*mm))
items.append(Paragraph(
"A second DAIR carries a <b>54.5% overall success rate</b>, rising to <b>83.3%</b> when patients "
"with all four high-risk factors are excluded (Auñón et al., Surg Infect 2025, PMID 39612194). "
"The 2018 ICM recorded 85% consensus <i>against</i> routine repeat DAIR.",
style_body))
items.append(Spacer(1, 2*mm))
criteria_data = [
[Paragraph("<b>FAVOUR Repeat DAIR</b>", S("fav", fontName="Helvetica-Bold", fontSize=9,
textColor=GREEN, alignment=TA_CENTER)),
Paragraph("<b>AGAINST Repeat DAIR</b>", S("aga", fontName="Helvetica-Bold", fontSize=9,
textColor=RED, alignment=TA_CENTER))],
[
Paragraph(
"\u2714 First DAIR <2-4 weeks ago\n"
"\u2714 Sensitive, mono-microbial organism\n"
"\u2714 Modular components not exchanged at 1st DAIR\n"
"\u2714 No sinus tract\n"
"\u2714 First DAIR performed by specialist team\n"
"\u2714 Patient medically fit\n"
"\u2714 Adequate soft tissue coverage\n"
"\u2714 Excluding all 4 risk factors → 83.3% success",
S("fc", fontName="Helvetica", fontSize=8.5, textColor=GREY, leading=13)),
Paragraph(
"\u2716 Polymicrobial or antibiotic-resistant organism\n"
"\u2716 Non-specialist team performed first DAIR\n"
"\u2716 Modular components not exchanged at 1st DAIR\n"
"\u2716 Sinus tract present\n"
"\u2716 Symptom duration >4 weeks\n"
"\u2716 Positive blood cultures (OR 12 for failure)\n"
"\u2716 Prior history of PJI at same joint\n"
"\u2716 Severe immunosuppression\n"
"\u2716 High CCI (>4)",
S("ac", fontName="Helvetica", fontSize=8.5, textColor=GREY, leading=13)),
]
]
crit_t = Table(criteria_data, colWidths=[8*cm, 8*cm])
crit_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (0,0), GREEN_LIGHT),
("BACKGROUND", (1,0), (1,0), RED_LIGHT),
("BOX", (0,0), (0,-1), 1, GREEN),
("BOX", (1,0), (1,-1), 1, RED),
("GRID", (0,0), (-1,-1), 0.3, HexColor("#CCCCCC")),
("LEFTPADDING", (0,0), (-1,-1), 4*mm),
("TOPPADDING", (0,0), (-1,-1), 3*mm),
("BOTTOMPADDING", (0,0), (-1,-1), 3*mm),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
items.append(crit_t)
items.append(Spacer(1, 2*mm))
items.append(Paragraph(
"Triple DAIR is generally not recommended (success 50-60%) and should be reserved for "
"exceptional circumstances in highly selected patients at specialised centres.",
style_note))
return items
# ── Section 9: Outcomes Comparison ───────────────────────────────────────────
def section_outcomes():
items = []
items.append(Spacer(1, 4*mm))
items.append(ColorBanner("9 | COMPARATIVE OUTCOMES AFTER FAILED DAIR", NAVY))
items.append(Spacer(1, 3*mm))
out_data = [
[Paragraph("<b>Strategy</b>", style_white_bold),
Paragraph("<b>Success Rate</b>", style_white_bold),
Paragraph("<b>Failure Predictors</b>", style_white_bold),
Paragraph("<b>Best Indication</b>", style_white_bold)],
["Second DAIR\n(selected patients)",
"54.5% overall\n83.3% (optimal cohort)",
"Polymicrobial, resistant, no modular exchange, non-specialist",
"Acute, sensitive organism, components not exchanged"],
["One-Stage Revision",
"~76.2%",
"No clear independent predictors identified",
"Known sensitive organism, good bone stock"],
["Two-Stage Revision\n(gold standard)",
"~79.3%",
"Polymicrobial infection, prior revision arthroplasty",
"Chronic, resistant, biofilm organisms, default choice"],
["Arthrodesis",
"Variable; high fusion rates",
"Bone loss, vascular compromise",
"Failed revision, poor soft tissue, resistant infection"],
["Suppressive Antibiotics\n(non-surgical)",
"Infection control only, not eradication",
"Non-compliance, resistance development",
"Unfit for surgery, patient preference, palliation"],
]
out_t = Table(out_data, colWidths=[3.5*cm, 3.5*cm, 4.5*cm, 4.5*cm])
out_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), NAVY),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 8),
("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, GREY_LIGHT]),
("GRID", (0,0), (-1,-1), 0.3, HexColor("#CCCCCC")),
("LEFTPADDING", (0,0), (-1,-1), 3*mm),
("TOPPADDING", (0,0), (-1,-1), 2.5*mm),
("BOTTOMPADDING", (0,0), (-1,-1), 2.5*mm),
("VALIGN", (0,0), (-1,-1), "TOP"),
("FONTNAME", (0,1), (0,-1), "Helvetica-Bold"),
]))
items.append(out_t)
return items
# ── Section 10: References ────────────────────────────────────────────────────
def section_references():
items = []
items.append(Spacer(1, 4*mm))
items.append(ColorBanner("10 | KEY REFERENCES", HexColor("#555555")))
items.append(Spacer(1, 3*mm))
refs = [
("PMID 40480337", "Abbaszadeh A et al. Efficacy of DAIR in Total Hip and Knee Arthroplasty: A Systematic Review and Meta-Analysis. J Arthroplasty 2026. [Overall failure rate 35.9%; TKA>THA]"),
("PMID 39612194", "Auñón Á et al. Outcomes of Subsequent PJI Revisions after Failed DAIR: Multicentric Study of 197 Patients. Surg Infect 2025. [2nd DAIR 54.5%; one-stage 76.2%; two-stage 79.3%]"),
("PMID 40746658", "Frear AJ et al. Type of Acute PJI May Not Affect Failure of DAIR after TKA. JBJI 2025. [Overall TKA DAIR failure 42%; 78.4% within year 1]"),
("PMID 41566067", "Pedemonte-Parramón G et al. Positive Blood Culture as Risk Factor for DAIR Failure in Hematogenous TKA Infection. J Orthop Traumatol 2026. [Positive BC: OR 12 for failure]"),
("PMID 39223364", "Salman LA et al. Single vs Multiple DAIR in Hip and Knee PJI: Systematic Review and Meta-Analysis. Eur J Orthop Surg 2024. [Double DAIR 70% vs single 67%, p=0.74]"),
("PMID 38797446", "Ashkenazi I et al. Perioperative Characteristics of Failed DAIR: Can We Predict Failure? J Arthroplasty 2024. [CCI, CRP, synovial WBC, PMN% predict failure; TKA OR 6.08]"),
("PMID 37714518", "Longo UG et al. DAIR for Early PJI of TKA and THA: A Systematic Review. J ISAKOS 2024. [Mean DAIR success 71%; range 55.5-90%]"),
("PMID 35360944", "Veerman K et al. DAIR after Revision Arthroplasty: Antibiotic Mismatch, Timing, Repeated DAIR. Bone Joint J 2022. [Antibiotic mismatch OR 0.13; repeat DAIR OR 0.37; >30 days OR 0.24]"),
("PMID 36998382", "Alrayes MM, Sukeik M. Two-Stage Revision in Periprosthetic Knee Joint Infections. World J Orthop 2023."),
("ICM 2018", "Proceedings of the International Consensus Meeting on Musculoskeletal Infection. Philadelphia, 2018."),
]
ref_rows = [[Paragraph(f"<b>{pmid}</b>", S("rid", fontName="Helvetica-Bold", fontSize=7.5,
textColor=TEAL)),
Paragraph(txt, style_small)] for pmid, txt in refs]
ref_t = Table(ref_rows, colWidths=[2.5*cm, 13.5*cm])
ref_t.setStyle(TableStyle([
("ROWBACKGROUNDS", (0,0), (-1,-1), [WHITE, GREY_LIGHT]),
("GRID", (0,0), (-1,-1), 0.3, HexColor("#CCCCCC")),
("LEFTPADDING", (0,0), (-1,-1), 3*mm),
("TOPPADDING", (0,0), (-1,-1), 2*mm),
("BOTTOMPADDING", (0,0), (-1,-1), 2*mm),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
items.append(ref_t)
return items
# ── Assemble document ─────────────────────────────────────────────────────────
def build_pdf():
doc = SimpleDocTemplate(
OUTPUT_PATH,
pagesize=A4,
topMargin=28*mm,
bottomMargin=18*mm,
leftMargin=15*mm,
rightMargin=15*mm,
)
story = []
story += cover_page()
story.append(PageBreak())
story += section_dair_overview()
story += section_failure_rates()
story += section_risk_factors()
story += section_defining_failure()
story += section_algorithm()
story += section_two_stage()
story += section_antibiotics()
story += section_repeat_dair()
story += section_outcomes()
story += section_references()
doc.build(story, onFirstPage=add_header_footer, onLaterPages=add_header_footer)
print(f"PDF created: {OUTPUT_PATH}")
if __name__ == "__main__":
build_pdf()
cd /tmp/workspace/failed-dair-guide && python generate_pdf.py 2>&1
ls -lh /tmp/workspace/failed-dair-guide/Failed_DAIR_TKA_Clinical_Guide.pdf
python3 -c " from pypdf import PdfReader r = PdfReader('/tmp/workspace/failed-dair-guide/Failed_DAIR_TKA_Clinical_Guide.pdf') print(f'Pages: {len(r.pages)}') for i, p in enumerate(r.pages): txt = p.extract_text() print(f' Page {i+1}: {len(txt)} chars | preview: {txt[:80].strip()}') "
Failed DAIR TKA Clinical Guide
PDF Document · PDF
| Section | Content |
|---|---|
| Cover page | Key stats snapshot (35.9% failure rate, OR 6× for TKA vs THA, etc.) |
| Section 1 | DAIR overview - indications table + mandatory 7-step surgical checklist |
| Section 2 | Failure rates by infection type with full evidence table |
| Section 3 | Risk factor grid across 3 domains (patient, microbiological, surgical/lab) + high-risk alert box |
| Section 4 | Definition of DAIR failure - 6 criteria with the MSIS reporting tool |
| Section 5 | Full decision algorithm - flow diagram: 2nd DAIR vs one-stage vs two-stage vs salvage |
| Section 6 | Two-stage revision operative detail (Stage 1 / Interval / Stage 2) |
| Section 7 | Antibiotic guide by organism with rifampicin warning box |
| Section 8 | Repeat DAIR decision: favour vs against checklist |
| Section 9 | Comparative outcomes table for all strategies |
| Section 10 | 10 key references with PMIDs |
Can you give me a detailed PDF of this
ls /tmp/workspace/failed-dair-guide/ && echo "---" && ls /tmp/skills/pdf/
periprosthetic joint infection total knee arthroplasty pathophysiology biofilm diagnosis
two-stage revision knee arthroplasty spacer reimplantation outcomes
PMID: 41511894
PMID: 39780262
PMID: 36302451
periprosthetic joint infection ICM criteria diagnosis serology synovial fluid 2018
Pdf please
Generate a comprehensive PDF on failed DAIR in total knee arthroplasty