Skip to main content

vitalscore

Clinical scoring calculators for Python.

PyPI version Python 3.10+ License: MIT

vitalscore provides typed, validated implementations of the clinical scoring tools used in emergency medicine, critical care, and pre-hospital settings — all exposed as clean Python dataclasses with interpretation strings built in.


Scores included

Score Function Description
GCS score_gcs Glasgow Coma Scale (3–15)
AVPU score_avpu Consciousness level: Alert / Voice / Pain / Unresponsive
APGAR score_apgar Newborn assessment (0–10)
START score_start Mass-casualty triage: Immediate / Delayed / Minor / Deceased
NEWS2 score_news2 National Early Warning Score 2 (0–20)
qSOFA score_qsofa Sepsis screening (0–3)
HEART score_heart Chest pain risk stratification (0–10)

Assessment models

Model Description
CUPS Critical / Unstable / Potentially unstable / Stable
OPQRST Symptom characterisation mnemonic
SAMPLE Patient history mnemonic

Installation

pip install vitalscore

Requires Python 3.10+. No external dependencies.


Quick start

from vitalscore import score_gcs, score_avpu, score_start, score_news2, score_qsofa, score_heart
from vitalscore import OPQRST, SAMPLE, CUPS, CUPSCategory
from vitalscore import batch_triage, TriagePatient

GCS — Glasgow Coma Scale

result = score_gcs(eye=2, verbal=1, motor=3)

result.total        # 6
result.severity     # "Severe TBI"
result.interpretation
# "Severe TBI (GCS 6) — critical, consider intubation and ICU"

print(result)
# GCS 6/15  [E2 V1 M3]
#   Eye:    2 — Eye opening to pain
#   Verbal: 1 — No verbal response
#   Motor:  3 — Flexion to pain (decorticate)
#   → Severe TBI (GCS 6) — critical, consider intubation and ICU

AVPU — Consciousness scale

result = score_avpu("P")      # also accepts AVPULevel enum

result.is_critical    # True
result.gcs_equivalent # "GCS ~8 (estimated)"
result.recommended_action
# "Immediate intervention; manage airway, IV access, call for help"

APGAR — Newborn score

result = score_apgar(appearance=2, pulse=2, grimace=1, activity=1, respiration=2)

result.total     # 8
result.category  # "Normal"

START — Mass-casualty triage

# Walking wounded → Minor (Green)
score_start(can_walk=True, respiratory_rate=18).priority
# <STARTPriority.MINOR: 'Minor'>

# Apnoeic → Deceased (Black)
score_start(can_walk=False, respiratory_rate=None).priority
# <STARTPriority.DECEASED: 'Deceased'>

# RR > 30 → Immediate (Red)
score_start(can_walk=False, respiratory_rate=35).priority
# <STARTPriority.IMMEDIATE: 'Immediate'>

# All OK → Delayed (Yellow)
score_start(
    can_walk=False, respiratory_rate=20,
    has_radial_pulse=True, follows_commands=True
).priority
# <STARTPriority.DELAYED: 'Delayed'>

NEWS2 — National Early Warning Score 2

result = score_news2(
    respiratory_rate=22,
    spo2=94.0,
    on_oxygen=False,
    systolic_bp=105,
    heart_rate=98,
    consciousness="A",   # ACVPU: A / C / V / P / U
    temperature=37.2,
)

result.total        # 5
result.risk.value   # "Medium"
result.interpretation
# "NEWS2 5 — MEDIUM risk. Urgent SpR/senior clinician review within 1 hour."

# For COPD patients (SpO₂ target 88–92%), use Scale 2:
result = score_news2(..., use_spo2_scale2=True)

qSOFA — Sepsis screening

result = score_qsofa(gcs=13, respiratory_rate=24, systolic_bp=95)

result.total         # 3
result.sepsis_alert  # True
result.interpretation
# "qSOFA 3/3 — SEPSIS ALERT. Urgent full SOFA assessment, blood cultures, lactate."

HEART — Chest pain risk

from vitalscore.scores.heart import HistoryScore, ECGScore, AgeScore, RiskFactorScore, TroponinScore

result = score_heart(
    history=HistoryScore.HIGHLY_SUSPICIOUS,
    ecg=ECGScore.SIGNIFICANT_ST_DEV,
    age=AgeScore.AGE_65_UP,
    risk_factors=RiskFactorScore.THREE_OR_MORE_OR_KNOWN_DISEASE,
    troponin=TroponinScore.ABOVE_THREE,
)
# or pass plain integers 0–2 for each field

result.total            # 10
result.risk_category    # "High"
result.mace_probability # "~50–65%"

Assessment models

CUPS — Transport priority

from vitalscore import CUPS, CUPSCategory

c = CUPS(category=CUPSCategory.CRITICAL, chief_complaint="Unresponsive, no pulse")
c.transport_priority   # 1
c.description          # "Life-threatening condition. Immediate intervention required..."

# String input also accepted:
CUPS(category="Unstable")

OPQRST — Symptom characterisation

from vitalscore import OPQRST

pain = OPQRST(
    onset="Sudden, during exertion",
    quality="Crushing pressure",
    radiation="Radiates to left arm and jaw",
    severity=9,          # 0–10 NRS
    time="30 minutes",
    associated="Diaphoresis, nausea",
)
print(pain.summary)

SAMPLE — Patient history

from vitalscore import SAMPLE

history = SAMPLE(
    signs_symptoms="Crushing chest pain, diaphoresis",
    allergies="Penicillin (anaphylaxis)",
    medications="Aspirin 81 mg, Metformin 500 mg BD",
    pertinent_history="HTN, T2DM, previous MI 2019",
    last_oral_intake="6 hours ago",
    events="Onset at rest, watching TV",
)

history.has_allergies   # True
print(history)

Batch triage

Score a list of patients and receive them sorted from most to least critical.

from vitalscore import batch_triage, TriagePatient
from vitalscore.batch import format_triage_report

patients = [
    TriagePatient("P1", can_walk=True,  respiratory_rate=18),
    TriagePatient("P2", can_walk=False, respiratory_rate=35),
    TriagePatient("P3", can_walk=False, respiratory_rate=None),   # apnoeic
    TriagePatient("P4", can_walk=False, respiratory_rate=20,
                  has_radial_pulse=True, follows_commands=True),
]

sorted_pts = batch_triage(patients, method="start")
# → [P2 (Immediate), P4 (Delayed), P1 (Minor), P3 (Deceased)]

print(format_triage_report(sorted_pts))

For NEWS2 batch scoring, populate the NEWS2 fields (spo2, on_oxygen, systolic_bp, heart_rate, consciousness, temperature) and pass method="news2". Use method="both" to run both algorithms simultaneously.


CLI

Every score is accessible from the terminal:

# GCS
vitalscore gcs --eye 2 --verbal 1 --motor 3

# AVPU
vitalscore avpu --level P

# APGAR
vitalscore apgar --appearance 2 --pulse 2 --grimace 1 --activity 1 --respiration 2

# START triage
vitalscore start --no-walk --rr 35
vitalscore start --no-walk --rr 20 --pulse --follows-commands

# NEWS2
vitalscore news2 --rr 22 --spo2 94 --no-o2 --sbp 105 --hr 98 --acvpu A --temp 37.2
vitalscore news2 --rr 10 --spo2 89 --o2 --sbp 90 --hr 115 --acvpu V --temp 35.0 --scale2

# qSOFA
vitalscore qsofa --gcs 13 --rr 24 --sbp 95

# HEART
vitalscore heart --history 2 --ecg 1 --age 2 --risk 2 --troponin 1

Output is ANSI-coloured when run in a terminal (red for critical, yellow for urgent, green for low risk).


Input validation

All score_* functions raise a ValueError with a clear message if any input is out of range:

score_gcs(eye=0, verbal=5, motor=6)
# ValueError: Eye opening must be 1–4, got 0

score_news2(respiratory_rate=18, spo2=110.0, ...)
# ValueError: spo2 must be 50–100%, got 110.0

Project layout

vitalscore/
├── vitalscore/
│   ├── scores/        # gcs.py  avpu.py  apgar.py  start.py
│   │                  # news2.py  qsofa.py  heart.py
│   ├── models/        # assessment.py  (CUPS, OPQRST, SAMPLE)
│   ├── batch.py       # batch_triage, TriagePatient, format_triage_report
│   └── cli.py         # vitalscore CLI entry point
└── tests/
    └── test_scores.py # 80 unit tests

Contributing

Pull requests are welcome. Please run the test suite before submitting:

pip install -e ".[dev]"
pytest

Disclaimer

vitalscore is a software tool intended to support clinical decision-making, not replace it. Always defer to qualified medical professionals and local clinical guidelines. The authors accept no liability for patient outcomes.

Read the full disclaimer →


License

MIT © 2026

Metadata

Release files for vitalscore 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for vitalscore 0.1.1
File Size Uploaded
vitalscore-0.1.1.tar.gz 32.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for vitalscore 0.1.1
File Interpreter ABI Platform
vitalscore-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 62.4 kB

Release files / vitalscore-0.1.1.tar.gz

Download URL vitalscore-0.1.1.tar.gz
Size 32.0 kB
Tags Source
SHA-256 checksum
How to use checksums
97ff3c6078d4110e03f35115e512b0bbd01a6413dedeb51b19ab99213167cb61
BLAKE2b-256 checksum
How to use checksums
4c818c727a6da3b63f2c41644803fc044bfbfc48c74fb5d7e16955ac4af91c62
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.3

Release files / vitalscore-0.1.1-py3-none-any.whl

Download URL vitalscore-0.1.1-py3-none-any.whl
Size 30.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2badef7051cb78c5b3b0fc48b6a9d8f91c548dd88b3417174ec1a22a3df5eb70
BLAKE2b-256 checksum
How to use checksums
050f60361b0867894b0c68513efffb9de891ce187418f99cd2f87e4e465e7b80
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.3

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page