Refine balance scoring and dashboard views
This commit is contained in:
+82
-29
@@ -1547,6 +1547,7 @@ final class App
|
||||
$isPersisted = isset($entryMap[$dayDate]);
|
||||
$hasContent = $isPersisted || $this->entryHasContent($entry);
|
||||
$visualScore = $this->dashboardVisualScore($entry, $isPersisted);
|
||||
$lineLevel = $this->dashboardLineLevel($entry, $isPersisted);
|
||||
|
||||
$days[] = [
|
||||
'date' => $dayDate,
|
||||
@@ -1556,9 +1557,9 @@ final class App
|
||||
'is_current' => $dayDate === $date,
|
||||
'has_content' => $hasContent,
|
||||
'visual_score' => $visualScore,
|
||||
'score_level' => $visualScore,
|
||||
'line_level' => $this->dashboardLineLevel($entry, $isPersisted),
|
||||
'line_tone' => $visualScore === null ? 'empty' : signal_value_class($visualScore),
|
||||
'score_level' => $lineLevel,
|
||||
'line_level' => $lineLevel,
|
||||
'line_tone' => $lineLevel === null ? 'empty' : signal_value_class($lineLevel),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1608,7 +1609,7 @@ final class App
|
||||
$iso = $day->format('Y-m-d');
|
||||
$entry = $entryMap[$iso] ?? null;
|
||||
$hasContent = $entry !== null && $this->entryHasContent($entry);
|
||||
$visualScore = $entry !== null ? $this->dashboardVisualScore($entry, true) : null;
|
||||
$lineLevel = $entry !== null ? $this->dashboardLineLevel($entry, true) : null;
|
||||
|
||||
$days[] = [
|
||||
'date' => $iso,
|
||||
@@ -1617,8 +1618,8 @@ final class App
|
||||
'day' => $day->format('j'),
|
||||
'entry' => $entry,
|
||||
'has_content' => $hasContent,
|
||||
'score_level' => $visualScore,
|
||||
'line_tone' => $visualScore === null ? 'empty' : signal_value_class($visualScore),
|
||||
'score_level' => $lineLevel,
|
||||
'line_tone' => $lineLevel === null ? 'empty' : signal_value_class($lineLevel),
|
||||
'is_current' => $iso === $selectedDate,
|
||||
];
|
||||
}
|
||||
@@ -1721,15 +1722,15 @@ final class App
|
||||
$iso = $day->format('Y-m-d');
|
||||
$entry = $entryMap[$iso] ?? null;
|
||||
$hasContent = $entry !== null && $this->entryHasContent($entry);
|
||||
$visualScore = $entry !== null ? $this->dashboardVisualScore($entry, true) : null;
|
||||
$lineLevel = $entry !== null ? $this->dashboardLineLevel($entry, true) : null;
|
||||
$days[] = [
|
||||
'date' => $iso,
|
||||
'day' => $day->format('j'),
|
||||
'weekday' => format_display_date($iso, true),
|
||||
'entry' => $entry,
|
||||
'has_content' => $hasContent,
|
||||
'score_level' => $visualScore,
|
||||
'line_tone' => $visualScore === null ? 'empty' : signal_value_class($visualScore),
|
||||
'score_level' => $lineLevel,
|
||||
'line_tone' => $lineLevel === null ? 'empty' : signal_value_class($lineLevel),
|
||||
'is_future' => $iso > $selectedDate,
|
||||
];
|
||||
}
|
||||
@@ -1787,14 +1788,18 @@ final class App
|
||||
return null;
|
||||
}
|
||||
|
||||
if (is_array($entry['evaluation']['balance'] ?? null)) {
|
||||
return max(-2, min(2, (int) ($entry['evaluation']['balance']['level'] ?? 0)));
|
||||
}
|
||||
|
||||
$percentage = max(0.0, min(100.0, (float) ($entry['evaluation']['percentage'] ?? 0)));
|
||||
|
||||
return (int) round($percentage / 5);
|
||||
return max(-2, min(2, (int) round(($percentage - 50.0) / 25.0)));
|
||||
}
|
||||
|
||||
private function dashboardLineTone(array $entry, bool $isPersisted = false): string
|
||||
{
|
||||
return signal_value_class($this->dashboardVisualScore($entry, $isPersisted) ?? 0);
|
||||
return signal_value_class($this->dashboardLineLevel($entry, $isPersisted) ?? 0);
|
||||
}
|
||||
|
||||
private function dashboardEventFromPost(array $input): array
|
||||
@@ -2555,6 +2560,10 @@ final class App
|
||||
? round(array_sum(array_map(static fn (array $entry): float => (float) $entry['evaluation']['total'], $entries)) / $count, 1)
|
||||
: 0.0;
|
||||
|
||||
$avgBalance = $count > 0
|
||||
? round(array_sum(array_map(static fn (array $entry): float => (float) ($entry['evaluation']['balance']['raw'] ?? 0), $entries)) / $count, 1)
|
||||
: 0.0;
|
||||
|
||||
$avgMood = $count > 0
|
||||
? round(array_sum(array_map(static fn (array $entry): int => (int) $entry['mood'], $entries)) / $count, 1)
|
||||
: 0.0;
|
||||
@@ -2566,6 +2575,7 @@ final class App
|
||||
return [
|
||||
'tracked_days' => $count,
|
||||
'average_score' => $avgScore,
|
||||
'average_balance' => $avgBalance,
|
||||
'average_mood' => $avgMood,
|
||||
'average_stress' => $avgStress,
|
||||
'streak' => $this->calculateStreak($entries),
|
||||
@@ -2825,13 +2835,20 @@ final class App
|
||||
|
||||
return [
|
||||
'calendar' => array_map(static function (array $entry): array {
|
||||
$balance = is_array($entry['evaluation']['balance'] ?? null) ? $entry['evaluation']['balance'] : [];
|
||||
return [
|
||||
'date' => $entry['date'],
|
||||
'score' => $entry['evaluation']['total'],
|
||||
'max' => $entry['evaluation']['max_total'],
|
||||
'score' => (float) ($balance['percentage'] ?? $entry['evaluation']['percentage'] ?? 0),
|
||||
'max' => 100,
|
||||
'label' => $entry['evaluation']['label'],
|
||||
];
|
||||
}, $calendar),
|
||||
'balance' => array_map(static function (array $entry): array {
|
||||
return [
|
||||
'date' => $entry['date'],
|
||||
'value' => (float) ($entry['evaluation']['balance']['raw'] ?? 0),
|
||||
];
|
||||
}, $recent),
|
||||
'mood' => array_map(static function (array $entry): array {
|
||||
return [
|
||||
'date' => $entry['date'],
|
||||
@@ -2915,13 +2932,24 @@ final class App
|
||||
$settings['sleep'] = [
|
||||
'optimal_hours' => max(1.0, min(16.0, round((float) ($input['sleep']['optimal_hours'] ?? ($settings['sleep']['optimal_hours'] ?? 7.0)), 1))),
|
||||
];
|
||||
$scoreMode = (string) ($input['display']['score_mode'] ?? ($settings['display']['score_mode'] ?? 'scale'));
|
||||
$settings['display'] = [
|
||||
'score_mode' => in_array($scoreMode, ['scale', 'percent', 'points'], true) ? $scoreMode : 'scale',
|
||||
];
|
||||
$settings['day_balance'] = [
|
||||
'mood_weight' => max(0, min(10, (int) ($input['day_balance']['mood_weight'] ?? ($settings['day_balance']['mood_weight'] ?? 3)))),
|
||||
'energy_weight' => max(0, min(10, (int) ($input['day_balance']['energy_weight'] ?? ($settings['day_balance']['energy_weight'] ?? 2)))),
|
||||
'stress_weight' => max(0, min(10, (int) ($input['day_balance']['stress_weight'] ?? ($settings['day_balance']['stress_weight'] ?? 2)))),
|
||||
'adjustment_cap' => max(0.0, min(2.0, round((float) ($input['day_balance']['adjustment_cap'] ?? ($settings['day_balance']['adjustment_cap'] ?? 1.0)), 1))),
|
||||
'points_per_step' => max(1, min(50, (int) ($input['day_balance']['points_per_step'] ?? ($settings['day_balance']['points_per_step'] ?? 12)))),
|
||||
];
|
||||
|
||||
$settings['scoring']['mood_multiplier'] = max(0, min(10, (int) ($input['scoring']['mood_multiplier'] ?? 3)));
|
||||
$settings['scoring']['energy_multiplier'] = max(0, min(10, (int) ($input['scoring']['energy_multiplier'] ?? 2)));
|
||||
$settings['scoring']['stress_multiplier'] = max(0, min(10, (int) ($input['scoring']['stress_multiplier'] ?? 2)));
|
||||
$settings['scoring']['mood_multiplier'] = max(0, min(10, (int) ($input['scoring']['mood_multiplier'] ?? ($settings['scoring']['mood_multiplier'] ?? 3))));
|
||||
$settings['scoring']['energy_multiplier'] = max(0, min(10, (int) ($input['scoring']['energy_multiplier'] ?? ($settings['scoring']['energy_multiplier'] ?? 2))));
|
||||
$settings['scoring']['stress_multiplier'] = max(0, min(10, (int) ($input['scoring']['stress_multiplier'] ?? ($settings['scoring']['stress_multiplier'] ?? 2))));
|
||||
$settings['scoring']['pain_multiplier'] = max(0, min(10, (int) ($input['scoring']['pain_multiplier'] ?? ($settings['scoring']['pain_multiplier'] ?? 3))));
|
||||
$settings['scoring']['sleep_feeling_multiplier'] = max(0, min(10, (int) ($input['scoring']['sleep_feeling_multiplier'] ?? 2)));
|
||||
$settings['scoring']['journal_points'] = max(0, min(20, (int) ($input['scoring']['journal_points'] ?? 2)));
|
||||
$settings['scoring']['sleep_feeling_multiplier'] = max(0, min(10, (int) ($input['scoring']['sleep_feeling_multiplier'] ?? ($settings['scoring']['sleep_feeling_multiplier'] ?? 2))));
|
||||
$settings['scoring']['journal_points'] = max(0, min(20, (int) ($input['scoring']['journal_points'] ?? ($settings['scoring']['journal_points'] ?? 2))));
|
||||
$stepBonus = is_array($settings['scoring']['step_bonus'] ?? null) ? $settings['scoring']['step_bonus'] : $defaults['scoring']['step_bonus'];
|
||||
$settings['scoring']['step_bonus'] = [
|
||||
'min' => max(0, min(100000, (int) ($input['scoring']['step_bonus']['min'] ?? $stepBonus['min'] ?? 10000))),
|
||||
@@ -2932,11 +2960,13 @@ final class App
|
||||
$settings['scoring']['step_bonus']['max'] = $settings['scoring']['step_bonus']['min'];
|
||||
}
|
||||
$settings['tracking'] = [
|
||||
'pain_enabled' => isset($input['tracking']['pain_enabled']) && $input['tracking']['pain_enabled'] === '1',
|
||||
'pain_enabled' => array_key_exists('tracking', $input)
|
||||
? (isset($input['tracking']['pain_enabled']) && $input['tracking']['pain_enabled'] === '1')
|
||||
: !empty($settings['tracking']['pain_enabled']),
|
||||
];
|
||||
|
||||
foreach ($defaults['scoring']['sleep_duration_points'] as $key => $default) {
|
||||
$settings['scoring']['sleep_duration_points'][$key] = max(0, min(20, (int) ($input['scoring']['sleep_duration_points'][$key] ?? $default)));
|
||||
$settings['scoring']['sleep_duration_points'][$key] = max(0, min(20, (int) ($input['scoring']['sleep_duration_points'][$key] ?? ($settings['scoring']['sleep_duration_points'][$key] ?? $default))));
|
||||
}
|
||||
|
||||
foreach (['sport_bands', 'walk_bands'] as $bandKey) {
|
||||
@@ -2946,25 +2976,35 @@ final class App
|
||||
$settings['scoring'][$bandKey][$index] = [
|
||||
'min' => max(0, min(2000, (int) ($input['scoring'][$bandKey][$index]['min'] ?? $currentBand['min'] ?? $defaultBand['min']))),
|
||||
'max' => max(0, min(2000, (int) ($input['scoring'][$bandKey][$index]['max'] ?? $currentBand['max'] ?? $defaultBand['max']))),
|
||||
'points' => max(0, min(20, (int) ($input['scoring'][$bandKey][$index]['points'] ?? $currentBand['points'] ?? $defaultBand['points']))),
|
||||
'points' => max(-20, min(20, (int) ($input['scoring'][$bandKey][$index]['points'] ?? $currentBand['points'] ?? $defaultBand['points']))),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($defaults['scoring']['walk_step_targets'] as $index => $defaultTarget) {
|
||||
$currentTarget = $settings['scoring']['walk_step_targets'][$index] ?? $defaultTarget;
|
||||
$settings['scoring']['walk_step_targets'][$index] = [
|
||||
'steps' => max(0, min(100000, (int) ($input['scoring']['walk_step_targets'][$index]['steps'] ?? $currentTarget['steps'] ?? $defaultTarget['steps']))),
|
||||
'points' => max(-20, min(20, (int) ($input['scoring']['walk_step_targets'][$index]['points'] ?? $currentTarget['points'] ?? $defaultTarget['points']))),
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($defaults['ratings'] as $index => $defaultRating) {
|
||||
$currentRating = $settings['ratings'][$index] ?? $defaultRating;
|
||||
$settings['ratings'][$index] = [
|
||||
'label' => trim((string) ($input['ratings'][$index]['label'] ?? $defaultRating['label'])) ?: $defaultRating['label'],
|
||||
'min' => max(0, min(999, (int) ($input['ratings'][$index]['min'] ?? $defaultRating['min']))),
|
||||
'max' => max(0, min(999, (int) ($input['ratings'][$index]['max'] ?? $defaultRating['max']))),
|
||||
'label' => trim((string) ($input['ratings'][$index]['label'] ?? $currentRating['label'] ?? $defaultRating['label'])) ?: $defaultRating['label'],
|
||||
'min' => max(0, min(999, (int) ($input['ratings'][$index]['min'] ?? $currentRating['min'] ?? $defaultRating['min']))),
|
||||
'max' => max(0, min(999, (int) ($input['ratings'][$index]['max'] ?? $currentRating['max'] ?? $defaultRating['max']))),
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($defaults['guardrails'] as $index => $defaultGuardrail) {
|
||||
$energyRaw = $input['guardrails'][$index]['energy_max'] ?? $defaultGuardrail['energy_max'];
|
||||
$currentGuardrail = $settings['guardrails'][$index] ?? $defaultGuardrail;
|
||||
$energyRaw = $input['guardrails'][$index]['energy_max'] ?? $currentGuardrail['energy_max'] ?? $defaultGuardrail['energy_max'];
|
||||
$settings['guardrails'][$index] = [
|
||||
'mood_max' => max(1, min(10, (int) ($input['guardrails'][$index]['mood_max'] ?? $defaultGuardrail['mood_max']))),
|
||||
'mood_max' => max(1, min(10, (int) ($input['guardrails'][$index]['mood_max'] ?? $currentGuardrail['mood_max'] ?? $defaultGuardrail['mood_max']))),
|
||||
'energy_max' => $energyRaw === '' || $energyRaw === null ? null : max(1, min(10, (int) $energyRaw)),
|
||||
'cap_label' => trim((string) ($input['guardrails'][$index]['cap_label'] ?? $defaultGuardrail['cap_label'])) ?: $defaultGuardrail['cap_label'],
|
||||
'cap_label' => trim((string) ($input['guardrails'][$index]['cap_label'] ?? $currentGuardrail['cap_label'] ?? $defaultGuardrail['cap_label'])) ?: $defaultGuardrail['cap_label'],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -2974,7 +3014,7 @@ final class App
|
||||
$settings['sport_types'] = normalized_sport_types([
|
||||
'sport_types' => $sportTypesProvided && is_array($input['sport_types'] ?? null)
|
||||
? $input['sport_types']
|
||||
: ($sportTypesProvided ? [] : $defaults['sport_types']),
|
||||
: ($sportTypesProvided ? [] : ($settings['sport_types'] ?? $defaults['sport_types'])),
|
||||
]);
|
||||
|
||||
$time = trim((string) ($input['notifications']['time'] ?? ($settings['notifications']['time'] ?? $defaults['notifications']['time'])));
|
||||
@@ -2983,7 +3023,9 @@ final class App
|
||||
}
|
||||
|
||||
$settings['notifications'] = [
|
||||
'enabled' => isset($input['notifications']['enabled']) && $input['notifications']['enabled'] === '1',
|
||||
'enabled' => array_key_exists('notifications', $input)
|
||||
? (isset($input['notifications']['enabled']) && $input['notifications']['enabled'] === '1')
|
||||
: !empty($settings['notifications']['enabled']),
|
||||
'time' => $time,
|
||||
];
|
||||
|
||||
@@ -3002,6 +3044,17 @@ final class App
|
||||
is_array($settings['sleep'] ?? null) ? $settings['sleep'] : []
|
||||
);
|
||||
$settings['sleep']['optimal_hours'] = max(1.0, min(16.0, round((float) ($settings['sleep']['optimal_hours'] ?? 7.0), 1)));
|
||||
$settings['display'] = array_replace(
|
||||
Defaults::settings()['display'],
|
||||
is_array($settings['display'] ?? null) ? $settings['display'] : []
|
||||
);
|
||||
if (!in_array((string) ($settings['display']['score_mode'] ?? 'scale'), ['scale', 'percent', 'points'], true)) {
|
||||
$settings['display']['score_mode'] = 'scale';
|
||||
}
|
||||
$settings['day_balance'] = array_replace(
|
||||
Defaults::settings()['day_balance'],
|
||||
is_array($settings['day_balance'] ?? null) ? $settings['day_balance'] : []
|
||||
);
|
||||
$settings['tracking'] = array_replace(
|
||||
Defaults::settings()['tracking'],
|
||||
is_array($settings['tracking'] ?? null) ? $settings['tracking'] : []
|
||||
|
||||
Reference in New Issue
Block a user