pdf-quiz-generator/backend/scripts/seed_lab_references.py
Daniel da8a717489 feat: PREP as tags, stats toggle, comments redesign, quizzes CTA
PREP provenance becomes keyword tags tied to the source quizzes; PREP question categories retired. Response statistics get a persistent hide/show toggle. Comments redesigned with avatars, badges and a cleaner compose box. Create Custom Test is now a prominent card on the Quizzes page only. Hierarchy conversion gains pediatric sub-specialties and disease children. 96 frontend tests pass.
2026-09-09 01:30:20 +02:00

167 lines
14 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Seed published lab reference values from cited public pediatric references.
Sources (cited per row):
- University of Iowa Health Care Pathology Handbook, Pediatric Reference Ranges
https://www.healthcare.uiowa.edu/path_handbook/Appendix/Heme/PEDIATRIC_NORMALS.html
- PediRounds Pediatric Laboratory Reference Ranges (reviewed by a paediatric
critical care specialist) https://www.pedirounds.com/lab-values/
- Valley Children's HealthHub (ammonia) https://health.valleychildrens.org/
- AAP Pediatrics 2018 CSF reference values (via PediRounds CSF summary)
Idempotent: existing (group, name, age_group) rows are left untouched.
"""
import sys
from app.database import SessionLocal
from app.models.lab_reference import LabReference
from app.models.user import User
UI = "University of Iowa Health Care Pathology Handbook — Pediatric Reference Ranges"
UI_URL = "https://www.healthcare.uiowa.edu/path_handbook/Appendix/Heme/PEDIATRIC_NORMALS.html"
PEDI = "PediRounds — Paediatric Laboratory Reference Ranges (reviewed paediatric critical care specialist)"
PEDI_URL = "https://www.pedirounds.com/lab-values/"
VALLEY = "Valley Children's Pediatric HealthHub — Ammonia reference ranges"
VALLEY_URL = "https://health.valleychildrens.org/hematology/diagnostico/ammonia"
CSF_SRC = "PediRounds CSF summary; AAP Pediatrics 2018 CSF reference values for young infants"
CSF_URL = "https://publications.aap.org/pediatrics/article/141/3/e20173405/37649/Cerebrospinal-Fluid-Reference-Values-for-Young"
ROWS = [
# ── Hematology (UI Health Care, conventional units) ─────────────────
("Hematology", "Hemoglobin", "13.419.9", "g/dL", "01 month", "Whole blood", UI, UI_URL),
("Hematology", "Hemoglobin", "10.717.1", "g/dL", "12 months", "Whole blood", UI, UI_URL),
("Hematology", "Hemoglobin", "9.014.1", "g/dL", "23 months", "Whole blood", UI, UI_URL),
("Hematology", "Hemoglobin", "9.514.1", "g/dL", "36 months", "Whole blood", UI, UI_URL),
("Hematology", "Hemoglobin", "11.314.1", "g/dL", "6 months1 year", "Whole blood", UI, UI_URL),
("Hematology", "Hemoglobin", "10.915.0", "g/dL", "15 years", "Whole blood", UI, UI_URL),
("Hematology", "Hemoglobin", "11.915.0", "g/dL", "511 years", "Whole blood", UI, UI_URL),
("Hematology", "Hemoglobin", "12.717.7 (M) / 11.915.0 (F)", "g/dL", "1118 years", "Whole blood", UI, UI_URL),
("Hematology", "Hematocrit", "4265", "%", "01 month", "Whole blood", UI, UI_URL),
("Hematology", "Hematocrit", "3355", "%", "12 months", "Whole blood", UI, UI_URL),
("Hematology", "Hematocrit", "2841", "%", "23 months", "Whole blood", UI, UI_URL),
("Hematology", "Hematocrit", "2941", "%", "36 months", "Whole blood", UI, UI_URL),
("Hematology", "Hematocrit", "3141", "%", "6 months1 year", "Whole blood", UI, UI_URL),
("Hematology", "Hematocrit", "3144", "%", "15 years", "Whole blood", UI, UI_URL),
("Hematology", "Hematocrit", "3544", "%", "511 years", "Whole blood", UI, UI_URL),
("Hematology", "Hematocrit", "3748 (M) / 3444 (F)", "%", "1118 years", "Whole blood", UI, UI_URL),
("Hematology", "White cell count", "9,00030,000", "/mm³", "01 month", "Whole blood", UI, UI_URL),
("Hematology", "White cell count", "5,00019,500", "/mm³", "13 months", "Whole blood", UI, UI_URL),
("Hematology", "White cell count", "6,00017,500", "/mm³", "3 months1 year", "Whole blood", UI, UI_URL),
("Hematology", "White cell count", "6,00017,000", "/mm³", "12 years", "Whole blood", UI, UI_URL),
("Hematology", "White cell count", "5,50015,500", "/mm³", "24 years", "Whole blood", UI, UI_URL),
("Hematology", "Platelets", "150400", "×10³/mm³", "All ages", "Whole blood", PEDI, PEDI_URL),
("Hematology", "Neutrophils", "1,00020,000", "/mm³", "01 month", "Whole blood", UI, UI_URL),
("Hematology", "Neutrophils", "1,5008,500", "/mm³", "6 months5 years", "Whole blood", UI, UI_URL),
("Hematology", "Neutrophils", "1,7007,500", "/mm³", "518 years", "Whole blood", UI, UI_URL),
("Hematology", "Lymphocytes", "2,00011,000", "/mm³", "01 month", "Whole blood", UI, UI_URL),
("Hematology", "Lymphocytes", "4,00010,500", "/mm³", "6 months1 year", "Whole blood", UI, UI_URL),
("Hematology", "Lymphocytes", "2,0008,000", "/mm³", "25 years", "Whole blood", UI, UI_URL),
("Hematology", "MCV", "88123", "fL", "01 month", "Whole blood", UI, UI_URL),
("Hematology", "MCV", "74108", "fL", "36 months", "Whole blood", UI, UI_URL),
("Hematology", "MCV", "7085", "fL", "6 months1 year", "Whole blood", UI, UI_URL),
("Hematology", "Reticulocytes", "0.51.5", "%", "All ages (higher in newborn)", "Whole blood", PEDI, PEDI_URL),
# ── Chemistries ─────────────────────────────────────────────────────
("Chemistries", "Sodium", "135145", "mmol/L", ">10 days", "Plasma", PEDI, PEDI_URL),
("Chemistries", "Potassium", "3.56.0", "mEq/L", "<10 days", "Plasma", UI, UI_URL),
("Chemistries", "Potassium", "3.55.0", "mEq/L", ">10 days", "Plasma", UI, UI_URL),
("Chemistries", "Chloride", "98106", "mmol/L", "All ages", "Plasma", PEDI, PEDI_URL),
("Chemistries", "Bicarbonate (CO2)", "1827", "mEq/L", "All ages", "Plasma", UI, UI_URL),
("Chemistries", "Bicarbonate", "2226", "mmol/L", "All ages", "Plasma", PEDI, PEDI_URL),
("Chemistries", "Urea nitrogen (BUN)", "718 (urea 2.56.5 mmol/L)", "mg/dL", "All ages", "Plasma", PEDI, PEDI_URL),
("Chemistries", "Creatinine", "0.20.9", "mg/dL", "Neonate", "Plasma", UI, UI_URL),
("Chemistries", "Creatinine", "0.20.4", "mg/dL", "212 months", "Plasma", UI, UI_URL),
("Chemistries", "Creatinine", "0.20.5", "mg/dL", "12 years", "Plasma", UI, UI_URL),
("Chemistries", "Creatinine", "0.30.7", "mg/dL", "36 years", "Plasma", UI, UI_URL),
("Chemistries", "Creatinine", "0.30.9", "mg/dL", "1112 years", "Plasma", UI, UI_URL),
("Chemistries", "Creatinine", "0.40.9", "mg/dL", "1315 years", "Plasma", UI, UI_URL),
("Chemistries", "Glucose", "5099", "mg/dL", "01 month", "Plasma", UI, UI_URL),
("Chemistries", "Glucose", "6599", "mg/dL", ">1 month", "Plasma", UI, UI_URL),
("Chemistries", "Calcium (total)", "8.510.6", "mg/dL", "Birth30 days", "Plasma", UI, UI_URL),
("Chemistries", "Calcium (total)", "8.710.5", "mg/dL", "31 days1 year", "Plasma", UI, UI_URL),
("Chemistries", "Calcium (total)", "8.810.6", "mg/dL", "16 years", "Plasma", UI, UI_URL),
("Chemistries", "Calcium (ionized)", "4.25.9", "mg/dL", "Neonate", "Plasma", UI, UI_URL),
("Chemistries", "Magnesium", "1.62.4 (0.71.0 mmol/L)", "mg/dL", "All ages", "Plasma", PEDI, PEDI_URL),
("Chemistries", "Phosphate", "4.29.0", "mg/dL", "Newborn11 months", "Plasma", UI, UI_URL),
("Chemistries", "Phosphate", "3.26.3", "mg/dL", "12 months15 years", "Plasma", UI, UI_URL),
("Chemistries", "Albumin", "2.84.4", "g/dL", "04 days", "Plasma", UI, UI_URL),
("Chemistries", "Albumin", "3.85.4", "g/dL", "4 days14 years", "Plasma", UI, UI_URL),
("Chemistries", "Total protein", "4.16.3", "g/dL", "Birth31 days", "Plasma", UI, UI_URL),
("Chemistries", "Total protein", "5.78.0", "g/dL", "118 years", "Plasma", UI, UI_URL),
("Chemistries", "Bilirubin (total)", "<1.8", "mg/dL", "Cord blood (24 h)", "Serum", UI, UI_URL),
("Chemistries", "Bilirubin (total)", "≤6.0", "mg/dL", "Term, 24 h", "Serum", UI, UI_URL),
("Chemistries", "Bilirubin (total)", "≤7.0", "mg/dL", "Term, 48 h", "Serum", UI, UI_URL),
("Chemistries", "Bilirubin (total)", "≤12.0", "mg/dL", "35 days", "Serum", UI, UI_URL),
("Chemistries", "Bilirubin (total)", "<1.0", "mg/dL", "1 monthadult", "Serum", UI, UI_URL),
("Chemistries", "ALT", "<40", "U/L", "All ages", "Plasma", PEDI, PEDI_URL),
("Chemistries", "AST", "<45", "U/L", "All ages (higher in infants)", "Plasma", PEDI, PEDI_URL),
("Chemistries", "GGT", "<55", "U/L", "Child (high in neonates)", "Plasma", PEDI, PEDI_URL),
("Chemistries", "Alkaline phosphatase", "75316 (M) / 48406 (F)", "U/L", "130 days", "Plasma", UI, UI_URL),
("Chemistries", "Alkaline phosphatase", "82383 (M) / 124341 (F)", "U/L", "31 days1 year", "Plasma", UI, UI_URL),
("Chemistries", "Alkaline phosphatase", "104345 (M) / 108317 (F)", "U/L", "13 years", "Plasma", UI, UI_URL),
("Chemistries", "Ammonia", "170341", "µg/dL", "010 days (enzymatic)", "Plasma", VALLEY, VALLEY_URL),
("Chemistries", "Ammonia", "68136", "µg/dL", "10 days2 years (enzymatic)", "Plasma", VALLEY, VALLEY_URL),
("Chemistries", "Ammonia", "1960", "µg/dL", ">2 years (enzymatic)", "Plasma", VALLEY, VALLEY_URL),
# ── Acidbase / gases ────────────────────────────────────────────────
("Acid-base / gases", "pH (arterial)", "7.327.42", "", "01 month", "Arterial blood", UI, UI_URL),
("Acid-base / gases", "pH (arterial)", "7.357.45", "", ">1 month", "Arterial blood", UI, UI_URL),
("Acid-base / gases", "pCO2 (arterial)", "3040", "mmHg", "018 years", "Arterial blood", UI, UI_URL),
("Acid-base / gases", "pO2 (arterial)", "6080", "mmHg", "01 month", "Arterial blood", UI, UI_URL),
("Acid-base / gases", "pO2 (arterial)", "80100", "mmHg", ">1 month", "Arterial blood", UI, UI_URL),
("Acid-base / gases", "Base excess", "2 to +2", "mmol/L", "All ages", "Blood gas", PEDI, PEDI_URL),
("Acid-base / gases", "Lactate", "<2.0", "mmol/L", "All ages", "Blood", PEDI, PEDI_URL),
# ── Coagulation ──────────────────────────────────────────────────────
("Coagulation", "PT", "1114", "seconds", "All ages", "Plasma", PEDI, PEDI_URL),
("Coagulation", "aPTT", "2535", "seconds", "All ages", "Plasma", PEDI, PEDI_URL),
("Coagulation", "INR", "0.91.2", "", "All ages", "Plasma", PEDI, PEDI_URL),
("Coagulation", "Fibrinogen", "150400 (1.54.0 g/L)", "mg/dL", "All ages", "Plasma", PEDI, PEDI_URL),
("Coagulation", "D-dimer", "<0.5", "mg/L", "All ages (assay-dependent)", "Plasma", PEDI, PEDI_URL),
# ── Inflammatory ─────────────────────────────────────────────────────
("Inflammatory", "CRP", "<10 (<1.0 mg/dL)", "mg/L", "All ages", "Serum", PEDI, PEDI_URL),
("Inflammatory", "Procalcitonin", "<0.5", "ng/mL", "All ages", "Serum", PEDI, PEDI_URL),
("Inflammatory", "ESR", "<15", "mm/h", "All ages (varies)", "Blood", PEDI, PEDI_URL),
# ── CSF ──────────────────────────────────────────────────────────────
("CSF", "CSF white cell count", "<5 (neonate up to ~20)", "/mm³", "Child vs neonate", "CSF", CSF_SRC, CSF_URL),
("CSF", "CSF protein", "1540 (neonate up to ~100)", "mg/dL", "Child vs neonate", "CSF", CSF_SRC, CSF_URL),
("CSF", "CSF glucose", "≥2/3 of plasma glucose", "", "All ages", "CSF", CSF_SRC, CSF_URL),
# ── Endocrine ────────────────────────────────────────────────────────
("Endocrine", "TSH", "0.55.0", "mU/L", "All ages (higher in neonates)", "Plasma", PEDI, PEDI_URL),
("Endocrine", "Free T4", "1222", "pmol/L", "All ages", "Plasma", PEDI, PEDI_URL),
("Endocrine", "Cortisol (08:00)", "170500", "nmol/L", "All ages", "Plasma", PEDI, PEDI_URL),
# ── Urine ────────────────────────────────────────────────────────────
("Urine", "Specific gravity", "1.0011.035", "", "All ages", "Urine", PEDI, PEDI_URL),
]
def main():
db = SessionLocal()
try:
updated_by = db.query(User.id).filter(User.role.in_(["admin", "moderator"])).order_by(User.id).first()
updated_by = updated_by[0] if updated_by else None
# Normalize vague population labels on existing rows to match the new seed data.
db.query(LabReference).filter(LabReference.age_group == "Child").update({"age_group": "All ages"}, synchronize_session=False)
db.query(LabReference).filter(LabReference.age_group == "Child (higher in infants)").update({"age_group": "All ages (higher in infants)"}, synchronize_session=False)
db.query(LabReference).filter(LabReference.age_group == "Child (higher in newborn)").update({"age_group": "All ages (higher in newborn)"}, synchronize_session=False)
db.query(LabReference).filter(LabReference.age_group == "Child (higher in neonate)").update({"age_group": "All ages (higher in neonates)"}, synchronize_session=False)
db.commit()
existing = {(row.group, row.name, row.age_group) for row in db.query(LabReference).all()}
added = 0
for group, name, ref, units, age, specimen, source, url in ROWS:
key = (group, name, age)
if key in existing:
continue
db.add(LabReference(
name=name, group=group, reference_range=ref, units=units,
age_group=age, specimen=specimen, source=source, source_url=url,
is_published=True, updated_by=updated_by,
))
added += 1
existing.add(key)
db.commit()
total = db.query(LabReference).filter(LabReference.is_published.is_(True)).count()
print(f"Seeded {added} new published lab references; {total} published total.")
finally:
db.close()
if __name__ == "__main__":
sys.exit(main())