feat: preparar migración del evangelio diario (issue #20)

This commit is contained in:
2026-08-30 08:44:10 -04:00
parent 3da7c79f5a
commit 67857638ba
2 changed files with 113 additions and 9 deletions
+82
View File
@@ -0,0 +1,82 @@
<?php
/**
* Migra A la fuente cada día al índice estable del libro.
*
* Uso (por defecto solo informa):
* wp eval-file scripts/import_evangelio_diario.php --path=/var/www/html
* Escritura explícita: FEA_EVANGELIO_APPLY=1 wp eval-file ...
*
* Requiere que entradas-libro.json esté junto a este script durante la ejecución.
* Nunca elimina posts: actualiza únicamente los ya identificados de categoría 14 y
* crea los índices ausentes. Es idempotente por _fea_book_index.
*/
$apply = getenv('FEA_EVANGELIO_APPLY') === '1';
$book_file = __DIR__ . '/../data/evangelio-diario/entradas-libro.json';
if (!is_readable($book_file)) { fwrite(STDERR, "No se lee $book_file\n"); exit(2); }
$book = json_decode(file_get_contents($book_file), true, 512, JSON_THROW_ON_ERROR);
if (count($book) !== 500) { fwrite(STDERR, "El libro debe tener 500 entradas\n"); exit(2); }
$norm = static function ($text) {
$text = html_entity_decode(wp_strip_all_tags((string) $text), ENT_QUOTES | ENT_HTML5, 'UTF-8');
return preg_replace('/\s+/u', ' ', trim(mb_strtolower($text, 'UTF-8')));
};
$by_motto = [];
foreach ($book as $i => $entry) {
$key = $norm($entry['motto']);
if ($key !== '') $by_motto[$key][] = $i + 1;
}
$existing = get_posts([
'post_type' => 'post', 'post_status' => 'any', 'posts_per_page' => -1,
'category' => 14, 'orderby' => 'ID', 'order' => 'ASC',
]);
$used = [];
$updated = $ambiguous = $unmatched = 0;
$to_update = [];
foreach ($existing as $post) {
$matches = [];
foreach ($by_motto as $motto => $indices) {
if (str_contains($norm($post->post_content), $motto)) $matches = array_merge($matches, $indices);
}
$matches = array_values(array_unique($matches));
if (count($matches) !== 1 || isset($used[$matches[0]])) {
$ambiguous += count($matches) > 1; $unmatched += count($matches) === 0;
continue;
}
$idx = $matches[0]; $used[$idx] = true;
$entry = $book[$idx - 1];
$to_update[] = [$post->ID, $idx, $entry];
$updated++;
}
$planned_new = count($book) - count($used);
if ($ambiguous || $unmatched) {
printf("DRY-RUN: existentes indexados=%d; nuevos=%d; ambiguos=%d; sin coincidencia=%d; total=%d\n",
$updated, $planned_new, $ambiguous, $unmatched, count($book));
fwrite(STDERR, "ABORTAR APLICACIÓN: hay posts existentes sin correspondencia única.\n");
exit(3);
}
if ($apply) foreach ($to_update as [$post_id, $idx, $entry]) {
update_post_meta($post_id, '_fea_book_index', (string) $idx);
update_post_meta($post_id, '_fea_cita', $entry['citation']);
update_post_meta($post_id, '_fea_fuente', 'A la fuente cada día — Fray Marcos, entrada #' . $idx);
}
$created = 0;
foreach ($book as $i => $entry) {
$idx = $i + 1;
if (isset($used[$idx])) continue;
$body = '<h1>' . esc_html($entry['title']) . '</h1>'
. '<p><strong>' . esc_html($entry['citation']) . '</strong></p>'
. '<p>' . esc_html($entry['gospel']) . '</p>'
. '<p><em>' . esc_html($entry['motto']) . '</em></p>';
foreach ($entry['paragraphs'] as $paragraph) $body .= '<p>' . esc_html($paragraph) . '</p>';
if ($apply) {
$id = wp_insert_post(['post_title' => $entry['title'], 'post_content' => $body,
'post_status' => 'publish', 'post_category' => [14]], true);
if (is_wp_error($id)) { fwrite(STDERR, $id->get_error_message() . "\n"); exit(1); }
update_post_meta($id, '_fea_book_index', (string) $idx);
update_post_meta($id, '_fea_cita', $entry['citation']);
update_post_meta($id, '_fea_fuente', 'A la fuente cada día — Fray Marcos, entrada #' . $idx);
}
$created++;
}
printf("%s: existentes indexados=%d; nuevos=%d; ambiguos=%d; sin coincidencia=%d; total=%d\n",
$apply ? 'APLICADO' : 'DRY-RUN', $updated, $created, $ambiguous, $unmatched, count($book));