278 lines
10 KiB
Python
278 lines
10 KiB
Python
"""
|
|
convert Excel Tags to md : This script converts Excel files containing tags into Markdown tables.
|
|
Updated to work with a single Excel file and filter based on paths defined in a JSON config.
|
|
"""
|
|
|
|
# Standard library imports
|
|
import os
|
|
import sys
|
|
import json
|
|
import tkinter as tk
|
|
from tkinter import filedialog
|
|
|
|
# Third-party imports
|
|
try:
|
|
import pandas as pd
|
|
except ImportError:
|
|
print(
|
|
"Error: La librería 'pandas' no está instalada. Por favor, instálala con 'pip install pandas openpyxl'."
|
|
)
|
|
sys.exit(1)
|
|
|
|
# Determine script_root and add to sys.path for custom module import
|
|
try:
|
|
current_script_path = os.path.abspath(__file__)
|
|
script_root = os.path.dirname(
|
|
os.path.dirname(os.path.dirname(os.path.dirname(current_script_path)))
|
|
)
|
|
if script_root not in sys.path:
|
|
sys.path.append(script_root)
|
|
|
|
from backend.script_utils import load_configuration
|
|
|
|
except ImportError:
|
|
print(
|
|
"Error: No se pudo importar 'load_configuration' desde 'backend.script_utils'."
|
|
)
|
|
sys.exit(1)
|
|
except NameError: # __file__ is not defined
|
|
print(
|
|
"Error: __file__ no está definido. Este script podría no estar ejecutándose en un entorno Python estándar."
|
|
)
|
|
sys.exit(1)
|
|
|
|
|
|
def load_path_config():
|
|
"""
|
|
Carga la configuración de paths desde un archivo JSON
|
|
Si no existe, crea uno con valores predeterminados en el working_directory
|
|
"""
|
|
# Obtener la configuración global
|
|
configs = load_configuration()
|
|
working_directory = configs.get("working_directory")
|
|
|
|
if not working_directory:
|
|
print("Error: 'working_directory' no se encontró en la configuración.")
|
|
return None
|
|
|
|
if not os.path.isdir(working_directory):
|
|
print(
|
|
f"Error: El directorio de trabajo '{working_directory}' no existe o no es un directorio."
|
|
)
|
|
return None
|
|
|
|
# Path para el archivo JSON de configuración
|
|
json_config_path = os.path.join(working_directory, "io_paths_config.json")
|
|
|
|
# Si el archivo existe, cargarlo
|
|
if os.path.exists(json_config_path):
|
|
try:
|
|
with open(json_config_path, 'r', encoding='utf-8') as f:
|
|
config = json.load(f)
|
|
print(f"Configuración de paths cargada desde: {json_config_path}")
|
|
return config
|
|
except Exception as e:
|
|
print(f"Error al cargar el archivo de configuración JSON: {e}")
|
|
return None
|
|
|
|
# Si no existe, crear uno con valores predeterminados
|
|
default_config = {
|
|
"paths": [
|
|
{
|
|
"path": "Inputs",
|
|
"type": "Input",
|
|
"no_used_path": "IO Not in Hardware\\InputsMaster"
|
|
},
|
|
{
|
|
"path": "Outputs",
|
|
"type": "Output",
|
|
"no_used_path": "IO Not in Hardware\\OutputsMaster"
|
|
},
|
|
{
|
|
"path": "OutputsFesto",
|
|
"type": "Output",
|
|
"no_used_path": "IO Not in Hardware\\OutputsMaster"
|
|
},
|
|
{
|
|
"path": "IO Not in Hardware\\InputsMaster",
|
|
"type": "Input",
|
|
"no_used_path": "IO Not in Hardware\\InputsMaster"
|
|
},
|
|
{
|
|
"path": "IO Not in Hardware\\OutputsMaster",
|
|
"type": "Output",
|
|
"no_used_path": "IO Not in Hardware\\OutputsMaster"
|
|
}
|
|
]
|
|
}
|
|
|
|
try:
|
|
with open(json_config_path, 'w', encoding='utf-8') as f:
|
|
json.dump(default_config, f, indent=2)
|
|
print(f"Archivo de configuración creado: {json_config_path}")
|
|
return default_config
|
|
except Exception as e:
|
|
print(f"Error al crear el archivo de configuración JSON: {e}")
|
|
return None
|
|
|
|
|
|
def convert_excel_to_markdown_tables():
|
|
"""
|
|
Busca un archivo Excel en el working_directory o solicita al usuario seleccionarlo,
|
|
filtra las entradas según los paths configurados en JSON,
|
|
y genera un archivo Markdown con tablas.
|
|
"""
|
|
try:
|
|
configs = load_configuration()
|
|
working_directory = configs.get("working_directory")
|
|
if not working_directory:
|
|
print("Error: 'working_directory' no se encontró en la configuración.")
|
|
return
|
|
if not os.path.isdir(working_directory):
|
|
print(
|
|
f"Error: El directorio de trabajo '{working_directory}' no existe o no es un directorio."
|
|
)
|
|
return
|
|
except Exception as e:
|
|
print(f"Error al cargar la configuración: {e}")
|
|
return
|
|
|
|
working_directory_abs = os.path.abspath(working_directory)
|
|
print(f"Usando directorio de trabajo: {working_directory_abs}")
|
|
|
|
# Cargar la configuración de paths
|
|
path_config = load_path_config()
|
|
if not path_config:
|
|
print("Error: No se pudo cargar la configuración de paths.")
|
|
return
|
|
|
|
# Verificar si existe el archivo PLCTags.xlsx en el directorio de trabajo
|
|
default_excel_path = os.path.join(working_directory_abs, "PLCTags.xlsx")
|
|
|
|
if os.path.exists(default_excel_path):
|
|
excel_file_path = default_excel_path
|
|
print(f"Usando archivo Excel predeterminado: {excel_file_path}")
|
|
else:
|
|
# Solicitar al usuario que seleccione el archivo Excel
|
|
root = tk.Tk()
|
|
root.withdraw() # Ocultar ventana principal
|
|
|
|
print("Archivo PLCTags.xlsx no encontrado. Seleccione el archivo Excel exportado de TIA Portal:")
|
|
excel_file_path = filedialog.askopenfilename(
|
|
title="Seleccione el archivo Excel exportado de TIA Portal",
|
|
filetypes=[("Excel files", "*.xlsx"), ("All files", "*.*")],
|
|
initialdir=working_directory_abs
|
|
)
|
|
|
|
if not excel_file_path:
|
|
print("No se seleccionó ningún archivo Excel. Saliendo...")
|
|
return
|
|
|
|
print(f"Procesando archivo Excel: {excel_file_path}...")
|
|
|
|
output_md_filename = "Master IO Tags.md"
|
|
output_md_filepath_abs = os.path.join(working_directory_abs, output_md_filename)
|
|
|
|
markdown_content = []
|
|
|
|
# Definición de las columnas y sus anchos para el formato Markdown
|
|
md_header_names = ["Master Tag", "Type", "Data Type", "Description"]
|
|
col_widths = {"Master Tag": 32, "Type": 6, "Data Type": 9, "Description": 71}
|
|
|
|
# Crear el encabezado y separador Markdown
|
|
header_parts = [f" {name:<{col_widths[name]}} " for name in md_header_names]
|
|
markdown_table_header = f"|{'|'.join(header_parts)}|"
|
|
|
|
separator_parts = [f" {'-'*col_widths[name]} " for name in md_header_names]
|
|
markdown_table_separator = f"|{'|'.join(separator_parts)}|"
|
|
|
|
# Obtener la lista de paths a procesar
|
|
valid_paths = [path_entry["path"] for path_entry in path_config["paths"]]
|
|
print(f"Paths configurados para procesar: {valid_paths}")
|
|
|
|
try:
|
|
# Leer el Excel exportado de TIA Portal
|
|
excel_data = pd.read_excel(excel_file_path, sheet_name=0)
|
|
|
|
# Verificar columnas requeridas
|
|
excel_col_name = "Name"
|
|
excel_col_path = "Path"
|
|
excel_col_data_type = "Data Type"
|
|
excel_col_comment = "Comment"
|
|
|
|
excel_required_cols = [
|
|
excel_col_name,
|
|
excel_col_path,
|
|
excel_col_data_type,
|
|
excel_col_comment,
|
|
]
|
|
|
|
missing_cols = [col for col in excel_required_cols if col not in excel_data.columns]
|
|
if missing_cols:
|
|
print(f"Error: Columnas faltantes en el archivo Excel: {', '.join(missing_cols)}")
|
|
return
|
|
|
|
# Organizar entradas por path y crear tablas para cada tipo
|
|
for path_entry in path_config["paths"]:
|
|
path_name = path_entry["path"]
|
|
io_type = path_entry["type"] # Input u Output
|
|
|
|
# Filtrar datos por el path actual
|
|
path_data = excel_data[excel_data[excel_col_path] == path_name]
|
|
|
|
if path_data.empty:
|
|
print(f"No se encontraron entradas para el path: {path_name}")
|
|
continue
|
|
|
|
# Agregar encabezado para este path
|
|
markdown_content.append(f"## {path_name} ({io_type}s)\n")
|
|
markdown_content.append(markdown_table_header)
|
|
markdown_content.append(markdown_table_separator)
|
|
|
|
# Procesar cada entrada en este path
|
|
for _, row in path_data.iterrows():
|
|
master_tag = str(row.get(excel_col_name, ""))
|
|
data_type = str(row.get(excel_col_data_type, ""))
|
|
comment_text = str(row.get(excel_col_comment, ""))
|
|
description = f'"{comment_text}"'
|
|
|
|
# Usar el tipo del path desde la configuración
|
|
tag_type_for_md = io_type
|
|
|
|
master_tag_cell = f"{master_tag:<{col_widths['Master Tag']}.{col_widths['Master Tag']}}"
|
|
type_cell = f"{tag_type_for_md:<{col_widths['Type']}.{col_widths['Type']}}"
|
|
data_type_cell = f"{data_type:<{col_widths['Data Type']}.{col_widths['Data Type']}}"
|
|
description_cell = f"{description:<{col_widths['Description']}.{col_widths['Description']}}"
|
|
|
|
md_row = f"| {master_tag_cell} | {type_cell} | {data_type_cell} | {description_cell} |"
|
|
markdown_content.append(md_row)
|
|
|
|
markdown_content.append("\n") # Espacio después de cada tabla
|
|
|
|
except FileNotFoundError:
|
|
print(f"Error: El archivo '{excel_file_path}' no se encontró.")
|
|
return
|
|
except pd.errors.EmptyDataError:
|
|
print(f"Error: El archivo '{excel_file_path}' está vacío.")
|
|
return
|
|
except Exception as e:
|
|
print(f"Error al procesar el archivo Excel: {e}")
|
|
return
|
|
|
|
if markdown_content:
|
|
try:
|
|
with open(output_md_filepath_abs, "w", encoding="utf-8") as f:
|
|
f.write("\n".join(markdown_content))
|
|
print(
|
|
f"¡Éxito! Archivo Excel convertido a Markdown en: {output_md_filepath_abs}"
|
|
)
|
|
except IOError as e:
|
|
print(
|
|
f"Error al escribir el archivo Markdown '{output_md_filepath_abs}': {e}"
|
|
)
|
|
else:
|
|
print("No se generó contenido para el archivo Markdown.")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
convert_excel_to_markdown_tables() |