motulco 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of motulco might be problematic. Click here for more details.
- motulco-0.2.2/LICENSE +1 -0
- motulco-0.2.2/PKG-INFO +23 -0
- motulco-0.2.2/README.md +2 -0
- motulco-0.2.2/motulco/__init__.py +8 -0
- motulco-0.2.2/motulco/colors_utils.py +12 -0
- motulco-0.2.2/motulco/df_utils.py +185 -0
- motulco-0.2.2/motulco/doc_utils.py +84 -0
- motulco-0.2.2/motulco/excel_utils.py +155 -0
- motulco-0.2.2/motulco/pdf_utils.py +29 -0
- motulco-0.2.2/motulco/ppt_utils.py +309 -0
- motulco-0.2.2/motulco/py_utils.py +54 -0
- motulco-0.2.2/motulco/shapes_utils.py +693 -0
- motulco-0.2.2/motulco.egg-info/PKG-INFO +23 -0
- motulco-0.2.2/motulco.egg-info/SOURCES.txt +17 -0
- motulco-0.2.2/motulco.egg-info/dependency_links.txt +1 -0
- motulco-0.2.2/motulco.egg-info/requires.txt +11 -0
- motulco-0.2.2/motulco.egg-info/top_level.txt +1 -0
- motulco-0.2.2/pyproject.toml +26 -0
- motulco-0.2.2/setup.cfg +4 -0
motulco-0.2.2/LICENSE
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
MIT License
|
motulco-0.2.2/PKG-INFO
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: motulco
|
|
3
|
+
Version: 0.2.2
|
|
4
|
+
Summary: Librería de Python para manejo de archivos y DataFrames de Daniel Ochoa
|
|
5
|
+
Author-email: DanielOchoa <df.ochoa202@gmail.com>
|
|
6
|
+
Requires-Python: >=3.8
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: pandas>=2.0.0
|
|
10
|
+
Requires-Dist: numpy>=1.26.0
|
|
11
|
+
Requires-Dist: openpyxl>=3.1.0
|
|
12
|
+
Requires-Dist: unidecode>=1.3.6
|
|
13
|
+
Requires-Dist: pywin32>=306
|
|
14
|
+
Requires-Dist: pypdf>=4.0.0
|
|
15
|
+
Requires-Dist: Pillow>=10.0.0
|
|
16
|
+
Requires-Dist: nbformat>=5.9.0
|
|
17
|
+
Requires-Dist: nbconvert>=7.9.0
|
|
18
|
+
Requires-Dist: matplotlib>=3.8.0
|
|
19
|
+
Requires-Dist: html2image>=2.0.4
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
|
|
22
|
+
# motulco
|
|
23
|
+
Librería de Python para manejo de archivos y procesamiento de DataFrames.
|
motulco-0.2.2/README.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
################################################################################################################################
|
|
5
|
+
################################################################################################################################
|
|
6
|
+
####################################################### Convierte de HEX a RGB #################################################
|
|
7
|
+
################################################################################################################################
|
|
8
|
+
################################################################################################################################
|
|
9
|
+
def hexToRgb(hex_color):
|
|
10
|
+
"""Convierte color hex (#RRGGBB) a tupla RGB (R,G,B)"""
|
|
11
|
+
hex_color = hex_color.lstrip("#")
|
|
12
|
+
return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import os
|
|
3
|
+
from unidecode import unidecode
|
|
4
|
+
from openpyxl import load_workbook
|
|
5
|
+
|
|
6
|
+
################################################################################################################################
|
|
7
|
+
################################################################################################################################
|
|
8
|
+
################################# Quita los .0 en los textos de un df en las columnas dadas ####################################
|
|
9
|
+
################################################################################################################################
|
|
10
|
+
################################################################################################################################
|
|
11
|
+
def QuitarPuntosDecimalesTextos(df, columnas):
|
|
12
|
+
"""Convierte las columnas especificadas a string y elimina el sufijo '.0' de los valores numéricos.
|
|
13
|
+
Returns:
|
|
14
|
+
pd.DataFrame: El DataFrame con las columnas modificadas.
|
|
15
|
+
"""
|
|
16
|
+
df[columnas] = df[columnas].astype(str).replace(r'^(\d+)\.0$', r'\1', regex=True)
|
|
17
|
+
return df
|
|
18
|
+
|
|
19
|
+
################################################################################################################################
|
|
20
|
+
################################################################################################################################
|
|
21
|
+
################################## Hace la union (merge/join) de df1 y df2 y valida cruces ####################################
|
|
22
|
+
################################################################################################################################
|
|
23
|
+
################################################################################################################################
|
|
24
|
+
def mergeBases(dataframe1, dataframe2, lefton, righton):
|
|
25
|
+
"""Une dos DataFrames de Pandas asegurando que no haya duplicados en el cruce y detectando registros sin coincidencias en cada DataFrame.
|
|
26
|
+
|
|
27
|
+
Parámetros:
|
|
28
|
+
- dataframe1: Primer DataFrame de Pandas.
|
|
29
|
+
- dataframe2: Segundo DataFrame de Pandas.
|
|
30
|
+
- lefton: Lista de columnas clave en dataframe1.
|
|
31
|
+
- righton: Lista de columnas clave en dataframe2.
|
|
32
|
+
|
|
33
|
+
Retorna:
|
|
34
|
+
- dfmerge: DataFrame con la unión.
|
|
35
|
+
- df_no_match_left: Registros en dataframe1 que no encontraron match en dataframe2.
|
|
36
|
+
- df_no_match_right: Registros en dataframe2 que no encontraron match en dataframe1.
|
|
37
|
+
- alerta_duplicados: Mensaje de alerta si hay duplicados en la clave.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
# Realizar el merge con how='left'
|
|
41
|
+
if lefton == righton:
|
|
42
|
+
dfmerge = pd.merge(dataframe1, dataframe2, on=lefton, how='left',suffixes=('','_right'))
|
|
43
|
+
else:
|
|
44
|
+
dfmerge = pd.merge(dataframe1, dataframe2, left_on=lefton, right_on=righton, how='left', suffixes=('','_right'))
|
|
45
|
+
|
|
46
|
+
# Eliminar columnas duplicadas generadas por el merge
|
|
47
|
+
if isinstance(righton, list): # Si righton es una lista de columnas
|
|
48
|
+
for col in righton:
|
|
49
|
+
columna_duplicada = f"{col}_right"
|
|
50
|
+
if columna_duplicada in dfmerge.columns:
|
|
51
|
+
dfmerge = dfmerge.drop(columns=[columna_duplicada])
|
|
52
|
+
else: # Si righton es una sola columna
|
|
53
|
+
columna_duplicada = f"{righton}_right"
|
|
54
|
+
if columna_duplicada in dfmerge.columns:
|
|
55
|
+
dfmerge = dfmerge.drop(columns=[columna_duplicada])
|
|
56
|
+
|
|
57
|
+
# Detectar registros de df1 que no tienen coincidencias en df2 (valores NaN en columnas de df2)
|
|
58
|
+
columnas_df2 = [col for col in dataframe2.columns if col not in righton]
|
|
59
|
+
df_no_match_left = dfmerge[dfmerge[columnas_df2].isna().all(axis=1)]
|
|
60
|
+
|
|
61
|
+
# Detectar registros de df2 que no tienen coincidencias en df1
|
|
62
|
+
claves_merge = set(dfmerge[lefton].drop_duplicates().itertuples(index=False, name=None))
|
|
63
|
+
df_no_match_right = dataframe2[~dataframe2[righton].apply(lambda x: tuple(x) in claves_merge, axis=1)]
|
|
64
|
+
|
|
65
|
+
# Detectar duplicados en las claves del merge
|
|
66
|
+
duplicados = dfmerge.groupby(lefton).size()
|
|
67
|
+
duplicados = duplicados[duplicados > 1] # Filtrar claves duplicadas
|
|
68
|
+
|
|
69
|
+
alerta_duplicados = "⚠️ Hay duplicados en la clave del cruce" if len(duplicados) > 0 else "✅ No hay duplicados en la clave"
|
|
70
|
+
|
|
71
|
+
return dfmerge, df_no_match_left, df_no_match_right, alerta_duplicados
|
|
72
|
+
|
|
73
|
+
################################################################################################################################
|
|
74
|
+
################################################################################################################################
|
|
75
|
+
################################### Quita las tildes del contenido de un DataFrame ############################################
|
|
76
|
+
################################################################################################################################
|
|
77
|
+
################################################################################################################################
|
|
78
|
+
def dfSinTildes(dataframe):
|
|
79
|
+
for columna in dataframe.columns:
|
|
80
|
+
if dataframe[columna].dtype == 'object':
|
|
81
|
+
dataframe[columna] = dataframe[columna].apply(unidecode)
|
|
82
|
+
return dataframe
|
|
83
|
+
|
|
84
|
+
################################################################################################################################
|
|
85
|
+
################################################################################################################################
|
|
86
|
+
################################### Quita las tildes de los encabezados de un DataFrame ######################################
|
|
87
|
+
################################################################################################################################
|
|
88
|
+
################################################################################################################################
|
|
89
|
+
def dfEncabezadosSinTildes(dataframe):
|
|
90
|
+
nuevos_nombres_columnas = [unidecode(columna)
|
|
91
|
+
if dataframe[columna].dtype == 'object' else columna for columna in dataframe.columns]
|
|
92
|
+
dataframe.columns = nuevos_nombres_columnas
|
|
93
|
+
return dataframe
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
################################################################################################################################
|
|
97
|
+
################################################################################################################################
|
|
98
|
+
############################### Guarda un df como un excel en caso de existir reemplaza ######################################
|
|
99
|
+
################################################################################################################################
|
|
100
|
+
################################################################################################################################
|
|
101
|
+
def GuardarRemplazarExcel(dataframe,rutaParaGuardado,nombreHoja, ejecutar=True):
|
|
102
|
+
if ejecutar:
|
|
103
|
+
if os.path.exists(rutaParaGuardado):
|
|
104
|
+
os.remove(rutaParaGuardado)
|
|
105
|
+
dataframe.to_excel(rutaParaGuardado,sheet_name=nombreHoja, index=False)
|
|
106
|
+
|
|
107
|
+
################################################################################################################################
|
|
108
|
+
################################################################################################################################
|
|
109
|
+
############################### Guarda un df como un CSV en caso de existir reemplaza ########################################
|
|
110
|
+
################################################################################################################################
|
|
111
|
+
################################################################################################################################
|
|
112
|
+
def GuardarRemplazarCsv(dataframe,rutaParaGuardado, ejecutar=True):
|
|
113
|
+
if ejecutar:
|
|
114
|
+
if os.path.exists(rutaParaGuardado):
|
|
115
|
+
os.remove(rutaParaGuardado)
|
|
116
|
+
dataframe.to_csv(rutaParaGuardado, index=False)
|
|
117
|
+
|
|
118
|
+
################################################################################################################################
|
|
119
|
+
################################################################################################################################
|
|
120
|
+
############################### Guarda un df como un parquet en caso de existir reemplaza ####################################
|
|
121
|
+
################################################################################################################################
|
|
122
|
+
################################################################################################################################
|
|
123
|
+
def GuardarRemplazarParquet(dataframe,rutaParaGuardado, ejecutar=True, engine="fastparquet"):
|
|
124
|
+
if ejecutar:
|
|
125
|
+
if os.path.exists(rutaParaGuardado):
|
|
126
|
+
os.remove(rutaParaGuardado)
|
|
127
|
+
if engine=="":
|
|
128
|
+
dataframe.to_parquet(rutaParaGuardado, index=False)
|
|
129
|
+
else:
|
|
130
|
+
dataframe.to_parquet(rutaParaGuardado, index=False, engine=engine)
|
|
131
|
+
|
|
132
|
+
################################################################################################################################
|
|
133
|
+
################################################################################################################################
|
|
134
|
+
######################################################### Convierte columnas en tipo bool ####################################
|
|
135
|
+
################################################################################################################################
|
|
136
|
+
################################################################################################################################
|
|
137
|
+
def EstandarizarColumnasConDatosBool(dataframe):
|
|
138
|
+
for col in dataframe.columns:
|
|
139
|
+
# Si en la columna hay al menos un valor booleano
|
|
140
|
+
if dataframe[col].apply(lambda x: isinstance(x, bool)).any():
|
|
141
|
+
# Reemplazar NaN con False y convertir todo a tipo bool
|
|
142
|
+
dataframe[col] = dataframe[col].astype(object).fillna(False).astype(bool)
|
|
143
|
+
return dataframe
|
|
144
|
+
|
|
145
|
+
################################################################################################################################
|
|
146
|
+
################################################################################################################################
|
|
147
|
+
######################################################### Convierte columnas en tipo bool ####################################
|
|
148
|
+
################################################################################################################################
|
|
149
|
+
################################################################################################################################
|
|
150
|
+
def extraerMesInicioDelNombre(nombreArchivo,Separador):
|
|
151
|
+
""" Extrae el número y nombre del mes desde el nombre del archivo. """
|
|
152
|
+
nombreArchivo = nombreArchivo.replace(" ", "") # Elimina espacios extra
|
|
153
|
+
partes = nombreArchivo.split(Separador)
|
|
154
|
+
if len(partes) >= 1:
|
|
155
|
+
numero_mes = partes[0].strip() # "01"
|
|
156
|
+
return numero_mes
|
|
157
|
+
|
|
158
|
+
return None, None # Retornar valores nulos si el formato no es el esperado
|
|
159
|
+
|
|
160
|
+
################################################################################################################################
|
|
161
|
+
################################################################################################################################
|
|
162
|
+
################ Funcion que convierte una cadena a (UpperCamelCase), eliminando espacios y guiones bajos. ###################
|
|
163
|
+
################################################################################################################################
|
|
164
|
+
################################################################################################################################
|
|
165
|
+
def pasarACamelCase(texto,Separador):
|
|
166
|
+
# Reemplaza guiones bajos por espacios, divide en palabras, y las capitaliza
|
|
167
|
+
partes = texto.replace(Separador, " ").split()
|
|
168
|
+
return ''.join(p.capitalize() for p in partes)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
################################################################################################################################
|
|
172
|
+
################################################################################################################################
|
|
173
|
+
################ Renombrar columnas de un df con poniendo en CamelCase lo que esta a la izquierda del separador ###############
|
|
174
|
+
################################################################################################################################
|
|
175
|
+
################################################################################################################################
|
|
176
|
+
def renombraColumnasTablas(dataframe,Separador):
|
|
177
|
+
nuevas_columnas = {}
|
|
178
|
+
for col in dataframe.columns:
|
|
179
|
+
if "_" in col:
|
|
180
|
+
izquierda, derecha = col.split("_", 1) # Solo divide en el primer guion bajo
|
|
181
|
+
nuevo_nombre = f"{pasarACamelCase(izquierda,Separador)}_{derecha}"
|
|
182
|
+
else:
|
|
183
|
+
nuevo_nombre = pasarACamelCase(col,Separador)
|
|
184
|
+
nuevas_columnas[col] = nuevo_nombre
|
|
185
|
+
return dataframe.rename(columns=nuevas_columnas)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import os
|
|
4
|
+
import shutil
|
|
5
|
+
from unidecode import unidecode
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
################################################################################################################################
|
|
9
|
+
################################################################################################################################
|
|
10
|
+
####################################### Borrar los archivos si existen en la carpeta ###########################################
|
|
11
|
+
################################################################################################################################
|
|
12
|
+
################################################################################################################################
|
|
13
|
+
def BorrrarArchivo(rutaArchivo):
|
|
14
|
+
if os.path.exists(rutaArchivo):# Verificar si el archivo ya existe
|
|
15
|
+
os.remove(rutaArchivo)
|
|
16
|
+
|
|
17
|
+
################################################################################################################################
|
|
18
|
+
################################################################################################################################
|
|
19
|
+
################################# Mueve los archivos descargados a la carpeta que se requiere ##################################
|
|
20
|
+
################################################################################################################################
|
|
21
|
+
################################################################################################################################
|
|
22
|
+
def moverArchivo(RutaOrigen, RutaDestino):
|
|
23
|
+
try:
|
|
24
|
+
shutil.move(RutaOrigen, RutaDestino)
|
|
25
|
+
print(f"El archivo '{RutaOrigen}' se ha movido correctamente a '{RutaDestino}'.")
|
|
26
|
+
except Exception as e:
|
|
27
|
+
print(f"No se pudo mover el archivo '{RutaOrigen}' a '{RutaDestino}': {e}")
|
|
28
|
+
|
|
29
|
+
################################################################################################################################
|
|
30
|
+
################################################################################################################################
|
|
31
|
+
############################################### Copia un archivo de un destino a otro ##########################################
|
|
32
|
+
################################################################################################################################
|
|
33
|
+
################################################################################################################################
|
|
34
|
+
def copiarArchivo(RutaOrigen, RutaDestino):
|
|
35
|
+
try:
|
|
36
|
+
shutil.copy2(RutaOrigen, RutaDestino) # `copy2` copia metadatos (marca de tiempo) además del archivo
|
|
37
|
+
print(f"El archivo '{RutaOrigen}' se ha copiado correctamente a '{RutaDestino}'.")
|
|
38
|
+
except Exception as e:
|
|
39
|
+
print(f"No se pudo copiar el archivo '{RutaOrigen}' a '{RutaDestino}': {e}")
|
|
40
|
+
|
|
41
|
+
################################################################################################################################
|
|
42
|
+
################################################################################################################################
|
|
43
|
+
###################################### Obtiene el archivo más reciente de una carpeta ##########################################
|
|
44
|
+
################################################################################################################################
|
|
45
|
+
################################################################################################################################
|
|
46
|
+
def obtenerArchivoMasReciente(rutaCarpeta, formato=".txt"):
|
|
47
|
+
# Obtener la lista de archivos en la carpeta
|
|
48
|
+
archivos_en_carpeta = [archivo for archivo in os.listdir(rutaCarpeta) if archivo.endswith(formato)]
|
|
49
|
+
# Verificar si hay archivos en la carpeta
|
|
50
|
+
if archivos_en_carpeta:
|
|
51
|
+
# Obtener el archivo más reciente basado en la última modificación
|
|
52
|
+
archivo_mas_reciente = max(archivos_en_carpeta,key=lambda x: os.path.getmtime(os.path.join(rutaCarpeta, x)))
|
|
53
|
+
# Obtener la fecha de modificación del archivo más reciente
|
|
54
|
+
fecha_modificacion = os.path.getmtime(os.path.join(rutaCarpeta, archivo_mas_reciente))
|
|
55
|
+
# Retornar nombre y fecha
|
|
56
|
+
return archivo_mas_reciente, fecha_modificacion
|
|
57
|
+
else:
|
|
58
|
+
return None, None
|
|
59
|
+
|
|
60
|
+
################################################################################################################################
|
|
61
|
+
################################################################################################################################
|
|
62
|
+
###################################### Retorna la ruta que exista del arreglo de rutas #########################################
|
|
63
|
+
################################################################################################################################
|
|
64
|
+
################################################################################################################################
|
|
65
|
+
def ExisteRuta(arregloDeRutas):
|
|
66
|
+
carpeta_correcta = None
|
|
67
|
+
for carpeta in arregloDeRutas:
|
|
68
|
+
try:
|
|
69
|
+
# Verifica si la ruta existe y es una carpeta
|
|
70
|
+
if os.path.isdir(carpeta):
|
|
71
|
+
print(f"La carpeta existe en: {carpeta}")
|
|
72
|
+
carpeta_correcta = carpeta
|
|
73
|
+
break # Sale del ciclo si la carpeta existe
|
|
74
|
+
except Exception as e:
|
|
75
|
+
# Maneja otras posibles excepciones (e.g., problemas de permisos)
|
|
76
|
+
print(f"Error al verificar la carpeta {carpeta}: {e}")
|
|
77
|
+
continue
|
|
78
|
+
if carpeta_correcta is None:
|
|
79
|
+
print("La carpeta no se encontró en ninguna de las rutas proporcionadas.")
|
|
80
|
+
else:
|
|
81
|
+
# Aquí puedes continuar con el procesamiento usando la carpeta_correcta
|
|
82
|
+
pass
|
|
83
|
+
return carpeta_correcta
|
|
84
|
+
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import openpyxl
|
|
4
|
+
import time
|
|
5
|
+
import pythoncom
|
|
6
|
+
import win32com.client
|
|
7
|
+
from openpyxl import load_workbook
|
|
8
|
+
|
|
9
|
+
################################################################################################################################
|
|
10
|
+
################################################################################################################################
|
|
11
|
+
############################################### Carga un archivo de excel como un df ###########################################
|
|
12
|
+
################################################################################################################################
|
|
13
|
+
################################################################################################################################
|
|
14
|
+
def cargaExcel(rutaArchivo,sheetName, AsString=False,engine='openpyxl'):
|
|
15
|
+
df = pd.read_excel(rutaArchivo, sheet_name=sheetName, engine=engine)
|
|
16
|
+
if AsString:
|
|
17
|
+
df=df.astype(str)
|
|
18
|
+
return df
|
|
19
|
+
|
|
20
|
+
################################################################################################################################
|
|
21
|
+
################################################################################################################################
|
|
22
|
+
############################################# Ejecuta la funcion Actualizar todo de excel ######################################
|
|
23
|
+
################################################################################################################################
|
|
24
|
+
################################################################################################################################
|
|
25
|
+
|
|
26
|
+
def ActualizarTodoExcel(rutaArchivo):
|
|
27
|
+
pythoncom.CoInitialize() # Inicializa COM correctamente
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
rutaArchivoAbsoluta = os.path.abspath(rutaArchivo)
|
|
31
|
+
print(f"Intentando abrir el archivo: {rutaArchivoAbsoluta}")
|
|
32
|
+
|
|
33
|
+
if not os.path.exists(rutaArchivoAbsoluta):
|
|
34
|
+
print(f"⚠️ El archivo no existe en la ruta: {rutaArchivoAbsoluta}")
|
|
35
|
+
return
|
|
36
|
+
# Iniciar Excel y hacerlo visible
|
|
37
|
+
excel_app = win32com.client.DispatchEx("Excel.Application")
|
|
38
|
+
excel_app.Visible = True
|
|
39
|
+
excel_app.DisplayAlerts = False
|
|
40
|
+
# Abrir el archivo de Excel
|
|
41
|
+
workbook = excel_app.Workbooks.Open(rutaArchivoAbsoluta)
|
|
42
|
+
print("📂 Archivo abierto correctamente, iniciando actualización...")
|
|
43
|
+
|
|
44
|
+
# Activar la primera hoja (por si acaso)
|
|
45
|
+
workbook.Sheets(1).Activate()
|
|
46
|
+
# Iniciar actualización
|
|
47
|
+
workbook.RefreshAll()
|
|
48
|
+
print("🔄 Actualizando conexiones y tablas de Power Query...")
|
|
49
|
+
# Esperar que Excel termine la actualización y cálculos
|
|
50
|
+
while excel_app.CalculationState != 0: # 0 significa "listo"
|
|
51
|
+
print("⌛ Esperando que Excel termine los cálculos...")
|
|
52
|
+
time.sleep(2)
|
|
53
|
+
print("✅ Actualización completada.")
|
|
54
|
+
# Guardar y cerrar Excel
|
|
55
|
+
print(f'📁 Archivo actualizado y cerrado: {rutaArchivoAbsoluta}')
|
|
56
|
+
except Exception as e:
|
|
57
|
+
print(f"❌ Error al actualizar el archivo {rutaArchivoAbsoluta}: {e}")
|
|
58
|
+
finally:
|
|
59
|
+
# Asegurar que Excel se cierra correctamente
|
|
60
|
+
if 'excel_app' in locals():
|
|
61
|
+
a=10
|
|
62
|
+
pythoncom.CoUninitialize()
|
|
63
|
+
|
|
64
|
+
################################################################################################################################
|
|
65
|
+
################################################################################################################################
|
|
66
|
+
#################################### Consolida Archivos de excel cuyo nombre es semejante ######################################
|
|
67
|
+
################################################################################################################################
|
|
68
|
+
################################################################################################################################
|
|
69
|
+
def consolidarArchivosExcelPrefijo(NombreConElQueIniciaLosArchivos,RutaALaCarpeta,TipoArchivos='.xlsx'):
|
|
70
|
+
dataframes = []
|
|
71
|
+
for archivo in os.listdir(RutaALaCarpeta):
|
|
72
|
+
if archivo.endswith(TipoArchivos) and archivo.startswith(NombreConElQueIniciaLosArchivos):
|
|
73
|
+
ruta_archivo = os.path.join(RutaALaCarpeta, archivo)
|
|
74
|
+
df = pd.read_excel(ruta_archivo)
|
|
75
|
+
dataframes.append(df)
|
|
76
|
+
df_consolidado = pd.concat(dataframes, ignore_index=True)
|
|
77
|
+
return df_consolidado
|
|
78
|
+
|
|
79
|
+
################################################################################################################################
|
|
80
|
+
################################################################################################################################
|
|
81
|
+
############################################## Consolida Archivos de excel en una carpeta ######################################
|
|
82
|
+
################################################################################################################################
|
|
83
|
+
################################################################################################################################
|
|
84
|
+
def consolidarArchivosExcelGlobal(NombreConElQueIniciaLosArchivos,RutaALaCarpeta,TipoArchivos='.xlsx'):
|
|
85
|
+
dataframes = []
|
|
86
|
+
for archivo in os.listdir(RutaALaCarpeta):
|
|
87
|
+
if archivo.endswith(TipoArchivos):
|
|
88
|
+
ruta_archivo = os.path.join(RutaALaCarpeta, archivo)
|
|
89
|
+
df = pd.read_excel(ruta_archivo)
|
|
90
|
+
dataframes.append(df)
|
|
91
|
+
df_consolidado = pd.concat(dataframes, ignore_index=True)
|
|
92
|
+
return df_consolidado
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
################################################################################################################################
|
|
96
|
+
################################################################################################################################
|
|
97
|
+
################################## Funcion que retornas el arreglo de hojas visibles (no oculta) en excel ###################
|
|
98
|
+
################################################################################################################################
|
|
99
|
+
################################################################################################################################
|
|
100
|
+
def obtenerHojasVisiblesExcel(RutaArchivo):
|
|
101
|
+
"""Obtiene solo las hojas visibles de un archivo Excel (.xlsx)."""
|
|
102
|
+
try:
|
|
103
|
+
wb = load_workbook(RutaArchivo, read_only=True)
|
|
104
|
+
hojas_visibles = [sheet.title for sheet in wb.worksheets if sheet.sheet_state == "visible"]
|
|
105
|
+
wb.close()
|
|
106
|
+
return hojas_visibles
|
|
107
|
+
except Exception as e:
|
|
108
|
+
print(f"Error al leer hojas de {RutaArchivo}: {e}")
|
|
109
|
+
return []
|
|
110
|
+
|
|
111
|
+
################################################################################################################################
|
|
112
|
+
################################################################################################################################
|
|
113
|
+
################################## Funcion que retornas el arreglo de todas las hojas en excel ##############################
|
|
114
|
+
################################################################################################################################
|
|
115
|
+
################################################################################################################################
|
|
116
|
+
def obtenerTodasLasHojasExcel(RutaArchivo):
|
|
117
|
+
"""Obtiene todas las hojas de un archivo Excel (.xlsx), sin importar si están visibles u ocultas."""
|
|
118
|
+
try:
|
|
119
|
+
wb = load_workbook(RutaArchivo, read_only=True)
|
|
120
|
+
hojas = [sheet.title for sheet in wb.worksheets] # Todas las hojas
|
|
121
|
+
wb.close()
|
|
122
|
+
return hojas
|
|
123
|
+
except Exception as e:
|
|
124
|
+
print(f"Error al leer hojas de {RutaArchivo}: {e}")
|
|
125
|
+
return []
|
|
126
|
+
|
|
127
|
+
################################################################################################################################
|
|
128
|
+
################################################################################################################################
|
|
129
|
+
################################## Retorna el color de las celdas de una hoja de un excel ###################################
|
|
130
|
+
################################################################################################################################
|
|
131
|
+
################################################################################################################################
|
|
132
|
+
def RetornarColoresCeldas(workbook, sheet, solo_coloreadas=True):
|
|
133
|
+
ws = workbook[sheet]
|
|
134
|
+
data = []
|
|
135
|
+
for fila in ws.iter_rows():
|
|
136
|
+
for celda in fila:
|
|
137
|
+
|
|
138
|
+
# Opcional: ignorar celdas totalmente vacías
|
|
139
|
+
if solo_coloreadas and celda.value is None:
|
|
140
|
+
continue
|
|
141
|
+
|
|
142
|
+
fill = celda.fill
|
|
143
|
+
color = "FFFFFF" # blanco por defecto
|
|
144
|
+
|
|
145
|
+
if fill and fill.patternType and fill.start_color.type == "rgb":
|
|
146
|
+
color = fill.start_color.rgb[-6:] # quitar alpha
|
|
147
|
+
|
|
148
|
+
data.append({
|
|
149
|
+
"fila": celda.row,
|
|
150
|
+
"columna": celda.column_letter,
|
|
151
|
+
"valor": celda.value,
|
|
152
|
+
"color_hex": color
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
return pd.DataFrame(data)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from pypdf import PdfReader, PdfWriter
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
################################################################################################################################
|
|
6
|
+
################################################################################################################################
|
|
7
|
+
####################################### Rota los grados y paginas deseadas de un pdf ###########################################
|
|
8
|
+
################################################################################################################################
|
|
9
|
+
################################################################################################################################
|
|
10
|
+
def rotarPaginasPdf(pdf_entrada, pdf_salida, paginas_a_rotar, grados):
|
|
11
|
+
"""
|
|
12
|
+
Rota páginas específicas de un PDF.
|
|
13
|
+
|
|
14
|
+
pdf_entrada: str -> ruta del PDF original
|
|
15
|
+
pdf_salida: str -> ruta del PDF modificado
|
|
16
|
+
paginas_a_rotar: list[int] -> índices de páginas a rotar (0 = primera)
|
|
17
|
+
grados: int -> grados de rotación (90, 180, 270)
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
reader = PdfReader(pdf_entrada)
|
|
21
|
+
writer = PdfWriter()
|
|
22
|
+
|
|
23
|
+
for i, page in enumerate(reader.pages):
|
|
24
|
+
if i in paginas_a_rotar:
|
|
25
|
+
page.rotate(grados)
|
|
26
|
+
writer.add_page(page)
|
|
27
|
+
|
|
28
|
+
with open(pdf_salida, "wb") as f:
|
|
29
|
+
writer.write(f)
|