chore: add spec-kit and standardize signatures
- Added GitHub spec-kit for development workflow - Standardized file signatures to Descomplicar® format - Updated development configuration 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
825
src/includes/models/class-patient.php
Normal file
825
src/includes/models/class-patient.php
Normal file
@@ -0,0 +1,825 @@
|
||||
/**
|
||||
* Descomplicar® Crescimento Digital
|
||||
* https://descomplicar.pt
|
||||
*/
|
||||
|
||||
<?php
|
||||
/**
|
||||
* Patient Model
|
||||
*
|
||||
* Handles patient entity operations and medical data management
|
||||
*
|
||||
* @package KiviCare_API
|
||||
* @subpackage Models
|
||||
* @version 1.0.0
|
||||
* @author Descomplicar® <dev@descomplicar.pt>
|
||||
* @link https://descomplicar.pt
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
namespace KiviCare_API\Models;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class Patient
|
||||
*
|
||||
* Model for handling patient data, medical history and clinic associations
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class Patient {
|
||||
|
||||
/**
|
||||
* Patient user ID (wp_users table)
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $user_id;
|
||||
|
||||
/**
|
||||
* Patient data
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $data = array();
|
||||
|
||||
/**
|
||||
* Required fields for patient registration
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $required_fields = array(
|
||||
'first_name',
|
||||
'last_name',
|
||||
'user_email',
|
||||
'birth_date',
|
||||
'gender'
|
||||
);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param int|array $user_id_or_data Patient user ID or data array
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function __construct( $user_id_or_data = null ) {
|
||||
if ( is_numeric( $user_id_or_data ) ) {
|
||||
$this->user_id = (int) $user_id_or_data;
|
||||
$this->load_data();
|
||||
} elseif ( is_array( $user_id_or_data ) ) {
|
||||
$this->data = $user_id_or_data;
|
||||
$this->user_id = isset( $this->data['user_id'] ) ? (int) $this->data['user_id'] : null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load patient data from database
|
||||
*
|
||||
* @return bool True on success, false on failure
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private function load_data() {
|
||||
if ( ! $this->user_id ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$patient_data = self::get_patient_full_data( $this->user_id );
|
||||
|
||||
if ( $patient_data ) {
|
||||
$this->data = $patient_data;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new patient
|
||||
*
|
||||
* @param array $patient_data Patient data
|
||||
* @return int|WP_Error Patient user ID on success, WP_Error on failure
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function create( $patient_data ) {
|
||||
// Validate required fields
|
||||
$validation = self::validate_patient_data( $patient_data );
|
||||
if ( is_wp_error( $validation ) ) {
|
||||
return $validation;
|
||||
}
|
||||
|
||||
// Create WordPress user
|
||||
$user_data = array(
|
||||
'user_login' => self::generate_unique_username( $patient_data ),
|
||||
'user_email' => sanitize_email( $patient_data['user_email'] ),
|
||||
'first_name' => sanitize_text_field( $patient_data['first_name'] ),
|
||||
'last_name' => sanitize_text_field( $patient_data['last_name'] ),
|
||||
'role' => 'kivicare_patient',
|
||||
'user_pass' => isset( $patient_data['user_pass'] ) ? $patient_data['user_pass'] : wp_generate_password()
|
||||
);
|
||||
|
||||
$user_id = wp_insert_user( $user_data );
|
||||
|
||||
if ( is_wp_error( $user_id ) ) {
|
||||
return new \WP_Error(
|
||||
'patient_creation_failed',
|
||||
'Failed to create patient user: ' . $user_id->get_error_message(),
|
||||
array( 'status' => 500 )
|
||||
);
|
||||
}
|
||||
|
||||
// Add patient meta data
|
||||
$meta_fields = array(
|
||||
'birth_date' => sanitize_text_field( $patient_data['birth_date'] ),
|
||||
'gender' => sanitize_text_field( $patient_data['gender'] ),
|
||||
'mobile_number' => isset( $patient_data['mobile_number'] ) ? sanitize_text_field( $patient_data['mobile_number'] ) : '',
|
||||
'address' => isset( $patient_data['address'] ) ? sanitize_textarea_field( $patient_data['address'] ) : '',
|
||||
'city' => isset( $patient_data['city'] ) ? sanitize_text_field( $patient_data['city'] ) : '',
|
||||
'state' => isset( $patient_data['state'] ) ? sanitize_text_field( $patient_data['state'] ) : '',
|
||||
'country' => isset( $patient_data['country'] ) ? sanitize_text_field( $patient_data['country'] ) : '',
|
||||
'postal_code' => isset( $patient_data['postal_code'] ) ? sanitize_text_field( $patient_data['postal_code'] ) : '',
|
||||
'blood_group' => isset( $patient_data['blood_group'] ) ? sanitize_text_field( $patient_data['blood_group'] ) : '',
|
||||
'emergency_contact' => isset( $patient_data['emergency_contact'] ) ? sanitize_text_field( $patient_data['emergency_contact'] ) : '',
|
||||
'medical_notes' => isset( $patient_data['medical_notes'] ) ? sanitize_textarea_field( $patient_data['medical_notes'] ) : '',
|
||||
'patient_registration_date' => current_time( 'mysql' )
|
||||
);
|
||||
|
||||
foreach ( $meta_fields as $meta_key => $meta_value ) {
|
||||
update_user_meta( $user_id, $meta_key, $meta_value );
|
||||
}
|
||||
|
||||
// Create clinic mapping if clinic_id provided
|
||||
if ( ! empty( $patient_data['clinic_id'] ) ) {
|
||||
self::assign_to_clinic( $user_id, $patient_data['clinic_id'] );
|
||||
}
|
||||
|
||||
return $user_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update patient data
|
||||
*
|
||||
* @param int $user_id Patient user ID
|
||||
* @param array $patient_data Updated patient data
|
||||
* @return bool|WP_Error True on success, WP_Error on failure
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function update( $user_id, $patient_data ) {
|
||||
if ( ! self::exists( $user_id ) ) {
|
||||
return new \WP_Error(
|
||||
'patient_not_found',
|
||||
'Patient not found',
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
|
||||
// Update user data
|
||||
$user_update_data = array( 'ID' => $user_id );
|
||||
$user_fields = array( 'first_name', 'last_name', 'user_email' );
|
||||
|
||||
foreach ( $user_fields as $field ) {
|
||||
if ( isset( $patient_data[ $field ] ) ) {
|
||||
$user_update_data[ $field ] = sanitize_text_field( $patient_data[ $field ] );
|
||||
}
|
||||
}
|
||||
|
||||
if ( count( $user_update_data ) > 1 ) {
|
||||
$result = wp_update_user( $user_update_data );
|
||||
if ( is_wp_error( $result ) ) {
|
||||
return new \WP_Error(
|
||||
'patient_update_failed',
|
||||
'Failed to update patient: ' . $result->get_error_message(),
|
||||
array( 'status' => 500 )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Update meta fields
|
||||
$meta_fields = array(
|
||||
'birth_date', 'gender', 'mobile_number', 'address', 'city',
|
||||
'state', 'country', 'postal_code', 'blood_group',
|
||||
'emergency_contact', 'medical_notes'
|
||||
);
|
||||
|
||||
foreach ( $meta_fields as $meta_key ) {
|
||||
if ( isset( $patient_data[ $meta_key ] ) ) {
|
||||
$value = $patient_data[ $meta_key ];
|
||||
|
||||
if ( in_array( $meta_key, array( 'address', 'medical_notes' ) ) ) {
|
||||
$value = sanitize_textarea_field( $value );
|
||||
} else {
|
||||
$value = sanitize_text_field( $value );
|
||||
}
|
||||
|
||||
update_user_meta( $user_id, $meta_key, $value );
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a patient (soft delete - deactivate user)
|
||||
*
|
||||
* @param int $user_id Patient user ID
|
||||
* @return bool|WP_Error True on success, WP_Error on failure
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function delete( $user_id ) {
|
||||
if ( ! self::exists( $user_id ) ) {
|
||||
return new \WP_Error(
|
||||
'patient_not_found',
|
||||
'Patient not found',
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
|
||||
// Check for dependencies
|
||||
if ( self::has_dependencies( $user_id ) ) {
|
||||
return new \WP_Error(
|
||||
'patient_has_dependencies',
|
||||
'Cannot delete patient with existing appointments or medical records',
|
||||
array( 'status' => 409 )
|
||||
);
|
||||
}
|
||||
|
||||
// Soft delete - set user status to inactive
|
||||
update_user_meta( $user_id, 'patient_status', 'inactive' );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get patient by ID
|
||||
*
|
||||
* @param int $user_id Patient user ID
|
||||
* @return array|null Patient data or null if not found
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function get_by_id( $user_id ) {
|
||||
if ( ! self::exists( $user_id ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return self::get_patient_full_data( $user_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all patients with optional filtering
|
||||
*
|
||||
* @param array $args Query arguments
|
||||
* @return array Array of patient data
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function get_all( $args = array() ) {
|
||||
$defaults = array(
|
||||
'clinic_id' => null,
|
||||
'status' => 'active',
|
||||
'search' => '',
|
||||
'limit' => 50,
|
||||
'offset' => 0,
|
||||
'orderby' => 'display_name',
|
||||
'order' => 'ASC'
|
||||
);
|
||||
|
||||
$args = wp_parse_args( $args, $defaults );
|
||||
|
||||
$user_query_args = array(
|
||||
'role' => 'kivicare_patient',
|
||||
'number' => $args['limit'],
|
||||
'offset' => $args['offset'],
|
||||
'orderby' => $args['orderby'],
|
||||
'order' => $args['order'],
|
||||
'fields' => 'ID'
|
||||
);
|
||||
|
||||
// Add search
|
||||
if ( ! empty( $args['search'] ) ) {
|
||||
$user_query_args['search'] = '*' . $args['search'] . '*';
|
||||
$user_query_args['search_columns'] = array( 'user_login', 'user_email', 'display_name' );
|
||||
}
|
||||
|
||||
// Add status filter via meta query
|
||||
if ( ! empty( $args['status'] ) ) {
|
||||
$user_query_args['meta_query'] = array(
|
||||
'relation' => 'OR',
|
||||
array(
|
||||
'key' => 'patient_status',
|
||||
'value' => $args['status'],
|
||||
'compare' => '='
|
||||
),
|
||||
array(
|
||||
'key' => 'patient_status',
|
||||
'compare' => 'NOT EXISTS'
|
||||
)
|
||||
);
|
||||
|
||||
if ( $args['status'] === 'active' ) {
|
||||
$user_query_args['meta_query'][1]['value'] = 'active';
|
||||
}
|
||||
}
|
||||
|
||||
$user_query = new \WP_User_Query( $user_query_args );
|
||||
$user_ids = $user_query->get_results();
|
||||
|
||||
$patients = array();
|
||||
foreach ( $user_ids as $user_id ) {
|
||||
$patient_data = self::get_patient_full_data( $user_id );
|
||||
|
||||
// Filter by clinic if specified
|
||||
if ( ! is_null( $args['clinic_id'] ) &&
|
||||
(int) $patient_data['clinic_id'] !== (int) $args['clinic_id'] ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( $patient_data ) {
|
||||
$patients[] = $patient_data;
|
||||
}
|
||||
}
|
||||
|
||||
return $patients;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get patient full data with clinic information
|
||||
*
|
||||
* @param int $user_id Patient user ID
|
||||
* @return array|null Full patient data or null if not found
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function get_patient_full_data( $user_id ) {
|
||||
global $wpdb;
|
||||
|
||||
$user = get_user_by( 'id', $user_id );
|
||||
if ( ! $user || ! in_array( 'kivicare_patient', $user->roles ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get clinic mapping
|
||||
$clinic_mapping = $wpdb->get_row(
|
||||
$wpdb->prepare(
|
||||
"SELECT pcm.clinic_id, c.name as clinic_name
|
||||
FROM {$wpdb->prefix}kc_patient_clinic_mappings pcm
|
||||
LEFT JOIN {$wpdb->prefix}kc_clinics c ON pcm.clinic_id = c.id
|
||||
WHERE pcm.patient_id = %d
|
||||
LIMIT 1",
|
||||
$user_id
|
||||
),
|
||||
ARRAY_A
|
||||
);
|
||||
|
||||
// Get patient meta data
|
||||
$patient_data = array(
|
||||
'user_id' => $user_id,
|
||||
'username' => $user->user_login,
|
||||
'email' => $user->user_email,
|
||||
'first_name' => $user->first_name,
|
||||
'last_name' => $user->last_name,
|
||||
'display_name' => $user->display_name,
|
||||
'birth_date' => get_user_meta( $user_id, 'birth_date', true ),
|
||||
'gender' => get_user_meta( $user_id, 'gender', true ),
|
||||
'mobile_number' => get_user_meta( $user_id, 'mobile_number', true ),
|
||||
'address' => get_user_meta( $user_id, 'address', true ),
|
||||
'city' => get_user_meta( $user_id, 'city', true ),
|
||||
'state' => get_user_meta( $user_id, 'state', true ),
|
||||
'country' => get_user_meta( $user_id, 'country', true ),
|
||||
'postal_code' => get_user_meta( $user_id, 'postal_code', true ),
|
||||
'blood_group' => get_user_meta( $user_id, 'blood_group', true ),
|
||||
'emergency_contact' => get_user_meta( $user_id, 'emergency_contact', true ),
|
||||
'medical_notes' => get_user_meta( $user_id, 'medical_notes', true ),
|
||||
'status' => get_user_meta( $user_id, 'patient_status', true ) ?: 'active',
|
||||
'registration_date' => get_user_meta( $user_id, 'patient_registration_date', true ),
|
||||
'clinic_id' => $clinic_mapping ? (int) $clinic_mapping['clinic_id'] : null,
|
||||
'clinic_name' => $clinic_mapping ? $clinic_mapping['clinic_name'] : null,
|
||||
'age' => self::calculate_age( get_user_meta( $user_id, 'birth_date', true ) ),
|
||||
'total_appointments' => self::get_appointment_count( $user_id ),
|
||||
'last_visit' => self::get_last_visit_date( $user_id )
|
||||
);
|
||||
|
||||
return $patient_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get patient medical history
|
||||
*
|
||||
* @param int $user_id Patient user ID
|
||||
* @param array $args Query arguments
|
||||
* @return array Patient medical history
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function get_medical_history( $user_id, $args = array() ) {
|
||||
global $wpdb;
|
||||
|
||||
$defaults = array(
|
||||
'type' => null,
|
||||
'limit' => 50,
|
||||
'offset' => 0,
|
||||
'orderby' => 'created_at',
|
||||
'order' => 'DESC'
|
||||
);
|
||||
|
||||
$args = wp_parse_args( $args, $defaults );
|
||||
|
||||
$where_clauses = array( 'patient_id = %d' );
|
||||
$where_values = array( $user_id );
|
||||
|
||||
if ( ! is_null( $args['type'] ) ) {
|
||||
$where_clauses[] = 'type = %s';
|
||||
$where_values[] = $args['type'];
|
||||
}
|
||||
|
||||
$where_sql = implode( ' AND ', $where_clauses );
|
||||
|
||||
$query = $wpdb->prepare(
|
||||
"SELECT * FROM {$wpdb->prefix}kc_medical_history
|
||||
WHERE {$where_sql}
|
||||
ORDER BY {$args['orderby']} {$args['order']}
|
||||
LIMIT %d OFFSET %d",
|
||||
array_merge( $where_values, array( $args['limit'], $args['offset'] ) )
|
||||
);
|
||||
|
||||
$history = $wpdb->get_results( $query, ARRAY_A );
|
||||
|
||||
return array_map( function( $item ) {
|
||||
return array(
|
||||
'id' => (int) $item['id'],
|
||||
'encounter_id' => (int) $item['encounter_id'],
|
||||
'type' => $item['type'],
|
||||
'title' => $item['title'],
|
||||
'date' => $item['created_at'],
|
||||
'added_by' => (int) $item['added_by']
|
||||
);
|
||||
}, $history );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get patient encounters
|
||||
*
|
||||
* @param int $user_id Patient user ID
|
||||
* @param array $args Query arguments
|
||||
* @return array Patient encounters
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function get_encounters( $user_id, $args = array() ) {
|
||||
global $wpdb;
|
||||
|
||||
$defaults = array(
|
||||
'limit' => 50,
|
||||
'offset' => 0,
|
||||
'orderby' => 'encounter_date',
|
||||
'order' => 'DESC',
|
||||
'status' => null
|
||||
);
|
||||
|
||||
$args = wp_parse_args( $args, $defaults );
|
||||
|
||||
$where_clauses = array( 'patient_id = %d' );
|
||||
$where_values = array( $user_id );
|
||||
|
||||
if ( ! is_null( $args['status'] ) ) {
|
||||
$where_clauses[] = 'status = %d';
|
||||
$where_values[] = $args['status'];
|
||||
}
|
||||
|
||||
$where_sql = implode( ' AND ', $where_clauses );
|
||||
|
||||
$query = $wpdb->prepare(
|
||||
"SELECT e.*, c.name as clinic_name,
|
||||
CONCAT(u.first_name, ' ', u.last_name) as doctor_name
|
||||
FROM {$wpdb->prefix}kc_patient_encounters e
|
||||
LEFT JOIN {$wpdb->prefix}kc_clinics c ON e.clinic_id = c.id
|
||||
LEFT JOIN {$wpdb->prefix}users u ON e.doctor_id = u.ID
|
||||
WHERE {$where_sql}
|
||||
ORDER BY {$args['orderby']} {$args['order']}
|
||||
LIMIT %d OFFSET %d",
|
||||
array_merge( $where_values, array( $args['limit'], $args['offset'] ) )
|
||||
);
|
||||
|
||||
$encounters = $wpdb->get_results( $query, ARRAY_A );
|
||||
|
||||
return array_map( function( $encounter ) {
|
||||
return array(
|
||||
'id' => (int) $encounter['id'],
|
||||
'encounter_date' => $encounter['encounter_date'],
|
||||
'description' => $encounter['description'],
|
||||
'status' => (int) $encounter['status'],
|
||||
'clinic' => array(
|
||||
'id' => (int) $encounter['clinic_id'],
|
||||
'name' => $encounter['clinic_name']
|
||||
),
|
||||
'doctor' => array(
|
||||
'id' => (int) $encounter['doctor_id'],
|
||||
'name' => $encounter['doctor_name']
|
||||
),
|
||||
'appointment_id' => (int) $encounter['appointment_id'],
|
||||
'template_id' => (int) $encounter['template_id'],
|
||||
'created_at' => $encounter['created_at']
|
||||
);
|
||||
}, $encounters );
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign patient to clinic
|
||||
*
|
||||
* @param int $user_id Patient user ID
|
||||
* @param int $clinic_id Clinic ID
|
||||
* @return bool|WP_Error True on success, WP_Error on failure
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function assign_to_clinic( $user_id, $clinic_id ) {
|
||||
global $wpdb;
|
||||
|
||||
// Check if clinic exists
|
||||
if ( ! Clinic::exists( $clinic_id ) ) {
|
||||
return new \WP_Error(
|
||||
'clinic_not_found',
|
||||
'Clinic not found',
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
|
||||
// Check if mapping already exists
|
||||
$existing_mapping = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM {$wpdb->prefix}kc_patient_clinic_mappings
|
||||
WHERE patient_id = %d AND clinic_id = %d",
|
||||
$user_id, $clinic_id
|
||||
)
|
||||
);
|
||||
|
||||
if ( (int) $existing_mapping > 0 ) {
|
||||
return true; // Already assigned
|
||||
}
|
||||
|
||||
// Remove existing mappings (patient can only be assigned to one clinic at a time)
|
||||
$wpdb->delete(
|
||||
"{$wpdb->prefix}kc_patient_clinic_mappings",
|
||||
array( 'patient_id' => $user_id ),
|
||||
array( '%d' )
|
||||
);
|
||||
|
||||
// Create new mapping
|
||||
$result = $wpdb->insert(
|
||||
"{$wpdb->prefix}kc_patient_clinic_mappings",
|
||||
array(
|
||||
'patient_id' => $user_id,
|
||||
'clinic_id' => $clinic_id,
|
||||
'created_at' => current_time( 'mysql' )
|
||||
),
|
||||
array( '%d', '%d', '%s' )
|
||||
);
|
||||
|
||||
if ( $result === false ) {
|
||||
return new \WP_Error(
|
||||
'clinic_assignment_failed',
|
||||
'Failed to assign patient to clinic: ' . $wpdb->last_error,
|
||||
array( 'status' => 500 )
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if patient exists
|
||||
*
|
||||
* @param int $user_id Patient user ID
|
||||
* @return bool True if exists, false otherwise
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function exists( $user_id ) {
|
||||
$user = get_user_by( 'id', $user_id );
|
||||
return $user && in_array( 'kivicare_patient', $user->roles );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if patient has dependencies (appointments, encounters, etc.)
|
||||
*
|
||||
* @param int $user_id Patient user ID
|
||||
* @return bool True if has dependencies, false otherwise
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private static function has_dependencies( $user_id ) {
|
||||
global $wpdb;
|
||||
|
||||
// Check appointments
|
||||
$appointments_count = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM {$wpdb->prefix}kc_appointments WHERE patient_id = %d",
|
||||
$user_id
|
||||
)
|
||||
);
|
||||
|
||||
if ( (int) $appointments_count > 0 ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check encounters
|
||||
$encounters_count = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM {$wpdb->prefix}kc_patient_encounters WHERE patient_id = %d",
|
||||
$user_id
|
||||
)
|
||||
);
|
||||
|
||||
return (int) $encounters_count > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate patient data
|
||||
*
|
||||
* @param array $patient_data Patient data to validate
|
||||
* @return bool|WP_Error True if valid, WP_Error if invalid
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private static function validate_patient_data( $patient_data ) {
|
||||
$errors = array();
|
||||
|
||||
// Check required fields
|
||||
foreach ( self::$required_fields as $field ) {
|
||||
if ( empty( $patient_data[ $field ] ) ) {
|
||||
$errors[] = "Field '{$field}' is required";
|
||||
}
|
||||
}
|
||||
|
||||
// Validate email format
|
||||
if ( ! empty( $patient_data['user_email'] ) && ! is_email( $patient_data['user_email'] ) ) {
|
||||
$errors[] = 'Invalid email format';
|
||||
}
|
||||
|
||||
// Check for duplicate email
|
||||
if ( ! empty( $patient_data['user_email'] ) ) {
|
||||
$existing_user = get_user_by( 'email', $patient_data['user_email'] );
|
||||
if ( $existing_user ) {
|
||||
$errors[] = 'A user with this email already exists';
|
||||
}
|
||||
}
|
||||
|
||||
// Validate birth date format
|
||||
if ( ! empty( $patient_data['birth_date'] ) ) {
|
||||
$birth_date = \DateTime::createFromFormat( 'Y-m-d', $patient_data['birth_date'] );
|
||||
if ( ! $birth_date || $birth_date->format( 'Y-m-d' ) !== $patient_data['birth_date'] ) {
|
||||
$errors[] = 'Invalid birth date format. Use YYYY-MM-DD';
|
||||
}
|
||||
}
|
||||
|
||||
// Validate gender
|
||||
if ( ! empty( $patient_data['gender'] ) && ! in_array( $patient_data['gender'], array( 'M', 'F', 'O' ) ) ) {
|
||||
$errors[] = 'Invalid gender. Use M, F, or O';
|
||||
}
|
||||
|
||||
if ( ! empty( $errors ) ) {
|
||||
return new \WP_Error(
|
||||
'patient_validation_failed',
|
||||
'Patient validation failed',
|
||||
array(
|
||||
'status' => 400,
|
||||
'errors' => $errors
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate unique username for patient
|
||||
*
|
||||
* @param array $patient_data Patient data
|
||||
* @return string Unique username
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private static function generate_unique_username( $patient_data ) {
|
||||
$base_username = strtolower(
|
||||
$patient_data['first_name'] . '.' . $patient_data['last_name']
|
||||
);
|
||||
$base_username = sanitize_user( $base_username );
|
||||
|
||||
$username = $base_username;
|
||||
$counter = 1;
|
||||
|
||||
while ( username_exists( $username ) ) {
|
||||
$username = $base_username . $counter;
|
||||
$counter++;
|
||||
}
|
||||
|
||||
return $username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate patient age from birth date
|
||||
*
|
||||
* @param string $birth_date Birth date in Y-m-d format
|
||||
* @return int|null Age in years or null if invalid
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private static function calculate_age( $birth_date ) {
|
||||
if ( empty( $birth_date ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$birth = \DateTime::createFromFormat( 'Y-m-d', $birth_date );
|
||||
if ( ! $birth ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$now = new \DateTime();
|
||||
$age = $now->diff( $birth );
|
||||
|
||||
return $age->y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get patient appointment count
|
||||
*
|
||||
* @param int $user_id Patient user ID
|
||||
* @return int Total appointments
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private static function get_appointment_count( $user_id ) {
|
||||
global $wpdb;
|
||||
|
||||
return (int) $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM {$wpdb->prefix}kc_appointments WHERE patient_id = %d",
|
||||
$user_id
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get patient's last visit date
|
||||
*
|
||||
* @param int $user_id Patient user ID
|
||||
* @return string|null Last visit date or null
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private static function get_last_visit_date( $user_id ) {
|
||||
global $wpdb;
|
||||
|
||||
return $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT MAX(encounter_date) FROM {$wpdb->prefix}kc_patient_encounters WHERE patient_id = %d",
|
||||
$user_id
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get patient statistics
|
||||
*
|
||||
* @param int $user_id Patient user ID
|
||||
* @return array Patient statistics
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function get_statistics( $user_id ) {
|
||||
global $wpdb;
|
||||
|
||||
$stats = array(
|
||||
'total_appointments' => self::get_appointment_count( $user_id ),
|
||||
'completed_encounters' => 0,
|
||||
'pending_appointments' => 0,
|
||||
'total_prescriptions' => 0,
|
||||
'last_visit' => self::get_last_visit_date( $user_id ),
|
||||
'next_appointment' => null
|
||||
);
|
||||
|
||||
// Completed encounters
|
||||
$stats['completed_encounters'] = (int) $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM {$wpdb->prefix}kc_patient_encounters WHERE patient_id = %d AND status = 1",
|
||||
$user_id
|
||||
)
|
||||
);
|
||||
|
||||
// Pending appointments
|
||||
$stats['pending_appointments'] = (int) $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM {$wpdb->prefix}kc_appointments
|
||||
WHERE patient_id = %d AND status = 1 AND appointment_start_date >= CURDATE()",
|
||||
$user_id
|
||||
)
|
||||
);
|
||||
|
||||
// Total prescriptions
|
||||
$stats['total_prescriptions'] = (int) $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM {$wpdb->prefix}kc_prescription WHERE patient_id = %d",
|
||||
$user_id
|
||||
)
|
||||
);
|
||||
|
||||
// Next appointment
|
||||
$stats['next_appointment'] = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT MIN(appointment_start_date) FROM {$wpdb->prefix}kc_appointments
|
||||
WHERE patient_id = %d AND status = 1 AND appointment_start_date >= CURDATE()",
|
||||
$user_id
|
||||
)
|
||||
);
|
||||
|
||||
return $stats;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user