Add validation script for list fixes

Generates HTML files for visual comparison before/after
correction. Creates comparison_report.html for review.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
FrankZamora
2025-11-27 17:40:18 -06:00
parent a10831e2c2
commit 651e8124d4

View File

@@ -0,0 +1,347 @@
<?php
/**
* Validador de Correcciones - Genera archivos HTML para revisión visual
*
* PROPÓSITO: Crear archivos comparativos ANTES/DESPUÉS para validar
* que la corrección no rompe el contenido.
*
* USO: php validate-fix-lists.php
*
* GENERA:
* /tmp/list-fix-validation/
* ├── post_ID_before.html
* ├── post_ID_after.html
* └── comparison_report.html
*
* @package ROI_Theme
*/
error_reporting(E_ALL);
ini_set('display_errors', 1);
ini_set('memory_limit', '256M');
$db_config = [
'host' => 'localhost',
'database' => 'preciosunitarios_seo',
'username' => 'preciosunitarios_seo',
'password' => 'ACl%EEFd=V-Yvb??',
'charset' => 'utf8mb4'
];
$output_dir = '/tmp/list-fix-validation';
$sample_size = 5;
echo "==============================================\n";
echo " VALIDADOR DE CORRECCIONES\n";
echo " Fecha: " . date('Y-m-d H:i:s') . "\n";
echo "==============================================\n\n";
// Crear directorio de salida
if (!is_dir($output_dir)) {
mkdir($output_dir, 0755, true);
}
// Limpiar archivos anteriores
array_map('unlink', glob("$output_dir/*.html"));
/**
* Detectar problemas en HTML
*/
function detectIssues(string $html): array {
$issues = [];
libxml_use_internal_errors(true);
$doc = new DOMDocument('1.0', 'UTF-8');
$wrapped = '<div id="temp-wrapper">' . $html . '</div>';
$doc->loadHTML('<?xml encoding="UTF-8">' . $wrapped, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
libxml_clear_errors();
$validChildren = ['li', 'script', 'template'];
foreach (['ul', 'ol'] as $listTag) {
foreach ($doc->getElementsByTagName($listTag) as $list) {
foreach ($list->childNodes as $child) {
if ($child->nodeType === XML_ELEMENT_NODE) {
$tagName = strtolower($child->nodeName);
if (!in_array($tagName, $validChildren)) {
$issues[] = "<$listTag> contiene <$tagName>";
}
}
}
}
}
return $issues;
}
/**
* Corregir listas mal formadas
*/
function fixMalformedLists(string $html): array {
$result = ['fixed' => false, 'html' => $html, 'changes' => 0, 'details' => []];
libxml_use_internal_errors(true);
$doc = new DOMDocument('1.0', 'UTF-8');
$wrapped = '<div id="temp-wrapper">' . $html . '</div>';
$doc->loadHTML('<?xml encoding="UTF-8">' . $wrapped, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
libxml_clear_errors();
$lists = [];
foreach ($doc->getElementsByTagName('ul') as $ul) { $lists[] = $ul; }
foreach ($doc->getElementsByTagName('ol') as $ol) { $lists[] = $ol; }
$changes = 0;
$validChildren = ['li', 'script', 'template'];
foreach ($lists as $list) {
$nodesToProcess = [];
foreach ($list->childNodes as $child) {
if ($child->nodeType === XML_ELEMENT_NODE) {
$tagName = strtolower($child->nodeName);
if (!in_array($tagName, $validChildren)) {
$nodesToProcess[] = $child;
}
}
}
foreach ($nodesToProcess as $node) {
$tagName = strtolower($node->nodeName);
$prevLi = null;
$prev = $node->previousSibling;
while ($prev) {
if ($prev->nodeType === XML_ELEMENT_NODE && strtolower($prev->nodeName) === 'li') {
$prevLi = $prev;
break;
}
$prev = $prev->previousSibling;
}
if ($prevLi) {
$prevLi->appendChild($node);
$result['details'][] = "Movido <$tagName> dentro del <li> anterior";
$changes++;
} else {
$newLi = $doc->createElement('li');
$list->insertBefore($newLi, $node);
$newLi->appendChild($node);
$result['details'][] = "Envuelto <$tagName> en nuevo <li>";
$changes++;
}
}
}
if ($changes > 0) {
$wrapper = $doc->getElementById('temp-wrapper');
if ($wrapper) {
$innerHTML = '';
foreach ($wrapper->childNodes as $child) {
$innerHTML .= $doc->saveHTML($child);
}
$result['html'] = $innerHTML;
$result['fixed'] = true;
$result['changes'] = $changes;
}
}
return $result;
}
/**
* Generar HTML wrapper para visualización
*/
function wrapForVisualization(string $content, string $title, string $status): string {
$statusColor = $status === 'error' ? '#dc3545' : '#28a745';
return <<<HTML
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>$title</title>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 20px; line-height: 1.6; }
.status { padding: 10px 20px; background: $statusColor; color: white; border-radius: 4px; margin-bottom: 20px; }
.content { border: 1px solid #ddd; padding: 20px; border-radius: 4px; background: #fafafa; }
ul, ol { background: #fff3cd; padding: 15px 15px 15px 35px; border-left: 4px solid #ffc107; margin: 10px 0; }
li { background: #d4edda; padding: 5px 10px; margin: 5px 0; border-left: 3px solid #28a745; }
h1, h2, h3, h4, h5, h6 { color: #333; }
p { color: #555; }
</style>
</head>
<body>
<div class="status">$status</div>
<div class="content">
$content
</div>
</body>
</html>
HTML;
}
// Conectar a DB
$conn = new mysqli($db_config['host'], $db_config['username'], $db_config['password'], $db_config['database']);
$conn->set_charset($db_config['charset']);
if ($conn->connect_error) {
die("Error de conexión: " . $conn->connect_error);
}
echo "✓ Conexión establecida\n\n";
// Buscar posts con problemas
$query = "SELECT id, page, html FROM datos_seo_pagina WHERE html IS NOT NULL AND html != '' ORDER BY id LIMIT 500";
$result = $conn->query($query);
$samples = [];
while ($row = $result->fetch_assoc()) {
$issues = detectIssues($row['html']);
if (!empty($issues) && count($samples) < $sample_size) {
$samples[] = $row;
}
}
echo "Encontrados " . count($samples) . " posts con problemas para validar\n\n";
$comparison_data = [];
foreach ($samples as $idx => $post) {
$id = $post['id'];
$url = $post['page'];
$html_before = $post['html'];
echo "─────────────────────────────────\n";
echo "POST $id: $url\n";
// Detectar problemas antes
$issues_before = detectIssues($html_before);
echo " Problemas ANTES: " . count($issues_before) . "\n";
// Aplicar corrección
$fixResult = fixMalformedLists($html_before);
$html_after = $fixResult['html'];
// Detectar problemas después
$issues_after = detectIssues($html_after);
echo " Problemas DESPUÉS: " . count($issues_after) . "\n";
echo " Cambios aplicados: " . $fixResult['changes'] . "\n";
// Guardar archivos HTML
$file_before = "$output_dir/post_{$id}_BEFORE.html";
$file_after = "$output_dir/post_{$id}_AFTER.html";
file_put_contents($file_before, wrapForVisualization(
$html_before,
"Post $id - ANTES (con errores)",
"ANTES: " . count($issues_before) . " problemas de listas"
));
file_put_contents($file_after, wrapForVisualization(
$html_after,
"Post $id - DESPUÉS (corregido)",
"DESPUÉS: " . count($issues_after) . " problemas - " . $fixResult['changes'] . " correcciones aplicadas"
));
echo " ✓ Archivos generados:\n";
echo " - $file_before\n";
echo " - $file_after\n";
// Guardar datos para reporte
$comparison_data[] = [
'id' => $id,
'url' => $url,
'issues_before' => count($issues_before),
'issues_after' => count($issues_after),
'changes' => $fixResult['changes'],
'file_before' => "post_{$id}_BEFORE.html",
'file_after' => "post_{$id}_AFTER.html"
];
}
// Generar reporte comparativo
$report_html = <<<HTML
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Reporte de Validación - Corrección de Listas</title>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 20px; }
h1 { color: #333; border-bottom: 2px solid #007bff; padding-bottom: 10px; }
table { width: 100%; border-collapse: collapse; margin: 20px 0; }
th, td { padding: 12px; text-align: left; border: 1px solid #ddd; }
th { background: #007bff; color: white; }
tr:nth-child(even) { background: #f8f9fa; }
.success { color: #28a745; font-weight: bold; }
.warning { color: #ffc107; font-weight: bold; }
.error { color: #dc3545; font-weight: bold; }
a { color: #007bff; text-decoration: none; }
a:hover { text-decoration: underline; }
.instructions { background: #e7f3ff; padding: 15px; border-radius: 4px; margin: 20px 0; }
</style>
</head>
<body>
<h1>Reporte de Validación - Corrección de Listas HTML</h1>
<div class="instructions">
<strong>Instrucciones:</strong>
<ol>
<li>Abre cada par de archivos (ANTES/DESPUÉS) en el navegador</li>
<li>Verifica que el contenido se muestre correctamente</li>
<li>Las listas (fondo amarillo) deben contener solo items (fondo verde)</li>
<li>Si todo se ve bien, la corrección es segura</li>
</ol>
</div>
<table>
<thead>
<tr>
<th>ID</th>
<th>URL</th>
<th>Problemas Antes</th>
<th>Problemas Después</th>
<th>Cambios</th>
<th>Archivos</th>
</tr>
</thead>
<tbody>
HTML;
foreach ($comparison_data as $data) {
$status_class = $data['issues_after'] == 0 ? 'success' : ($data['issues_after'] < $data['issues_before'] ? 'warning' : 'error');
$report_html .= <<<HTML
<tr>
<td>{$data['id']}</td>
<td><a href="{$data['url']}" target="_blank">{$data['url']}</a></td>
<td class="error">{$data['issues_before']}</td>
<td class="$status_class">{$data['issues_after']}</td>
<td>{$data['changes']}</td>
<td>
<a href="{$data['file_before']}" target="_blank">ANTES</a> |
<a href="{$data['file_after']}" target="_blank">DESPUÉS</a>
</td>
</tr>
HTML;
}
$report_html .= <<<HTML
</tbody>
</table>
<p><strong>Generado:</strong> {$_SERVER['REQUEST_TIME_FLOAT']}</p>
</body>
</html>
HTML;
$report_file = "$output_dir/comparison_report.html";
file_put_contents($report_file, $report_html);
echo "\n─────────────────────────────────\n";
echo "REPORTE GENERADO:\n";
echo " $report_file\n\n";
echo "Para revisar, descarga el directorio:\n";
echo " scp -r VPSContabo:$output_dir ./validation/\n\n";
$conn->close();
echo "✓ Validación completada.\n";