Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
86.67% |
39 / 45 |
|
60.00% |
3 / 5 |
CRAP | |
0.00% |
0 / 1 |
| EvaluationEngineService | |
86.67% |
39 / 45 |
|
60.00% |
3 / 5 |
14.46 | |
0.00% |
0 / 1 |
| getActiveRuleSet | |
64.29% |
9 / 14 |
|
0.00% |
0 / 1 |
2.18 | |||
| evaluate | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
1 | |||
| calculateFinalScore | |
90.00% |
9 / 10 |
|
0.00% |
0 / 1 |
3.01 | |||
| calculateStatus | |
100.00% |
13 / 13 |
|
100.00% |
1 / 1 |
7 | |||
| isEligibleForAdvancement | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| 1 | <?php |
| 2 | |
| 3 | namespace App\Services; |
| 4 | |
| 5 | class 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 | } |