Files
Inna 3bc583048b Renombrar rutas /root/NovaWoW -> /root/NightSpire en los scripts DB2
El directorio del proyecto se renombró, así que las rutas absolutas de
sql/db2/*.py y la cabecera de sql/item_data.sql apuntaban a un sitio que ya no
existe.

Fuera del repo (no versionado, se anota aquí): el directorio pasó de
/root/NovaWoW a /root/NightSpire y las unidades systemd de novawow-next /
novawow-dpoints-reconcile a nightspire-*. De paso, nightspire-next.service ya no
declara After/Wants de novawow.service, que era el Django borrado y ya no
existía.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 15:55:41 +00:00

121 lines
4.9 KiB
Python

#!/usr/bin/env python
"""Genera web-next/sql/item_data.sql a partir de los DB2 del cliente 3.4.3.
USO: /root/NightSpire/.venv/bin/python /root/db2_to_sql.py
(el python del sistema no tiene pip/venv; usa SIEMPRE el del proyecto)
En 3.4.3 no existe `item_template`: los datos van en DB2 del cliente.
Locales: los nombres salen de <LOCALE>/ItemSparse.db2. Basta con dejar el
ItemSparse.db2 de cada idioma en /root/<locale>/ — el resto de tablas
(ItemAppearance, ItemModifiedAppearance) NO dependen del idioma y se leen del
primer locale que exista. Si falta un locale, su columna queda a NULL.
Ficheros que necesita:
/root/esES/ItemSparse.db2, ItemAppearance.db2, ItemModifiedAppearance.db2
/root/enUS/ItemSparse.db2 (opcional; para name_en)
/root/ItemSparse.dbd (definiciones oficiales de WoWDBDefs)
/root/wdc_db2.py, /root/itemsparse.py
"""
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import wdc_db2
from itemsparse import ItemSparse
DBD = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'ItemSparse.dbd')
OUT = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'item_data.sql')
# (columna_sql, carpeta). Añadir aquí si aparecen más idiomas.
LOCALES = [('name_es', '/root/esES'), ('name_en', '/root/enUS')]
def load_names(folder):
path = f'{folder}/ItemSparse.db2'
if not os.path.exists(path):
print(f' (sin {path}: esa columna queda NULL)', file=sys.stderr)
return {}
t = ItemSparse(path, DBD)
return {i: r for i, r in t.rows}
def esc(s):
return s.replace('\\', '\\\\').replace("'", "''")
per_locale = {}
for col, folder in LOCALES:
print(f'{col}: leyendo {folder}', file=sys.stderr)
per_locale[col] = load_names(folder)
base_folder = next((f for _c, f in LOCALES if os.path.exists(f'{f}/ItemAppearance.db2')), None)
if not base_folder:
sys.exit('falta ItemAppearance.db2 en algún locale')
# displayId: ItemModifiedAppearance.ItemID -> ItemAppearanceID -> ItemDisplayInfoID.
# No depende del idioma. Solo lo tienen los equipables.
ia = {rid: row for rid, row in wdc_db2.DB2(f'{base_folder}/ItemAppearance.db2').rows}
display = {}
for _rid, row in wdc_db2.DB2(f'{base_folder}/ItemModifiedAppearance.db2').rows:
item_id, app_id, order = row[1], row[3], row[4]
if app_id in ia:
prev = display.get(item_id)
if prev is None or order < prev[0]:
display[item_id] = (order, ia[app_id][1])
display = {k: v[1] for k, v in display.items()}
print(f'displayId: {len(display)} ítems equipables', file=sys.stderr)
name_cols = [c for c, _f in LOCALES]
entries = sorted({e for m in per_locale.values() for e in m})
with open(OUT, 'w', encoding='utf-8') as out:
cols_sql = ',\n'.join(
f" `{c}` varchar(255) DEFAULT NULL COMMENT 'nombre en {c[-2:]}'" for c in name_cols)
out.write(f"""-- Datos de ítems del cliente 3.4.3, extraídos de los DB2. NO editar a mano:
-- regenerar con `/root/NightSpire/.venv/bin/python /root/db2_to_sql.py`.
-- En 3.4.3 no existe `item_template` (los datos viven en DB2 del cliente).
-- Parseado con las definiciones oficiales de WoWDBDefs (layout D532973D).
-- Los nombres son los de Blizzard en 3.4.3: ~4% difieren de los del catálogo
-- antiguo de la tienda por revisiones de traducción de Blizzard.
DROP TABLE IF EXISTS `home_item_data`;
CREATE TABLE `home_item_data` (
`entry` int unsigned NOT NULL COMMENT 'item id de WoW',
{cols_sql},
`quality` tinyint unsigned NOT NULL COMMENT '0 pobre .. 6 artefacto, 7 reliquia',
`item_level` smallint unsigned NOT NULL,
`inventory_type` tinyint unsigned NOT NULL COMMENT '0 = no equipable',
`display_id` int unsigned DEFAULT NULL COMMENT 'ItemDisplayInfoID; NULL si no es equipable',
PRIMARY KEY (`entry`),
KEY `idx_name_es` (`name_es`(64)),
KEY `idx_quality` (`quality`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
""")
rows = []
for entry in entries:
names = []
any_name = False
for c in name_cols:
r = per_locale[c].get(entry)
n = (r or {}).get('Display_lang') or ''
names.append(f"'{esc(n)}'" if n else 'NULL')
any_name = any_name or bool(n)
if not any_name:
continue # ítems internos sin nombre en ningún idioma
ref = next((per_locale[c][entry] for c in name_cols if entry in per_locale[c]), {})
rows.append('(%d,%s,%d,%d,%d,%s)' % (
entry, ','.join(names), ref.get('OverallQualityID', 0) or 0,
ref.get('ItemLevel', 0) or 0, ref.get('InventoryType', 0) or 0,
display.get(entry, 'NULL')))
cols = ','.join(f'`{c}`' for c in name_cols)
for i in range(0, len(rows), 500):
out.write(f'INSERT INTO `home_item_data` (`entry`,{cols},`quality`,`item_level`,`inventory_type`,`display_id`) VALUES\n')
out.write(',\n'.join(rows[i:i + 500]))
out.write(';\n')
print(f'escritas {len(rows)} filas en {OUT}', file=sys.stderr)