Skip to content

Repository files navigation

NEWS2 Early Warning Monitoring System

Real-Time NEWS2 Patient Scoring and Monitoring with Tiered Alerts, Longitudinal Tracking, and Dual Visualisations


Executive Summary

Tech Stack: Python · FastAPI · Uvicorn · Pydantic · Matplotlib · Pytest · GitHub Actions (CI/CD)

This project is a Python-based clinical monitoring system that implements real-time NEWS2 (National Early Warning Score 2) calculation, tiered alert generation, and longitudinal vital sign tracking. The system accepts structured vital sign inputs via either a command-line interface (CLI) or a deployed FastAPI JSON API and applies deterministic scoring logic to compute NEWS2 values in accordance with established clinical thresholds. Based on aggregated scores, the system generates stratified alert levels to highlight potential patient deterioration and support structured monitoring workflows.

Vital sign data is persistently stored in CSV format with patient-level GDPR-compliant mapping, enabling longitudinal tracking across multiple observations. The system supports both lightweight terminal-based ASCII visualisations and Matplotlib-generated graphical trend plots, allowing users to interpret physiological trajectories over time. This dual visualisation approach demonstrates both operational usability and analytical reporting capability.

The architecture is fully deterministic and rule-based, reflecting direct implementation of validated clinical scoring criteria rather than probabilistic or machine learning inference. Strong input validation ensures structural integrity of submitted data, and outputs are returned in structured JSON format to enable integration with external systems or electronic health record workflows.

Version 2 extends the original CLI tool into a deployable backend service with documented API endpoints, enabling remote access and programmatic consumption. Automated unit tests, API endpoint testing, and continuous integration workflows validate scoring logic, alert generation, and system stability. The modular codebase supports future extension, including potential integration of predictive modelling or machine learning components, while preserving transparent and reproducible clinical scoring logic.

Overall, the system demonstrates deterministic clinical algorithm implementation, backend API deployment, persistent data handling, automated testing, and longitudinal physiological trend analysis within a production-oriented Python project.

The deployed API (v2) is live on Render and can be accessed here for interactive testing and demonstration.

Vitals Tracker Flowchart Figure 1: Overview of patient vitals input, NEWS2 scoring, alert logic, and trend visualisation workflow.


Clinical & Technical Highlights

Feature Description
Clinically-informed input handling Collects vitals and level of consciousness, mapping to structured format.
Tiered alert logic NEWS2 score reflects severity levels clinicians use for escalation decisions.
Trend visualisation ASCII charts for quick CLI checks; Matplotlib plots for detailed analysis and portfolio showcase.
Patient ID anonymisation Ensures privacy and GDPR-compliance for multi-patient tracking.
Robust error handling Handles CSV header mismatches, data type conversions, and overlapping alert thresholds; ensures reliable plotting and accurate historical analysis.
Modular architecture Functions separated by responsibility (input, scoring, alerting, visualization) for maintainability and expansion.
Foundation for predictive analytics Clinically-informed scoring and trend data enable future AI/ML models for deterioration detection and decision support.

CLI vs FastAPI: Skills Showcase

Feature CLI (v1) FastAPI (v2 - Live on Render)
User Interaction Command-line prompts, numeric & categorical inputs JSON-based API requests (GET /Root, POST /add_vitals/, GET /patient/{patient_id}, GET /trends/{patient_id}/png/, GET /trends/{patient_id}/json/)
Data Entry Manual typing via terminal Scriptable input via HTTP requests or Swagger UI
Visualisation ASCII bar charts; Matplotlib plots saved locally API returns JSON data; Matplotlib plots generated on server as PNG
Alerts & Scoring NEWS2 scoring, tiered alerts printed to console NEWS2 scoring calculated server-side; alerts included in structured JSON response
Persistence CSV storage with unique patient IDs CSV storage leveraged for backend; data retrieval via endpoints
Testing & CI/CD Manual verification of CLI workflows Automated endpoint validation using GitHub Actions, ensuring production-readiness
Clinical Insight Real-time feedback at bedside; supports immediate clinical decision-making Enables integration into EHRs, dashboards, or telemedicine apps, supports remote monitoring and analytics

How It Works

  1. User Input

    • Collects patient vitals (BP, HR, RR, Temp, O₂ sats, Level of Consciousness) via CLI.
    • Validates input ranges and types to ensure clinical reliability.
    • Maps multiple patients to anonymized IDs for longitudinal tracking.
  2. Scoring & Alerts

    • Computes NEWS2 score based on entered vitals.
    • Integrated NEWS2 scoring determines tiered alert level (Normal → Mild → Moderate → Severe) reflecting real-world clinical prioritisation.
    • Provides user-friendly alert messages highlighting actionable concerns.
  3. Trend & Output Display

    • Prints recent patient readings with aligned formatting for readability.
    • Generates visualisations:
      • ASCII charts for quick terminal-based monitoring.
        Provides lightweight, real-time monitoring in terminal environments for quick interpretation during ward rounds or telemedicine sessions.
      • Matplotlib plots for portfolio-quality, dual-axis trends (vitals + NEWS2).
        Enables detailed longitudinal monitoring, supporting clinical review, handover, and identification of subtle deterioration patterns over time.
    • Supports retrieval of historical data for multiple patients, enabling longitudinal trend analysis.

CLI Technical Highlights

  • Robust input validation: Ensures safe ranges, correct data types, and consistent nested data structures.
  • Comprehensive error handling:
    • Fixed CSV header issues to avoid KeyErrors.
    • Type conversion for plotting prevents string/float errors.
    • Adjusted diastolic BP alert ranges to prevent overlapping thresholds.
  • Data persistence: Stores historical readings in CSV (vitals.csv) with patient mapping (patient_mapping.csv).
  • Nested dictionary flattening: for CSV compatibility and smooth plotting.
  • ASCII normalisation: Values scaled proportionally to fixed-width bars for quick interpretation.
  • Matplotlib dual-axis plotting: Overlay NEWS2 scores with vitals over time.
  • Timestamp formatting: ISO timestamps converted and formatted for readability in plots.
  • Failsafes and edge-case handling:
    • Minimum/maximum values handled in ASCII plots.
    • Alert thresholds with default 0 if no match found.

Architecture & Implementation

Key Components

  1. Data Persistence
    • save_to_csv(): Writes flattened vitals with patient ID, timestamp, and NEWS2 score.
    • load_from_csv(): Retrieves historical readings for a given patient.
  2. Patient Management
    • get_or_create_patient_id(): Maps multiple patients to unique IDs. Patient ID anonymisation ensure GDPR-compliant data handling
    • find_patient_id(): CLI interface to locate patient ID via full name + DOB.
  3. Input & Validation
    • user_inputs(): Collects vitals in nested dictionary form.
    • validate_input(): Ensures numeric values are within safe clinical ranges.
    • Level of Consciousness handled as categorical input (Yes/No/Unsure).
  4. Alert & Scoring
    • check_alert(): Determines tiered alert level per NEWS2.
    • get_alert_message(): Converts alert level to user-friendly message.
    • Scores summed for total NEWS2; systolic BP counts in total, diastolic printed only.
  5. Display
    • print_patient_vitals(): Prints last 5 historical readings with alignment for readability.
    • plot_ascii(): ASCII-based trend visualisation for lightweight terminal output.
    • plot_matplotlib(): Professional Matplotlib plots with dual-axis for vitals + NEWS2.
  6. CLI Loop
    • Options: Add Reading, View Past Readings, View Trends, Exit.
    • Nested loops for multiple readings, plotting options, and patient queries.

v2 — API Deployment with FastAPI (LIVE)

Python | FastAPI | Pydantic | JSON Output | Render

Live Demo

  • Deployed API on Render: https://vitals-tracker-cli.onrender.com/docs
  • API endpoints:
    • GET /Root → {"message":"Clinically-Informed Vitals Tracker API is running"}
    • POST /add_vitals/ → Add new patient vitals; returns NEWS2 + alerts
    • GET /patient/{patient_id} → Retrieve patient history
    • GET /trends/{patient_id}/png → Matplotlib plot (PNG)
    • GET /trends/{patient_id}/json → Historical vitals & NEWS2 as JSON

Overview

  • CLI logic wrapped into deployable API
  • JSON output includes:
    • patient_id
    • total_news2_score
    • alerts (clinically structured)
  • Modular separation of CLI vs API code for maintainability and integration.
  • Enables future integration with your Early Warning Score (EWS) project for predictive clinical monitoring.

Endpoints

Endpoint Method Description
/Root GET Sanity check confirming API is running
/add_vitals/ POST Input: patient_name, dob, vitals JSON; Output: patient_id, total NEWS2, alerts
/patient/{patient_id} GET Retrieve all saved vitals for a patient as JSON list
/trends/{patient_id}/png GET Matplotlib plot of vitals + NEWS2 trends
/trends/{patient_id}/json GET Vitals and NEWS2 history as JSON

API JSON Output Keys

1. Add Vitals Response (POST /add_vitals/)

Key Description
patient_id Unique identifier for the patient (generated by the system)
total_news2_score Calculated NEWS2 score based on the submitted vitals
alerts Clinically structured dictionary of alert messages per vital sign; includes:
- value: numeric or string value of the vital
- level: severity category (e.g., Normal, Mild Alert, Moderate Alert, Severe Alert)
- score: NEWS2 points assigned for that vital
- message: Clinical advice or escalation recommendation

2. Fetch Past Readings (GET /patient/{patient_id})

Returns a list of historical readings:

Key Description
patient_id Unique patient identifier
timestamp ISO 8601 datetime of the reading
news2_score NEWS2 score at that timestamp
bp_systolic Systolic blood pressure
bp_diastolic Diastolic blood pressure
heart_rate Heart rate (bpm)
respiratory_rate Respiratory rate (breaths per minute)
temperature Body temperature (°C)
oxygen_sats Oxygen saturation (%)
loc Level of consciousness (Yes = fully awake, No = not fully awake)

3. Trends Responses

PNG Plot (GET /trends/{patient_id}/png)

  • Returns a binary image file of vital trends over time (Matplotlib plot).

JSON Trend Data (GET /trends/{patient_id}/json)

  • Returns historical vitals in JSON format.
  • Keys are identical to the Fetch Past Readings endpoint.

API Screenshots

Swagger UI Home
Figure 2: FastAPI Swagger UI home page showing available endpoints.

Swagger /Root
Figure 3: /Root endpoint returning a confirmation message.

Swagger /add_vitals Example
Figure 4: /add_vitals endpoint example adding vitals and structured JSON response with NEWS2 score and alerts.

Swagger /patient Example
Figure 5: /patient endpoint example fetches past readings for a patient, showing historical data in JSON format.

Swagger /trends PNG Example
Figure 6: /trends endpoint example returns a PNG plot of historical vital trends over time generated via Matplotlib.

Swagger /trends JSON Example
Figure 7: /trends endpoint example returns historical vital trends over time as JSON.

Root Endpoint

@app.get("/")
def root():
    return {"message": "Clinically-Informed Vitals Tracker API is running"}

API Testing / CI

Automated endpoint tests are run using GitHub Actions to ensure the API deployment works correctly.

  • Workflow file: .github/workflows/fastapi-app.yml
  • Uses pytest and httpx to test endpoints locally and against the live deployment.
  • Tests include:
    • GET / returns 200 and a JSON message confirming the API is running.
    • POST /add_vitals/ accepts a sample vitals payload, returns patient_id, total_news2_score, and alerts.
    • GET /patient/{patient_id} returns a list of historical readings.
    • Basic validation that responses have correct JSON structure and data types.

Example test snippet:

from fastapi.testclient import TestClient
from v2_api.app import app

client = TestClient(app)

def test_root_endpoint():
    # Test that the root endpoint returns a 200 and contains a message
    response = client.get("/")
    assert response.status_code == 200
    assert "message" in response.json()

def test_add_vitals_and_fetch():
    # Sample vitals payload
    vitals_payload = {
        "Blood pressure": {"systolic": 120, "diastolic": 80},
        "Heart rate": 75,
        "Respiratory rate": 18,
        "Temperature": 37.0,
        "Oxygen saturations": 98,
        "Level of consciousness (fully awake and responsive?)": "Yes"
    }
  • Workflow triggers:
    • Push to main
    • Scheduled weekly checks

This demonstrates a production-ready backend with continuous validation.

How to run locally

cd v2_api
uvicorn app:app --reload
  • Visit http://127.0.0.1:8000/docs for interactive Swagger UI
  • Use the UI to test endpoints such as:
    • GET /Root → sanity check that the API is running
    • POST /add_vitals/ → add new patient vitals and receive NEWS2 scores + alerts
    • GET /patient/{patient_id} → fetch historical vitals for a patient
    • GET /trends/{patient_id}/png → retrieve Matplotlib trend plot as PNG
    • GET /trends/{patient_id}/json → fetch historical vitals and NEWS2 scores as JSON
  • You can also test endpoints programmatically using httpx or your own scripts.

Notes / Future Work

  • Demonstrates production-ready backend for a clinician-technologist portfolio
  • JSON outputs and CSV persistence make it easy to integrate into dashboards, hospital EHRs, or telemedicine apps
  • Future improvements:
    • Add automated notifications/alerts for moderate or severe NEWS2 scores.
    • Integrate predictive AI/ML models for patient deterioration detection.
    • Expand support for additional vital signs or custom scoring systems.
    • Implement persistent logging and analytics for longitudinal patient data.
  • No frontend required; API + CLI + deployment is enough for portfolio showcase

Future Improvements

  • AI/ML Integration: Predictive models for deterioration, anomaly detection.
  • Web or GUI Front-End: Interactive dashboards for hospitals or telemedicine.
  • EHR Integration: Directly pull/push patient vitals for automated trend analysis.
  • Alerts & Notifications: Automated escalation for moderate/severe NEWS2 scores.

Quick Start

Clone and run:

git clone https://github.com/SimonYip22/NEWS2-Vitals-Tracker.git
cd NEWS2-Vitals-Tracker
python3 vitals_tracker.py
  • Follow CLI prompts to add readings, view past vitals, or plot trends.
  • Select ASCII for lightweight monitoring or Matplotlib for portfolio-ready visuals.

CLI Matplotlib Plot Example

Vitals Tracker Matplotlib Plot Figure 8: Professional trend visualisation of a patient’s vitals over time, highlighting systolic BP, heart rate, respiratory rate, temperature, oxygen saturations, and NEWS2 scores. Enables clinicians to rapidly detect deterioration and make informed escalation decisions.

  • The included vitals-tracker-matplotlib.png demonstrates a sample patient's vitals trends over time. It includes:
    • Line plots for key vital signs: Systolic BP, Heart Rate, Respiratory Rate, Temperature, and Oxygen Saturations.
    • Dual-axis plotting: Vitals on the primary y-axis and NEWS2 scores on the secondary y-axis (red dashed line).
    • Markers at each data point for clarity.
    • Formatted timestamps on the x-axis for readability.
  • This PNG exemplifies the professional plotting capabilities of the CLI tool and can be used in portfolio showcases or presentations to illustrate the trend visualisation functionality.

CLI Example session

Patient vitals Monitoring App
=============================
1. Add new reading
2. View past readings
3. View trends
4. Exit

Select an option (1/2/3/4): 1
=== Enter Patient Vitals ===
Systolic BP (mmHg): 120
Diastolic BP (mmHg): 80
Heart Rate (bpm): 75
Respiratory Rate (bpm): 16
Temperature (°C): 37
Oxygen Saturation (%): 98
Fully awake and responsive? (Yes/No/Unsure): Yes
Vitals recorded successfully!
Total NEWS2 score: 0
Patient vitals Monitoring App
=============================
1. Add new reading
2. View past readings
3. View trends
4. Exit

Select an option (1/2/3/4): 2
Enter full name: simon yip
Please enter date of birth (dd/mm/yy): 26/11/00
Patient found. ID: 1. Retrieving vitals...

Vitals for patient 1:

Patient has 4 recorded vital sign entries. Showing the last 4:
2025-08-28T00:11:30.266959 | BP: 120/ 80 mmHg | HR:  75 bpm | RR:  16 bpm | Temp:    37 °C | O2 sats:  98 % | Awake and fully responsive?: Yes | 
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2025-08-28T00:11:55.336599 | BP: 110/ 75 mmHg | HR:  95 bpm | RR:  22 bpm | Temp:  38.5 °C | O2 sats:  95 % | Awake and fully responsive?: Yes | 
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2025-08-28T00:12:25.660340 | BP: 100/ 70 mmHg | HR: 115 bpm | RR:  25 bpm | Temp:  39.2 °C | O2 sats:  93 % | Awake and fully responsive?: Yes | 
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2025-08-28T00:12:55.637678 | BP:  85/ 55 mmHg | HR: 135 bpm | RR:  28 bpm | Temp:  34.5 °C | O2 sats:  89 % | Awake and fully responsive?: No/Unsure | 
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
View another past reading? (y/n): n
Patient vitals Monitoring App
=============================
1. Add new reading
2. View past readings
3. View trends
4. Exit

Select an option (1/2/3/4): 3
Enter full name: simon yip
Please enter date of birth (dd/mm/yy): 26/11/00
Patient found. ID: 1. Retrieving vitals...
View trends as (1) ASCII or (2) matplotlib? 1

news2_score trends:
2025-08-28T00:11:30.266959 |   0.0 | 

------------------------------------------------------------
2025-08-28T00:11:55.336599 |   4.0 | ###########

------------------------------------------------------------
2025-08-28T00:12:25.660340 |  11.0 | ##############################

------------------------------------------------------------
2025-08-28T00:12:55.637678 |  18.0 | ##################################################

------------------------------------------------------------

bp_systolic trends:
2025-08-28T00:11:30.266959 | 120.0 | ##################################################

------------------------------------------------------------
2025-08-28T00:11:55.336599 | 110.0 | ###################################

------------------------------------------------------------
2025-08-28T00:12:25.660340 | 100.0 | #####################

------------------------------------------------------------
2025-08-28T00:12:55.637678 |  85.0 | 

------------------------------------------------------------

bp_diastolic trends:
2025-08-28T00:11:30.266959 |  80.0 | ##################################################

------------------------------------------------------------
2025-08-28T00:11:55.336599 |  75.0 | ########################################

------------------------------------------------------------
2025-08-28T00:12:25.660340 |  70.0 | ##############################

------------------------------------------------------------
2025-08-28T00:12:55.637678 |  55.0 | 

------------------------------------------------------------

heart_rate trends:
2025-08-28T00:11:30.266959 |  75.0 | 

------------------------------------------------------------
2025-08-28T00:11:55.336599 |  95.0 | ################

------------------------------------------------------------
2025-08-28T00:12:25.660340 | 115.0 | #################################

------------------------------------------------------------
2025-08-28T00:12:55.637678 | 135.0 | ##################################################

------------------------------------------------------------

respiratory_rate trends:
2025-08-28T00:11:30.266959 |  16.0 | 

------------------------------------------------------------
2025-08-28T00:11:55.336599 |  22.0 | #########################

------------------------------------------------------------
2025-08-28T00:12:25.660340 |  25.0 | #####################################

------------------------------------------------------------
2025-08-28T00:12:55.637678 |  28.0 | ##################################################

------------------------------------------------------------

temperature trends:
2025-08-28T00:11:30.266959 |  37.0 | ##########################

------------------------------------------------------------
2025-08-28T00:11:55.336599 |  38.5 | ##########################################

------------------------------------------------------------
2025-08-28T00:12:25.660340 |  39.2 | ##################################################

------------------------------------------------------------
2025-08-28T00:12:55.637678 |  34.5 | 

------------------------------------------------------------

oxygen_sats trends:
2025-08-28T00:11:30.266959 |  98.0 | ##################################################

------------------------------------------------------------
2025-08-28T00:11:55.336599 |  95.0 | #################################

------------------------------------------------------------
2025-08-28T00:12:25.660340 |  93.0 | ######################

------------------------------------------------------------
2025-08-28T00:12:55.637678 |  89.0 | 

------------------------------------------------------------
  • Full sample_run.md included demonstrating normal → severe alerts, past readings, ASCII trends, and Matplotlib plots.

Running Tests

Test the CLI workflows, NEWS2 scoring, CSV persistence, and plotting functions:

pytest -v

Tests include:

  • Validation of vital sign input ranges (validate_input)
  • Correct NEWS2 alert level assignment (check_alert, get_alert_message)
  • Flattening of nested vitals for CSV storage (flatten_vitals)
  • Patient management and CSV persistence (get_or_create_patient_id, save_to_csv, load_from_csv)
  • Display functions and CLI output (print_patient_vitals)
  • Trend visualisation functions (plot_ascii, plot_matplotlib)

Project Structure

NEWS2-Vitals-Tracker/
├── .github/
│   └── workflows/
│       ├── fastapi-app.yml 
│       └── python-app.yml
├── test_vitals_tracker_CLI/
│   ├── test_patient_mapping.csv
│   ├── test_vitals_tracker.py
│   └── test_vitals.csv
├── v2_api/
│   ├── app.py
│   ├── swagger_get_patient.png
│   ├── swagger_get_root.png
│   ├── swagger_get_trends_json.png
│   ├── swagger_get_trends_png.png
│   ├── swagger_home.png
│   ├── swagger_post_add_vitals.png
│   ├── test_api_endpoint.py
│   └── vitals_tracker_v2.py
├── notes.md
├── patient_mapping.csv
├── README.md
├── reflection.md
├── requirements.txt
├── sample_run.md
├── vitals_tracker.py
├── vitals-tracker-flowchart.png
├── vitals-tracker-matplotlib.png
├── vitals.csv

Explanations:

  • github/workflows/
    • python-app.yml — Runs unit tests and CLI validation
    • fastapi-app.yml — Tests FastAPI endpoints and API responses
  • test_vitals_tracker_CLI/ — Unit tests and mock CSV files for testing input validation, scoring, and plotting
  • v2_api/
    • app.py — API routes wrapping CLI logic
    • swagger_*.png — Screenshots of Swagger UI endpoints
    • test_api_endpoint.py — Tests for API endpoints
    • vitals_tracker_v2.py — Core functions for API usage
  • notes.md — Daily development logs
  • patient_mapping.csv — Maps patient names + DOB to IDs.
  • README.md — Project documentation
  • reflection.md - Final project reflection
  • requirements.txt — Dependencies for the API and CLI
  • sample_run.md — Demonstrates all scenarios.
  • vitals_tracker.py — Main CLI program.
  • vitals-tracker-flowchart.png - Flowchart explaining logic
  • vitals-tracker-matplotlib.png — Example Matplotlib output.
  • vitals.csv — Historical patient readings for persistence.

Disclaimer

  • Educational and portfolio purposes only.
  • Not a substitute for professional medical advice.
  • For emergency concerns, contact NHS 111 or 999 immediately.

About

Real-time clinical vitals monitoring system that calculates NEWS2 scores, triggers tiered risk alerts, tracks temporal trends, and maps data using GDPR-compliant persistence

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages