Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
86.67% covered (warning)
86.67%
39 / 45
60.00% covered (warning)
60.00%
3 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
EvaluationEngineService
86.67% covered (warning)
86.67%
39 / 45
60.00% covered (warning)
60.00%
3 / 5
14.46
0.00% covered (danger)
0.00%
0 / 1
 getActiveRuleSet
64.29% covered (warning)
64.29%
9 / 14
0.00% covered (danger)
0.00%
0 / 1
2.18
 evaluate
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 calculateFinalScore
90.00% covered (success)
90.00%
9 / 10
0.00% covered (danger)
0.00%
0 / 1
3.01
 calculateStatus
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
7
 isEligibleForAdvancement
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace App\Services;
4
5class EvaluationEngineService
6{
7    /**
8     * @return array<string, float|int|string|bool|null>
9     */
10    public function getActiveRuleSet(): array
11    {
12        $rule = db_connect()
13            ->table('evaluation_rules')
14            ->where('is_active', 1)
15            ->orderBy('scope_type', 'ASC')
16            ->orderBy('id', 'DESC')
17            ->get()
18            ->getRowArray();
19
20        if ($rule !== null) {
21            return $rule;
22        }
23
24        return [
25            'almost_ready_threshold' => 3.5,
26            'ready_threshold' => 4.0,
27            'minimum_critical_threshold' => 3.0,
28        ];
29    }
30
31    /**
32     * @param array<int, array<string, float|int|bool|null>> $criteriaScores
33     * @param array<string, float|int|string|bool|null> $rules
34     * @return array{final_score: float, final_status: string, promotion_signal: bool}
35     */
36    public function evaluate(array $criteriaScores, array $rules): array
37    {
38        $finalScore = $this->calculateFinalScore($criteriaScores);
39        $status     = $this->calculateStatus($finalScore, $criteriaScores, $rules);
40
41        return [
42            'final_score' => $finalScore,
43            'final_status' => $status,
44            'promotion_signal' => $this->isEligibleForAdvancement($finalScore, $criteriaScores, $rules),
45        ];
46    }
47
48    /**
49     * @param array<int, array<string, float|int|bool|null>> $criteriaScores
50     */
51    public function calculateFinalScore(array $criteriaScores): float
52    {
53        $weightedScore = 0.0;
54        $weightTotal   = 0.0;
55
56        foreach ($criteriaScores as $criterion) {
57            $score  = (float) ($criterion['score'] ?? 0);
58            $weight = (float) ($criterion['weight'] ?? 1);
59
60            $weightedScore += $score * $weight;
61            $weightTotal   += $weight;
62        }
63
64        if ($weightTotal === 0.0) {
65            return 0.0;
66        }
67
68        return round($weightedScore / $weightTotal, 2);
69    }
70
71    /**
72     * @param array<int, array<string, float|int|bool|null>> $criteriaScores
73     * @param array<string, float|int|string|bool|null> $rules
74     */
75    public function calculateStatus(float $finalScore, array $criteriaScores, array $rules): string
76    {
77        $readyThreshold           = (float) ($rules['ready_threshold'] ?? 4.0);
78        $almostReadyThreshold     = (float) ($rules['almost_ready_threshold'] ?? 3.5);
79        $minimumCriticalThreshold = (float) ($rules['minimum_critical_threshold'] ?? 3.0);
80
81        foreach ($criteriaScores as $criterion) {
82            $isCritical = (bool) ($criterion['is_critical'] ?? false);
83            $score      = (float) ($criterion['score'] ?? 0);
84
85            if ($isCritical && $score < $minimumCriticalThreshold) {
86                return $finalScore >= $almostReadyThreshold ? 'ALMOST READY' : 'HOLD';
87            }
88        }
89
90        if ($finalScore >= $readyThreshold) {
91            return 'READY';
92        }
93
94        if ($finalScore >= $almostReadyThreshold) {
95            return 'ALMOST READY';
96        }
97
98        return 'HOLD';
99    }
100
101    /**
102     * @param array<int, array<string, float|int|bool|null>> $criteriaScores
103     * @param array<string, float|int|string|bool|null> $rules
104     */
105    public function isEligibleForAdvancement(float $finalScore, array $criteriaScores, array $rules): bool
106    {
107        return $this->calculateStatus($finalScore, $criteriaScores, $rules) === 'READY';
108    }
109}