Handle flexible Health Auto Export payloads
This commit is contained in:
+89
-7
@@ -338,7 +338,7 @@ final class App
|
|||||||
sort($dates, SORT_STRING);
|
sort($dates, SORT_STRING);
|
||||||
|
|
||||||
if ($dates === []) {
|
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));
|
$totalItems = max(1, $this->countHealthImportItems($metricImport, $workoutImport));
|
||||||
@@ -468,25 +468,107 @@ final class App
|
|||||||
|
|
||||||
private function healthMetricsFromPayload(array $payload): array
|
private function healthMetricsFromPayload(array $payload): array
|
||||||
{
|
{
|
||||||
return array_values(array_filter(
|
$metrics = [];
|
||||||
is_array($payload['metrics'] ?? null) ? $payload['metrics'] : [],
|
$this->collectHealthMetrics($payload, $metrics);
|
||||||
'is_array'
|
|
||||||
));
|
return $metrics;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function healthWorkoutsFromPayload(array $payload): array
|
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)) {
|
if (is_array($payload['workouts'] ?? null)) {
|
||||||
return array_values(array_filter($payload['workouts'], 'is_array'));
|
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])) {
|
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 [];
|
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
|
private function healthEventsFromMetrics(array $metrics): array
|
||||||
{
|
{
|
||||||
$steps = [];
|
$steps = [];
|
||||||
@@ -724,7 +806,7 @@ final class App
|
|||||||
|
|
||||||
private function healthSleepHours(array $point): float
|
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)) {
|
if (is_numeric($point[$key] ?? null)) {
|
||||||
return max(0.0, min(24.0, (float) $point[$key]));
|
return max(0.0, min(24.0, (float) $point[$key]));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user