feat: generador de Manual de Instalacion (docx)

Script Python que genera Manual_Instalacion.docx con:
- Portada y requisitos
- Instalacion paso a paso (ejecutar como administrador)
- Configuracion .env
- Primer uso y FAQ

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-27 09:12:13 -05:00
parent 2670771aa2
commit 7631909606

429
app/generar_manual_instalacion.py Executable file
View File

@@ -0,0 +1,429 @@
"""
Genera el manual de instalación y primeros pasos de Utilerías RECON 2.
"""
from docx import Document
from docx.shared import Pt, RGBColor, Cm
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.oxml.ns import qn
from docx.oxml import OxmlElement
import datetime, os
doc = Document()
for sec in doc.sections:
sec.top_margin = Cm(2.5)
sec.bottom_margin = Cm(2.5)
sec.left_margin = Cm(2.2)
sec.right_margin = Cm(2.2)
style = doc.styles['Normal']
style.font.name = 'Calibri'
style.font.size = Pt(11)
# ── Helpers ──────────────────────────────────────────────────────────────────
def h1(text):
p = doc.add_heading(text, level=1)
for r in p.runs: r.font.color.rgb = RGBColor(0x15, 0x65, 0xC0)
def h2(text):
p = doc.add_heading(text, level=2)
for r in p.runs: r.font.color.rgb = RGBColor(0x1E, 0x88, 0xE5)
def h3(text):
p = doc.add_heading(text, level=3)
for r in p.runs: r.font.color.rgb = RGBColor(0x42, 0xA5, 0xF5)
def parr(text, bold=False):
p = doc.add_paragraph()
r = p.add_run(text)
r.bold = bold
return p
def code_block(text):
p = doc.add_paragraph()
p.paragraph_format.left_indent = Cm(0.4)
r = p.add_run(text)
r.font.name = 'Consolas'
r.font.size = Pt(9.5)
pPr = p._p.get_or_add_pPr()
shd = OxmlElement('w:shd')
shd.set(qn('w:val'), 'clear')
shd.set(qn('w:color'), 'auto')
shd.set(qn('w:fill'), 'F0F0F0')
pPr.append(shd)
return p
def bullet(text, level=0):
p = doc.add_paragraph(text, style='List Bullet')
p.paragraph_format.left_indent = Cm(0.6 + level * 0.6)
return p
def numbered(text):
p = doc.add_paragraph(text, style='List Number')
p.paragraph_format.left_indent = Cm(0.6)
return p
def nota(titulo, text):
p = doc.add_paragraph()
p.paragraph_format.left_indent = Cm(0.4)
pPr = p._p.get_or_add_pPr()
shd = OxmlElement('w:shd')
shd.set(qn('w:val'), 'clear'); shd.set(qn('w:color'), 'auto')
shd.set(qn('w:fill'), 'FFF8E1')
pPr.append(shd)
r = p.add_run(titulo + ': '); r.bold = True
r.font.color.rgb = RGBColor(0xE6, 0x5C, 0x00)
p.add_run(text)
def alerta(titulo, text):
p = doc.add_paragraph()
p.paragraph_format.left_indent = Cm(0.4)
pPr = p._p.get_or_add_pPr()
shd = OxmlElement('w:shd')
shd.set(qn('w:val'), 'clear'); shd.set(qn('w:color'), 'auto')
shd.set(qn('w:fill'), 'FFEBEE')
pPr.append(shd)
r = p.add_run(titulo + ': '); r.bold = True
r.font.color.rgb = RGBColor(0xC6, 0x28, 0x28)
p.add_run(text)
def tip(titulo, text):
p = doc.add_paragraph()
p.paragraph_format.left_indent = Cm(0.4)
pPr = p._p.get_or_add_pPr()
shd = OxmlElement('w:shd')
shd.set(qn('w:val'), 'clear'); shd.set(qn('w:color'), 'auto')
shd.set(qn('w:fill'), 'E8F5E9')
pPr.append(shd)
r = p.add_run(titulo + ': '); r.bold = True
r.font.color.rgb = RGBColor(0x2E, 0x7D, 0x32)
p.add_run(text)
def table_simple(headers, rows, col_widths_cm=None):
t = doc.add_table(rows=1 + len(rows), cols=len(headers))
t.style = 'Light Grid Accent 1'
hdr = t.rows[0].cells
for i, h in enumerate(headers):
hdr[i].text = h
for p in hdr[i].paragraphs:
for r in p.runs:
r.bold = True
r.font.color.rgb = RGBColor(0xFF, 0xFF, 0xFF)
tcPr = hdr[i]._tc.get_or_add_tcPr()
shd = OxmlElement('w:shd')
shd.set(qn('w:val'), 'clear')
shd.set(qn('w:fill'), '1565C0')
tcPr.append(shd)
for r_idx, row in enumerate(rows, start=1):
cells = t.rows[r_idx].cells
for c_idx, val in enumerate(row):
cells[c_idx].text = str(val)
if col_widths_cm:
for i, w in enumerate(col_widths_cm):
for row in t.rows:
row.cells[i].width = Cm(w)
return t
# ── PORTADA ───────────────────────────────────────────────────────────────────
title = doc.add_paragraph()
title.alignment = WD_ALIGN_PARAGRAPH.CENTER
r = title.add_run('\n\n\nSistema de Utilerías RECON 2')
r.bold = True; r.font.size = Pt(34); r.font.color.rgb = RGBColor(0x15, 0x65, 0xC0)
sub = doc.add_paragraph()
sub.alignment = WD_ALIGN_PARAGRAPH.CENTER
r = sub.add_run('Guía de Instalación y Primeros Pasos')
r.font.size = Pt(20); r.font.color.rgb = RGBColor(0x42, 0xA5, 0xF5)
p = doc.add_paragraph()
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
r = p.add_run(f'\n\nVersión {datetime.date.today().isoformat()}')
r.font.size = Pt(12); r.italic = True
doc.add_page_break()
# ── ÍNDICE ────────────────────────────────────────────────────────────────────
h1('Contenido')
for item in [
'1. Requisitos previos',
'2. Instalación paso a paso',
'3. Configuración inicial (.env)',
'4. Primer uso de la aplicación',
'5. Preguntas frecuentes',
]:
doc.add_paragraph(item)
doc.add_page_break()
# ── 1. REQUISITOS ─────────────────────────────────────────────────────────────
h1('1. Requisitos previos')
parr('Antes de instalar, verifica que la computadora cumple con lo siguiente:')
table_simple(
['Requisito', 'Detalle'],
[
['Sistema operativo', 'Windows 10 / Windows 11 (64 bits)'],
['Permisos', 'Cuenta con permisos de Administrador local'],
['ODBC Driver', 'Microsoft ODBC Driver 17 o 18 for SQL Server'],
['Acceso a red', 'Conexión al servidor donde está la base de datos SCAII'],
['Espacio en disco', 'Mínimo 500 MB libres en la unidad de instalación'],
],
col_widths_cm=[4.5, 11]
)
doc.add_paragraph()
alerta(
'IMPORTANTE — Permisos de Administrador',
'La aplicación DEBE instalarse y ejecutarse como Administrador. '
'Sin este permiso, el instalador no podrá registrar los componentes necesarios '
'y la aplicación fallará al intentar conectarse a la base de datos.'
)
doc.add_paragraph()
h3('Cómo verificar si tienes el ODBC Driver instalado')
numbered('Presiona Windows + R, escribe odbcad32 y presiona Enter')
numbered('En la pestaña "Controladores" busca "ODBC Driver 17 for SQL Server" o "ODBC Driver 18 for SQL Server"')
numbered('Si no aparece, solicita al área de sistemas que lo instale antes de continuar')
# ── 2. INSTALACIÓN ────────────────────────────────────────────────────────────
doc.add_page_break()
h1('2. Instalación paso a paso')
alerta(
'Ejecutar como Administrador',
'Todos los pasos de esta sección deben realizarse con permisos de Administrador. '
'Si no lo haces, la instalación puede completarse pero la aplicación no funcionará correctamente.'
)
doc.add_paragraph()
h2('Paso 1 — Obtener el instalador')
parr('El archivo instalador tiene un nombre similar a:')
code_block('UtileriasReconV2_Setup.exe')
parr('Solicítalo al área técnica o descárgalo de la ubicación que te indiquen.')
doc.add_paragraph()
h2('Paso 2 — Ejecutar el instalador como Administrador')
numbered('Localiza el archivo UtileriasReconV2_Setup.exe en tu computadora')
numbered('Haz clic derecho sobre el archivo')
numbered('Selecciona "Ejecutar como administrador"')
numbered('Si Windows muestra una ventana de Control de Cuentas de Usuario (UAC), haz clic en ""')
doc.add_paragraph()
nota(
'Clic derecho → Ejecutar como administrador',
'Es fundamental este paso. Si haces doble clic normal y tu cuenta no es administrador, '
'la instalación puede parecer exitosa pero la app fallará al abrir.'
)
doc.add_paragraph()
h2('Paso 3 — Seguir el asistente de instalación')
numbered('Se abre el asistente de instalación. Haz clic en "Siguiente"')
numbered('Lee y acepta los términos de uso, luego haz clic en "Siguiente"')
numbered('Elige la carpeta de instalación (se recomienda dejar la carpeta predeterminada)')
code_block('C:\\Program Files\\UtileriasReconV2\\')
numbered('Haz clic en "Instalar" y espera a que termine el proceso (tarda aproximadamente 1-2 minutos)')
numbered('Al terminar, haz clic en "Finalizar"')
tip(
'Carpeta de instalación',
'Si instalas en una ruta diferente a la predeterminada, anota la ruta. '
'La necesitarás para el paso de configuración.'
)
doc.add_paragraph()
h2('Paso 4 — Verificar la instalación')
parr('Al finalizar, deberías ver en la carpeta de instalación los siguientes archivos principales:')
table_simple(
['Archivo / Carpeta', 'Descripción'],
[
['UtileriasReconV2.exe', 'El ejecutable principal de la aplicación'],
['app.ipynb', 'El notebook con toda la lógica de la app'],
['.env.example', 'Plantilla de configuración — debes renombrarlo a .env'],
['_internal/', 'Carpeta con dependencias internas (no modificar)'],
],
col_widths_cm=[5, 10]
)
# ── 3. CONFIGURACIÓN ─────────────────────────────────────────────────────────
doc.add_page_break()
h1('3. Configuración inicial (.env)')
parr(
'La aplicación usa un archivo llamado .env para saber a qué servidor conectarse '
'y con qué credenciales. Este archivo NO viene incluido en el instalador por seguridad — '
'debes crearlo tú a partir de la plantilla.'
)
doc.add_paragraph()
h2('Crear el archivo .env')
numbered('Ve a la carpeta de instalación')
numbered('Localiza el archivo .env.example')
numbered('Copia ese archivo y renombra la copia como .env (sin el .example)')
numbered('Abre el archivo .env con el Bloc de notas o cualquier editor de texto')
numbered('Modifica los valores según los datos de tu servidor:')
doc.add_paragraph()
code_block(
'# Servidor SCAII (SQL Server)\n'
'SCAII_SERVER=nombre-o-ip-de-tu-servidor\n'
'SCAII_USER=tu_usuario\n'
'SCAII_PASSWORD=tu_contraseña\n'
'\n'
'# Autenticación Windows (opcional)\n'
'# Si tu servidor usa Windows Auth en lugar de usuario/contraseña,\n'
'# cambia el valor a "yes"\n'
'SCAII_TRUSTED=no\n'
'\n'
'# Ruta donde están los archivos .asc del DataStage\n'
'DATASTAGE_PATH=C:\\ruta\\a\\tus\\archivos\\asc\n'
)
nota(
'Archivo sensible',
'El archivo .env contiene contraseñas. No lo compartas por correo ni lo subas a ningún '
'repositorio. Mantenlo solo en la computadora de trabajo.'
)
doc.add_paragraph()
h2('Autenticación Windows vs. usuario y contraseña')
table_simple(
['Escenario', 'Configuración en .env'],
[
['Tu servidor pide usuario y contraseña de SQL Server',
'SCAII_TRUSTED=no\nSCAII_USER=sa\nSCAII_PASSWORD=TuContraseña'],
['Tu servidor usa Windows Authentication (credenciales de Windows)',
'SCAII_TRUSTED=yes\n(SCAII_USER y SCAII_PASSWORD se ignoran)'],
],
col_widths_cm=[6, 9]
)
# ── 4. PRIMER USO ─────────────────────────────────────────────────────────────
doc.add_page_break()
h1('4. Primer uso de la aplicación')
alerta(
'Ejecutar como Administrador',
'Cada vez que abras la aplicación, hazlo con clic derecho → '
'"Ejecutar como administrador". De lo contrario puede fallar la conexión a la base de datos.'
)
doc.add_paragraph()
h2('Abrir la aplicación')
numbered('Localiza el acceso directo en el escritorio o ve a la carpeta de instalación')
numbered('Haz clic derecho sobre UtileriasReconV2.exe')
numbered('Selecciona "Ejecutar como administrador"')
numbered('Aparece una ventana negra de consola — NO la cierres, es normal')
numbered('Después de unos segundos, tu navegador (Edge o Chrome) abre automáticamente la aplicación')
tip(
'Si el navegador no abre solo',
'Escribe manualmente en la barra de direcciones: http://127.0.0.1:8866/'
)
doc.add_paragraph()
nota(
'La consola negra',
'La ventana negra debe permanecer abierta todo el tiempo que uses la aplicación. '
'Si la cierras, la app se apaga. Cuando termines de trabajar, primero cierra el '
'navegador y luego cierra la consola.'
)
doc.add_paragraph()
h2('Verificar la conexión a la base de datos')
parr(
'Al cargar la aplicación, en la parte superior verás una barra de estado. '
'Su color te indica si la conexión es correcta:'
)
table_simple(
['Color de la barra', 'Significado'],
[
['Azul / Verde', 'Conexión exitosa. Puedes trabajar normalmente.'],
['Rojo', 'Error de conexión. Revisa el archivo .env y que el servidor esté disponible.'],
],
col_widths_cm=[4, 11]
)
doc.add_paragraph()
h2('Navegar por las pestañas')
parr('La aplicación tiene varias pestañas en la parte superior. Cada una corresponde a una función:')
table_simple(
['Pestaña', 'Para qué sirve'],
[
['Conexión / Config', 'Seleccionar la base de datos activa y ver el estado de la conexión'],
['Descargas', 'Generar y complementar descargas de inventario contra facturas'],
['Análisis de Saldos', 'Ver el estado del inventario: saldos, comparativos, exportar a Excel'],
['Sustitutos NLP', 'Sugerir automáticamente números de parte similares'],
['Descarga % KGS', 'Descargas basadas en porcentaje de peso (BOMs con cantidades en %)'],
['DataStage', 'Cargar archivos .asc del SAAIM3 y generar estructuras SCAII'],
['Estructuras SCAII', 'Generar los Excel de partidas y encabezados para importar en SCAII'],
],
col_widths_cm=[4, 11]
)
# ── 5. FAQ ────────────────────────────────────────────────────────────────────
doc.add_page_break()
h1('5. Preguntas frecuentes')
h3('¿Por qué tengo que ejecutarlo como Administrador?')
parr(
'La aplicación necesita permisos de administrador para dos cosas: '
'(1) conectarse al servidor de SQL Server a través del driver ODBC, y '
'(2) escribir los archivos temporales que genera Jupyter internamente. '
'Sin estos permisos, la app puede abrir visualmente pero falla al intentar '
'cualquier operación con la base de datos.'
)
doc.add_paragraph()
h3('¿Puedo instalarla sin ser administrador?')
parr(
'No. El instalador requiere privilegios de administrador para registrar los '
'componentes de Jupyter y el driver ODBC. Si no tienes estos permisos en tu '
'equipo, solicita al área de sistemas que haga la instalación.'
)
doc.add_paragraph()
h3('La app abre pero dice "Error de conexión"')
bullet('Verifica que el archivo .env existe en la carpeta de instalación (no solo .env.example)')
bullet('Revisa que el servidor SCAII_SERVER sea correcto (nombre o IP)')
bullet('Confirma que el usuario y contraseña sean correctos')
bullet('Asegúrate de que el servidor SQL Server esté encendido y accesible desde tu red')
bullet('Si usas Windows Auth (SCAII_TRUSTED=yes), verifica que tu usuario de Windows tenga acceso al servidor')
doc.add_paragraph()
h3('¿Cómo sé que la instalación fue exitosa?')
parr('Si puedes abrir la app como Administrador y la barra superior muestra azul o verde sin errores, '
'la instalación está correcta.')
doc.add_paragraph()
h3('¿Tengo que instalarla cada vez que actualicen la app?')
parr(
'Depende del tipo de actualización. Si el área técnica entrega un nuevo instalador '
'(.exe de setup), sí hay que reinstalar. Si solo entregan un nuevo archivo app.ipynb, '
'basta con reemplazar ese archivo en la carpeta de instalación sin reinstalar todo.'
)
doc.add_paragraph()
alerta(
'Soporte',
'Para cualquier problema de instalación o configuración que no esté cubierto en este manual, '
'contacta al área técnica de Aduanasoft.'
)
# ── GUARDAR ───────────────────────────────────────────────────────────────────
out_path = 'Manual_Instalacion.docx'
try:
doc.save(out_path)
except PermissionError:
out_path = f'Manual_Instalacion_{datetime.datetime.now().strftime("%Y%m%d_%H%M%S")}.docx'
doc.save(out_path)
print('(El anterior estaba abierto en Word. Generado con nombre alternativo.)')
print(f'Manual generado: {os.path.abspath(out_path)}')