Seminar for Developmental dysplasia of hip
developmental dysplasia of the hip DDH infant diagnosis treatment
Seminar for Developmental dysplasia of hip
How would you like the DDH seminar delivered? · Who is the seminar for / what depth level?
"developmental dysplasia of the hip" screening
mkdir -p /tmp/workspace/ddh-seminar/assets /tmp/workspace/ddh-seminar/output && ls -la /tmp/workspace/
cd /tmp/workspace/ddh-seminar && python /tmp/skills/shared/scripts/fetch_images.py \ "https://cdn.orris.care/cdss_images/d66be1f007b5089e29f60d819abb12ca3829fa40e6c1c71835255a85730b59c6.png" \ "https://cdn.orris.care/cdss_images/7b3d09f4bccc6b1797f77eb777180f3c529fdc05b0d8845d5c7fa72177902910.png" \ "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_1469f406d64fa689733c65ed57d9980682f85363cee4b2fcf5cf91544d720a44.jpg" \ "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_21780184298b79fb70e9e1c27695fb73536bfea8764a67a070ebbfd37e167bdc.jpg" \ "https://cdn.orris.care/cdss_images/BAILY_1760540965019_16082eb7-4dde-4e56-a109-a594195281cb.png" \ "https://cdn.orris.care/cdss_images/2a36ee41f5a819f6c82fd70bcb8a489f5d1317af7a55dd96444e3daf5597bc3a.png" \ "https://cdn.orris.care/cdss_images/7cfabd959f3e41bc311ec507d55593ae84e8035972f817cffa8ba009c6411071.png" \ "https://cdn.orris.care/cdss_images/83867bb38d763ca50487d8acf7b3755eb6586c103fb145c9e207bb8e682e08bf.png" \ > fetch_out.json 2>fetch_err.log; echo "EXIT:$?"; wc -c fetch_out.json
cd /tmp/workspace/ddh-seminar && python3 << 'EOF' import json, base64 with open("fetch_out.json") as f: data = json.load(f) names = ["ortolani", "pavlik", "graf_angles", "graf_dysplastic_normal", "acetabular_lcea", "algorithm", "ap_pelvis_ddh", "bilateral_dislocation"] for i, item in enumerate(data): if item.get("base64"): b64 = item["base64"] # format "image/png;base64,XXXX" header, encoded = b64.split(",", 1) ext = "jpg" if "jpeg" in header else "png" raw = base64.b64decode(encoded) fname = f"assets/{names[i]}.{ext}" with open(fname, "wb") as out: out.write(raw) print(fname, len(raw)) else: print("FAILED", item.get("url"), item.get("error")) EOF ls -la assets/
~/ddh-seminar/build_ppt.py
#!/usr/bin/env python3
"""
Seminar: Developmental Dysplasia of the Hip (DDH)
Audience: Orthopaedic Residents / PG
"""
from pptx import Presentation
from pptx.util import Inches, Pt, Emu
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
from pptx.enum.shapes import MSO_SHAPE
from pptx.oxml.ns import qn
import copy
# ---------- Theme ----------
NAVY = RGBColor(0x0B, 0x2B, 0x4A)
BLUE = RGBColor(0x1F, 0x5C, 0x99)
LBLUE = RGBColor(0xE8, 0xF0, 0xF9)
ACCENT = RGBColor(0xC8, 0x6A, 0x1F) # burnt orange accent
WHITE = RGBColor(0xFF, 0xFF, 0xFF)
DARK = RGBColor(0x2A, 0x2A, 0x2A)
GREY = RGBColor(0x6E, 0x6E, 0x6E)
GREEN = RGBColor(0x2E, 0x7D, 0x32)
RED = RGBColor(0xB0, 0x2A, 0x2A)
FONT = "Calibri"
prs = Presentation()
prs.slide_width = Inches(13.333)
prs.slide_height = Inches(7.5)
BLANK = prs.slide_layouts[6]
SW, SH = prs.slide_width, prs.slide_height
def add_slide():
return prs.slides.add_slide(BLANK)
def bg(slide, color=WHITE):
slide.background.fill.solid()
slide.background.fill.fore_color.rgb = color
def rect(slide, x, y, w, h, color, line=False):
shp = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, x, y, w, h)
shp.fill.solid()
shp.fill.fore_color.rgb = color
if line:
shp.line.color.rgb = color
shp.line.width = Pt(0.5)
else:
shp.line.fill.background()
shp.shadow.inherit = False
return shp
def textbox(slide, x, y, w, h, text, size=18, color=DARK, bold=False,
align=PP_ALIGN.LEFT, italic=False, font=FONT, anchor=None, wrap=True):
tb = slide.shapes.add_textbox(x, y, w, h)
tf = tb.text_frame
tf.word_wrap = wrap
if anchor:
tf.vertical_anchor = anchor
tf.margin_left = 0
tf.margin_right = 0
tf.margin_top = 0
tf.margin_bottom = 0
lines = text.split("\n")
for i, line in enumerate(lines):
p = tf.paragraphs[0] if i == 0 else tf.add_paragraph()
p.text = line
p.alignment = align
for r in p.runs:
r.font.size = Pt(size)
r.font.color.rgb = color
r.font.bold = bold
r.font.italic = italic
r.font.name = font
return tb
def bullets(slide, x, y, w, h, items, size=16, color=DARK, font=FONT,
space_after=8, bullet_color=None, line_spacing=1.05):
"""items: list of (level, text, bold) or plain str"""
tb = slide.shapes.add_textbox(x, y, w, h)
tf = tb.text_frame
tf.word_wrap = True
tf.margin_left = 0
tf.margin_right = 0
tf.margin_top = 0
tf.margin_bottom = 0
first = True
for item in items:
if isinstance(item, tuple):
level, txt, b = (item + (False,))[:3] if len(item) == 2 else item
else:
level, txt, b = 0, item, False
p = tf.paragraphs[0] if first else tf.add_paragraph()
first = False
p.level = level
marker = "\u25AA " if level == 0 else "\u2013 "
p.text = marker + txt
p.space_after = Pt(space_after)
p.line_spacing = line_spacing
for r in p.runs:
r.font.size = Pt(size - level * 1.5)
r.font.color.rgb = bullet_color or color
r.font.bold = b
r.font.name = font
return tb
def header(slide, title, kicker=None, num=None, total=None):
bg(slide, WHITE)
rect(slide, 0, 0, SW, Inches(1.15), NAVY)
rect(slide, 0, Inches(1.15), SW, Pt(3), ACCENT)
if kicker:
textbox(slide, Inches(0.5), Inches(0.12), Inches(8), Inches(0.3),
kicker.upper(), size=12, color=RGBColor(0xAF,0xC6,0xE6), bold=True)
textbox(slide, Inches(0.5), Inches(0.42), Inches(11.5), Inches(0.65),
title, size=28, color=WHITE, bold=True)
if num:
textbox(slide, SW - Inches(1.1), Inches(0.35), Inches(0.7), Inches(0.4),
f"{num}", size=14, color=RGBColor(0xAF,0xC6,0xE6), bold=True, align=PP_ALIGN.RIGHT)
def footer(slide, num):
textbox(slide, Inches(0.5), SH - Inches(0.4), Inches(6), Inches(0.3),
"Developmental Dysplasia of the Hip \u2014 Orthopaedic Seminar", size=10, color=GREY)
textbox(slide, SW - Inches(1.2), SH - Inches(0.4), Inches(0.7), Inches(0.3),
str(num), size=10, color=GREY, align=PP_ALIGN.RIGHT)
def pic(slide, path, x, y, w=None, h=None):
return slide.shapes.add_picture(path, x, y, width=w, height=h)
def add_pic_framed(slide, path, x, y, w, h, caption=None, cap_size=11):
slide.shapes.add_picture(path, x, y, width=w, height=h)
frame = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, x, y, w, h)
frame.fill.background()
frame.line.color.rgb = RGBColor(0xB0,0xB0,0xB0)
frame.line.width = Pt(1)
frame.shadow.inherit = False
if caption:
textbox(slide, x, y+h+Inches(0.03), w, Inches(0.35), caption, size=11,
color=GREY, italic=True, align=PP_ALIGN.CENTER)
ASSET = "/tmp/workspace/ddh-seminar/assets/"
slide_num = 0
def next_num():
global slide_num
slide_num += 1
return slide_num
# ============================================================
# SLIDE 1 -- TITLE
# ============================================================
s = add_slide()
bg(s, NAVY)
rect(s, 0, Inches(5.6), SW, Inches(1.9), BLUE)
rect(s, 0, Inches(5.55), SW, Pt(4), ACCENT)
textbox(s, Inches(0.9), Inches(1.7), Inches(11.5), Inches(0.5),
"ORTHOPAEDIC SEMINAR", size=20, color=RGBColor(0xAF,0xC6,0xE6), bold=True)
textbox(s, Inches(0.9), Inches(2.25), Inches(11.5), Inches(1.6),
"Developmental Dysplasia\nof the Hip (DDH)", size=48, color=WHITE, bold=True)
textbox(s, Inches(0.9), Inches(4.15), Inches(11), Inches(0.8),
"Etiology, Screening, Diagnosis, Classification, and Management\nAcross the Growing Child", size=18, color=RGBColor(0xD8,0xE4,0xF2), italic=True)
textbox(s, Inches(0.9), Inches(6.0), Inches(10), Inches(0.5),
"For Orthopaedic Residents / Postgraduates", size=16, color=WHITE, bold=True)
textbox(s, Inches(0.9), Inches(6.5), Inches(10), Inches(0.5),
"Sources: Campbell's Operative Orthopaedics 15e \u2022 Bailey & Love's Short Practice of Surgery 28e \u2022 Miller's Review of Orthopaedics 9e \u2022 Schwartz's Principles of Surgery 11e",
size=11, color=RGBColor(0xC9,0xD6,0xE8))
# ============================================================
# SLIDE 2 -- OUTLINE
# ============================================================
s = add_slide(); header(s, "Seminar Outline", "Roadmap", next_num())
left_items = [
"Definition & Terminology",
"Epidemiology & Incidence",
"Etiology & Risk Factors",
"Pathoanatomy",
"Screening Guidelines (AAOS)",
"Clinical Examination by Age",
]
right_items = [
"Imaging: Ultrasound (Graf) & Radiography",
"Classification Systems",
"Management by Age Group",
"Pelvic & Femoral Osteotomies",
"Complications",
"Late-Presenting DDH, Outcomes & Recent Evidence",
]
rect(s, Inches(0.6), Inches(1.5), Inches(5.9), Inches(5.3), LBLUE)
rect(s, Inches(6.8), Inches(1.5), Inches(5.9), Inches(5.3), LBLUE)
bullets(s, Inches(0.95), Inches(1.8), Inches(5.3), Inches(4.8),
[(0, t, False) for t in left_items], size=18, space_after=22)
bullets(s, Inches(7.15), Inches(1.8), Inches(5.3), Inches(4.8),
[(0, t, False) for t in right_items], size=18, space_after=22)
footer(s, slide_num)
# ============================================================
# SLIDE 3 -- DEFINITION
# ============================================================
s = add_slide(); header(s, "Definition & Terminology", "Introduction", next_num())
bullets(s, Inches(0.6), Inches(1.5), Inches(7.6), Inches(5.2), [
(0, "DDH describes a spectrum of hip abnormalities ranging from a stable but shallow (dysplastic) acetabulum, to a subluxatable/dislocatable hip, to a frank irreducible dislocation of the femoral head from the true acetabulum.", False),
(0, "In a newborn, the femoral head can often be manipulated into and out of the true acetabulum (reducible).", False),
(0, "In an older child, the femoral head often remains dislocated, with secondary adaptive changes in both femoral head and acetabulum.", False),
(0, "\u201CDevelopmental\u201D replaced the older term \u201Ccongenital\u201D dislocation of the hip \u2014 recognizing that a neonatally normal hip can still dislocate later (Westin et al.).", True),
(0, "Teratologic dislocation: occurs in utero, often associated with neuromuscular disorders (e.g., arthrogryposis, myelomeningocele) \u2014 fixed, resistant to standard treatment.", False),
], size=17, space_after=16)
rect(s, Inches(8.5), Inches(1.6), Inches(4.2), Inches(1.7), RGBColor(0xFF,0xF3,0xE0))
textbox(s, Inches(8.7), Inches(1.75), Inches(3.8), Inches(1.4),
"Terminology spectrum:\nDysplasia \u2192 Subluxation \u2192 Dislocation\n(Barlow-positive \u2192 Ortolani-positive/negative)",
size=14, color=ACCENT, bold=True)
add_pic_framed(s, ASSET+"ortolani.jpg", Inches(8.5), Inches(3.5), Inches(4.2), Inches(3.15),
caption="Ortolani screening maneuver in a neonate")
footer(s, slide_num)
# ============================================================
# SLIDE 4 -- EPIDEMIOLOGY
# ============================================================
s = add_slide(); header(s, "Epidemiology & Incidence", "Background", next_num())
bullets(s, Inches(0.6), Inches(1.5), Inches(6.0), Inches(5.2), [
(0, "True DDH incidence: ~1 per 1000 live births (needing treatment).", True),
(0, "Detected by physical exam (pediatricians): 8.6 / 1000.", False),
(0, "Detected by orthopaedic screening exam: 11.5 / 1000.", False),
(0, "Detected by universal ultrasound: up to 25 / 1000 (many resolve spontaneously).", False),
(0, "Neonatal instability (any hip laxity): ~20 / 1000; true dislocation: ~2 / 1000 \u2014 many stabilize spontaneously by 6 weeks.", False),
(0, "Left hip > right hip; bilateral > right-hip-alone.", False),
(0, "UK incidence approaches 1 in 400 live births; USA 3\u20134 per 1000.", False),
], size=16, space_after=14)
rect(s, Inches(6.9), Inches(1.5), Inches(5.8), Inches(5.2), LBLUE)
textbox(s, Inches(7.2), Inches(1.65), Inches(5.2), Inches(0.4), "Key Risk Factor Statistics", size=16, bold=True, color=NAVY)
bullets(s, Inches(7.2), Inches(2.15), Inches(5.3), Inches(4.4), [
(0, "Female : Male \u2248 5 : 1 (hormonally mediated ligamentous laxity)", True),
(0, "Breech presentation: 3\u20134% of deliveries; female + breech \u2192 DDH in 2.8% of births", False),
(0, "First-born children at higher risk (tight primigravid uterus)", False),
(0, "Positive family history \u2192 risk rises to ~10%", False),
(0, "Oligohydramnios (restricted fetal movement)", False),
(0, "Associated: congenital muscular torticollis (~8% co-existence), metatarsus adductus, talipes calcaneovalgus", False),
(0, "Cultural practices: swaddling \u2191 risk; carrying baby astride hip (flexed-abducted) \u2193 risk", False),
(0, "More common in white than Black children; high in Navajo, low in Chinese populations", False),
], size=15, space_after=12)
footer(s, slide_num)
# ============================================================
# SLIDE 5 -- ETIOLOGY / PATHOANATOMY
# ============================================================
s = add_slide(); header(s, "Etiology & Pathoanatomy", "Mechanism", next_num())
textbox(s, Inches(0.6), Inches(1.4), Inches(6.0), Inches(0.4), "Multifactorial Etiology", size=18, bold=True, color=NAVY)
bullets(s, Inches(0.6), Inches(1.85), Inches(6.0), Inches(2.6), [
(0, "Mechanical: breech position, oligohydramnios, primigravida, large fetus \u2014 all limit intrauterine movement.", False),
(0, "Physiological/Hormonal: maternal relaxin/estrogen crosses placenta \u2192 transient ligamentous laxity.", False),
(0, "Genetic: positive family history, ethnic variation \u2014 likely polygenic, generalized joint laxity trait.", False),
(0, "Postnatal/Environmental: swaddling with hips extended and adducted worsens instability.", False),
], size=15, space_after=10)
textbox(s, Inches(0.6), Inches(4.6), Inches(6.0), Inches(0.4), "Blocks to Reduction (older/untreated hip)", size=18, bold=True, color=NAVY)
bullets(s, Inches(0.6), Inches(5.05), Inches(6.0), Inches(1.8), [
(0, "Hourglass-constricted capsule; interposed iliopsoas tendon", False),
(0, "Hypertrophied, inverted limbus/labrum", False),
(0, "Hypertrophied & elongated ligamentum teres", False),
(0, "Contracted transverse acetabular ligament; excess fibrofatty pulvinar", False),
], size=14, space_after=6)
add_pic_framed(s, ASSET+"ap_pelvis_ddh.jpg", Inches(7.0), Inches(1.5), Inches(5.6), Inches(5.35),
caption="AP pelvis: acetabular dysplasia with subluxation, left hip (age 7 yrs)")
footer(s, slide_num)
# ============================================================
# SLIDE 6 -- SCREENING GUIDELINES
# ============================================================
s = add_slide(); header(s, "Screening Guidelines", "AAOS Clinical Practice Guideline", next_num())
rect(s, Inches(0.6), Inches(1.45), Inches(12.1), Inches(5.35), LBLUE)
bullets(s, Inches(0.95), Inches(1.7), Inches(11.4), Inches(5.0), [
(0, "Moderate evidence AGAINST universal ultrasound screening of all newborns.", True),
(0, "Moderate evidence FOR imaging before 6 months in infants with risk factors: breech presentation, family history, or history of clinical instability.", True),
(0, "Limited evidence: consider ultrasound in infants <6 weeks with a positive instability exam, to guide decision to brace.", False),
(0, "Limited evidence: use AP pelvic radiograph instead of ultrasound from 4 months of age onward.", False),
(0, "Limited evidence: re-examine infants with a previously \u2018normal\u2019 exam at subsequent visits before 6 months.", False),
(0, "Limited evidence: serial physical exams + periodic imaging (US or X-ray by age) during management of unstable hips.", False),
(0, "Clinical exam <6 weeks of age should DRIVE treatment decisions \u2014 not ultrasound alone (early ligamentous laxity causes false positives).", True),
], size=17, space_after=18)
footer(s, slide_num)
# ============================================================
# SLIDE 7 -- CLINICAL EXAM: NEONATE
# ============================================================
s = add_slide(); header(s, "Clinical Examination \u2014 Neonate (0\u20136 wks)", "Physical Exam", next_num())
bullets(s, Inches(0.6), Inches(1.5), Inches(7.4), Inches(5.2), [
(0, "Examine one hip at a time; infant calm, relaxed, pacified.", False),
(0, "Barlow test (provocative): hip flexed 90\u00b0, adducted; posterior/axial pressure applied \u2014 detects a dislocatable/subluxatable hip.", True),
(0, "Ortolani test (reduction): flexed hip gently abducted with anteromedial pressure on greater trochanter \u2014 palpable \u2018clunk\u2019 as femoral head reduces.", True),
(0, "A palpable (not audible) clunk is diagnostic; hip clicks alone are nonspecific.", False),
(0, "Summary Box \u2014 Sequential questions:", True),
(1, "Is the hip dislocated?", False),
(1, "If so \u2014 reducible (Ortolani +) or not (Ortolani \u2212)?", False),
(1, "If not dislocated \u2014 is it dislocatable/subluxable (Barlow +)?", False),
(1, "If clinically normal \u2014 do risk factors still warrant US/X-ray?", False),
], size=16, space_after=10)
add_pic_framed(s, ASSET+"ortolani.jpg", Inches(8.3), Inches(1.55), Inches(4.4), Inches(3.3),
caption="Ortolani maneuver for routine newborn screening")
rect(s, Inches(8.3), Inches(5.15), Inches(4.4), Inches(1.6), RGBColor(0xFF,0xF3,0xE0))
textbox(s, Inches(8.5), Inches(5.3), Inches(4.0), Inches(1.3),
"Caution: Ortolani/Barlow become less reliable after ~3 months as soft tissues contract and reduction becomes fixed.",
size=13, color=ACCENT, italic=True)
footer(s, slide_num)
# ============================================================
# SLIDE 8 -- CLINICAL EXAM: OLDER INFANT / CHILD
# ============================================================
s = add_slide(); header(s, "Clinical Examination \u2014 Older Infant, Child, Adolescent", "Physical Exam", next_num())
bullets(s, Inches(0.6), Inches(1.5), Inches(11.9), Inches(2.6), [
(0, "6\u201318 months (crawling age): limited hip abduction (adductor contracture) \u2014 most reliable sign; asymmetric skin folds (unreliable alone); positive Galeazzi sign (apparent femoral shortening, knees at unequal heights in flexion).", False),
(0, "Walking child: Trendelenburg gait/sign (unilateral); waddling gait + lumbar lordosis (bilateral); unilateral toe-walking on affected side.", False),
(0, "Toddler/child: extra thigh crease, subtle limp \u2014 easily missed in an unsteady toddler.", False),
(0, "Adolescent: exercise-induced groin/hip pain, sometimes referred to the knee; hips often dysplastic/subluxated rather than frankly dislocated.", False),
], size=16, space_after=10)
rect(s, Inches(0.6), Inches(4.35), Inches(11.9), Inches(2.5), LBLUE)
textbox(s, Inches(0.9), Inches(4.5), Inches(6), Inches(0.4), "Associated musculoskeletal findings", size=16, bold=True, color=NAVY)
bullets(s, Inches(0.9), Inches(4.95), Inches(11.2), Inches(1.8), [
(0, "Congenital muscular torticollis, metatarsus adductus, talipes calcaneovalgus, oligohydramnios sequence, clubfoot \u2014 all raise index of suspicion for DDH and warrant hip screening.", False),
], size=16, space_after=6)
footer(s, slide_num)
# ============================================================
# SLIDE 9 -- IMAGING: ULTRASOUND
# ============================================================
s = add_slide(); header(s, "Imaging \u2014 Ultrasound (Graf Method)", "Diagnostic Workup", next_num())
bullets(s, Inches(0.6), Inches(1.5), Inches(6.4), Inches(5.2), [
(0, "Modality of choice from birth to ~4\u20136 months (before femoral ossific nucleus appears).", True),
(0, "Coronal view with high-frequency linear probe; infant lateral, hip flexed/abducted; dynamic stress views assess translation.", False),
(0, "\u03B1 angle: bony acetabular roof inclination (steepness of acetabular roof) \u2014 stability indicator.", True),
(0, "\u03B2 angle: cartilaginous roof coverage of femoral head.", True),
(0, "Graf classification (simplified):", True),
(1, "Type I \u2014 mature, normal (\u03B1 \u2265 60\u00b0)", False),
(1, "Type IIa \u2014 physiologically immature (<12 wks, \u03B1 50\u201359\u00b0)", False),
(1, "Type IIb \u2014 delayed ossification (>12 wks, same angles = pathologic)", False),
(1, "Type IIc \u2014 critically dysplastic (\u03B1 43\u201349\u00b0)", False),
(1, "Type D \u2014 decentering hip", False),
(1, "Type III/IV \u2014 subluxated / dislocated hip", False),
(0, "Highly observer-dependent \u2014 overdiagnosis is a recognized pitfall; before 6 weeks, treat based on exam, not US alone.", True),
], size=14.5, space_after=8)
add_pic_framed(s, ASSET+"graf_angles.jpg", Inches(7.2), Inches(1.55), Inches(5.5), Inches(2.85),
caption="\u03B1 and \u03B2 angle measurement (Graf method) and femoral head coverage (d/D \u00d7 100)")
add_pic_framed(s, ASSET+"graf_dysplastic_normal.jpg", Inches(7.2), Inches(4.6), Inches(5.5), Inches(2.3),
caption="Dysplastic (left) vs. normal (right) infant hip on ultrasound")
footer(s, slide_num)
# ============================================================
# SLIDE 10 -- IMAGING: RADIOGRAPHY
# ============================================================
s = add_slide(); header(s, "Imaging \u2014 Radiography", "Diagnostic Workup", next_num())
bullets(s, Inches(0.6), Inches(1.5), Inches(6.5), Inches(5.3), [
(0, "Becomes reliable once capital femoral epiphysis ossifies (~4\u20136 months) \u2014 AP pelvis is standard.", True),
(0, "Hilgenreiner line: horizontal line through both triradiate cartilages.", False),
(0, "Perkins line: vertical line through lateral edge of acetabulum, perpendicular to Hilgenreiner line.", False),
(0, "Normal metaphyseal beak lies in the inner-lower quadrant formed by these two lines.", False),
(0, "Shenton line: smooth arc from medial femoral neck to superior obturator foramen \u2014 disrupted in dislocation.", False),
(0, "Acetabular index: angle between Hilgenreiner line and acetabular roof.", True),
(1, "Normal \u2264 30\u00b0 at birth; <25\u00b0 by 6 months; \u2191 index = dysplasia", False),
(0, "Center-edge angle (CEA / Wiberg angle): femoral head coverage in older children/adults \u2014 normal >25\u00b0; <20\u00b0 = dysplastic.", True),
(0, "International Hip Dysplasia Institute (IHDI) grading (I\u2013IV): refines femoral head position relative to Hilgenreiner-Perkins quadrants \u2014 excellent inter/intra-rater reliability.", False),
(0, "CT / MRI: assess reduction quality after closed/open reduction (arthrogram alternative), 3-D pelvic anatomy for osteotomy planning.", False),
], size=14.5, space_after=8)
add_pic_framed(s, ASSET+"acetabular_lcea.jpg", Inches(7.4), Inches(1.6), Inches(5.3), Inches(3.6),
caption="AP pelvis: acetabular index (left hip) and lateral center-edge angle, LCEA = 21\u00b0 (right hip)")
footer(s, slide_num)
prs.save("/tmp/workspace/ddh-seminar/output/DDH_Seminar.pptx")
print("Part 1 saved. Slides so far:", len(prs.slides.__iter__.__self__._sldIdLst))
EOF_MARK = True
~/ddh-seminar/build_ppt.py
Successfully replaced 1 instance(s) of the string in '/tmp/workspace/ddh-seminar/build_ppt.py'
~/ddh-seminar/build_ppt.py
Successfully replaced 1 instance(s) of the string in '/tmp/workspace/ddh-seminar/build_ppt.py'
cd /tmp/workspace/ddh-seminar && python3 build_ppt.py
cd /tmp/workspace/ddh-seminar/output && ls -la && python3 -c " from pptx import Presentation prs = Presentation('DDH_Seminar.pptx') print('Slides:', len(prs.slides)) for i, slide in enumerate(prs.slides, 1): texts = [sh.text_frame.text[:40] for sh in slide.shapes if sh.has_text_frame and sh.text_frame.text.strip()] print(i, '-', texts[0] if texts else '(no text)') "
DDH Seminar
Presentation · PPTX