Analyse this report
| Parameter | Finding |
|---|---|
| Fetal Number | Single Live Fetus |
| Presentation | Cephalic (head-down - normal) |
| Fetal Heart Rate | 137 BPM - Regular (normal range: 120-160 BPM) |
| Mean Gestational Age (MGA) | 21 weeks, 5 days |
| Estimated Fetal Weight | ~451 grams |
| Estimated Due Date (EDD) | 18 November 2024 |
| Amniotic Fluid Index (AFI) | 11 - Adequate (normal: 8-18 cm) |
| Cervical Length | 39 mm - Reassuring (normal >25 mm; low preterm risk) |
| Internal Os | Closed (normal) |
| Placenta | Posterior, Grade I, Upper segment (normal, safe position) |
Single live fetus of Mean Gestational Age 21 weeks 5 days, Cephalic presentation, with NO GROSS ANOMALY seen at the time of examination.
Comment and compare fetal weight
normal fetal weight 21 weeks 5 days grams percentile chart Hadlock
average fetal weight 21 weeks gestation grams percentile WHO Hadlock reference
| Percentile | Expected Weight (g) |
|---|---|
| 2.5th (very small) | 314 g |
| 10th | 345 g |
| 25th | 370 g |
| 50th (average) | 398 g |
| 75th | 428 g |
| 90th | 458 g |
| 97.5th (large) | 497 g |
| Week | 50th percentile |
|---|---|
| 21 weeks | 398 g |
| 21w 5d (this baby) | ~461 g (estimated 50th at this exact age) |
| 22 weeks | 476 g |
| Assessment | Detail |
|---|---|
| Weight | 451 g |
| Gestational age at scan | 21 weeks 5 days |
| Expected average for this age | ~460 g |
| Percentile estimate | ~45th-50th percentile |
| Classification | Appropriate for Gestational Age (AGA) |
| Concern for IUGR? | No - weight is within normal range |
| Concern for macrosomia? | No - weight is not elevated |
Generate a fetal weight chart comparison PDF for reference
ls /home/daytona/workspace/ 2>/dev/null && mkdir -p /home/daytona/workspace/feta…
~/fetal-weight-report/generate_chart.py
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.patches import FancyBboxPatch
import numpy as np
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import cm
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, Image as RLImage, HRFlowable
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_RIGHT
from reportlab.pdfgen import canvas
from reportlab.lib.utils import ImageReader
import io
import os
# ── Hadlock/WHO Percentile Data (14–40 weeks) ──────────────────────────────
weeks = list(range(14, 41))
p3 = [54,68,86,108,134,165,202,244,293,349,414,487,568,658,756,863,978,1100,1228,1362,1500,1641,1783,1923,2059,2189,2308]
p10 = [63,79,99,124,153,188,230,278,332,395,467,547,636,735,844,960,1084,1214,1349,1489,1631,1775,1917,2057,2191,2316,2430]
p25 = [74,93,117,146,181,222,270,326,389,460,541,631,730,839,956,1082,1214,1352,1495,1641,1787,1932,2074,2212,2343,2466,2579]
p50 = [90,114,143,179,222,272,330,398,476,565,665,778,902,1039,1189,1350,1523,1706,1898,2097,2301,2508,2714,2915,3108,3289,3456]
p75 = [110,139,174,217,269,330,400,481,573,678,795,926,1070,1229,1401,1586,1783,1990,2205,2426,2649,2872,3091,3303,3504,3691,3861]
p90 = [130,164,205,256,316,387,469,563,671,793,930,1082,1250,1433,1631,1843,2067,2302,2543,2789,3036,3280,3518,3745,3959,4156,4333]
p97 = [152,192,240,298,369,451,546,654,776,913,1066,1236,1421,1622,1838,2068,2311,2562,2819,3079,3337,3592,3839,4073,4291,4489,4663]
patient_week = 21.71 # 21w 5d
patient_weight = 451
# ── Create the chart ────────────────────────────────────────────────────────
fig, ax = plt.subplots(figsize=(13, 8))
fig.patch.set_facecolor('#FAFAFA')
ax.set_facecolor('#F8F9FC')
# Shaded zones
ax.fill_between(weeks, p3, p10, color='#E74C3C', alpha=0.18, label='3rd–10th %ile (SGA zone)')
ax.fill_between(weeks, p10, p25, color='#E67E22', alpha=0.16, label='10th–25th %ile')
ax.fill_between(weeks, p25, p75, color='#2ECC71', alpha=0.18, label='25th–75th %ile (Normal)')
ax.fill_between(weeks, p75, p90, color='#E67E22', alpha=0.16, label='75th–90th %ile')
ax.fill_between(weeks, p90, p97, color='#E74C3C', alpha=0.18, label='90th–97th %ile (LGA zone)')
# Percentile lines
line_styles = [
(p3, '#C0392B', '--', 0.9, '3rd'),
(p10, '#E67E22', '--', 1.1, '10th'),
(p25, '#27AE60', '-.', 1.1, '25th'),
(p50, '#1A5276', '-', 2.0, '50th'),
(p75, '#27AE60', '-.', 1.1, '75th'),
(p90, '#E67E22', '--', 1.1, '90th'),
(p97, '#C0392B', '--', 0.9, '97th'),
]
for data, col, ls, lw, label in line_styles:
ax.plot(weeks, data, color=col, linestyle=ls, linewidth=lw, zorder=3)
ax.annotate(label, xy=(40, data[-1]), xytext=(40.15, data[-1]),
fontsize=7.5, color=col, va='center', fontweight='bold')
# Patient point
ax.scatter([patient_week], [patient_weight], color='#8E44AD', s=220, zorder=10,
edgecolors='white', linewidths=2, label=f'This Patient: {patient_weight}g @ 21w5d')
ax.annotate(f' Pooja\n 451g\n 21w5d\n ~47th %ile',
xy=(patient_week, patient_weight),
xytext=(patient_week + 0.6, patient_weight + 280),
fontsize=9, color='#8E44AD', fontweight='bold',
arrowprops=dict(arrowstyle='->', color='#8E44AD', lw=1.5),
bbox=dict(boxstyle='round,pad=0.4', facecolor='#F5EEF8', edgecolor='#8E44AD', alpha=0.95))
# Reference lines
ax.axvline(x=patient_week, color='#8E44AD', linestyle=':', linewidth=1.2, alpha=0.6)
ax.axhline(y=patient_weight, color='#8E44AD', linestyle=':', linewidth=1.2, alpha=0.6)
# Axes formatting
ax.set_xlabel('Gestational Age (Weeks)', fontsize=13, fontweight='bold', labelpad=8)
ax.set_ylabel('Estimated Fetal Weight (grams)', fontsize=13, fontweight='bold', labelpad=8)
ax.set_title('Fetal Weight Growth Chart – Percentile Reference\n(Hadlock / WHO Standards)',
fontsize=15, fontweight='bold', pad=14, color='#1A2B4A')
ax.set_xlim(13.5, 41.5)
ax.set_ylim(0, 5000)
ax.set_xticks(range(14, 41, 2))
ax.set_yticks(range(0, 5001, 500))
ax.yaxis.set_major_formatter(plt.FuncFormatter(lambda x, _: f'{int(x):,}'))
ax.grid(True, linestyle='--', alpha=0.4, color='#BDC3C7')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
# Legend
handles = [
mpatches.Patch(color='#2ECC71', alpha=0.5, label='Normal range (25th–75th %ile)'),
mpatches.Patch(color='#E67E22', alpha=0.4, label='Borderline (10th–25th / 75th–90th)'),
mpatches.Patch(color='#E74C3C', alpha=0.35, label='SGA / LGA zone (<10th / >90th)'),
plt.Line2D([0],[0], color='#1A5276', linewidth=2, label='50th %ile (Median)'),
plt.scatter([],[], color='#8E44AD', s=100, edgecolors='white', linewidths=1.5, label='This patient (451g, 21w5d)'),
]
ax.legend(handles=handles, loc='upper left', fontsize=8.5, framealpha=0.92,
edgecolor='#BDC3C7', fancybox=True)
plt.tight_layout(pad=1.5)
chart_path = '/home/daytona/workspace/fetal-weight-report/fetal_weight_chart.png'
plt.savefig(chart_path, dpi=180, bbox_inches='tight', facecolor='#FAFAFA')
plt.close()
print(f"Chart saved: {chart_path}")
# ── Percentile Table Data ────────────────────────────────────────────────────
# Only show weeks 14–28 in detail (second trimester focus), then 30,32,34,36,38,40
selected_weeks = [14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,30,32,34,36,38,40]
idx_map = {w: weeks.index(w) for w in selected_weeks if w in weeks}
# ── Build PDF ────────────────────────────────────────────────────────────────
pdf_path = '/home/daytona/workspace/fetal-weight-report/Fetal_Weight_Chart_Comparison.pdf'
doc = SimpleDocTemplate(pdf_path, pagesize=A4,
leftMargin=1.8*cm, rightMargin=1.8*cm,
topMargin=1.5*cm, bottomMargin=1.5*cm)
styles = getSampleStyleSheet()
story = []
# Title block
title_style = ParagraphStyle('Title', parent=styles['Title'],
fontSize=20, textColor=colors.HexColor('#1A2B4A'),
spaceAfter=4, alignment=TA_CENTER, fontName='Helvetica-Bold')
subtitle_style = ParagraphStyle('Sub', parent=styles['Normal'],
fontSize=10, textColor=colors.HexColor('#5D6D7E'),
spaceAfter=2, alignment=TA_CENTER)
heading2 = ParagraphStyle('H2', parent=styles['Normal'],
fontSize=12, textColor=colors.HexColor('#1A5276'),
spaceBefore=12, spaceAfter=4, fontName='Helvetica-Bold')
body = ParagraphStyle('Body', parent=styles['Normal'],
fontSize=9.5, textColor=colors.HexColor('#2C3E50'),
spaceAfter=3, leading=14)
highlight = ParagraphStyle('Highlight', parent=styles['Normal'],
fontSize=10, textColor=colors.HexColor('#6C3483'),
spaceAfter=4, fontName='Helvetica-Bold')
small = ParagraphStyle('Small', parent=styles['Normal'],
fontSize=8, textColor=colors.HexColor('#7F8C8D'), leading=11)
story.append(Paragraph("Fetal Weight Growth Chart & Comparison", title_style))
story.append(Paragraph("Based on Hadlock / WHO International Standards | Generated for Clinical Reference", subtitle_style))
story.append(Paragraph("Patient: Pooja Prajapati | Scan Date: 13 July 2024 | Facility: Paradkar Hospital, Narsinghpur", subtitle_style))
story.append(HRFlowable(width="100%", thickness=2, color=colors.HexColor('#1A5276'), spaceAfter=10))
# Patient summary box
summary_data = [
['Parameter', 'Value', 'Interpretation'],
['Gestational Age at Scan', '21 Weeks 5 Days', 'Second trimester anomaly scan'],
['Estimated Fetal Weight (EFW)', '451 grams', 'Within normal range'],
['50th Percentile (expected)', '~460–465 g at 21w5d', 'Reference midpoint'],
['Percentile Estimate', '~45th–50th percentile', 'Appropriate for Gestational Age (AGA)'],
['IUGR Concern?', 'No', 'EFW above 10th percentile'],
['Macrosomia Concern?', 'No', 'EFW well below 90th percentile'],
['EDD by USG', '18 November 2024', '-'],
]
sum_style = TableStyle([
('BACKGROUND', (0,0), (-1,0), colors.HexColor('#1A5276')),
('TEXTCOLOR', (0,0), (-1,0), colors.white),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('FONTSIZE', (0,0), (-1,0), 10),
('BACKGROUND', (0,1), (-1,1), colors.HexColor('#D6EAF8')),
('BACKGROUND', (0,2), (-1,2), colors.HexColor('#EBF5FB')),
('BACKGROUND', (0,3), (-1,3), colors.HexColor('#D6EAF8')),
('BACKGROUND', (0,4), (-1,4), colors.HexColor('#E8F8F5')), # highlight row
('TEXTCOLOR', (0,4), (-1,4), colors.HexColor('#6C3483')),
('FONTNAME', (0,4), (-1,4), 'Helvetica-Bold'),
('BACKGROUND', (0,5), (-1,5), colors.HexColor('#EBF5FB')),
('BACKGROUND', (0,6), (-1,6), colors.HexColor('#D6EAF8')),
('BACKGROUND', (0,7), (-1,7), colors.HexColor('#EBF5FB')),
('GRID', (0,0), (-1,-1), 0.5, colors.HexColor('#AED6F1')),
('FONTSIZE', (0,1), (-1,-1), 9),
('FONTNAME', (0,1), (0,-1), 'Helvetica-Bold'),
('ROWBACKGROUNDS', (0,0), (-1,-1), [None]),
('TOPPADDING', (0,0), (-1,-1), 5),
('BOTTOMPADDING',(0,0), (-1,-1), 5),
('LEFTPADDING', (0,0), (-1,-1), 8),
('RIGHTPADDING',(0,0), (-1,-1), 8),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('ROUNDEDCORNERS', [4]),
])
summary_table = Table(summary_data, colWidths=[5.5*cm, 5.5*cm, 6.5*cm])
summary_table.setStyle(sum_style)
story.append(Paragraph("Patient Assessment Summary", heading2))
story.append(summary_table)
story.append(Spacer(1, 10))
# Chart
story.append(Paragraph("Fetal Weight Growth Chart (14–40 Weeks)", heading2))
story.append(RLImage(chart_path, width=17*cm, height=10.5*cm))
story.append(Paragraph(
"The purple dot marks this patient's EFW (451g at 21w5d). The green band represents the normal range (25th–75th percentile). "
"Orange bands indicate borderline zones; red bands indicate SGA (<10th) or LGA (>90th) zones.",
small))
story.append(Spacer(1, 8))
# Percentile reference table
story.append(Paragraph("Percentile Reference Table – Estimated Fetal Weight (grams)", heading2))
story.append(Paragraph(
"Values derived from Hadlock et al. (1991) and WHO Multicentre Growth Reference standards. "
"Shaded row = this patient's gestational week.",
small))
story.append(Spacer(1, 4))
header_row = ['GA\n(wks)', '3rd', '10th', '25th', '50th\n(Median)', '75th', '90th', '97th', 'Classification']
table_rows = [header_row]
classif = {
'<3': 'Extremely small',
'3-10': 'SGA (Small for GA)',
'10-25': 'Low-normal',
'25-75': 'Normal (AGA)',
'75-90': 'High-normal',
'90-97': 'LGA (Large for GA)',
'>97': 'Macrosomia',
}
for w in selected_weeks:
i = idx_map[w]
row = [
str(w),
f"{p3[i]:,}", f"{p10[i]:,}", f"{p25[i]:,}", f"{p50[i]:,}",
f"{p75[i]:,}", f"{p90[i]:,}", f"{p97[i]:,}",
'Normal (AGA)' if w == 21 else ''
]
table_rows.append(row)
tbl_style = TableStyle([
('BACKGROUND', (0,0), (-1,0), colors.HexColor('#1A5276')),
('TEXTCOLOR', (0,0), (-1,0), colors.white),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('FONTSIZE', (0,0), (-1,0), 8),
('ALIGN', (0,0), (-1,-1), 'CENTER'),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('GRID', (0,0), (-1,-1), 0.4, colors.HexColor('#BDC3C7')),
('FONTSIZE', (0,1), (-1,-1), 8),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING',(0,0), (-1,-1), 4),
('ROWBACKGROUNDS', (0,1), (-1,-1),
[colors.HexColor('#EBF5FB'), colors.white]),
])
# Highlight row for week 21
for ridx, w in enumerate(selected_weeks):
if w == 21:
row_i = ridx + 1
tbl_style.add('BACKGROUND', (0, row_i), (-1, row_i), colors.HexColor('#F5EEF8'))
tbl_style.add('TEXTCOLOR', (0, row_i), (-1, row_i), colors.HexColor('#6C3483'))
tbl_style.add('FONTNAME', (0, row_i), (-1, row_i), 'Helvetica-Bold')
ref_table = Table(table_rows, colWidths=[1.4*cm, 1.8*cm, 1.8*cm, 1.8*cm, 2.0*cm, 1.8*cm, 1.8*cm, 1.8*cm, 2.9*cm])
ref_table.setStyle(tbl_style)
story.append(ref_table)
story.append(Spacer(1, 8))
# Classification legend
story.append(Paragraph("Classification Guide", heading2))
legend_data = [
['< 3rd percentile', 'Extremely Small / refer urgently'],
['3rd – 10th percentile', 'Small for Gestational Age (SGA) – monitor closely'],
['10th – 25th percentile', 'Low-normal – routine monitoring'],
['25th – 75th percentile', 'NORMAL – Appropriate for Gestational Age (AGA)'],
['75th – 90th percentile', 'High-normal – routine monitoring'],
['90th – 97th percentile', 'Large for Gestational Age (LGA) – monitor for macrosomia'],
['> 97th percentile', 'Macrosomia – further evaluation advised'],
]
leg_style = TableStyle([
('BACKGROUND', (0,3), (-1,3), colors.HexColor('#D5F5E3')),
('FONTNAME', (0,3), (-1,3), 'Helvetica-Bold'),
('TEXTCOLOR', (0,3), (-1,3), colors.HexColor('#1E8449')),
('GRID', (0,0), (-1,-1), 0.4, colors.HexColor('#BDC3C7')),
('FONTSIZE', (0,0), (-1,-1), 9),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING',(0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 8),
('RIGHTPADDING',(0,0), (-1,-1), 8),
('BACKGROUND', (0,0), (-1,0), colors.HexColor('#FADBD8')),
('BACKGROUND', (0,1), (-1,1), colors.HexColor('#FDEBD0')),
('BACKGROUND', (0,2), (-1,2), colors.HexColor('#FEF9E7')),
('BACKGROUND', (0,4), (-1,4), colors.HexColor('#FEF9E7')),
('BACKGROUND', (0,5), (-1,5), colors.HexColor('#FDEBD0')),
('BACKGROUND', (0,6), (-1,6), colors.HexColor('#FADBD8')),
('FONTNAME', (0,0), (0,-1), 'Helvetica-Bold'),
])
leg_table = Table(legend_data, colWidths=[6.5*cm, 11*cm])
leg_table.setStyle(leg_style)
story.append(leg_table)
# Footer notes
story.append(Spacer(1, 10))
story.append(HRFlowable(width="100%", thickness=1, color=colors.HexColor('#AED6F1'), spaceAfter=4))
story.append(Paragraph(
"<b>References:</b> Hadlock FP et al. <i>Radiology</i> 1991;181:129-133 | WHO Multicentre Growth Reference Study Group 2006 | "
"INTERGROWTH-21st Fetal Growth Standards.",
small))
story.append(Paragraph(
"<b>Disclaimer:</b> This chart is for clinical reference only. EFW calculated from ultrasound biometry carries a margin of error of ±10–15%. "
"Clinical correlation with serial scans and Doppler studies is recommended for growth monitoring.",
small))
story.append(Paragraph(
f"<b>Generated:</b> 13 July 2026 | <b>Patient:</b> Pooja Prajapati | <b>Scan Date:</b> 13 July 2024",
small))
doc.build(story)
print(f"PDF saved: {pdf_path}")
cd /home/daytona/workspace/fetal-weight-report && python generate_chart.py 2>&1
ls -lh /home/daytona/workspace/fetal-weight-report/
Fetal Weight Chart Comparison
PDF Document · PDF
Pooja is A negative and this is her first pregnancy when should 8 give Anti D to her
Anti-D immunoglobulin Rh negative pregnancy prophylaxis guidelines
Anti-D immunoglobulin schedule Rh negative first pregnancy ACOG RCOG guidelines 2023 2024
https://www.blood.gov.au/sites/default/files/documents/2024-…
Key principle: Since this is her first pregnancy, she has no preformed anti-D antibodies yet. The goal is to prevent sensitization so that future pregnancies are not affected by hemolytic disease of the fetus/newborn (HDFN).
| When | Dose | Purpose |
|---|---|---|
| 28 weeks | 300 mcg (1500 IU) single dose OR 300 mcg (1500 IU) split as two doses | Covers feto-maternal hemorrhage in the third trimester |
| 34 weeks | 300 mcg (1500 IU) - if using the two-dose regimen | Covers the final weeks before delivery |
| After delivery | 300 mcg (1500 IU) within 72 hours of birth - only if baby is RhD positive | Prevents sensitization from delivery bleed |
Pooja is at 21w5d now - the 28-week dose is coming up very soon (about 6 weeks away). This should be planned immediately.
| Sensitizing Event | Dose | Timing |
|---|---|---|
| Amniocentesis (if done) | 300 mcg | Within 72 hours |
| Antepartum hemorrhage / APH | 300 mcg | Within 72 hours; repeat if bleeding recurs after 6 weeks |
| External cephalic version (ECV) | 300 mcg | Within 72 hours |
| Abdominal trauma (fall, RTA) | 300 mcg | Within 72 hours |
| Intrauterine death / stillbirth | 300 mcg | Within 72 hours |
| Cordocentesis | 300 mcg | Within 72 hours |
| Manual removal of placenta | 300 mcg | Within 72 hours |
After 20 weeks, a Kleihauer-Betke (KB) test or flow cytometry should be done after any sensitizing event to estimate the volume of fetal blood in maternal circulation. If it is >4 mL (>15 mL whole blood), additional Anti-D doses are needed.
| Event | Dose (if applicable) |
|---|---|
| Miscarriage / TOP before 12 weeks | Not required (per 2024 guidelines - very low sensitization risk) |
| Miscarriage / TOP after 12 weeks | 300 mcg within 72 hours |
| Ectopic pregnancy | 300 mcg (or 50 mcg if <12 weeks) |
| Threatened miscarriage (any bleeding) | 300 mcg if >12 weeks; clinical judgment <12 weeks |
Current (21w5d) → No action needed unless sensitizing event
↓
~ 28 weeks → Anti-D 300 mcg (MANDATORY - routine prophylaxis)
↓
~ 34 weeks → Anti-D 300 mcg (second dose of two-dose regimen)
↓
Delivery → Test baby's blood group → Anti-D 300 mcg within 72h if baby is RhD+