fix(#143): corregir autoría 'Fe Adulta' y avatares de iniciales
Artículos K2 cuyo created_by de Joomla no tenía usuario WP caían a
post_author=1 ('Fe Adulta'). Herramientas:
- fix_k2_authors.php: crea el usuario (nombre real de ew4r_users) y
reasigna post_author (idempotente, dry-run/APPLY).
- gen_avatars_initials.py: recrea el generador de avatares de iniciales
del #62 (paleta determinista + DejaVuSans-Bold).
- import_avatars_143.php: alta de attachment + foto_perfil con backup.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
/**
|
||||
* fix_k2_authors.php (#143) — Corrige la autoría de artículos de origen K2 que
|
||||
* quedaron atribuidos al usuario genérico «Fe Adulta» (post_author 1 / 890)
|
||||
* porque el importador de delta (import_new_k2_items.py) no encontró un usuario
|
||||
* WP para su `created_by` de Joomla y cayó al fallback admin.
|
||||
*
|
||||
* Qué hace, por cada artículo del TSV de entrada:
|
||||
* 1. Crea (idempotente) un usuario WP rol 'subscriber' con el nombre real del
|
||||
* autor (display_name), login = slug del nombre, email = slug@feadulta.com.
|
||||
* 2. Reasigna post_author del/los post WP de ese K2 (los que sigan en 1/890).
|
||||
*
|
||||
* Entrada: un TSV «k2_id<TAB>created_by<TAB>nombre», generado desde Joomla:
|
||||
* IDS=<lista de k2_id atribuidos a 1/890> # de wp: meta _fgj2wp_old_k2_id
|
||||
* mysql --skip-ssl ... fejoomla3 -N -e \
|
||||
* "SELECT i.id, i.created_by, COALESCE(u.name,'') \
|
||||
* FROM ew4r_k2_items i LEFT JOIN ew4r_users u ON u.id=i.created_by \
|
||||
* WHERE i.id IN ($IDS);" > /tmp/autores143.tsv
|
||||
*
|
||||
* Uso (en el servidor, dentro de /web/wp-nuevo):
|
||||
* FEA_TSV=/tmp/autores143.tsv wp eval-file scripts/fix_k2_authors.php # dry-run
|
||||
* APPLY=1 FEA_TSV=/tmp/autores143.tsv wp eval-file scripts/fix_k2_authors.php # aplica
|
||||
*
|
||||
* Notas:
|
||||
* - Los autores con created_by cuyo usuario Joomla ya no existe llegan con
|
||||
* nombre vacío en el TSV → se SALTAN (no recuperable; firma en el cuerpo).
|
||||
* - El nombre literal «Fe Adulta» se salta (es legítimo).
|
||||
* - Los nuevos usuarios quedan sin foto_perfil (avatar genérico). Si se quiere
|
||||
* avatar propio, generarlo aparte (ver flujo de avatares #62).
|
||||
*/
|
||||
|
||||
$APPLY = getenv('APPLY') === '1';
|
||||
$TSV = getenv('FEA_TSV') ?: '/tmp/autores143.tsv';
|
||||
if (!is_readable($TSV)) { fwrite(STDERR, "No puedo leer TSV: $TSV\n"); exit(1); }
|
||||
|
||||
global $wpdb;
|
||||
$GENERIC = [1, 890];
|
||||
$SKIP_NAMES = ['Fe Adulta'];
|
||||
|
||||
$byname = [];
|
||||
foreach (file($TSV) as $line) {
|
||||
$r = explode("\t", rtrim($line, "\n"));
|
||||
if (count($r) < 3) continue;
|
||||
$name = trim($r[2]);
|
||||
if ($name === '' || in_array($name, $SKIP_NAMES, true)) continue;
|
||||
$byname[$name][] = (int) $r[0];
|
||||
}
|
||||
|
||||
$created = 0; $reassigned = 0; $log = [];
|
||||
foreach ($byname as $name => $k2ids) {
|
||||
$login = sanitize_user(sanitize_title($name), true);
|
||||
$u = get_user_by('login', $login);
|
||||
if (!$u) {
|
||||
$email = $login . '@feadulta.com'; $i = 2;
|
||||
while (email_exists($email)) { $email = $login . $i . '@feadulta.com'; $i++; }
|
||||
if ($APPLY) {
|
||||
$uid = wp_insert_user([
|
||||
'user_login' => $login,
|
||||
'user_pass' => wp_generate_password(20),
|
||||
'user_email' => $email,
|
||||
'display_name' => $name,
|
||||
'nickname' => $name,
|
||||
'role' => 'subscriber',
|
||||
]);
|
||||
if (is_wp_error($uid)) { $log[] = "ERROR crear '$name': " . $uid->get_error_message(); continue; }
|
||||
$u = get_userdata($uid); $created++;
|
||||
$log[] = "USER creado: '$name' -> id $uid ($login / $email)";
|
||||
} else {
|
||||
$log[] = "[dry] crearia USER '$name' ($login / $email)"; $created++;
|
||||
}
|
||||
} else {
|
||||
$log[] = "USER ya existe: '$name' -> id {$u->ID} ($login)";
|
||||
}
|
||||
$uid = $u ? $u->ID : 0;
|
||||
foreach ($k2ids as $k2) {
|
||||
$pids = $wpdb->get_col($wpdb->prepare(
|
||||
"SELECT DISTINCT post_id FROM {$wpdb->postmeta}
|
||||
WHERE meta_key='_fgj2wp_old_k2_id' AND meta_value=%s", (string) $k2));
|
||||
foreach ($pids as $pid) {
|
||||
$a = (int) get_post_field('post_author', $pid);
|
||||
if (!in_array($a, $GENERIC, true)) continue;
|
||||
if ($APPLY && $uid) {
|
||||
wp_update_post(['ID' => (int) $pid, 'post_author' => $uid]);
|
||||
$reassigned++; $log[] = " post $pid (k2 $k2) author $a -> $uid";
|
||||
} else {
|
||||
$reassigned++; $log[] = " [dry] post $pid (k2 $k2) author $a -> '$name'";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
echo implode("\n", $log) . "\n";
|
||||
echo "\nRESUMEN: usuarios " . ($APPLY ? 'creados' : 'a crear') . ": $created ; "
|
||||
. "posts " . ($APPLY ? 'reasignados' : 'a reasignar') . ": $reassigned "
|
||||
. "(modo " . ($APPLY ? 'APPLY' : 'DRY-RUN') . ")\n";
|
||||
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Genera avatares de INICIALES (mismo formato que #62) para autores sin foto.
|
||||
|
||||
Formato replicado del #62: PNG 200x200 RGBA, círculo de color sólido (paleta
|
||||
determinista de 10 tonos elegida por hash del nombre) con las iniciales en blanco,
|
||||
DejaVuSans-Bold, esquinas transparentes (borde circular supersampleado).
|
||||
|
||||
Entrada: TSV «uid<TAB>display_name» (env FEA_TSV, por defecto /tmp/users29.tsv).
|
||||
Salida: uploads/avatares/autores/autor-<uid>.png
|
||||
|
||||
Uso:
|
||||
FEA_TSV=/tmp/users29.tsv python3 scripts/gen_avatars_initials.py
|
||||
"""
|
||||
import hashlib, os, unicodedata
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
OUT = "/home/rafa/joomla-migration/wordpress/wp-content/uploads/avatares/autores"
|
||||
TSV = os.environ.get("FEA_TSV", "/tmp/users29.tsv")
|
||||
FONT = "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf"
|
||||
SIZE, SS = 200, 4
|
||||
|
||||
# Paleta del #62 (extraída de los avatares existentes; 10 tonos apagados)
|
||||
PALETTE = [
|
||||
(91, 110, 80), (100, 110, 60), (160, 110, 70), (176, 122, 57), (150, 90, 110),
|
||||
(80, 80, 110), (74, 95, 120), (60, 90, 90), (139, 26, 46), (120, 82, 72),
|
||||
]
|
||||
STOP = {"la", "las", "el", "los", "un", "una", "de", "del", "y", "e", "da", "do", "the"}
|
||||
|
||||
|
||||
def strip_accents(s: str) -> str:
|
||||
return "".join(c for c in unicodedata.normalize("NFD", s)
|
||||
if unicodedata.category(c) != "Mn")
|
||||
|
||||
|
||||
def initials(name: str) -> str:
|
||||
raw = name.replace("/", " ").replace('"', " ").replace("'", " ")
|
||||
toks = [t for t in raw.split() if t and t.lower() not in STOP]
|
||||
if not toks:
|
||||
return "?"
|
||||
picks = toks[:2] if len(toks) >= 2 else toks[:1]
|
||||
return strip_accents("".join(t[0] for t in picks)).upper()
|
||||
|
||||
|
||||
def color_for(name: str):
|
||||
h = int(hashlib.md5(name.encode("utf-8")).hexdigest(), 16)
|
||||
return PALETTE[h % len(PALETTE)]
|
||||
|
||||
|
||||
def make(uid: str, name: str):
|
||||
big = SIZE * SS
|
||||
im = Image.new("RGBA", (big, big), (0, 0, 0, 0))
|
||||
d = ImageDraw.Draw(im)
|
||||
d.ellipse((0, 0, big - 1, big - 1), fill=color_for(name) + (255,))
|
||||
txt = initials(name)
|
||||
# ajusta tamaño hasta cap-height ~77px (en escala SS)
|
||||
font = ImageFont.truetype(FONT, int(104 * SS))
|
||||
bbox = d.textbbox((0, 0), txt, font=font)
|
||||
tw, th = bbox[2] - bbox[0], bbox[3] - bbox[1]
|
||||
x = (big - tw) / 2 - bbox[0]
|
||||
y = (big - th) / 2 - bbox[1]
|
||||
d.text((x, y), txt, font=font, fill=(255, 255, 255, 255))
|
||||
im = im.resize((SIZE, SIZE), Image.LANCZOS)
|
||||
im.save(f"{OUT}/autor-{uid}.png")
|
||||
return txt
|
||||
|
||||
|
||||
os.makedirs(OUT, exist_ok=True)
|
||||
n = 0
|
||||
for line in open(TSV, encoding="utf-8"):
|
||||
parts = line.rstrip("\n").split("\t")
|
||||
if len(parts) < 2:
|
||||
continue
|
||||
uid, name = parts[0].strip(), parts[1].strip()
|
||||
ini = make(uid, name)
|
||||
print(f"autor-{uid}.png {ini:3} {name}")
|
||||
n += 1
|
||||
print(f"\n{n} avatares generados en {OUT}")
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/**
|
||||
* import_avatars_143.php (#143) — da de alta los avatares de INICIALES de los
|
||||
* usuarios nuevos creados al corregir la autoría K2 (ver fix_k2_authors.php).
|
||||
*
|
||||
* Crea/actualiza el attachment del PNG uploads/avatares/autores/autor-<uid>.png
|
||||
* y reapunta foto_perfil (ACF user_meta). Backup del valor previo en
|
||||
* user_meta _foto_perfil_pre143. Idempotente (si ya apunta, solo regenera metadata).
|
||||
*
|
||||
* Entrada: TSV «uid<TAB>display_name» (env FEA_TSV, por defecto /tmp/users29.tsv).
|
||||
* Uso (en el servidor, dentro de /web/wp-nuevo):
|
||||
* FEA_TSV=/tmp/users29.tsv wp eval-file scripts/import_avatars_143.php # dry-run
|
||||
* APPLY=1 FEA_TSV=/tmp/users29.tsv wp eval-file scripts/import_avatars_143.php # aplica
|
||||
*/
|
||||
require_once ABSPATH . 'wp-admin/includes/image.php';
|
||||
|
||||
$apply = getenv('APPLY') === '1';
|
||||
$tsv = getenv('FEA_TSV') ?: '/tmp/users29.tsv';
|
||||
$updir = wp_get_upload_dir();
|
||||
if (!is_readable($tsv)) { echo "No puedo leer TSV: $tsv\n"; return; }
|
||||
|
||||
$done = $regen = $err = 0;
|
||||
foreach (file($tsv) as $line) {
|
||||
$p = explode("\t", rtrim($line, "\n"));
|
||||
if (count($p) < 2) continue;
|
||||
$uid = (int) $p[0];
|
||||
$name = trim($p[1]);
|
||||
$rel = "avatares/autores/autor-{$uid}.png";
|
||||
$abs = $updir['basedir'] . '/' . $rel;
|
||||
if (!file_exists($abs)) { echo "MISSING uid=$uid ($name)\n"; $err++; continue; }
|
||||
|
||||
$cur = (int) get_user_meta($uid, 'foto_perfil', true);
|
||||
if ($cur && get_post_meta($cur, '_wp_attached_file', true) === $rel) {
|
||||
echo "REGEN #$uid $name (attachment $cur ya apunta al PNG)\n";
|
||||
if ($apply) wp_update_attachment_metadata($cur, wp_generate_attachment_metadata($cur, $abs));
|
||||
$regen++; continue;
|
||||
}
|
||||
|
||||
echo "NUEVO #$uid $name (foto_perfil actual: " . ($cur ?: 'ninguna') . ")\n";
|
||||
if (!$apply) { $done++; continue; }
|
||||
|
||||
if (get_user_meta($uid, '_foto_perfil_pre143', true) === '') {
|
||||
update_user_meta($uid, '_foto_perfil_pre143', $cur);
|
||||
}
|
||||
$aid = wp_insert_attachment([
|
||||
'post_mime_type' => 'image/png',
|
||||
'post_title' => "Avatar {$name}",
|
||||
'post_status' => 'inherit',
|
||||
'guid' => $updir['baseurl'] . '/' . $rel,
|
||||
], $abs, 0, true);
|
||||
if (is_wp_error($aid)) { echo " ERR: " . $aid->get_error_message() . "\n"; $err++; continue; }
|
||||
wp_update_attachment_metadata($aid, wp_generate_attachment_metadata($aid, $abs));
|
||||
update_user_meta($uid, 'foto_perfil', $aid);
|
||||
$done++;
|
||||
}
|
||||
echo "\n" . ($apply ? "APLICADO" : "DRY-RUN") . ": nuevos=$done regen=$regen errores=$err\n";
|
||||
Reference in New Issue
Block a user