""" 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. Also converts IO Excel file from electrical diagram to Markdown. """ # 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_io_excel_to_markdown(): """ Convierte el archivo Excel de IO desde el esquema eléctrico a Markdown. Utiliza la configuración io_excel_file_from_ediagram para obtener la ruta del archivo. """ try: configs = load_configuration() working_directory = configs.get("working_directory") level2_configs = configs.get("level2", {}) level3_configs = configs.get("level3", {}) io_excel_file = level3_configs.get("io_excel_file_from_ediagram") resultados_exp_directory = level2_configs.get("resultados_exp_directory", ".") if not working_directory or not os.path.isdir(working_directory): print( f"Error: El directorio de trabajo '{working_directory}' no es válido." ) return False except Exception as e: print(f"Error al cargar la configuración: {e}") return False working_directory_abs = os.path.abspath(working_directory) print(f"Usando directorio de trabajo: {working_directory_abs}") # Construir la ruta del archivo Excel de IO if io_excel_file: excel_file_path = os.path.join(working_directory_abs, io_excel_file) if not os.path.exists(excel_file_path): print(f"Error: El archivo Excel de IO '{excel_file_path}' no existe.") return False else: print("Error: 'io_excel_file_from_ediagram' no está configurado en level3.") return False print(f"Procesando archivo Excel de IO: {excel_file_path}...") output_dir_abs = os.path.join(working_directory_abs, resultados_exp_directory) os.makedirs(output_dir_abs, exist_ok=True) output_md_filename = "Hardware_ED.md" output_md_filepath_abs = os.path.join(output_dir_abs, output_md_filename) try: # Leer el Excel de IO excel_data = pd.read_excel(excel_file_path, sheet_name=0) # Verificar que el archivo no esté vacío if excel_data.empty: print(f"Error: El archivo Excel '{excel_file_path}' está vacío.") return False # Obtener las columnas del Excel columns = list(excel_data.columns) print(f"Columnas encontradas en el Excel: {columns}") # Crear el contenido Markdown markdown_content = [] markdown_content.append("# IO desde Esquema Eléctrico\n") # Crear la tabla Markdown if len(columns) > 0: # Crear el encabezado de la tabla header = "| " + " | ".join(columns) + " |" markdown_content.append(header) # Crear el separador separator = "| " + " | ".join(["---"] * len(columns)) + " |" markdown_content.append(separator) # Agregar las filas de datos for _, row in excel_data.iterrows(): row_data = [] for col in columns: cell_value = str(row.get(col, "")).strip() # Si la celda está vacía, usar un espacio if not cell_value or cell_value == "nan": cell_value = " " # Escapar caracteres especiales de Markdown si es necesario if "|" in cell_value: cell_value = cell_value.replace("|", "\\|") row_data.append(cell_value) row_line = "| " + " | ".join(row_data) + " |" markdown_content.append(row_line) # Escribir el archivo Markdown try: with open(output_md_filepath_abs, "w", encoding="utf-8") as f: f.write("\n".join(markdown_content)) print( f"¡Éxito! Archivo Markdown de IO generado en: {output_md_filepath_abs}" ) return True except IOError as e: print( f"Error al escribir el archivo Markdown '{output_md_filepath_abs}': {e}" ) return False except FileNotFoundError: print(f"Error: El archivo '{excel_file_path}' no se encontró.") return False except pd.errors.EmptyDataError: print(f"Error: El archivo '{excel_file_path}' está vacío.") return False except Exception as e: print(f"Error al procesar el archivo Excel de IO: {e}") return False def convert_excel_to_markdown_tables(): """ Busca un archivo Excel en el directorio configurado, lo procesa y genera un archivo Markdown con tablas filtradas en el directorio de resultados. """ try: configs = load_configuration() working_directory = configs.get("working_directory") level2_configs = configs.get("level2", {}) level3_configs = configs.get("level3", {}) tags_exp_directory = level3_configs.get("tags_exp_directory", ".") resultados_exp_directory = level2_configs.get("resultados_exp_directory", ".") if not working_directory or not os.path.isdir(working_directory): print( f"Error: El directorio de trabajo '{working_directory}' no es válido." ) 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}") path_config = load_path_config() if not path_config: print("Error: No se pudo cargar la configuración de paths.") return tags_exp_dir_abs = os.path.join(working_directory_abs, tags_exp_directory) os.makedirs(tags_exp_dir_abs, exist_ok=True) print(f"Buscando archivos Excel en: {tags_exp_dir_abs}") excel_files = [ f for f in os.listdir(tags_exp_dir_abs) if f.lower().endswith(".xlsx") ] excel_file_path = "" if len(excel_files) == 1: excel_file_path = os.path.join(tags_exp_dir_abs, excel_files[0]) print(f"Archivo Excel encontrado automáticamente: {excel_file_path}") else: if len(excel_files) == 0: print(f"No se encontraron archivos Excel en '{tags_exp_dir_abs}'.") else: print( f"Se encontraron múltiples archivos Excel en '{tags_exp_dir_abs}'. Por favor seleccione uno." ) root = tk.Tk() root.withdraw() excel_file_path = filedialog.askopenfilename( title="Seleccione el archivo Excel exportado de TIA Portal", filetypes=[("Excel files", "*.xlsx"), ("All files", "*.*")], initialdir=tags_exp_dir_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_dir_abs = os.path.join(working_directory_abs, resultados_exp_directory) os.makedirs(output_dir_abs, exist_ok=True) output_md_filename = "Master IO Tags.md" output_md_filepath_abs = os.path.join(output_dir_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 Markdown generado 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.") def main(): """ Función principal que ejecuta ambas conversiones: 1. Convierte el Excel de tags de TIA Portal a 'Master IO Tags.md' 2. Convierte el Excel de IO desde esquema eléctrico a 'IO Adapted.md' """ print("=== Conversión de archivos Excel a Markdown ===\n") # 1. Convertir Excel de tags de TIA Portal print("1. Convirtiendo Excel de tags de TIA Portal...") convert_excel_to_markdown_tables() print("\n" + "=" * 50 + "\n") # 2. Convertir Excel de IO desde esquema eléctrico print("2. Convirtiendo Excel de IO desde esquema eléctrico...") convert_io_excel_to_markdown() print("\n=== Proceso completado ===") if __name__ == "__main__": main()