feat(assessments): layered clinical assessment engine end-to-end
Deploy Ladill Care / deploy (push) Successful in 1m26s
Deploy Ladill Care / deploy (push) Successful in 1m26s
Add a template-driven assessment system with universal intake, clinical pathways, disease instruments (stroke MVP + extended, diabetes, and ten specialty packs), scoring, patient outcome trends, REST/API + FHIR export, and Enterprise org-level assessment analytics. Seed packs and design/licensing docs ship for deploy and pre-GA review.
This commit is contained in:
@@ -3,10 +3,13 @@
|
||||
namespace App\Services\Care;
|
||||
|
||||
use App\Models\Appointment;
|
||||
use App\Models\Assessment;
|
||||
use App\Models\AssessmentScore;
|
||||
use App\Models\Bill;
|
||||
use App\Models\Diagnosis;
|
||||
use App\Models\InvestigationRequest;
|
||||
use App\Models\Patient;
|
||||
use App\Models\PatientPathway;
|
||||
use App\Models\Payment;
|
||||
use App\Models\Visit;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
@@ -200,4 +203,101 @@ class ReportService
|
||||
->limit(20)
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Org-level assessment analytics (Enterprise / assessment_analytics).
|
||||
*
|
||||
* @return array{
|
||||
* summary: array<string, int>,
|
||||
* by_template: Collection<int, object>,
|
||||
* by_pathway: Collection<int, object>,
|
||||
* score_averages: Collection<int, object>
|
||||
* }
|
||||
*/
|
||||
public function assessmentsReport(
|
||||
string $ownerRef,
|
||||
int $organizationId,
|
||||
Carbon $from,
|
||||
Carbon $to,
|
||||
?int $branchId = null,
|
||||
): array {
|
||||
$base = Assessment::query()
|
||||
->from('care_assessments')
|
||||
->where('care_assessments.owner_ref', $ownerRef)
|
||||
->where('care_assessments.organization_id', $organizationId)
|
||||
->when($branchId, fn ($q) => $q->where('care_assessments.branch_id', $branchId))
|
||||
->whereBetween('care_assessments.created_at', [$from, $to])
|
||||
->whereNull('care_assessments.deleted_at');
|
||||
|
||||
$summary = [
|
||||
'total_started' => (clone $base)->count(),
|
||||
'completed' => (clone $base)->where('care_assessments.status', Assessment::STATUS_COMPLETED)->count(),
|
||||
'draft' => (clone $base)->where('care_assessments.status', Assessment::STATUS_DRAFT)->count(),
|
||||
'cancelled' => (clone $base)->where('care_assessments.status', Assessment::STATUS_CANCELLED)->count(),
|
||||
'patients_with_assessments' => (clone $base)->distinct('care_assessments.patient_id')->count('care_assessments.patient_id'),
|
||||
'pathways_activated' => PatientPathway::query()
|
||||
->where('care_patient_pathways.owner_ref', $ownerRef)
|
||||
->where('care_patient_pathways.organization_id', $organizationId)
|
||||
->whereBetween('care_patient_pathways.activated_at', [$from, $to])
|
||||
->whereNull('care_patient_pathways.deleted_at')
|
||||
->count(),
|
||||
];
|
||||
|
||||
$byTemplate = (clone $base)
|
||||
->join('care_assessment_templates', 'care_assessments.template_id', '=', 'care_assessment_templates.id')
|
||||
->select(
|
||||
'care_assessment_templates.code as template_code',
|
||||
'care_assessment_templates.name as template_name',
|
||||
DB::raw('count(*) as total'),
|
||||
DB::raw("sum(case when care_assessments.status = 'completed' then 1 else 0 end) as completed"),
|
||||
)
|
||||
->groupBy('care_assessment_templates.code', 'care_assessment_templates.name')
|
||||
->orderByDesc('total')
|
||||
->limit(30)
|
||||
->get();
|
||||
|
||||
$byPathway = PatientPathway::query()
|
||||
->from('care_patient_pathways')
|
||||
->where('care_patient_pathways.owner_ref', $ownerRef)
|
||||
->where('care_patient_pathways.organization_id', $organizationId)
|
||||
->whereBetween('care_patient_pathways.activated_at', [$from, $to])
|
||||
->whereNull('care_patient_pathways.deleted_at')
|
||||
->join('care_clinical_pathways', 'care_patient_pathways.pathway_id', '=', 'care_clinical_pathways.id')
|
||||
->select(
|
||||
'care_clinical_pathways.code as pathway_code',
|
||||
'care_clinical_pathways.name as pathway_name',
|
||||
DB::raw('count(*) as total'),
|
||||
DB::raw("sum(case when care_patient_pathways.status = 'active' then 1 else 0 end) as still_active"),
|
||||
)
|
||||
->groupBy('care_clinical_pathways.code', 'care_clinical_pathways.name')
|
||||
->orderByDesc('total')
|
||||
->limit(20)
|
||||
->get();
|
||||
|
||||
$scoreAverages = AssessmentScore::query()
|
||||
->from('care_assessment_scores')
|
||||
->where('care_assessment_scores.owner_ref', $ownerRef)
|
||||
->whereHas('assessment', function (Builder $q) use ($organizationId, $branchId, $from, $to) {
|
||||
$q->where('care_assessments.organization_id', $organizationId)
|
||||
->when($branchId, fn ($qq) => $qq->where('care_assessments.branch_id', $branchId))
|
||||
->whereBetween('care_assessments.completed_at', [$from, $to]);
|
||||
})
|
||||
->select(
|
||||
'care_assessment_scores.template_code',
|
||||
DB::raw('count(*) as completions'),
|
||||
DB::raw('avg(care_assessment_scores.total_score) as avg_total'),
|
||||
DB::raw('avg(care_assessment_scores.max_score) as avg_max'),
|
||||
)
|
||||
->groupBy('care_assessment_scores.template_code')
|
||||
->orderByDesc('completions')
|
||||
->limit(20)
|
||||
->get();
|
||||
|
||||
return [
|
||||
'summary' => $summary,
|
||||
'by_template' => $byTemplate,
|
||||
'by_pathway' => $byPathway,
|
||||
'score_averages' => $scoreAverages,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user