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:
347
Shared/Infrastructure/Scripts/validate-fix-lists.php
Normal file
347
Shared/Infrastructure/Scripts/validate-fix-lists.php
Normal 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";
|
||||
Reference in New Issue
Block a user