3bc583048b
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>
121 lines
4.9 KiB
Python
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)
|