import numpy as np from bs4 import BeautifulSoup import psycopg2 from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.chrome.options import Options from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import argparse import getpass try: # Configuración de Selenium options = Options() options.add_argument("--headless") options.add_argument("--disable-gpu") options.add_argument("--no-sandbox") options.add_argument("--disable-dev-shm-usage") options.add_argument("--window-size=1920x1080") PATH = "/usr/bin/chromedriver" service = Service(PATH) driver = webdriver.Chrome(service=service, options=options) except Exception as e: print(f"Error configurando el navegador: {e}") exit() try: # Parseo de argumentos parser = argparse.ArgumentParser() parser.add_argument("clave", help="Clave ULSA argument") args = parser.parse_args() clave = args.clave # Solicitar la contraseña de manera segura contraseña = getpass.getpass("Contraseña: ") if not clave or not contraseña: raise ValueError("Clave y/o contraseña no válidos") except Exception as e: print(f"Error en los argumentos: {e}") driver.quit() exit() try: # Navegar a la URL url = "sgu.ulsa.edu.mx/psulsa/alumnos/consultainformacionalumnos/consultainformacion.aspx" formatted_url = f"https://{clave}:{contraseña}@{url}" driver.get(formatted_url) driver.get(f'https://{url}') except Exception as e: print(f"Error navegando a la URL: {e}") driver.quit() exit() try: # If dentro del código existe un (case insensitive) Unauthorized if "Unauthorized" in driver.page_source: raise Exception("Credenciales inválidas") elemento = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, "ctl00_contenedor_HistorialAlumno1_lblBtnSeccionHAcademico")) ) elemento.click() except Exception as e: print(f"Error interactuando con la página: {e}") driver.quit() exit() try: # Procesamiento de HTML con BeautifulSoup html_doc = driver.page_source soup = BeautifulSoup(html_doc, 'lxml') table = soup.find('table', attrs={'id': 'ctl00_contenedor_HistorialAlumno1_gvMaterias'}) if table is None: raise Exception("Tabla no encontrada en la página") materias_sgu = [] headers = [header.text for header in table.find_all('th')] def is_cell_empty(cell_content): return not cell_content.strip() or cell_content == u'\xa0' for row in table.find_all('tr'): cols = row.find_all('td') if cols and not any(is_cell_empty(col.text) for col in cols): materias_sgu.append({headers[i]: col.text for i, col in enumerate(cols)}) for materia in materias_sgu: materia['SEMESTRE'] = materia.pop('\xa0') servicio_social = soup.find('span', attrs={'id': 'ctl00_contenedor_HistorialAlumno1_Header1_lblSS'}).text # puede ser Realizado, No Realizado if servicio_social == "No Realizado": Alumno_serviciosocial = False elif servicio_social == "Realizado": Alumno_serviciosocial = True except Exception as e: print(f"Error procesando HTML: {e}") exit() finally: driver.quit() # Conexión a la base de datos y operaciones conexion = psycopg2.connect( dbname="sgi", user="postgres", password="sys4lci", host="200.13.89.27", port="5432" ) # Ejecutar la consulta para actualizar public."Alumno"."Alumno_serviciosocial" try: with conexion.cursor() as cursor: cursor.execute(f'UPDATE public."Alumno" SET "Alumno_serviciosocial" = {Alumno_serviciosocial} WHERE "Usuario_claveULSA" = \'{clave[2:]}\'') conexion.commit() except Exception as e: print(f"Error al actualizar el servicio social: {e}") if conexion: conexion.rollback() try: # Ejecutar la consulta para obtener los periodos with conexion.cursor() as cursor: cursor.execute('SELECT * FROM "Periodo"') Periodos = cursor.fetchall() except Exception as e: print(f"Error obteniendo los periodos de la base de datos: {e}") try: # Ejecutar la consulta para obtener los tipos de calificación with conexion.cursor() as cursor: cursor.execute('SELECT * FROM "TipoCalificacion"') TiposCalificacion = cursor.fetchall() except Exception as e: print(f"Error obteniendo los tipos de calificación de la base de datos: {e}") try: # Ejecutar la consulta para obtener las materias base with conexion.cursor() as cursor: cursor.execute('SELECT * FROM "Materia"') materias_base = cursor.fetchall() except Exception as e: print(f"Error obteniendo las materias de la base de datos: {e}") try: # Procesar y preparar las calificaciones para inserción/actualización calificaciones = [] with conexion.cursor() as cursor: cursor.execute(f''' SELECT "Carrera_id", "PlanEstudio_id" FROM "Alumno_view" WHERE "Usuario_claveULSA" = {clave[2:]} ''') Alumno_base = cursor.fetchone() for materia_sgu in materias_sgu: materia_base = next((materia_base for materia_base in materias_base if (materia_sgu['Cve ULSA'] in materia_base[-1] or materia_sgu['Cve SEP'] in materia_base[-1]) and (Alumno_base[1] == materia_base[0])), None) if not materia_base: continue Periodo_base = next((Periodo_base for Periodo_base in Periodos if Periodo_base[-2] == materia_sgu['PERIODO']), None) if not Periodo_base: continue Calificacion_base = next((Calificacion_base for Calificacion_base in TiposCalificacion if Calificacion_base[-2] == materia_sgu['EXAMEN']), None) if not Calificacion_base: raise Exception(f"No se encontró el tipo de calificación {materia_sgu['EXAMEN']} en la base de datos") # buscar en la base de datos el grupo WHERE Grupo_desc = materia_sgu['GRUPO'] and if is not in the base insert it (note: semestre when printed in the console is '\xa0': '5') with conexion.cursor() as cursor: cursor.execute(f''' SELECT "Grupo_id" FROM "Grupo_view" WHERE REGEXP_REPLACE("Grupo_desc", '[^\d]', '', 'g') = '{materia_sgu["GRUPO"]}' AND (("Carrera_id" = {Alumno_base[0]}) OR "Carrera_esComun") ''') Grupo = cursor.fetchone() if Grupo: cursor.execute(f''' INSERT INTO public."Alumno_Materia"("Usuario_claveULSA", "Materia_id", "Periodo_id", "Grupo_id") VALUES ({clave[2:]}, {materia_base[0]}, {Periodo_base[0]}, {Grupo[0]}) ON CONFLICT ("Usuario_claveULSA", "Materia_id", "Periodo_id") DO NOTHING ; ''') conexion.commit() if not materia_base or not Periodo_base or not Calificacion_base: print(f"No se encontraron coincidencias para la materia {materia_sgu['Cve ULSA']} o el periodo {materia_sgu['PERIODO']} o el tipo de calificación {materia_sgu['EXAMEN']}") continue # Saltar esta iteración si alguna coincidencia falla calificaciones.append(f"({clave[2:]}, {materia_base[0]}, {Periodo_base[0]}, {Calificacion_base[0]}, {materia_sgu['CALIF']}, CURRENT_DATE, 'SGU')") if not calificaciones or len(calificaciones) == 0: raise Exception("No hay calificaciones para insertar o actualizar.") # Inserción/actualización de calificaciones en la base de datos with conexion.cursor() as cursor: cursor.execute(f''' insert into "Alumno_Materia_Calificacion" ("Usuario_claveULSA", "Materia_id", "Periodo_id", "TipoCalificacion_id", "Calificacion_calif", "Calificacion_fecha", "Calificacion_comentario") values {','.join(calificaciones)} on conflict ("Usuario_claveULSA", "Materia_id", "Periodo_id", "TipoCalificacion_id") DO UPDATE SET "Calificacion_calif" = EXCLUDED."Calificacion_calif", "Calificacion_comentario" = EXCLUDED."Calificacion_comentario"; ''') conexion.commit() except Exception as e: print(f"Error al insertar/actualizar las calificaciones: {e} Stack: {e.__traceback__}") # print the whole stack if conexion: conexion.rollback() # Revertir cambios en caso de error conexion.close()