Handle flexible Health Auto Export payloads

This commit is contained in:
2026-05-19 15:26:32 +02:00
parent a555f552c2
commit d8636f6c41
+89 -7
View File
@@ -338,7 +338,7 @@ final class App
sort($dates, SORT_STRING);
if ($dates === []) {
throw new RuntimeException('Der Import enthielt keine unterstützten Health-Daten.');
throw new RuntimeException('Der Import enthielt keine unterstützten Health-Daten. ' . $this->healthPayloadSummary($payload, $metrics, $workouts));
}
$totalItems = max(1, $this->countHealthImportItems($metricImport, $workoutImport));
@@ -468,25 +468,107 @@ final class App
private function healthMetricsFromPayload(array $payload): array
{
return array_values(array_filter(
is_array($payload['metrics'] ?? null) ? $payload['metrics'] : [],
'is_array'
));
$metrics = [];
$this->collectHealthMetrics($payload, $metrics);
return $metrics;
}
private function healthWorkoutsFromPayload(array $payload): array
{
if (is_array($payload['data'] ?? null)) {
$nested = $this->healthWorkoutsFromPayload($payload['data']);
if ($nested !== []) {
return $nested;
}
}
if (is_array($payload['workouts'] ?? null)) {
return array_values(array_filter($payload['workouts'], 'is_array'));
}
if ($this->looksLikeHealthWorkout($payload)) {
return [$payload];
}
if (array_is_list($payload) && isset($payload[0]) && is_array($payload[0])) {
return array_values(array_filter($payload, 'is_array'));
return array_values(array_filter($payload, fn (array $item): bool => $this->looksLikeHealthWorkout($item)));
}
return [];
}
private function collectHealthMetrics(array $payload, array &$metrics): void
{
if (is_array($payload['metrics'] ?? null)) {
foreach ($payload['metrics'] as $metric) {
if (is_array($metric)) {
$this->collectHealthMetrics($metric, $metrics);
}
}
}
if (is_array($payload['data'] ?? null)) {
$this->collectHealthMetrics($payload['data'], $metrics);
}
if (array_is_list($payload)) {
foreach ($payload as $item) {
if (is_array($item)) {
$this->collectHealthMetrics($item, $metrics);
}
}
return;
}
$name = trim((string) ($payload['name'] ?? ($payload['metric'] ?? ($payload['metricName'] ?? ($payload['type'] ?? '')))));
if ($name === '') {
return;
}
$data = is_array($payload['data'] ?? null)
? $payload['data']
: (is_array($payload['records'] ?? null) ? $payload['records'] : []);
if ($data === [] && (isset($payload['qty']) || isset($payload['value']) || isset($payload['date']) || isset($payload['startDate']))) {
$data = [$payload];
}
if ($data !== []) {
$metrics[] = ['name' => $name, 'data' => array_values(array_filter($data, 'is_array'))];
}
}
private function looksLikeHealthWorkout(array $payload): bool
{
return isset($payload['start'], $payload['duration'])
|| isset($payload['startDate'], $payload['duration'])
|| isset($payload['workoutActivityType'])
|| isset($payload['workoutType']);
}
private function healthPayloadSummary(array $payload, array $metrics, array $workouts): string
{
$keys = array_slice(array_keys($payload), 0, 8);
$metricNames = [];
foreach (array_slice($metrics, 0, 5) as $metric) {
if (is_array($metric) && trim((string) ($metric['name'] ?? '')) !== '') {
$metricNames[] = trim((string) $metric['name']);
}
}
$parts = ['Erkannt: ' . count($metrics) . ' Metriken, ' . count($workouts) . ' Workouts'];
if ($keys !== []) {
$parts[] = 'Top-Level: ' . implode(', ', $keys);
}
if ($metricNames !== []) {
$parts[] = 'Metriken: ' . implode(', ', $metricNames);
}
return implode('. ', $parts) . '.';
}
private function healthEventsFromMetrics(array $metrics): array
{
$steps = [];
@@ -724,7 +806,7 @@ final class App
private function healthSleepHours(array $point): float
{
foreach (['totalSleep', 'asleep', 'qty', 'inBed'] as $key) {
foreach (['totalSleep', 'asleep', 'qty', 'value', 'inBed'] as $key) {
if (is_numeric($point[$key] ?? null)) {
return max(0.0, min(24.0, (float) $point[$key]));
}