> */ function normalizeChoresList($raw): array { if (!is_array($raw) || !array_is_list($raw)) { return []; } $out = []; foreach ($raw as $row) { if (is_array($row) && !empty($row['id']) && is_string($row['id'])) { $out[] = $row; } } return $out; } /** * @param array> $people * @return array */ function legacyAssigneeNameToIds(string $name, array $people): array { $name = trim($name); if ($name === '') { return []; } $lower = mb_strtolower($name, 'UTF-8'); $ids = []; foreach ($people as $p) { $pn = trim((string) ($p['name'] ?? '')); if ($pn !== '' && mb_strtolower($pn, 'UTF-8') === $lower) { $ids[] = (string) $p['id']; } } return $ids; } /** * @param array $c * @param array> $people * @return array */ function migrateLegacyChoreRow(array $c, array $people): array { if (isset($c['name']) && !isset($c['title'])) { $c['title'] = (string) $c['name']; } if (!isset($c['assignee_ids']) || !is_array($c['assignee_ids'])) { if (isset($c['assignee'])) { $c['assignee_ids'] = legacyAssigneeNameToIds((string) $c['assignee'], $people); } else { $c['assignee_ids'] = []; } } $ids = []; foreach ($c['assignee_ids'] as $id) { if (is_string($id) && $id !== '') { $ids[] = $id; } } $c['assignee_ids'] = array_values(array_unique($ids)); if (!isset($c['lists']) || !is_array($c['lists'])) { $c['lists'] = []; } $c['lists'] = normalizeChoreLists($c['lists']); if (!isset($c['description'])) { $c['description'] = ''; } if (!isset($c['image'])) { $c['image'] = ''; } if (!isset($c['author_id'])) { $c['author_id'] = ''; } if (!isset($c['value'])) { $c['value'] = 0; } $c['value'] = is_numeric($c['value']) ? (float) $c['value'] : 0.0; if (!isset($c['due_date'])) { $c['due_date'] = ''; } $c['due_date'] = is_string($c['due_date']) ? trim($c['due_date']) : ''; if ($c['due_date'] !== '' && !preg_match('/^\d{4}-\d{2}-\d{2}$/', $c['due_date'])) { $c['due_date'] = ''; } $sched = $c['schedule'] ?? CHORE_SCHEDULE_ONCE; $c['schedule'] = $sched === CHORE_SCHEDULE_RECURRING ? CHORE_SCHEDULE_RECURRING : CHORE_SCHEDULE_ONCE; if (!isset($c['recurrence_days']) || !is_numeric($c['recurrence_days'])) { $c['recurrence_days'] = 7; } $c['recurrence_days'] = max(1, (int) $c['recurrence_days']); if (!isset($c['status'])) { $c['status'] = 'active'; } $st = (string) $c['status']; $c['status'] = in_array($st, ['active', 'completed'], true) ? $st : 'active'; if (!array_key_exists('pending_submission', $c)) { $c['pending_submission'] = null; } if ($c['pending_submission'] !== null && !is_array($c['pending_submission'])) { $c['pending_submission'] = null; } return $c; } /** * @param mixed $lists * @return array}> */ function normalizeChoreLists($lists): array { if (!is_array($lists)) { return []; } $out = []; foreach ($lists as $block) { if (!is_array($block)) { continue; } $type = isset($block['type']) ? (string) $block['type'] : 'checkbox'; if (!in_array($type, ['ordered', 'unordered', 'checkbox'], true)) { $type = 'checkbox'; } $items = $block['items'] ?? []; if (!is_array($items)) { $items = []; } $clean = []; foreach ($items as $it) { $s = trim((string) $it); if ($s !== '') { $clean[] = $s; } } $out[] = ['type' => $type, 'items' => $clean]; } return $out; } /** * @param array> $chores * @return array> */ function migrateAllChores(array $chores, array $people): array { $out = []; foreach ($chores as $c) { $out[] = migrateLegacyChoreRow($c, $people); } return $out; } /** * @param array> $chores */ function findChoreById(array $chores, string $id): ?array { foreach ($chores as $c) { if (($c['id'] ?? '') === $id) { return $c; } } return null; } /** * @param array> $chores */ function findChoreIndexById(array $chores, string $id): ?int { foreach ($chores as $i => $c) { if (($c['id'] ?? '') === $id) { return $i; } } return null; } /** * @param array $assigneeIds * @param array> $people */ function choreAssigneeIdsValid(array $assigneeIds, array $people): bool { $set = []; foreach ($people as $p) { if (!empty($p['id'])) { $set[(string) $p['id']] = true; } } foreach ($assigneeIds as $id) { if (!isset($set[$id])) { return false; } } return true; }