JBB
setup_hooks(); } return self::$instance; } /** * Constructor is private to enforce singleton. */ private function __construct() { // Intentionally left blank. } /** * Setup WordPress hooks */ private function setup_hooks() { add_action( ‘init’, array( $this, ‘register_post_type’ ) ); add_action( ‘init’, array( $this, ‘register_shortcodes’ ) ); // Form submission handlers (logged-in only): use admin_post action. add_action( ‘admin_post_lvad_entry_submit’, array( $this, ‘handle_entry_submission’ ) ); // Enqueue assets for front-end chart and form. add_action( ‘wp_enqueue_scripts’, array( $this, ‘enqueue_assets’ ) ); } /** * Register the Log Entry custom post type. * * Using a non-public CPT but with show_ui so it’s manageable in WP admin. */ public static function register_post_type() { $labels = array( ‘name’ => __( ‘Log Entries’, ‘lvad-health-monitor’ ), ‘singular_name’ => __( ‘Log Entry’, ‘lvad-health-monitor’ ), ‘menu_name’ => __( ‘LVAD Log Entries’, ‘lvad-health-monitor’ ), ‘name_admin_bar’ => __( ‘Log Entry’, ‘lvad-health-monitor’ ), ‘add_new’ => __( ‘Add New’, ‘lvad-health-monitor’ ), ‘add_new_item’ => __( ‘Add New Log Entry’, ‘lvad-health-monitor’ ), ‘new_item’ => __( ‘New Log Entry’, ‘lvad-health-monitor’ ), ‘edit_item’ => __( ‘Edit Log Entry’, ‘lvad-health-monitor’ ), ‘view_item’ => __( ‘View Log Entry’, ‘lvad-health-monitor’ ), ‘all_items’ => __( ‘All Log Entries’, ‘lvad-health-monitor’ ), ‘search_items’ => __( ‘Search Log Entries’, ‘lvad-health-monitor’ ), ‘not_found’ => __( ‘No log entries found.’, ‘lvad-health-monitor’ ), ‘not_found_in_trash’ => __( ‘No log entries found in Trash.’, ‘lvad-health-monitor’ ), ); $args = array( ‘labels’ => $labels, ‘public’ => false, // private to site (not queryable by public) ‘show_ui’ => true, // show in admin ‘show_in_menu’ => true, ‘menu_position’ => 26, ‘menu_icon’ => ‘dashicons-heart’, ‘supports’ => array( ‘title’, ‘editor’, ‘author’ ), ‘has_archive’ => false, ‘rewrite’ => false, ‘capability_type’ => ‘post’, ); register_post_type( ‘log_entry’, $args ); } /** * Register shortcodes. */ public function register_shortcodes() { add_shortcode( ‘lvad_entry_form’, array( $this, ‘render_entry_form_shortcode’ ) ); add_shortcode( ‘lvad_dashboard’, array( $this, ‘render_dashboard_shortcode’ ) ); } /** * Enqueue frontend assets including Chart.js from CDN and a small inline initializer. */ public function enqueue_assets() { // Only enqueue Chart.js when necessary. // We’ll always register so it’s available when needed by the shortcode. wp_register_script( ‘lvad-chartjs’, ‘https://cdn.jsdelivr.net/npm/chart.js’, array(), ‘4.4.1’, // It’s good practice to add a version number true ); // Optional small stylesheet for table/chart spacing wp_register_style( ‘lvad-styles’, false, array(), ‘1.0.0’ // Example version ); $custom_css = ‘ .lvad-entry-form p { margin-bottom: 1rem; } .lvad-entry-form label { font-weight: bold; display: block; margin-bottom: 0.25rem; } .lvad-entry-form input, .lvad-entry-form textarea { width: 100%; max-width: 400px; padding: 8px; } .lvad-table { width:100%; border-collapse: collapse; margin-bottom: 1rem; } .lvad-table th, .lvad-table td { border:1px solid #ddd; padding: 8px; text-align:left; font-size:14px; } .lvad-table thead th { background-color: #f7f7f7; } .lvad-chart-wrap { max-width:900px; margin: 2rem auto; } ‘; wp_add_inline_style( ‘lvad-styles’, $custom_css ); wp_enqueue_style( ‘lvad-styles’ ); } /* ———————- Entry Form ———————- */ /** * Render the entry form shortcode output. * * Only visible to logged-in users. The actual form posts to admin-post.php and action ‘lvad_entry_submit’. * * @return string HTML */ public function render_entry_form_shortcode() { if ( ! is_user_logged_in() ) { return ‘

Please log in to enter your data.

‘; } // For UX: preserve previous values after redirect (if provided) $old = array(); foreach ( array_merge( $this->meta_keys, array( ‘notes’ ) ) as $k ) { $old[ $k ] = isset( $_REQUEST[ $k ] ) ? esc_attr( wp_unslash( $_REQUEST[ $k ] ) ) : ”; } $action_url = esc_url( admin_url( ‘admin-post.php’ ) ); ob_start(); ?>

Weight (kg) Pump Flow (L/min) Power (W) Pulsatility Index (PI) Heart Rate (bpm) SpO2 (%) INR Warfarin Dose (mg) Notes

wp_strip_all_tags( $post_title ), ‘post_content’ => $notes, ‘post_status’ => ‘private’, ‘post_type’ => ‘log_entry’, ‘post_author’ => $current_user->ID, ); $post_id = wp_insert_post( $post_data, true ); if ( is_wp_error( $post_id ) ) { // Something went wrong; redirect back with failure. $redirect = wp_get_referer() ? wp_get_referer() : home_url(); wp_safe_redirect( add_query_arg( ‘lvad_status’, ‘error’, $redirect ) ); exit; } // Save each meta field after sanitizing. foreach ( $this->meta_keys as $key ) { if ( isset( $_POST[ $key ] ) && ” !== $_POST[ $key ] ) { // Sanitize as a floating point number. $sanitized = floatval( wp_unslash( $_POST[ $key ] ) ); update_post_meta( $post_id, $key, $sanitized ); } else { // If field is omitted or blank, ensure no old value persists. delete_post_meta( $post_id, $key ); } } // Successful submission — redirect back with success param. $redirect = wp_get_referer() ? wp_get_referer() : home_url(); $redirect = add_query_arg( ‘lvad_status’, ‘success’, $redirect ); wp_safe_redirect( $redirect ); exit; } /* ———————- Dashboard ———————- */ /** * Render the per-user dashboard with recent entries and Chart.js visualization. * * Only the logged-in user who owns the data can see their entries. * * @return string HTML */ public function render_dashboard_shortcode() { if ( ! is_user_logged_in() ) { return ‘

Please log in to view your dashboard.

‘; } $current_user = wp_get_current_user(); $user_id = $current_user->ID; // Query the 10 most recent entries by this user $args = array( ‘post_type’ => ‘log_entry’, ‘posts_per_page’ => 10, ‘post_status’ => ‘private’, // as created earlier ‘author’ => $user_id, ‘orderby’ => ‘date’, ‘order’ => ‘DESC’, ); $query = new WP_Query( $args ); ob_start(); if ( ! $query->have_posts() ) { echo ‘

No log entries found. Use the form to add your first entry.

‘; wp_reset_postdata(); return ob_get_clean(); } // Build table header and rows echo ‘‘; echo ‘‘; echo ‘‘; echo ‘‘; echo ‘‘; echo ‘‘; echo ‘‘; echo ‘‘; echo ‘‘; echo ‘‘; echo ‘‘; echo ‘‘; echo ‘‘; echo ‘‘; echo ‘‘; $labels = array(); // x-axis labels (dates) $data_weight = array(); // weight series $data_map = array(); // map series $data_pump_flow = array(); // pump flow series while ( $query->have_posts() ) { $query->the_post(); $post_id = get_the_ID(); $post_date = get_the_date( ‘Y-m-d H:i’, $post_id ); echo ‘‘; echo ‘‘; // Fetch meta values $meta = array(); foreach ( $this->meta_keys as $key ) { $meta_value = get_post_meta( $post_id, $key, true ); // Use ‘—’ for empty values for consistent display. $meta[ $key ] = ( ” !== $meta_value ) ? $meta_value : ‘—’; } echo ‘‘; echo ‘‘; echo ‘‘; echo ‘‘; echo ‘‘; echo ‘‘; echo ‘‘; echo ‘‘; echo ‘‘; echo ‘‘; echo ‘‘; // Prepare chart arrays (keep same order as table: newest -> oldest) $labels[] = get_the_date( ‘M j’, $post_id ); // Use a shorter date format for the chart // For JS, use null for empty values so Chart.js can handle gaps. $data_weight[] = is_numeric( $meta[‘weight’] ) ? floatval( $meta[‘weight’] ) : null; $data_map[] = is_numeric( $meta[‘map’] ) ? floatval( $meta[‘map’] ) : null; $data_pump_flow[] = is_numeric( $meta[‘pump_flow’] ) ? floatval( $meta[‘pump_flow’] ) : null; } echo ‘‘; echo ‘
DateMAPWeightPump FlowPowerPIHRSpO2INRWarfarin DoseNotes
‘ . esc_html( $post_date ) . ‘‘ . esc_html( $meta[‘map’] ) . ‘‘ . esc_html( $meta[‘weight’] ) . ‘‘ . esc_html( $meta[‘pump_flow’] ) . ‘‘ . esc_html( $meta[‘power’] ) . ‘‘ . esc_html( $meta[‘pi’] ) . ‘‘ . esc_html( $meta[‘hr’] ) . ‘‘ . esc_html( $meta[‘spo2’] ) . ‘‘ . esc_html( $meta[‘inr’] ) . ‘‘ . esc_html( $meta[‘warfarin_dose’] ) . ‘‘ . wp_kses_post( wp_trim_words( get_the_content(), 15, ‘…’ ) ) . ‘
‘; wp_reset_postdata(); // Chart container echo ‘
‘; echo ‘‘; echo ‘
‘; // Prepare JS data for chart in chronological order (oldest -> newest). $labels_js = array_reverse( $labels ); $weight_js = array_reverse( $data_weight ); $map_js = array_reverse( $data_map ); $pump_js = array_reverse( $data_pump_flow ); // Enqueue Chart.js and add inline init script using a localized object. wp_enqueue_script( ‘lvad-chartjs’ ); $chart_data = array( ‘labels’ => $labels_js, ‘datasets’ => array( ‘weight’ => array( ‘label’ => ‘Weight (kg)’, ‘data’ => $weight_js, ), ‘map’ => array( ‘label’ => ‘MAP’, ‘data’ => $map_js, ), ‘pump’ => array( ‘label’ => ‘Pump Flow (L/min)’, ‘data’ => $pump_js, ), ), ); // Localize the data into a JS var LVAD_CHART_DATA attached to lvad-chartjs wp_localize_script( ‘lvad-chartjs’, ‘LVAD_CHART_DATA’, $chart_data ); // Add inline script that initializes the Chart. $inline_js = <<<'JS' (function(){ // Wait until DOM and Chart are ready document.addEventListener('DOMContentLoaded', function(){ if ( typeof Chart === 'undefined' || typeof LVAD_CHART_DATA === 'undefined' ) { console.error('Chart.js or chart data not available.'); return; } const ctx = document.getElementById('lvadChart'); if (!ctx) return; // Helper to check if a dataset has at least one numeric value. function hasNumericData(dataArray) { if (!Array.isArray(dataArray)) return false; return dataArray.some(val => val !== null && !isNaN(val)); } const datasets = []; const chartConfig = LVAD_CHART_DATA.datasets; // Dynamically build datasets array, only including those with data. if (chartConfig.weight && hasNumericData(chartConfig.weight.data)) { datasets.push({ label: chartConfig.weight.label, data: chartConfig.weight.data, borderColor: ‘rgb(75, 192, 192)’, tension: 0.2, yAxisID: ‘yWeight’, }); } if (chartConfig.map && hasNumericData(chartConfig.map.data)) { datasets.push({ label: chartConfig.map.label, data: chartConfig.map.data, borderColor: ‘rgb(255, 99, 132)’, tension: 0.2, yAxisID: ‘yMap’, }); } if (chartConfig.pump && hasNumericData(chartConfig.pump.data)) { datasets.push({ label: chartConfig.pump.label, data: chartConfig.pump.data, borderColor: ‘rgb(54, 162, 235)’, tension: 0.2, yAxisID: ‘yPump’, }); } if (datasets.length === 0) { // Don’t render an empty chart return; } new Chart(ctx, { type: ‘line’, data: { labels: LVAD_CHART_DATA.labels, datasets: datasets }, options: { responsive: true, maintainAspectRatio: false, scales: { yWeight: { type: ‘linear’, display: true, position: ‘left’, title: { display: true, text: ‘Weight (kg)’ } }, yMap: { type: ‘linear’, display: true, position: ‘right’, title: { display: true, text: ‘MAP’ }, grid: { drawOnChartArea: false, // only want the grid lines for one axis to show }, }, yPump: { // This axis won’t be displayed, but it’s used to associate the dataset display: false } } } }); }); })(); JS; wp_add_inline_script( ‘lvad-chartjs’, $inline_js ); return ob_get_clean(); } } // Initialize the class. LVAD_Health_Monitor::get_instance(); LVAD Clinical Decision Support — Scored Triage

LVAD Clinical Decision Support

For education and decision support only. Not a substitute for clinical judgment. In emergencies, contact the LVAD center/EMS.

⚙️ Patient Baseline Configuration

Disclaimer: Defaults are demo-only. Set accurate personal baselines from device logs in consultation with your LVAD team.
4.5
3.9
4.2
80
Baseline last adjusted: just now

📊 Real-Time Analysis


Potential Causes

Run analysis to see ranked causes based on real-time data.