vitalscore
Clinical scoring calculators for Python.
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.
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)
| File | Size | Uploaded | |
|---|---|---|---|
| vitalscore-0.1.1.tar.gz | 32.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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
|