Files
streamline-emr/docker/_streamline-src/app/Http/Controllers/AuditTrailController.php
T

626 lines
27 KiB
PHP
Executable File

<?php
namespace Streamline\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use OwenIt\Auditing\Models\Audit;
use Carbon\Carbon;
use Streamline\Models\User;
use Streamline\Models\Patient;
use Streamline\Models\PatientEpisode;
use Streamline\Models\Triage;
use Barryvdh\Snappy\Facades\SnappyPdf;
class AuditTrailController extends Controller
{
public function __construct()
{
$this->middleware('auth');
$this->middleware('permission:audit_trail-list', ['only' => ['index', 'search', 'patient_timeline', 'user_timeline']]);
}
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$audits = Audit::with('user')
->where('created_at', '>=', Carbon::now()->subDay())
->orderBy('created_at', 'desc')
->paginate(100);
if ($audits->count() <= 0) {
flash("There are no records in the audit trail")->error();
}
// users with activity
$user_id_array = DB::table('audits')
->groupBy('user_id')
->select('user_id')
->get()
->toArray();
$user_id = array(count($user_id_array));
foreach ($user_id_array as $value) {
$user_id[$value->user_id] = get_full_name($value->user_id, 'id', 'first_name', 'last_name', 'users');
}
// remove the count from the array
unset($user_id[0]);
// add --select--
$user_id = ['all' => 'ALL'] + $user_id;
// users with activity
$action_performed_array = DB::table('audits')
->groupBy('event')
->select('event')
->get()
->toArray();
$action_performed = array(count($action_performed_array));
foreach ($action_performed_array as $value) {
$action_performed[$value->event] = ucwords($value->event);
}
// remove the count from the array
unset($action_performed[0]);
// add --select--
$action_performed = ['all' => 'ALL'] + $action_performed;
// users with activity
$records_affected_array = DB::table('audits')
->groupBy('auditable_type')
->select('auditable_type')
->get()
->toArray();
$records_affected = array(count($records_affected_array));
foreach ($records_affected_array as $value) {
$record = str_replace('Streamline\\', '', $value->auditable_type);
/*================ show better text on forming the drop down ===========*/
$cleaned_model_name = str_replace('Streamline\\', '', $value->auditable_type);
$cleaned_model_name = preg_split('/(?=[A-Z])/', $cleaned_model_name);
$spaced_model_name = implode(' ', $cleaned_model_name);
/*========================================================*/
$records_affected[$record] = $spaced_model_name;
}
// remove the count from the array
unset($records_affected[0]);
// add --select--
$records_affected = ['all' => 'ALL'] + $records_affected;
// get all first names
$first_names = DB::table('users')->pluck('first_name', 'id');
// get all last names
$last_names = DB::table('users')->pluck('last_name', 'id');
return view('audit_trail.index',
compact([
'audits',
'first_names',
'last_names',
'user_id',
'action_performed',
'records_affected'
]));
}
public function audit_trail_print(Request $request)
{
$audit_trail_values = json_decode($request->data, 1);
$hospitalInfo = DB::table('hospital_information')->find(1);
$receipt_date = date('Y-m-d h:i:s');
$data = [
'hospitalInfo' => $hospitalInfo,
'audit_trail' => $request->data,
'receipt_data' => $receipt_date
];
$audit_trail = json_decode($data['audit_trail'])->data;
//return view('audit_trail.audit_trail_print', compact('audit_trail'));
$pdf = SnappyPDF::loadView('audit_trail.audit_trail_print', compact('audit_trail'))
->setOrientation('portrait')
->setOption('margin-bottom', 7)
->setOption('margin-top', 5)
->setOption('footer-html', '<i>Stre@mline</i>');
return $pdf->inline('Audit Trail' . date(" d-m-y h:ia") . '.pdf');
}
public function failed_login_print(Request $request){
$audit_trail_values = json_decode($request->data, 1);
$hospitalInfo = DB::table('hospital_information')->find(1);
$receipt_date = date('Y-m-d h:i:s');
$data = [
'hospitalInfo' => $hospitalInfo,
'failed_login_data' => $request->data,
'receipt_data' => $receipt_date
];
$failed_login_data = json_decode($data['failed_login_data'])->data;
//return view('audit_trail.failed_login_print', compact('data', 'failed_login_data'));
$pdf = SnappyPDF::loadView('audit_trail.failed_login_print', compact('failed_login_data'))
->setOrientation('portrait')
->setOption('margin-bottom', 7)
->setOption('margin-top', 5)
->setOption('footer-html', '<i>Stre@mline</i>');
return $pdf->inline('Audit Trail' . date(" d-m-y h:ia") . '.pdf');
}
public function online_user_print(Request $request){
$audit_trail_values = json_decode($request->data, 1);
$hospitalInfo = DB::table('hospital_information')->find(1);
$receipt_date = date('Y-m-d h:i:s');
$data = [
'hospitalInfo' => $hospitalInfo,
'audit_trail' => $audit_trail_values,
'receipt_data' => $receipt_date
];
$online_users = $audit_trail_values['data'];
//return view('audit_trail.online_user_print', compact('online_users'));
$pdf = SnappyPDF::loadView('audit_trail.online_user_print', compact('online_users'))
->setOrientation('portrait')
->setOption('margin-bottom', 7)
->setOption('margin-top', 5)
->setOption('footer-html', '<i>Stre@mline</i>');
return $pdf->inline('Audit Trail' . date(" d-m-y h:ia") . '.pdf');
}
/**
* Return audit table.
*
* @return \Illuminate\Http\Response
*/
public static function return_audit_table() {
$audits = Audit::with('user')->orderBy('created_at', 'desc')->paginate(100);
echo " <b>Old values</b>
<table>
@if(array_key_exists('updated_at', $audit->toArray()['old_values']))
@foreach ($audit->toArray()['old_values'] as $key => $value)
<tr>
<td>{{ format_key($key) }}</td>
$patient = Streamline\Models\Patient::where('id', $value)->first();
$user = Streamline\Models\User::where('id', $value)->first();
if($patient->id == $value && $key == 'patient_id')
<td>{{ $patient ? $patient->first_name : '' }}
{{ $patient ? $patient->last_name : '' }}</td>
@elseif($key == 'updated_by' || $key == 'created_by' || $key ==
'staff_id')
<td>{{ $user ? $user->first_name : '' }}
{{ $user ? $user->last_name : '' }}</td>
@else
<td>{{ $value }}</td>
@endif
</tr>
@endforeach
@endif
</table>
<b>New values</b>
</html>";
}
/**
* Search for audit trail records with given parameters
*
* @param Request $request
* @return \Illuminate\Contracts\View\Factory|\Illuminate\Foundation\Application|\Illuminate\View\View
*/
public function search(Request $request)
{
$user_id = $request->user_id;
$action_performed = $request->action_performed;
$records_affected = $request->records_affected;
// Get the selected date criteria
$dates = $request->dates;
$filters = [];
if ($user_id != 'all') :
array_push($filters, ['user_id', '=', $user_id]);
endif;
if ($action_performed != 'all') :
array_push($filters, ['event', '=', $action_performed]);
endif;
if ($records_affected != 'all') :
array_push($filters, ['auditable_type', '=', 'Streamline\\' . $records_affected]);
endif;
// switch the selected date search criteria - today, yesterday, last week, last month etc
switch ($dates) {
case 'today':
$today = Carbon::today()->format('Y-m-d');
$audits = Audit::orderBy('created_at', 'desc')->where($filters)->whereDate('created_at', $today)->paginate(100);
break;
case 'yesterday':
$yesterday = Carbon::yesterday()->format('Y-m-d');
$audits = Audit::orderBy('created_at', 'desc')->where($filters)->whereDate('created_at', $yesterday)->paginate(100);
break;
case 'week':
$week_ago = Carbon::today()->subDays(7)->format('Y-m-d');
$audits = Audit::orderBy('created_at', 'desc')->where($filters)->whereDate('created_at', '>=', $week_ago)->paginate(100);
break;
case 'month':
$month_ago = Carbon::today()->subDays(30)->format('Y-m-d');
$audits = Audit::orderBy('created_at', 'desc')->where($filters)->whereDate('created_at', '>=', $month_ago)->paginate(100);
break;
case 'custom-date':
$start_date = Carbon::parse($request->start_date)->format('Y-m-d');
$audits = Audit::orderBy('created_at', 'desc')->where($filters)->whereDate('created_at', '=', $start_date)->paginate(100);
break;
case 'custom-range':
$start_date = Carbon::parse($request->start_date)->format('Y-m-d');
$end_date = Carbon::parse($request->end_date)->format('Y-m-d');
$audits = Audit::orderBy('created_at', 'desc')->where($filters)->whereBetween('created_at', [$start_date, $end_date])->paginate(100);
break;
default:
$audits = Audit::orderBy('created_at', 'desc')->where($filters)->paginate(150);
break;
}
if ($audits->count() <= 0) {
flash("There are no records found in the search")->error();
}
// users with activity
$user_id_array = DB::table('audits')->groupBy('user_id')->select('user_id')->get()->toArray();
$user_id = array(count($user_id_array));
foreach ($user_id_array as $value) {
$user_id[$value->user_id] = get_full_name($value->user_id, 'id', 'first_name', 'last_name', 'users');
}
// remove the count from the array
unset($user_id[0]);
// add --select--
$user_id = ['all' => 'ALL'] + $user_id;
// users with activity
$action_performed_array = DB::table('audits')->groupBy('event')->select('event')->get()->toArray();
$action_performed = array(count($action_performed_array));
foreach ($action_performed_array as $value) {
$action_performed[$value->event] = ucwords($value->event);
}
// remove the count from the array
unset($action_performed[0]);
// add --select--
$action_performed = ['all' => 'ALL'] + $action_performed;
// users with activity
$records_affected_array = DB::table('audits')->groupBy('auditable_type')->select('auditable_type')->get()->toArray();
$records_affected = array(count($records_affected_array));
foreach ($records_affected_array as $value) {
$record = str_replace('Streamline\\', '', $value->auditable_type);
//$records_affected[$record] = $record;
/*================ show better text on forming the drop down ===========*/
$cleaned_model_name = str_replace('Streamline\\', '', $value->auditable_type);
$cleaned_model_name = preg_split('/(?=[A-Z])/', $cleaned_model_name);
$spaced_model_name = implode(' ', $cleaned_model_name);
/*========================================================*/
$records_affected[$record] = $spaced_model_name;
}
// remove the count from the array
unset($records_affected[0]);
// add --select--
$records_affected = ['all' => 'ALL'] + $records_affected;
// get all first names
$first_names = DB::table('users')->pluck('first_name', 'id');
// get all last names
$last_names = DB::table('users')->pluck('last_name', 'id');
return view('audit_trail.index', compact('audits', 'first_names', 'last_names', 'user_id', 'action_performed', 'records_affected'));
}
public function audit_trail_details($id)
{
//$audits = Audit::where(['id' => $id])->get();
$audit = Audit::where(['id' => $id])->first();
$user = User::where('id', $audit->user_id)->first();
//dd($audit);
// get all first names
$first_names = DB::table('users')->pluck('first_name', 'id');
// get all last names
$last_names = DB::table('users')->pluck('last_name', 'id');
// get full names
$full_names = $user->first_name." ".$user->last_name;
return view('audit_trail.audit_trail_details', compact('full_names', 'audit', 'last_names', 'first_names'));
}
public function select_patient_and_episode(Request $request) {
$patient = [];
$patient_episodes_details = [];
if (isset($request->patient_id)) {
$patient_searched = true;
$patient = Patient::find($request->patient_id);
if ($patient) {
$patient_episodes = PatientEpisode::where('patient_id', $patient->id)->get();
$counter = 0;
foreach ($patient_episodes as $episode) {
$patient_episodes_details[$counter]['id'] = $episode->id;
$patient_episodes_details[$counter]['created_at'] = streamline_date_time($episode->created_at);
$counter++;
}
}
} else {
$patient_searched = false;
}
return view('audit_trail.select_patient_and_episode', compact('patient_searched', 'patient', 'patient_episodes_details'));
}
public function patient_timeline($patient_id, $episode_id)
{
$patient = Patient::find($patient_id);
$episode = PatientEpisode::find($episode_id);
$patient_timeline = [];
$index = 0;
// add when episode was created
$patient_timeline[$index]["date"] = streamline_date_time($episode->created_at);
$patient_timeline[$index]["text"] = "Episode was started by " . get_full_name($episode->created_by, 'id', 'first_name', 'last_name', 'users');
$index++;
// fetch triage info
if (!is_null($episode->triage_id)) {
$triage_audits = Audit::where(['auditable_type' => 'Streamline\\Triage', 'auditable_id' => $episode->triage_id])
->select('old_values', 'new_values', 'event', 'created_at', 'user_id')
->orderBy('created_at', 'desc')
->get()
->toArray();
foreach ($triage_audits as $audit) {
if ($audit["event"] == "created") {
$patient_timeline[$index]["date"] = streamline_date_time($audit["created_at"]);
$patient_timeline[$index]["text"] = "Patient Triage was carried out by " . get_full_name($audit["user_id"], 'id', 'first_name', 'last_name', 'users') .
" with patient being assigned to the " . get_name($audit["new_values"]["clinic_allocation"], 'id', 'name', 'clinics') . " clinic with severe grade of " . severe_grade($audit["new_values"]["severe_grade"]);
} else {
$text = "Patient Triage was edited by " . get_full_name($audit["user_id"], 'id', 'first_name', 'last_name', 'users');
if (key_exists("clinic_allocation", $audit["new_values"])) {
$text .= "<br>Clinic allocation changed to " . get_name($audit["new_values"]["clinic_allocation"], 'id', 'name', 'clinics');
}
if (key_exists("severe_grade", $audit["new_values"])) {
$text .= "<br>Severe Grade changed to " . severe_grade($audit["new_values"]["severe_grade"]);
}
$patient_timeline[$index]["date"] = streamline_date_time($audit["created_at"]);
$patient_timeline[$index]["text"] = $text;
}
$index++;
}
}
// fetch consultation data
if (!is_null($episode->consultation_id)) {
$consultation_audits = Audit::where(['auditable_type' => 'Streamline\\Consultation', 'auditable_id' => $episode->consultation_id])
->select('old_values', 'new_values', 'event', 'created_at', 'user_id')
->orderBy('created_at', 'desc')
->get()
->toArray();
foreach ($consultation_audits as $audit) {
if ($audit["event"] == "created") {
$patient_timeline[$index]["date"] = streamline_date_time($audit["created_at"]);
$patient_timeline[$index]["text"] = "Patient Consultation was carried out by " . get_full_name($audit["user_id"], 'id', 'first_name', 'last_name', 'users') .
" with primary diagnosis set as " . get_name($audit["new_values"]["primary_diagnosis"], 'id', 'name', 'diagnoses') . " and outcome of " . get_name($audit["new_values"]["outcome_id"], 'id', 'name', 'outcomes');
} else {
$text = "Patient Consultation was edited by " . get_full_name($audit["user_id"], 'id', 'first_name', 'last_name', 'users');
if (key_exists("primary_diagnosis", $audit["new_values"])) {
$text .= "<br>Primary Diagnosis changed to " . get_name($audit["new_values"]["primary_diagnosis"], 'id', 'name', 'diagnoses');
}
if (key_exists("outcome_id", $audit["new_values"])) {
$text .= "<br>Outcome changed to " . get_name($audit["new_values"]["outcome_id"], 'id', 'name', 'outcomes');
}
$patient_timeline[$index]["date"] = streamline_date_time($audit["created_at"]);
$patient_timeline[$index]["text"] = $text;
}
$index++;
}
}
// fetch treatment data
if (!is_null($episode->treatment_id)) {
$treatment_audits = Audit::where(['auditable_type' => 'Streamline\\Treatment', 'auditable_id' => $episode->treatment_id])
->select('old_values', 'new_values', 'event', 'created_at', 'user_id')
->orderBy('created_at', 'desc')
->get()
->toArray();
foreach ($treatment_audits as $audit) {
if ($audit["event"] == "created") {
$patient_timeline[$index]["date"] = streamline_date_time($audit["created_at"]);
$text = "Prescriptions were ordered by " . get_full_name($audit["user_id"], 'id', 'first_name', 'last_name', 'users');
$text .= "<br><b>Treatments</b>:";
$drugs = explode(",", $audit["new_values"]["drugs"]);
$durations = explode(",", $audit["new_values"]["durations"]);
for ($i = 0; $i < count($drugs); $i++) {
$text .= "<br>" . get_name($drugs[$i], 'id', 'name', 'drugs') .
" for " . $durations[$i];
}
$patient_timeline[$index]["text"] = $text;
} else if ($audit["event"] == "updated" && key_exists("drugs", $audit["new_values"]) && key_exists("durations", $audit["new_values"])) {
$patient_timeline[$index]["date"] = streamline_date_time($audit["created_at"]);
$text = "Prescriptions were edited by " . get_full_name($audit["user_id"], 'id', 'first_name', 'last_name', 'users');
$text .= "<br><b>Treatments</b>:";
$drugs = explode(",", $audit["new_values"]["drugs"]);
$durations = explode(",", $audit["new_values"]["durations"]);
for ($i = 0; $i < count($drugs); $i++) {
$text .= "<br>" . get_name($drugs[$i], 'id', 'name', 'drugs') .
" for " . $durations[$i];
}
$patient_timeline[$index]["text"] = $text;
}
$index++;
}
}
// check for any ordered investigations
$ordered_investigations = DB::table('ordered_investigations')
->where('patient_id', $patient_id)
->where('episode_id', $episode_id)
->get();
foreach ($ordered_investigations as $inv) {
$investigation_audits = Audit::where(['auditable_type' => 'Streamline\\OrderedInvestigation', 'auditable_id' => $inv->id])
->select('old_values', 'new_values', 'event', 'created_at', 'user_id')
->orderBy('created_at', 'desc')
->get()
->toArray();
foreach ($investigation_audits as $audit) {
if ($audit["event"] == "created") {
$patient_timeline[$index]["date"] = streamline_date_time($audit["created_at"]);
$text = "Investigations were ordered by " . get_full_name($audit["user_id"], 'id', 'first_name', 'last_name', 'users');
$text .= "<br><b>Investigations ( " . $audit["new_values"]["order_type"] . ")</b>:";
$invs_id = explode(",", $audit["new_values"]["investigation_id"]);
for ($i = 0; $i < count($invs_id); $i++) {
$text .= "<br>" . get_name($invs_id[$i], 'id', 'name', 'investigations');
}
$patient_timeline[$index]["text"] = $text;
} else if ($audit["event"] == "updated" && key_exists("investigation_id", $audit["new_values"])) {
$patient_timeline[$index]["date"] = streamline_date_time($audit["created_at"]);
$text = "Investigations were edited by " . get_full_name($audit["user_id"], 'id', 'first_name', 'last_name', 'users');
$text .= "<br><b>Investigations ( " . get_name($inv->id, 'id', 'order_type', 'ordered_investigations') . ")</b>:";
$invs_id = explode(",", $audit["new_values"]["investigation_id"]);
for ($i = 0; $i < count($invs_id); $i++) {
$text .= "<br>" . get_name($invs_id[$i], 'id', 'name', 'investigations');
}
$patient_timeline[$index]["text"] = $text;
}
$index++;
}
}
// check for any ordered procedures
$ordered_procedures = DB::table('ordered_procedures')
->where('patient_id', $patient_id)
->where('episode_id', $episode_id)
->get();
// here we shall categorize the procedures. If their created at is within 5 seconds
// then they are in the same group of being created
$categorized_procedures = [];
$counter = 0;
$time_last_procedure_was_created = 0;
foreach ($ordered_procedures as $procedure) {
if ($time_last_procedure_was_created != 0) {
$diff_in_time = Carbon::createFromTimeString($procedure->created_at)->diffInSeconds($time_last_procedure_was_created);
if ($diff_in_time < 5) {
$categorized_procedures[$counter - 1]['ids'][] = $procedure->procedure_id;
$categorized_procedures[$counter - 1]['deleted'][] = $procedure->deleted_at;
} else {
$categorized_procedures[$counter]['ids'] = [$procedure->procedure_id];
$categorized_procedures[$counter]['time'] = $procedure->created_at;
$categorized_procedures[$counter]['user_id'] = $procedure->created_by;
$categorized_procedures[$counter]['deleted'] = [$procedure->deleted_at];
$time_last_procedure_was_created = $procedure->created_at;
$counter++;
}
} else {
$categorized_procedures[$counter]['ids'] = [$procedure->procedure_id];
$categorized_procedures[$counter]['time'] = $procedure->created_at;
$categorized_procedures[$counter]['user_id'] = $procedure->created_by;
$categorized_procedures[$counter]['deleted'] = [$procedure->deleted_at];
$time_last_procedure_was_created = $procedure->created_at;
$counter++;
}
}
foreach ($categorized_procedures as $record) {
$patient_timeline[$index]["date"] = streamline_date_time($record['time']);
$text = "Procedures were ordered by " . get_full_name($record['user_id'], 'id', 'first_name', 'last_name', 'users');
$text .= "<br><b>Procedures</b>:";
for ($i = 0; $i < count($record['ids']); $i++) {
$text .= "<br>" . get_name($record['ids'][$i], 'id', 'name', 'procedures');
if ($record['deleted'][$i]) {
$text .= " <span style='color: red'>was later removed at " . streamline_date_time($record['deleted'][$i]) . "</span>";
}
}
$patient_timeline[$index]["text"] = $text;
$index++;
}
// sort the array in descending order according to the date
usort($patient_timeline, array($this, "sort_timeline_by_date"));
return view('audit_trail.patient_timeline', compact('patient', 'patient_timeline'));
}
public function user_timeline(Request $request)
{
//
}
function sort_timeline_by_date($a, $b)
{
return strcmp($a["date"], $b["date"]);
}
}