Compare commits
10 Commits
a0081f54d2
...
040af3a802
| Author | SHA1 | Date | |
|---|---|---|---|
| 040af3a802 | |||
|
|
c80f6c75a4 | ||
| 7631909606 | |||
| 2670771aa2 | |||
|
|
e77813a45c | ||
|
|
c450cfa150 | ||
| 4c85daf0f2 | |||
| 71ef0b390a | |||
|
|
a65f34d852 | ||
|
|
1afe23182f |
@@ -1,12 +1,8 @@
|
||||
# Copia este archivo como .env y completa los valores reales.
|
||||
# NO subas el .env al repositorio (esta en .gitignore).
|
||||
|
||||
# Configuracion de conexion a PostgreSQL (DataStage)
|
||||
DB_HOST=localhost
|
||||
DB_PORT=5432
|
||||
DB_NAME=dbSat
|
||||
DB_USER=servicemanager
|
||||
DB_PASSWORD=cambia_esto
|
||||
# DataStage corre embebido en SQLite (archivo datastage.db junto al .exe).
|
||||
# No requiere configuracion. Para cambiar la ruta usa: DATASTAGE_DB=ruta\completa\datastage.db
|
||||
|
||||
# Configuracion de conexion a SQL Server (SCAII)
|
||||
# SCAII_TRUSTED=yes -> usa Windows Authentication (ignora SCAII_USER/PASSWORD)
|
||||
|
||||
BIN
app/Manual_Descargas.docx
Normal file
BIN
app/Manual_Descargas.docx
Normal file
Binary file not shown.
BIN
app/Manual_Reintegro.docx
Normal file
BIN
app/Manual_Reintegro.docx
Normal file
Binary file not shown.
27773
app/app.ipynb
27773
app/app.ipynb
File diff suppressed because it is too large
Load Diff
131
app/build_exe.bat
Executable file
131
app/build_exe.bat
Executable file
@@ -0,0 +1,131 @@
|
||||
@echo off
|
||||
REM ============================================================
|
||||
REM Build .exe de Utilerias RECON 2 con PyInstaller (modo onedir)
|
||||
REM Usa un conda env limpio para empaquetado reproducible.
|
||||
REM ============================================================
|
||||
|
||||
setlocal
|
||||
cd /d "%~dp0"
|
||||
|
||||
set ENV_NAME=utilerias-recon-build
|
||||
set PY_VERSION=3.11
|
||||
|
||||
REM ------------------------------------------------------------
|
||||
REM 1) Verificar conda
|
||||
REM ------------------------------------------------------------
|
||||
where conda >nul 2>nul
|
||||
if errorlevel 1 (
|
||||
echo [ERROR] Conda no esta en PATH. Abre "Anaconda Prompt" y vuelve a ejecutar.
|
||||
goto :end
|
||||
)
|
||||
|
||||
REM ------------------------------------------------------------
|
||||
REM 2) Crear env si no existe
|
||||
REM ------------------------------------------------------------
|
||||
call conda env list | findstr /B "%ENV_NAME% " >nul
|
||||
if errorlevel 1 (
|
||||
echo [INFO] Creando env conda "%ENV_NAME%" con Python %PY_VERSION%...
|
||||
call conda create -y -n %ENV_NAME% python=%PY_VERSION%
|
||||
if errorlevel 1 (
|
||||
echo [ERROR] No se pudo crear el env.
|
||||
goto :end
|
||||
)
|
||||
)
|
||||
|
||||
REM ------------------------------------------------------------
|
||||
REM 3) Activar env e instalar dependencias
|
||||
REM ------------------------------------------------------------
|
||||
call conda activate %ENV_NAME%
|
||||
if errorlevel 1 (
|
||||
echo [ERROR] No se pudo activar el env.
|
||||
goto :end
|
||||
)
|
||||
|
||||
echo [INFO] Instalando dependencias...
|
||||
call pip install --upgrade pip
|
||||
call pip install -r requirements.txt
|
||||
if errorlevel 1 (
|
||||
echo [ERROR] Fallo la instalacion de dependencias.
|
||||
goto :end
|
||||
)
|
||||
|
||||
REM ------------------------------------------------------------
|
||||
REM 4) Limpiar builds previos
|
||||
REM ------------------------------------------------------------
|
||||
if exist build rmdir /s /q build
|
||||
if exist dist rmdir /s /q dist
|
||||
if exist UtileriasReconV2.spec del /q UtileriasReconV2.spec
|
||||
|
||||
REM ------------------------------------------------------------
|
||||
REM 5) Ejecutar PyInstaller (onedir, noconsole)
|
||||
REM ------------------------------------------------------------
|
||||
echo [INFO] Construyendo .exe (esto tarda 3-8 minutos)...
|
||||
|
||||
pyinstaller launcher.py ^
|
||||
--name UtileriasReconV2 ^
|
||||
--onedir ^
|
||||
--noconsole ^
|
||||
--noconfirm ^
|
||||
--collect-all voila ^
|
||||
--collect-all jupyter_server ^
|
||||
--collect-all jupyter_client ^
|
||||
--collect-all jupyter_core ^
|
||||
--collect-all nbformat ^
|
||||
--collect-all nbconvert ^
|
||||
--collect-all ipykernel ^
|
||||
--collect-all ipywidgets ^
|
||||
--collect-all jupyterlab_pygments ^
|
||||
--collect-all notebook ^
|
||||
--collect-all rfc3987_syntax ^
|
||||
--collect-all jsonschema_specifications ^
|
||||
--collect-all jsonschema ^
|
||||
--collect-all jupyter_events ^
|
||||
--collect-all nbclient ^
|
||||
--collect-all terminado ^
|
||||
--collect-all debugpy ^
|
||||
--collect-all matplotlib_inline ^
|
||||
--collect-all matplotlib ^
|
||||
--collect-all dotenv ^
|
||||
--collect-all sklearn ^
|
||||
--collect-all scipy ^
|
||||
--collect-all pandas ^
|
||||
--collect-all numpy ^
|
||||
--collect-all sqlalchemy ^
|
||||
--collect-all openpyxl ^
|
||||
--hidden-import pyodbc ^
|
||||
--hidden-import sqlalchemy.dialects.sqlite ^
|
||||
--hidden-import sqlalchemy.dialects.mssql ^
|
||||
--hidden-import sklearn.utils._typedefs ^
|
||||
--hidden-import sklearn.neighbors._partition_nodes
|
||||
|
||||
if errorlevel 1 (
|
||||
echo.
|
||||
echo [ERROR] PyInstaller fallo. Revisa el log arriba.
|
||||
goto :end
|
||||
)
|
||||
|
||||
REM ------------------------------------------------------------
|
||||
REM 6) Copiar app.ipynb y .env.example al lado del .exe
|
||||
REM ------------------------------------------------------------
|
||||
copy /Y app.ipynb dist\UtileriasReconV2\app.ipynb >nul
|
||||
echo [INFO] app.ipynb copiado a dist\UtileriasReconV2\
|
||||
|
||||
if exist ..\.env.example (
|
||||
copy /Y ..\.env.example dist\UtileriasReconV2\.env.example >nul
|
||||
echo [INFO] .env.example copiado a dist\UtileriasReconV2\
|
||||
)
|
||||
|
||||
echo.
|
||||
echo ============================================================
|
||||
echo BUILD EXITOSO (--onedir)
|
||||
echo Carpeta lista: dist\UtileriasReconV2\
|
||||
echo Ejecutable: dist\UtileriasReconV2\UtileriasReconV2.exe
|
||||
echo.
|
||||
echo Siguiente paso: empaquetar como instalador con Inno Setup
|
||||
echo .\build_installer.bat
|
||||
echo ============================================================
|
||||
|
||||
:end
|
||||
echo.
|
||||
pause
|
||||
endlocal
|
||||
72
app/build_installer.bat
Executable file
72
app/build_installer.bat
Executable file
@@ -0,0 +1,72 @@
|
||||
@echo off
|
||||
REM ============================================================
|
||||
REM Build completo: PyInstaller (onedir) + Inno Setup
|
||||
REM Genera installer_output\UtileriasReconV2_Setup_X.X.X.exe
|
||||
REM ============================================================
|
||||
|
||||
setlocal
|
||||
cd /d "%~dp0"
|
||||
|
||||
REM ------------------------------------------------------------
|
||||
REM 1) Localizar el compilador de Inno Setup (ISCC.exe)
|
||||
REM ------------------------------------------------------------
|
||||
set "ISCC="
|
||||
if exist "%ProgramFiles(x86)%\Inno Setup 6\ISCC.exe" set "ISCC=%ProgramFiles(x86)%\Inno Setup 6\ISCC.exe"
|
||||
if exist "%ProgramFiles%\Inno Setup 6\ISCC.exe" set "ISCC=%ProgramFiles%\Inno Setup 6\ISCC.exe"
|
||||
|
||||
if "%ISCC%"=="" (
|
||||
echo [ERROR] No se encontro Inno Setup 6.
|
||||
echo Descargalo de: https://jrsoftware.org/isdl.php
|
||||
echo Instala la version "Stable Release", luego vuelve a ejecutar este script.
|
||||
goto :end
|
||||
)
|
||||
echo [INFO] Inno Setup encontrado en: %ISCC%
|
||||
|
||||
REM ------------------------------------------------------------
|
||||
REM 2) Construir el .exe con PyInstaller (onedir)
|
||||
REM ------------------------------------------------------------
|
||||
echo.
|
||||
echo [INFO] Paso 1/2: PyInstaller...
|
||||
call build_exe.bat
|
||||
if errorlevel 1 (
|
||||
echo [ERROR] PyInstaller fallo. Revisa el log arriba.
|
||||
goto :end
|
||||
)
|
||||
|
||||
if not exist "dist\UtileriasReconV2\UtileriasReconV2.exe" (
|
||||
echo [ERROR] No se genero dist\UtileriasReconV2\UtileriasReconV2.exe
|
||||
goto :end
|
||||
)
|
||||
|
||||
REM ------------------------------------------------------------
|
||||
REM 3) Limpiar build previo del instalador
|
||||
REM ------------------------------------------------------------
|
||||
if exist installer_output rmdir /s /q installer_output
|
||||
|
||||
REM ------------------------------------------------------------
|
||||
REM 4) Compilar el instalador
|
||||
REM ------------------------------------------------------------
|
||||
echo.
|
||||
echo [INFO] Paso 2/2: Inno Setup...
|
||||
"%ISCC%" installer.iss
|
||||
if errorlevel 1 (
|
||||
echo [ERROR] Inno Setup fallo.
|
||||
goto :end
|
||||
)
|
||||
|
||||
echo.
|
||||
echo ============================================================
|
||||
echo INSTALADOR LISTO
|
||||
echo Archivo: installer_output\UtileriasReconV2_Setup_1.0.0.exe
|
||||
echo.
|
||||
echo Distribuye SOLO ese .exe. El usuario:
|
||||
echo 1. Ejecuta el instalador (1 vez, ~30 seg)
|
||||
echo 2. Edita .env desde el menu Inicio (acceso directo)
|
||||
echo 3. Doble clic en el icono del escritorio
|
||||
echo 4. La app arranca en ~3 seg (sin re-extraer)
|
||||
echo ============================================================
|
||||
|
||||
:end
|
||||
echo.
|
||||
pause
|
||||
endlocal
|
||||
429
app/generar_manual_instalacion.py
Executable file
429
app/generar_manual_instalacion.py
Executable 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 "Sí"')
|
||||
|
||||
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)}')
|
||||
67
app/installer.iss
Executable file
67
app/installer.iss
Executable file
@@ -0,0 +1,67 @@
|
||||
; ============================================================
|
||||
; Instalador de Utilerias RECON 2
|
||||
; Empaqueta dist\UtileriasReconV2\ en un instalador .exe unico.
|
||||
; Requiere Inno Setup 6+: https://jrsoftware.org/isdl.php
|
||||
; ============================================================
|
||||
|
||||
#define MyAppName "Utilerias RECON 2"
|
||||
#define MyAppVersion "1.0.0"
|
||||
#define MyAppPublisher "Tecma / CurtManufacturing"
|
||||
#define MyAppExeName "UtileriasReconV2.exe"
|
||||
|
||||
[Setup]
|
||||
AppId={{B3F1A7A8-4D2E-4A6B-9C5A-2C0D1F8E9A11}
|
||||
AppName={#MyAppName}
|
||||
AppVersion={#MyAppVersion}
|
||||
AppPublisher={#MyAppPublisher}
|
||||
DefaultDirName={autopf}\UtileriasReconV2
|
||||
DefaultGroupName={#MyAppName}
|
||||
DisableProgramGroupPage=yes
|
||||
OutputDir=installer_output
|
||||
OutputBaseFilename=UtileriasReconV2_Setup_{#MyAppVersion}
|
||||
Compression=lzma2/ultra64
|
||||
SolidCompression=yes
|
||||
ArchitecturesAllowed=x64compatible
|
||||
ArchitecturesInstallIn64BitMode=x64compatible
|
||||
PrivilegesRequired=lowest
|
||||
PrivilegesRequiredOverridesAllowed=dialog
|
||||
WizardStyle=modern
|
||||
UninstallDisplayIcon={app}\{#MyAppExeName}
|
||||
|
||||
[Languages]
|
||||
Name: "spanish"; MessagesFile: "compiler:Languages\Spanish.isl"
|
||||
|
||||
[Tasks]
|
||||
Name: "desktopicon"; Description: "Crear acceso directo en el escritorio"; GroupDescription: "Iconos adicionales:"
|
||||
|
||||
[Files]
|
||||
; Empaqueta TODA la carpeta dist\UtileriasReconV2\ generada por PyInstaller.
|
||||
; Excluimos *.map (source maps de JS, solo para debug del browser, no se usan)
|
||||
; porque tienen nombres absurdamente largos y rompen la compresion.
|
||||
Source: "dist\UtileriasReconV2\*"; DestDir: "{app}"; Excludes: "*.map"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
; Plantilla .env (el usuario la copia como .env y edita)
|
||||
Source: "..\.env.example"; DestDir: "{app}"; Flags: ignoreversion
|
||||
|
||||
[Icons]
|
||||
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
|
||||
Name: "{group}\Editar configuracion (.env)"; Filename: "notepad.exe"; Parameters: """{app}\.env"""
|
||||
Name: "{group}\Desinstalar {#MyAppName}"; Filename: "{uninstallexe}"
|
||||
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
|
||||
|
||||
[Run]
|
||||
Filename: "{app}\{#MyAppExeName}"; Description: "Ejecutar {#MyAppName} ahora"; Flags: nowait postinstall skipifsilent
|
||||
|
||||
[Code]
|
||||
// Si no existe .env, copia .env.example como .env tras instalar
|
||||
procedure CurStepChanged(CurStep: TSetupStep);
|
||||
var
|
||||
EnvFile, EnvExample: string;
|
||||
begin
|
||||
if CurStep = ssPostInstall then
|
||||
begin
|
||||
EnvFile := ExpandConstant('{app}\.env');
|
||||
EnvExample := ExpandConstant('{app}\.env.example');
|
||||
if (not FileExists(EnvFile)) and FileExists(EnvExample) then
|
||||
CopyFile(EnvExample, EnvFile, False);
|
||||
end;
|
||||
end;
|
||||
208
app/launcher.py
208
app/launcher.py
@@ -1,10 +1,9 @@
|
||||
"""
|
||||
Launcher de la app GENPACT V2.
|
||||
Launcher de la app GENPACT V2 - Utilerias RECON 2.
|
||||
|
||||
Arranca un servidor Voila local en background y abre el browser apuntando al app.ipynb.
|
||||
Cuando se cierra la consola, el servidor de Voila se mata.
|
||||
|
||||
Funciona tanto desde Python directo como empaquetado con PyInstaller.
|
||||
Arranca Voila in-process (sin subprocess) para que funcione empaquetado con
|
||||
PyInstaller. Si el .exe es invocado como kernel de IPython, enruta al
|
||||
ipykernel_launcher en vez de relanzar Voila (evita el bucle infinito).
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
@@ -12,36 +11,113 @@ import time
|
||||
import socket
|
||||
import threading
|
||||
import webbrowser
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Localizar app.ipynb y .env
|
||||
# ------------------------------------------------------------------
|
||||
def base_dir() -> Path:
|
||||
"""Carpeta donde estan los recursos de la app.
|
||||
- En modo PyInstaller (--onedir), sys._MEIPASS apunta a la carpeta temporal.
|
||||
- En modo dev, es la carpeta del script.
|
||||
"""
|
||||
# ============================================================
|
||||
# Diagnostico: escribir log al disco apenas arranque el launcher.
|
||||
# Si este archivo no aparece, el problema es PyInstaller / antivirus
|
||||
# bloqueando la extraccion antes de que Python siquiera empiece.
|
||||
# ============================================================
|
||||
def _diag_log(msg):
|
||||
try:
|
||||
log_path = os.path.join(os.path.expanduser('~'), 'utilerias_recon_boot.log')
|
||||
with open(log_path, 'a', encoding='utf-8') as f:
|
||||
f.write(f'{time.strftime("%Y-%m-%d %H:%M:%S")} | {msg}\n')
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
_diag_log(f'=== LAUNCHER START ===')
|
||||
_diag_log(f'sys.executable={sys.executable}')
|
||||
_diag_log(f'sys.argv={sys.argv}')
|
||||
_diag_log(f'cwd={os.getcwd()}')
|
||||
_diag_log(f'frozen={getattr(sys, "frozen", False)}')
|
||||
|
||||
try:
|
||||
if sys.stdout is not None:
|
||||
print(f'[boot] launcher iniciando, sys.executable={sys.executable}', flush=True)
|
||||
print(f'[boot] argv={sys.argv}', flush=True)
|
||||
print(f'[boot] cwd={os.getcwd()}', flush=True)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Cargar .env ANTES de cualquier cosa para que las variables
|
||||
# queden en os.environ y se hereden a Voila + kernels subprocess.
|
||||
# (En --onefile el cwd del kernel apunta al temp de PyInstaller,
|
||||
# no a la carpeta del .exe, asi que load_dotenv relativo falla.)
|
||||
# ============================================================
|
||||
def _exe_dir() -> Path:
|
||||
if getattr(sys, 'frozen', False):
|
||||
return Path(sys.executable).parent
|
||||
return Path(__file__).resolve().parent
|
||||
|
||||
_env_file = _exe_dir() / '.env'
|
||||
if _env_file.exists():
|
||||
try:
|
||||
from dotenv import load_dotenv
|
||||
load_dotenv(_env_file, override=True)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Router de modo: detectar invocacion como kernel de Jupyter
|
||||
# jupyter_client spawnea: sys.executable -m ipykernel_launcher -f conn.json
|
||||
# En el .exe eso seria: UtileriasReconV2.exe -m ipykernel_launcher -f conn.json
|
||||
# ============================================================
|
||||
if 'ipykernel_launcher' in sys.argv:
|
||||
# En modo empaquetado, sys.stdout/stderr heredados del padre (Voila)
|
||||
# pueden ser invalidos para flush() y romper ipykernel.init_io.
|
||||
# ipykernel los reemplaza por canales ZMQ despues, asi que devnull es seguro.
|
||||
if getattr(sys, 'frozen', False):
|
||||
try:
|
||||
_devnull = open(os.devnull, 'w')
|
||||
sys.stdout = _devnull
|
||||
sys.stderr = _devnull
|
||||
except Exception:
|
||||
pass
|
||||
sys.argv = [a for a in sys.argv if a not in ('-m', 'ipykernel_launcher')]
|
||||
from ipykernel import kernelapp
|
||||
kernelapp.launch_new_instance()
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Localizar app.ipynb y .env
|
||||
# ============================================================
|
||||
def resources_dir() -> Path:
|
||||
"""Recursos embebidos (app.ipynb bundleado). En PyInstaller usa _MEIPASS."""
|
||||
if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'):
|
||||
return Path(sys._MEIPASS)
|
||||
return Path(sys.executable).parent if getattr(sys, 'frozen', False) \
|
||||
else Path(__file__).resolve().parent
|
||||
|
||||
|
||||
def user_dir() -> Path:
|
||||
"""Archivos del usuario (.env). Siempre al lado del .exe."""
|
||||
if getattr(sys, 'frozen', False):
|
||||
# PyInstaller --onedir: los datos quedan al lado del .exe
|
||||
return Path(sys.executable).parent
|
||||
return Path(__file__).resolve().parent
|
||||
|
||||
|
||||
BASE = base_dir()
|
||||
APP_NB = BASE / 'app.ipynb'
|
||||
RES = resources_dir()
|
||||
USR = user_dir()
|
||||
|
||||
# Si el .env esta un nivel arriba (proyecto), copialo o setea el cwd
|
||||
if not (BASE / '.env').exists() and (BASE.parent / '.env').exists():
|
||||
os.chdir(BASE.parent)
|
||||
# Buscar app.ipynb: primero al lado del .exe (override), luego en recursos
|
||||
APP_NB = USR / 'app.ipynb'
|
||||
if not APP_NB.exists():
|
||||
APP_NB = RES / 'app.ipynb'
|
||||
|
||||
# cwd donde este el .env (para que python-dotenv lo encuentre)
|
||||
if (USR / '.env').exists():
|
||||
os.chdir(USR)
|
||||
elif (USR.parent / '.env').exists():
|
||||
os.chdir(USR.parent)
|
||||
else:
|
||||
os.chdir(BASE)
|
||||
os.chdir(USR)
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Buscar puerto libre
|
||||
# ------------------------------------------------------------------
|
||||
def find_free_port(preferido: int = 8866) -> int:
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
try:
|
||||
@@ -50,7 +126,6 @@ def find_free_port(preferido: int = 8866) -> int:
|
||||
return preferido
|
||||
except OSError:
|
||||
sock.close()
|
||||
# Si el preferido esta ocupado, deja que el SO asigne uno
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.bind(('127.0.0.1', 0))
|
||||
p = s.getsockname()[1]
|
||||
@@ -58,63 +133,66 @@ def find_free_port(preferido: int = 8866) -> int:
|
||||
return p
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Arrancar Voila
|
||||
# ------------------------------------------------------------------
|
||||
def fatal(msg: str):
|
||||
"""Muestra error y sale. Si no hay consola, usa MessageBox de Windows."""
|
||||
try:
|
||||
if sys.stdout is not None:
|
||||
print(f'ERROR: {msg}')
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
import tkinter
|
||||
from tkinter import messagebox
|
||||
root = tkinter.Tk()
|
||||
root.withdraw()
|
||||
messagebox.showerror('Utilerias RECON 2', msg)
|
||||
except Exception:
|
||||
pass
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main():
|
||||
if not APP_NB.exists():
|
||||
print(f'ERROR: no se encuentra {APP_NB}')
|
||||
sys.exit(1)
|
||||
fatal(f'No se encuentra app.ipynb en {APP_NB}')
|
||||
|
||||
port = find_free_port(8866)
|
||||
# Cuando Voila apunta a un notebook especifico, lo sirve en la raiz "/"
|
||||
url = f'http://127.0.0.1:{port}/'
|
||||
|
||||
print('=' * 60)
|
||||
print(' GENPACT V2 - Sistema de Descargas SCAII')
|
||||
print('=' * 60)
|
||||
print(f' Iniciando servidor Voila en puerto {port}...')
|
||||
print(f' URL: {url}')
|
||||
print(' Cierra esta ventana para detener la app.')
|
||||
print('=' * 60)
|
||||
if sys.stdout is not None:
|
||||
print('=' * 60)
|
||||
print(' GENPACT V2 - Sistema de Descargas SCAII')
|
||||
print('=' * 60)
|
||||
print(f' URL: {url}')
|
||||
print(' Cierra esta ventana para detener la app.')
|
||||
print('=' * 60)
|
||||
|
||||
# Construir comando de Voila
|
||||
if getattr(sys, 'frozen', False):
|
||||
# En modo empaquetado, llamamos al binario de python integrado
|
||||
cmd = [sys.executable, '-m', 'voila',
|
||||
str(APP_NB),
|
||||
f'--port={port}',
|
||||
'--no-browser',
|
||||
'--Voila.ip=127.0.0.1',
|
||||
'--strip_sources=True']
|
||||
else:
|
||||
cmd = [sys.executable, '-m', 'voila',
|
||||
str(APP_NB),
|
||||
f'--port={port}',
|
||||
'--no-browser',
|
||||
'--Voila.ip=127.0.0.1',
|
||||
'--strip_sources=True']
|
||||
|
||||
# Lanzar proceso
|
||||
proc = subprocess.Popen(cmd, stdout=sys.stdout, stderr=sys.stderr)
|
||||
|
||||
# Abrir browser cuando el server este listo
|
||||
# Abrir browser cuando Voila acepte conexiones
|
||||
def _open():
|
||||
for _ in range(60):
|
||||
for _ in range(120):
|
||||
try:
|
||||
with socket.create_connection(('127.0.0.1', port), timeout=0.5):
|
||||
break
|
||||
except OSError:
|
||||
time.sleep(0.5)
|
||||
webbrowser.open(url)
|
||||
|
||||
threading.Thread(target=_open, daemon=True).start()
|
||||
|
||||
# Lanzar Voila in-process (sin subprocess)
|
||||
sys.argv = [
|
||||
'voila',
|
||||
str(APP_NB),
|
||||
f'--port={port}',
|
||||
'--no-browser',
|
||||
'--Voila.ip=127.0.0.1',
|
||||
'--strip_sources=True',
|
||||
]
|
||||
try:
|
||||
proc.wait()
|
||||
except KeyboardInterrupt:
|
||||
print('\nDeteniendo Voila...')
|
||||
proc.terminate()
|
||||
from voila.app import Voila
|
||||
Voila.launch_instance()
|
||||
except SystemExit:
|
||||
pass
|
||||
except Exception as e:
|
||||
fatal(f'Voila fallo al iniciar: {e}')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -11,5 +11,4 @@ jupyter_server>=2.0
|
||||
notebook>=7.0
|
||||
scikit-learn>=1.3
|
||||
pyinstaller>=6.0
|
||||
psycopg2-binary>=2.9
|
||||
sqlalchemy>=2.0
|
||||
|
||||
384
app/schema_registro_sqlite.sql
Executable file
384
app/schema_registro_sqlite.sql
Executable file
@@ -0,0 +1,384 @@
|
||||
CREATE TABLE IF NOT EXISTS "Registro501" (
|
||||
"Patente" TEXT,
|
||||
"Pedimento" TEXT,
|
||||
"SeccionAduanera" TEXT,
|
||||
"TipoOperacion" TEXT,
|
||||
"ClaveDocumento" TEXT,
|
||||
"SeccionAduaneraEntrada" TEXT,
|
||||
"CurpContribuyente" TEXT,
|
||||
"Rfc" TEXT,
|
||||
"CurpAgenteA" TEXT,
|
||||
"TipoCambio" NUMERIC,
|
||||
"TotalFletes" NUMERIC,
|
||||
"TotalSeguros" NUMERIC,
|
||||
"TotalEmbalajes" NUMERIC,
|
||||
"TotalIncrementables" NUMERIC,
|
||||
"TotalDeducibles" NUMERIC,
|
||||
"PesoBrutoMercancia" NUMERIC,
|
||||
"MedioTransporteSalida" TEXT,
|
||||
"MedioTransporteArribo" TEXT,
|
||||
"MedioTransporteEntrada_Salida" TEXT,
|
||||
"DestinoMercancia" TEXT,
|
||||
"NombreContribuyente" TEXT,
|
||||
"CalleContribuyente" TEXT,
|
||||
"NumInteriorContribuyente" TEXT,
|
||||
"NumExteriorContribuyente" TEXT,
|
||||
"CPContribuyente" TEXT,
|
||||
"MunicipioContribuyente" TEXT,
|
||||
"EntidadFedContribuyente" TEXT,
|
||||
"PaisContribuyente" TEXT,
|
||||
"TipoPedimento" TEXT,
|
||||
"FechaRecepcionPedimento" TEXT,
|
||||
"FechaPagoReal" TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "Registro502" (
|
||||
"Patente" TEXT,
|
||||
"Pedimento" TEXT,
|
||||
"SeccionAduanera" TEXT,
|
||||
"RfcTransportista" TEXT,
|
||||
"CurpTransportista" TEXT,
|
||||
"NombreTransportista" TEXT,
|
||||
"PaisTransporte" TEXT,
|
||||
"IdentificadorTransporte" TEXT,
|
||||
"FechaPagoReal" TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "Registro503" (
|
||||
"Patente" TEXT,
|
||||
"Pedimento" TEXT,
|
||||
"SeccionAduanera" TEXT,
|
||||
"NumeroGuia" TEXT,
|
||||
"TipoGuia" TEXT,
|
||||
"FechaPagoReal" TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "Registro504" (
|
||||
"Patente" TEXT,
|
||||
"Pedimento" TEXT,
|
||||
"SeccionAduanera" TEXT,
|
||||
"NumContenedor" TEXT,
|
||||
"TipoContenedor" TEXT,
|
||||
"FechaPagoReal" TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "Registro505" (
|
||||
"Patente" TEXT,
|
||||
"Pedimento" TEXT,
|
||||
"SeccionAduanera" TEXT,
|
||||
"FechaFacturacion" TEXT,
|
||||
"NumeroFactura" TEXT,
|
||||
"TerminoFacturacion" TEXT,
|
||||
"MonedaFacturacion" TEXT,
|
||||
"ValorDolares" NUMERIC,
|
||||
"ValorMonedaExtranjera" NUMERIC,
|
||||
"PaisFacturacion" TEXT,
|
||||
"EntidadFedFacturacion" TEXT,
|
||||
"IndentFiscalProveedor" TEXT,
|
||||
"ProveedorMercancia" TEXT,
|
||||
"CalleProveedor" TEXT,
|
||||
"NumInteriorProveedor" TEXT,
|
||||
"NumExteriorProveedor" TEXT,
|
||||
"CpProveedor" TEXT,
|
||||
"MunicipioProveedor" TEXT,
|
||||
"FechaPagoReal" TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "Registro506" (
|
||||
"Patente" TEXT,
|
||||
"Pedimento" TEXT,
|
||||
"SeccionAduanera" TEXT,
|
||||
"TipoFecha" TEXT,
|
||||
"FechaOperacion" TEXT,
|
||||
"FechaValidacionPagoR" TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "Registro507" (
|
||||
"Patente" TEXT,
|
||||
"Pedimento" TEXT,
|
||||
"SeccionAduanera" TEXT,
|
||||
"ClaveCaso" TEXT,
|
||||
"IdentificadorCaso" TEXT,
|
||||
"TipoPedimento" TEXT,
|
||||
"ComplementoCaso" TEXT,
|
||||
"FechaValidacionPagoR" TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "Registro508" (
|
||||
"Patente" TEXT,
|
||||
"Pedimento" TEXT,
|
||||
"SeccionAduanera" TEXT,
|
||||
"InstitucionEmisora" TEXT,
|
||||
"NumeroCuenta" TEXT,
|
||||
"FolioConstancia" TEXT,
|
||||
"FechaConstancia" TEXT,
|
||||
"TipoCuenta" TEXT,
|
||||
"ClaveGarantia" TEXT,
|
||||
"ValorUnitarioTitulo" NUMERIC,
|
||||
"TotalGarantia" NUMERIC,
|
||||
"CantidadUnidades" NUMERIC,
|
||||
"TitulosAsignados" TEXT,
|
||||
"FechaPagoReal" TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "Registro509" (
|
||||
"Patente" TEXT,
|
||||
"Pedimento" TEXT,
|
||||
"SeccionAduanera" TEXT,
|
||||
"ClaveContribucion" TEXT,
|
||||
"TasaContribucion" NUMERIC,
|
||||
"TipoTasa" TEXT,
|
||||
"TipoPedimento" TEXT,
|
||||
"FechaPagoReal" TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "Registro510" (
|
||||
"Patente" TEXT,
|
||||
"Pedimento" TEXT,
|
||||
"SeccionAduanera" TEXT,
|
||||
"ClaveContribucion" TEXT,
|
||||
"FormaPago" TEXT,
|
||||
"ImportePago" NUMERIC,
|
||||
"TipoPedimento" TEXT,
|
||||
"FechaPagoReal" TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "Registro511" (
|
||||
"Patente" TEXT,
|
||||
"Pedimento" TEXT,
|
||||
"SeccionAduanera" TEXT,
|
||||
"SecuenciaObservacion" TEXT,
|
||||
"Observaciones" TEXT,
|
||||
"TipoPedimento" TEXT,
|
||||
"FechaValidacionPagoR" TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "Registro512" (
|
||||
"Patente" TEXT,
|
||||
"Pedimento" TEXT,
|
||||
"SeccionAduanera" TEXT,
|
||||
"PatenteAduanalOrig" TEXT,
|
||||
"PedimentoOriginal" TEXT,
|
||||
"SeccionAduaneraDespOrig" TEXT,
|
||||
"DocumentoOriginal" TEXT,
|
||||
"FechaOperacionOrig" TEXT,
|
||||
"FraccionOriginal" TEXT,
|
||||
"UnidadMedida" TEXT,
|
||||
"MercanciaDescargada" NUMERIC,
|
||||
"TipoPedimento" TEXT,
|
||||
"FechaPagoReal" TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "Registro520" (
|
||||
"Patente" TEXT,
|
||||
"Pedimento" TEXT,
|
||||
"SeccionAduanera" TEXT,
|
||||
"IndentFiscalDestinatario" TEXT,
|
||||
"NombreDestinatarioMercancia" TEXT,
|
||||
"CalleDestinatario" TEXT,
|
||||
"NumInteriorDestinatario" TEXT,
|
||||
"NumExteriorDestinatario" TEXT,
|
||||
"CpDestinatario" TEXT,
|
||||
"MunicpioDestinatario" TEXT,
|
||||
"PaisDestinatario" TEXT,
|
||||
"FechaPagoReal" TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "Registro551" (
|
||||
"Patente" TEXT,
|
||||
"Pedimento" TEXT,
|
||||
"SeccionAduanera" TEXT,
|
||||
"Fraccion" TEXT,
|
||||
"SecuenciaFraccion" TEXT,
|
||||
"SubdivisionFraccion" TEXT,
|
||||
"DescripcionMercancia" TEXT,
|
||||
"PrecioUnitario" NUMERIC,
|
||||
"ValorAduana" NUMERIC,
|
||||
"ValorComercial" NUMERIC,
|
||||
"ValorDolares" NUMERIC,
|
||||
"CantidadUMComercial" NUMERIC,
|
||||
"UnidadMedidaComercial" TEXT,
|
||||
"CantidadUMTarifa" NUMERIC,
|
||||
"UnidadMedidaTarifa" TEXT,
|
||||
"ValorAgregado" NUMERIC,
|
||||
"ClaveVinculacion" TEXT,
|
||||
"MetodoValorizacion" TEXT,
|
||||
"CodigoMercanciaProducto" TEXT,
|
||||
"MarcaMercanciaProducto" TEXT,
|
||||
"ModeloMercanciaProducto" TEXT,
|
||||
"PaisOrigenDestino" TEXT,
|
||||
"PaisCompradorVendedor" TEXT,
|
||||
"EntidadFedOrigen" TEXT,
|
||||
"EntidadFedDestino" TEXT,
|
||||
"EntidadFedComprador" TEXT,
|
||||
"EntidadFedVendedor" TEXT,
|
||||
"TipoOperacion" TEXT,
|
||||
"ClaveDocumento" TEXT,
|
||||
"FechaPagoReal" TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "Registro552" (
|
||||
"Patente" TEXT,
|
||||
"Pedimento" TEXT,
|
||||
"SeccionAduanera" TEXT,
|
||||
"Fraccion" TEXT,
|
||||
"SecuenciaFraccion" TEXT,
|
||||
"VinNumeroSerie" TEXT,
|
||||
"KilometrajeVehiculo" TEXT,
|
||||
"FechaPagoReal" TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "Registro553" (
|
||||
"Patente" TEXT,
|
||||
"Pedimento" TEXT,
|
||||
"SeccionAduanera" TEXT,
|
||||
"Fraccion" TEXT,
|
||||
"SecuenciaFraccion" TEXT,
|
||||
"ClavePermiso" TEXT,
|
||||
"FirmaDescargo" TEXT,
|
||||
"NumeroPermiso" TEXT,
|
||||
"ValorComercialDolares" NUMERIC,
|
||||
"CantidadMUMTarifa" NUMERIC,
|
||||
"FechaPagoReal" TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "Registro554" (
|
||||
"Patente" TEXT,
|
||||
"Pedimento" TEXT,
|
||||
"SeccionAduanera" TEXT,
|
||||
"Fraccion" TEXT,
|
||||
"SecuenciaFraccion" TEXT,
|
||||
"ClaveCaso" TEXT,
|
||||
"IdentificadorCaso" TEXT,
|
||||
"ComplementoCaso" TEXT,
|
||||
"FechaPagoReal" TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "Registro555" (
|
||||
"Patente" TEXT,
|
||||
"Pedimento" TEXT,
|
||||
"SeccionAduanera" TEXT,
|
||||
"Fraccion" TEXT,
|
||||
"SecuenciaFraccion" TEXT,
|
||||
"InstitucionEmisora" TEXT,
|
||||
"NumeroCuenta" TEXT,
|
||||
"FolioConstancia" TEXT,
|
||||
"FechaConstancia" TEXT,
|
||||
"ClaveGarantia" TEXT,
|
||||
"ValorUnitarioTitulo" NUMERIC,
|
||||
"TotalGarantia" NUMERIC,
|
||||
"CantidadUnidadesMedida" NUMERIC,
|
||||
"TitulosAsignados" TEXT,
|
||||
"FechaPagoReal" TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "Registro556" (
|
||||
"Patente" TEXT,
|
||||
"Pedimento" TEXT,
|
||||
"SeccionAduanera" TEXT,
|
||||
"Fraccion" TEXT,
|
||||
"SecuenciaFraccion" TEXT,
|
||||
"ClaveContribucion" TEXT,
|
||||
"TasaContribucion" NUMERIC,
|
||||
"TipoTasa" TEXT,
|
||||
"FechaPagoReal" TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "Registro557" (
|
||||
"Patente" TEXT,
|
||||
"Pedimento" TEXT,
|
||||
"SeccionAduanera" TEXT,
|
||||
"Fraccion" TEXT,
|
||||
"SecuenciaFraccion" TEXT,
|
||||
"ClaveContribucion" TEXT,
|
||||
"FormaPago" TEXT,
|
||||
"ImportePago" NUMERIC,
|
||||
"FechaPagoReal" TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "Registro558" (
|
||||
"Patente" TEXT,
|
||||
"Pedimento" TEXT,
|
||||
"SeccionAduanera" TEXT,
|
||||
"Fraccion" TEXT,
|
||||
"SecuenciaFraccion" TEXT,
|
||||
"SecuenciaObservacion" TEXT,
|
||||
"Observaciones" TEXT,
|
||||
"FechaPagoReal" TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "Registro701" (
|
||||
"Patente" TEXT,
|
||||
"Pedimento" TEXT,
|
||||
"SeccionAduanera" TEXT,
|
||||
"ClaveDocumento" TEXT,
|
||||
"FechaPago" TEXT,
|
||||
"PedimentoAnterior" TEXT,
|
||||
"PatenteAnterior" TEXT,
|
||||
"SeccionAduaneraAnterior" TEXT,
|
||||
"DocumentoAnterior" TEXT,
|
||||
"FechaOperacionAnterior" TEXT,
|
||||
"PedimentoOriginal" TEXT,
|
||||
"PatenteAduanalOrig" TEXT,
|
||||
"SeccionAduaneraDespOrig" TEXT,
|
||||
"FechaPagoReal" TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "Registro702" (
|
||||
"Patente" TEXT,
|
||||
"Pedimento" TEXT,
|
||||
"SeccionAduanera" TEXT,
|
||||
"ClaveContribucion" TEXT,
|
||||
"FormaPago" TEXT,
|
||||
"ImportePago" NUMERIC,
|
||||
"TipoPedimento" TEXT,
|
||||
"FechaPagoReal" TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "RegistroInci" (
|
||||
"Patente" TEXT,
|
||||
"Pedimento" TEXT,
|
||||
"SeccionAduanera" TEXT,
|
||||
"ConsecutivoRemesa" TEXT,
|
||||
"NumeroSeleccion" TEXT,
|
||||
"FechaInicioReconocimiento" TEXT,
|
||||
"HoraInicioReconocimiento" TEXT,
|
||||
"FechaFinReconocimiento" TEXT,
|
||||
"HoraFinReconocimiento" TEXT,
|
||||
"Fraccion" TEXT,
|
||||
"SecuenciaFraccion" TEXT,
|
||||
"ClaveDocumento" TEXT,
|
||||
"TipoOperacion" TEXT,
|
||||
"GradoIncidencia" TEXT,
|
||||
"FechaSeleccion" TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "RegistroResumen" (
|
||||
"Folio" TEXT,
|
||||
"RFCoPatenteAduanal" TEXT,
|
||||
"Fecha_Inicial" TEXT,
|
||||
"Fecha_Final" TEXT,
|
||||
"Fecha_Ejecucion" TEXT,
|
||||
"Total_Fracciones" INTEGER,
|
||||
"Total_Contribuciones" INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "RegistroSel" (
|
||||
"Patente" TEXT,
|
||||
"Pedimento" TEXT,
|
||||
"SeccionAduanera" TEXT,
|
||||
"ConsecutivoRemesa" TEXT,
|
||||
"NumeroSeleccion" TEXT,
|
||||
"FechaSeleccion" TEXT,
|
||||
"HoraSeleccion" TEXT,
|
||||
"SemaforoFiscal" TEXT,
|
||||
"ClaveDocumento" TEXT,
|
||||
"TipoOperacion" TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS base_numpartes (
|
||||
numparte TEXT PRIMARY KEY,
|
||||
descripcion TEXT,
|
||||
unimed TEXT,
|
||||
fraccion TEXT
|
||||
);
|
||||
Reference in New Issue
Block a user