Classification and grading of neuroendocrine neoplasm of digestive system
"neuroendocrine neoplasm" AND "classification" AND "digestive"
PMID: 37690447
WHO 2019 classification grading neuroendocrine neoplasm digestive system NET NEC
https://pmc.ncbi.nlm.nih.gov/articles/PMC9281538
https://ronnyallan.net/2024/03/11/neuroendocrine-neoplasms-g…
| Grade | Mitotic Rate (per 2 mm²) | Ki-67 Index | Differentiation |
|---|---|---|---|
| G1 | < 2 | < 3% | Well-differentiated |
| G2 | 2-20 | 3-20% | Well-differentiated |
| G3 | > 20 | > 20% | Well-differentiated |
Key principle: When mitotic rate and Ki-67 are discordant, the higher result determines the grade. Ki-67 is assessed by counting at least 500 cells in "hotspot" regions of highest labeling.
| Subtype | Features |
|---|---|
| Small Cell NEC (SCNEC) | Tightly packed fusiform nuclei, finely granular chromatin, scant cytoplasm - resembles small cell lung cancer |
| Large Cell NEC (LCNEC) | Rounded markedly atypical nuclei, prominent nucleoli, abundant cytoplasm |
| Category | Differentiation | Grade | Mitoses/2 mm² | Ki-67 |
|---|---|---|---|---|
| NET G1 | Well | G1 | <2 | <3% |
| NET G2 | Well | G2 | 2-20 | 3-20% |
| NET G3 | Well | G3 | >20 | >20% |
| NEC (SCNEC/LCNEC) | Poorly | High (not graded) | >20 | >20% |
| MiNEN | Mixed | Per component | - | - |
| Division | Sites | Characteristics |
|---|---|---|
| Foregut | Esophagus, stomach, duodenum (proximal to ligament of Treitz), pancreas | Rarely metastasize; gastrinomas may cause Zollinger-Ellison syndrome |
| Midgut | Jejunum, ileum | Often multiple; tend to be aggressive; associated with carcinoid syndrome when metastatic |
| Hindgut | Appendix, colon, rectum | Appendiceal NETs nearly always benign; rectal NETs usually small, rarely metastasize |
| Feature | NET G3 | NEC |
|---|---|---|
| Differentiation | Well | Poorly |
| Architecture | Organoid/nested | Sheet-like |
| Chromatin | Coarse "salt and pepper" | Finely granular (SCNEC) or open (LCNEC) |
| Necrosis | Minimal | Abundant |
| Ki-67 | >20%, often <55% | Usually >55%, can be lower after chemo |
| TP53/RB1 | Intact | Often mutated/lost |
| Origin | Often de novo or from lower-grade NET | De novo |
| Response to platinum chemo | Poor | Good |
| Prognosis | Intermediate | Very poor |
| Year | Key Change |
|---|---|
| WHO 2000 | Introduced grading based on proliferative activity |
| WHO 2010 | Formalized mitotic rate/Ki-67 grading; all NETs classified as malignant |
| WHO 2017 (Pancreatic) | Added G3 NET category for pancreatic NENs |
| WHO 2019 (Digestive) | G3 NET extended to all GI/digestive sites; MANEC renamed MiNEN; "carcinoid" retired |
| WHO 2022 (Endocrine/NEN) | Consolidated all NEN classifications (GEP + non-GEP) into one framework |

Please give pdf
ls /home/daytona/workspace/ 2>/dev/null || mkdir -p /home/daytona/workspace/nen-…
mkdir -p /home/daytona/workspace/nen-pdf
~/nen-pdf/generate_nen_pdf.py
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import cm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, PageBreak
)
from reportlab.platypus import KeepTogether
OUTPUT = "/home/daytona/workspace/nen-pdf/NEN_Digestive_System_Classification_Grading.pdf"
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
rightMargin=2*cm, leftMargin=2*cm,
topMargin=2*cm, bottomMargin=2*cm,
title="Classification & Grading of NENs of the Digestive System",
author="Orris Medical"
)
styles = getSampleStyleSheet()
# Custom styles
title_style = ParagraphStyle(
'CustomTitle', parent=styles['Title'],
fontSize=20, textColor=colors.HexColor('#1a3c5e'),
spaceAfter=6, leading=24, alignment=TA_CENTER
)
subtitle_style = ParagraphStyle(
'Subtitle', parent=styles['Normal'],
fontSize=11, textColor=colors.HexColor('#4a6080'),
spaceAfter=14, alignment=TA_CENTER, italic=True
)
h1_style = ParagraphStyle(
'H1', parent=styles['Heading1'],
fontSize=14, textColor=colors.white,
backColor=colors.HexColor('#1a3c5e'),
spaceBefore=14, spaceAfter=6, leading=18,
leftIndent=-0.3*cm, rightIndent=-0.3*cm,
borderPad=5
)
h2_style = ParagraphStyle(
'H2', parent=styles['Heading2'],
fontSize=12, textColor=colors.HexColor('#1a3c5e'),
spaceBefore=10, spaceAfter=4, leading=16,
borderPad=3
)
h3_style = ParagraphStyle(
'H3', parent=styles['Heading3'],
fontSize=10.5, textColor=colors.HexColor('#2e6da4'),
spaceBefore=8, spaceAfter=3, leading=14, italic=True
)
body_style = ParagraphStyle(
'Body', parent=styles['Normal'],
fontSize=9.5, leading=14, spaceAfter=4, alignment=TA_JUSTIFY
)
bullet_style = ParagraphStyle(
'Bullet', parent=styles['Normal'],
fontSize=9.5, leading=13, spaceAfter=2,
leftIndent=14, bulletIndent=4
)
note_style = ParagraphStyle(
'Note', parent=styles['Normal'],
fontSize=9, leading=13, textColor=colors.HexColor('#555555'),
backColor=colors.HexColor('#f0f4f8'),
borderPad=6, spaceAfter=6, leftIndent=4, rightIndent=4
)
footer_style = ParagraphStyle(
'Footer', parent=styles['Normal'],
fontSize=8, textColor=colors.grey, alignment=TA_CENTER
)
TABLE_HEADER_BG = colors.HexColor('#1a3c5e')
TABLE_ALT_BG = colors.HexColor('#eaf2fb')
TABLE_GRID = colors.HexColor('#aac4de')
TABLE_WARN_BG = colors.HexColor('#fff3cd')
def section_heading(text):
return Paragraph(f" {text}", h1_style)
def sub_heading(text):
return Paragraph(text, h2_style)
def sub3_heading(text):
return Paragraph(text, h3_style)
def body(text):
return Paragraph(text, body_style)
def bullet(text):
return Paragraph(f"• {text}", bullet_style)
def note(text):
return Paragraph(f"<b>Note:</b> {text}", note_style)
def spacer(h=0.3):
return Spacer(1, h*cm)
def hr():
return HRFlowable(width="100%", thickness=0.5, color=colors.HexColor('#aac4de'), spaceAfter=6)
# ─── Table builder ────────────────────────────────────────────────────────────
def make_table(headers, rows, col_widths=None, alt=True):
data = [[Paragraph(f"<b>{h}</b>", ParagraphStyle('TH', parent=styles['Normal'],
fontSize=9, textColor=colors.white, leading=12)) for h in headers]]
for i, row in enumerate(rows):
data.append([Paragraph(str(c), ParagraphStyle('TD', parent=styles['Normal'],
fontSize=9, leading=12)) for c in row])
ts = TableStyle([
('BACKGROUND', (0,0), (-1,0), TABLE_HEADER_BG),
('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.white, TABLE_ALT_BG] if alt else [colors.white]),
('GRID', (0,0), (-1,-1), 0.4, TABLE_GRID),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 5),
('RIGHTPADDING', (0,0), (-1,-1), 5),
])
t = Table(data, colWidths=col_widths, repeatRows=1)
t.setStyle(ts)
return t
# ═══════════════════════════════════════════════════════════════════════════════
story = []
# ── TITLE PAGE ─────────────────────────────────────────────────────────────────
story.append(spacer(2))
story.append(Paragraph("Classification & Grading of", title_style))
story.append(Paragraph("Neuroendocrine Neoplasms (NENs)", title_style))
story.append(Paragraph("of the Digestive System", title_style))
story.append(spacer(0.5))
story.append(Paragraph("WHO 5th Edition (2019) · Updated WHO 2022 Framework", subtitle_style))
story.append(HRFlowable(width="80%", thickness=2, color=colors.HexColor('#1a3c5e'), hAlign='CENTER'))
story.append(spacer(0.5))
story.append(Paragraph("Based on: Robbins & Kumar Basic Pathology | Tietz Textbook of Laboratory Medicine | WHO Classification of Digestive System Tumours", subtitle_style))
story.append(spacer(3))
# ── SECTION 1: OVERVIEW ────────────────────────────────────────────────────────
story.append(section_heading("1. Overview & Terminology"))
story.append(spacer())
story.append(body(
"Neuroendocrine neoplasms (NENs) are a heterogeneous group of tumors arising from "
"neuroendocrine cells distributed throughout the GI tract and pancreas. The term "
"<b>\"carcinoid\"</b> has been retired in the current WHO framework. The umbrella term "
"<b>NEN</b> is divided into fundamentally distinct categories based on <b>differentiation</b>."
))
story.append(spacer(0.4))
overview_table = make_table(
["Category", "Abbreviation", "Differentiation", "Graded?"],
[
["Neuroendocrine Tumor", "NET", "Well-differentiated", "Yes (G1, G2, G3)"],
["Neuroendocrine Carcinoma", "NEC", "Poorly differentiated", "No (always high-grade)"],
["Mixed Neuroendocrine-Non-Neuroendocrine Neoplasm", "MiNEN", "Mixed", "Per component"],
],
col_widths=[7*cm, 2.5*cm, 4*cm, 3.5*cm]
)
story.append(overview_table)
story.append(spacer())
story.append(note(
"All NETs should be considered malignant — they carry metastatic potential regardless of grade. "
"The term NET should NOT be used interchangeably with NEN (NEN is the umbrella; NET is "
"specifically the well-differentiated subtype)."
))
# ── SECTION 2: WHO GRADING ─────────────────────────────────────────────────────
story.append(spacer())
story.append(section_heading("2. WHO 2019/2022 Grading System"))
story.append(spacer())
story.append(sub_heading("2.1 Grading of Well-Differentiated NETs (G1 / G2 / G3)"))
story.append(body(
"NETs are graded G1 to G3 based on two proliferative parameters: <b>mitotic rate</b> (mitoses/2 mm²) "
"and <b>Ki-67 proliferation index</b>. When the two parameters are discordant, the "
"<b>higher result determines the grade</b>."
))
story.append(spacer(0.3))
grading_table = make_table(
["Grade", "Mitotic Rate (per 2 mm²)", "Ki-67 Index", "Differentiation", "Behavior"],
[
["G1", "< 2", "< 3%", "Well-differentiated", "Indolent; low metastatic risk"],
["G2", "2 – 20", "3 – 20%", "Well-differentiated", "Intermediate aggressiveness"],
["G3 NET", "> 20", "> 20%", "Well-differentiated", "High proliferation; NOT equivalent to NEC"],
["NEC (SCNEC/LCNEC)", "> 20", "> 20% (often >>55%)", "Poorly differentiated", "Highly aggressive; treat as small/large cell carcinoma"],
],
col_widths=[2.5*cm, 3.5*cm, 2.5*cm, 3.5*cm, 5*cm]
)
story.append(grading_table)
story.append(spacer(0.4))
story.append(sub3_heading("Ki-67 Assessment Method"))
for b in [
"Count at least <b>500 cells</b> in regions of highest labeling (\"hotspots\"), identified by scanning magnification.",
"When two areas with differing indices are present, select the <b>higher-index area</b> for grading.",
"Ki-67 can be assessed on small biopsies; mitotic count requires at least 50 HPF (≥10 mm of tissue) and may be unreliable on small samples.",
]:
story.append(bullet(b))
story.append(spacer())
story.append(sub_heading("2.2 The G3 NET — A Critical New Category (WHO 2019)"))
story.append(body(
"The G3 NET category was formally established in WHO 2019 (first introduced for pancreatic NETs in 2017). "
"It recognizes that <b>high proliferative index does NOT equal poor differentiation</b>."
))
story.append(spacer(0.3))
g3_table = make_table(
["Feature", "NET G3", "NEC (Poorly Differentiated)"],
[
["Differentiation", "Well", "Poorly"],
["Architecture", "Organoid / nested", "Sheet-like, less nested"],
["Chromatin pattern", "Coarse 'salt-and-pepper'", "Finely granular (SCNEC) or open nucleoli (LCNEC)"],
["Necrosis", "Minimal", "Abundant"],
["Ki-67", "> 20%, typically < 55%", "Usually > 55% (can be lower after chemo)"],
["TP53 / RB1 status", "Typically intact", "Frequently mutated / lost"],
["G3 components / prior low-grade", "May arise from G1/G2 or contain low-grade areas", "De novo"],
["Response to platinum-based chemo", "Poor", "Good"],
["Prognosis", "Intermediate", "Very poor"],
],
col_widths=[5*cm, 5.5*cm, 6.5*cm]
)
story.append(g3_table)
# ── SECTION 3: NEC ─────────────────────────────────────────────────────────────
story.append(spacer())
story.append(section_heading("3. Neuroendocrine Carcinomas (NECs)"))
story.append(spacer())
story.append(body(
"NECs are <b>always high-grade by definition</b> and are <b>not assigned a numeric grade</b> "
"(to avoid confusion with G3 NETs). They are poorly differentiated epithelial neoplasms with "
"neuroendocrine differentiation confirmed by morphology and IHC."
))
story.append(spacer(0.3))
nec_table = make_table(
["Subtype", "ICD-O Code", "Morphology", "Nuclear Features", "Analogue"],
[
["Small Cell NEC (SCNEC)", "8041/3",
"Sheets; scant cytoplasm; crush artifact common",
"Fusiform, finely granular chromatin, inconspicuous nucleoli",
"Small cell lung carcinoma"],
["Large Cell NEC (LCNEC)", "8013/3",
"Sheets/nests; moderate-abundant cytoplasm; necrosis",
"Rounded, markedly atypical; prominent nucleoli",
"Large cell neuroendocrine lung carcinoma"],
],
col_widths=[3.2*cm, 2.2*cm, 4*cm, 4.5*cm, 3.1*cm]
)
story.append(nec_table)
story.append(spacer(0.3))
story.append(note(
"Previously the term NEC was used for metastatic well-differentiated NETs. In the current "
"classification, NEC applies ONLY to poorly differentiated neoplasms. Chemotherapy-treated NECs "
"may show Ki-67 in the 20–50% range, so proliferation index alone cannot differentiate NET G3 from NEC."
))
# ── SECTION 4: MiNEN ───────────────────────────────────────────────────────────
story.append(spacer())
story.append(section_heading("4. Mixed Neuroendocrine-Non-Neuroendocrine Neoplasm (MiNEN)"))
story.append(spacer())
story.append(body(
"Formerly termed <b>Mixed Adenoneuroendocrine Carcinoma (MANEC)</b> in earlier WHO editions, "
"renamed <b>MiNEN</b> in WHO 2019 to reflect that the non-neuroendocrine component is not always "
"adenocarcinoma (may be squamous, acinar, etc.)."
))
story.append(spacer(0.3))
minen_data = [
["ICD-O Code", "8154/3"],
["Definition threshold", "Each component (NE + non-NE) ≥ 30% of the tumor (morphology + IHC)"],
["Subtypes of NE component", "May be NET or NEC"],
["Subtypes of non-NE component", "Adenocarcinoma (most common), squamous, acinar"],
["Prognosis driver", "Highest-grade component present"],
]
minen_table_data = [[Paragraph(f"<b>{r[0]}</b>", ParagraphStyle('Key', parent=styles['Normal'], fontSize=9, leading=12)),
Paragraph(r[1], ParagraphStyle('Val', parent=styles['Normal'], fontSize=9, leading=12))]
for r in minen_data]
minen_table = Table(minen_table_data, colWidths=[5*cm, 12*cm])
minen_table.setStyle(TableStyle([
('ROWBACKGROUNDS', (0,0), (-1,-1), [colors.white, TABLE_ALT_BG]),
('GRID', (0,0), (-1,-1), 0.4, TABLE_GRID),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 5),
]))
story.append(minen_table)
# ── SECTION 5: SITE-BASED CLASSIFICATION ──────────────────────────────────────
story.append(PageBreak())
story.append(section_heading("5. Site-Based (Anatomical / Embryonic) Classification"))
story.append(spacer())
story.append(body(
"NENs of the digestive tract are grouped by embryonic origin. This influences secretory profile, "
"biological behavior, and prognosis."
))
story.append(spacer(0.3))
site_table = make_table(
["Division", "Sites", "Key Features / Syndromes"],
[
["Foregut",
"Esophagus, stomach, duodenum (proximal to ligament of Treitz), pancreas",
"Rarely metastasize; gastrinomas → Zollinger-Ellison syndrome; ECL cell types in stomach"],
["Midgut",
"Jejunum, ileum",
"Often multiple; tend to be aggressive; serotonin-secreting → carcinoid syndrome when liver metastases present"],
["Hindgut",
"Appendix, colon, rectum",
"Appendiceal NETs: nearly always benign; rectal NETs: usually small, rarely metastasize; produce polypeptide hormones"],
],
col_widths=[2.8*cm, 6*cm, 8.2*cm]
)
story.append(site_table)
story.append(spacer())
story.append(sub_heading("5.1 Gastric NET Subtypes"))
story.append(body("Gastric NETs arise from ECL (enterochromaffin-like) cells and are classified into 3 types:"))
story.append(spacer(0.3))
gastric_table = make_table(
["Type", "Background", "Association", "Behavior"],
[
["Type 1 (ECL cell NET)", "Chronic atrophic gastritis", "Hypergastrinemia (secondary); autoimmune gastritis", "Benign course; rarely metastasize; <1 cm usually"],
["Type 2 (ECL cell NET)", "Zollinger-Ellison + MEN-1", "Hypergastrinemia (primary/tumor-driven)", "Intermediate; locally aggressive in some cases"],
["Type 3 (Sporadic NET)", "Normal gastric mucosa", "No hypergastrinemia; no underlying disease", "Aggressive; high risk of metastasis; treat as malignant"],
],
col_widths=[3.5*cm, 4*cm, 5*cm, 4.5*cm]
)
story.append(gastric_table)
# ── SECTION 6: FUNCTIONAL CLASSIFICATION ──────────────────────────────────────
story.append(spacer())
story.append(section_heading("6. Functional Classification"))
story.append(spacer())
func_table = make_table(
["Tumor", "Hormone", "Syndrome / Presentation"],
[
["Insulinoma", "Insulin", "Hypoglycemia (Whipple's triad)"],
["Gastrinoma", "Gastrin", "Zollinger-Ellison syndrome (peptic ulcers, diarrhea, GERD)"],
["VIPoma", "VIP", "WDHA syndrome (watery diarrhea, hypokalemia, achlorhydria / Werner-Morrison)"],
["Glucagonoma", "Glucagon", "Necrolytic migratory erythema, diabetes, weight loss"],
["Somatostatinoma", "Somatostatin", "Steatorrhea, gallstones, hyperglycemia"],
["Non-functional", "None / subclinical", "~1/3 of pancreatic NENs; symptoms from mass effect or detected incidentally"],
],
col_widths=[4*cm, 3*cm, 10*cm]
)
story.append(func_table)
# ── SECTION 7: IHC & MARKERS ──────────────────────────────────────────────────
story.append(spacer())
story.append(section_heading("7. Histology, IHC Markers & Serum Tests"))
story.append(spacer())
story.append(sub_heading("7.1 Histological Features"))
story.append(body(
"NETs form intramural or submucosal masses, often yellow-tan in color with an intense desmoplastic reaction. "
"Microscopically: islands, trabeculae, strands, glands, or sheets of uniform cells with <b>scant pink granular cytoplasm</b> "
"and <b>round-to-oval stippled (\"salt-and-pepper\") nuclei</b>. NECs show large necrotic areas and less nested architecture."
))
story.append(spacer(0.3))
story.append(sub_heading("7.2 Immunohistochemistry Panel"))
ihc_table = make_table(
["Marker", "Sensitivity / Specificity", "Notes"],
[
["Synaptophysin", "Most sensitive", "Positive in virtually all NETs and NECs"],
["Chromogranin A (CgA)", "Most specific; >90% diagnostic sensitivity", "Can be false-positive with PPI use, renal failure, IBD; used for monitoring therapy"],
["Ki-67 (MIB-1)", "Proliferation marker", "Essential for grading; count in hotspots"],
["CD56 (NCAM)", "Moderate", "Supplementary neuroendocrine marker"],
["SSTR2 / SSTR5", "Variable", "Relevant for somatostatin analog therapy and 68Ga-DOTATATE PET imaging"],
["TP53 / RB1", "Molecular marker", "Loss favors NEC over NET G3 when morphology is ambiguous"],
],
col_widths=[4*cm, 5*cm, 8*cm]
)
story.append(ihc_table)
story.append(spacer(0.3))
story.append(sub_heading("7.3 Serum Tumor Markers"))
for b in [
"<b>Chromogranin A (CgA)</b>: diagnostic sensitivity >90%; elevated in most NENs; used for monitoring therapy response. "
"False elevations with PPI use, renal dysfunction, IBD, prostate cancer.",
"<b>24-hour urine 5-HIAA</b> (5-hydroxyindoleacetic acid): specific for serotonin-secreting midgut NETs; key test for carcinoid syndrome.",
"<b>Specific hormones</b>: fasting serum gastrin (gastrinoma), insulin/C-peptide (insulinoma), VIP (VIPoma), glucagon (glucagonoma).",
"<b>NSE (neuron-specific enolase)</b>: less specific; can be elevated in poorly differentiated NECs.",
]:
story.append(bullet(b))
# ── SECTION 8: HISTORICAL EVOLUTION ───────────────────────────────────────────
story.append(spacer())
story.append(section_heading("8. Historical Evolution of WHO Classification"))
story.append(spacer())
hist_table = make_table(
["Year / Edition", "Key Changes"],
[
["WHO 2000", "Introduced grading based on proliferative activity; classified as benign vs. malignant"],
["WHO 2010 (4th Edition)", "Formalized mitotic rate/Ki-67 grading (G1/G2); all NETs classified as malignant with metastatic potential; NECs defined as >20 mitoses/2mm²"],
["WHO 2017 (Pancreatic NENs)", "Added G3 NET category for pancreatic NENs (Ki-67 >20% but well-differentiated morphology)"],
["WHO 2019 (5th Edition – Digestive)", "G3 NET extended to ALL GI/digestive sites; MANEC renamed MiNEN; 'carcinoid' term retired; NEC strictly = poorly differentiated only"],
["WHO 2022 (Endocrine/NEN)", "Landmark consolidation — all GEP-NENs plus non-GEP NENs combined into a single framework; molecular criteria incorporated"],
],
col_widths=[4*cm, 13*cm]
)
story.append(hist_table)
# ── SECTION 9: SUMMARY TABLE ──────────────────────────────────────────────────
story.append(PageBreak())
story.append(section_heading("9. Complete WHO 2019 Classification Summary"))
story.append(spacer())
full_table = make_table(
["Category", "ICD-O", "Differentiation", "Grade", "Mitoses/2mm²", "Ki-67", "Behavior"],
[
["NET G1", "8240/3", "Well", "G1", "< 2", "< 3%", "Indolent"],
["NET G2", "8249/3", "Well", "G2", "2–20", "3–20%", "Intermediate"],
["NET G3", "8249/3", "Well", "G3", "> 20", "> 20%", "Aggressive (but NOT NEC)"],
["NEC – Small Cell (SCNEC)", "8041/3", "Poorly", "High (not graded)", "> 20", "> 20%", "Highly aggressive"],
["NEC – Large Cell (LCNEC)", "8013/3", "Poorly", "High (not graded)", "> 20", "> 20%", "Highly aggressive"],
["MiNEN", "8154/3", "Mixed", "Per component", "Per component", "Per component", "Per highest-grade component"],
],
col_widths=[3.2*cm, 1.8*cm, 2.5*cm, 2.5*cm, 2.5*cm, 2*cm, 2.5*cm]
)
story.append(full_table)
story.append(spacer())
story.append(note(
"Referring to G3 NETs as 'NECs' is a common but INCORRECT practice per current classification. "
"G3 NETs have different molecular biology, prognosis, and treatment response compared to NECs. "
"This distinction is critical for therapeutic decisions — NECs respond to platinum-based chemotherapy "
"while NET G3 does not. (Reference: Helderman et al., Neuroendocrinology 2024; PMID 37690447)"
))
story.append(spacer())
story.append(hr())
story.append(Paragraph(
"References: Robbins & Kumar Basic Pathology (Robbins Pathology); Tietz Textbook of Laboratory Medicine 7th Ed; "
"WHO Classification of Tumours of the Digestive System 5th Ed (2019); WHO Classification of Endocrine and Neuroendocrine Tumours (2022); "
"Helderman et al. Neuroendocrinology 2024 (PMID 37690447); Qasim et al. Cureus 2026.",
footer_style
))
# BUILD
doc.build(story)
print("PDF generated:", OUTPUT)
cd /home/daytona/workspace/nen-pdf && python generate_nen_pdf.py 2>&1
ls -lh /home/daytona/workspace/nen-pdf/NEN_Digestive_System_Classification_Gradi…
NEN Digestive System Classification Grading
PDF Document · PDF