sgu.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. import numpy as np
  2. from bs4 import BeautifulSoup
  3. import psycopg2
  4. from selenium import webdriver
  5. from selenium.webdriver.chrome.service import Service
  6. from selenium.webdriver.chrome.options import Options
  7. from selenium.webdriver.common.by import By
  8. from selenium.webdriver.support.ui import WebDriverWait
  9. from selenium.webdriver.support import expected_conditions as EC
  10. import argparse
  11. import getpass
  12. try:
  13. # Configuración de Selenium
  14. options = Options()
  15. options.add_argument("--headless")
  16. options.add_argument("--disable-gpu")
  17. options.add_argument("--no-sandbox")
  18. options.add_argument("--disable-dev-shm-usage")
  19. options.add_argument("--window-size=1920x1080")
  20. PATH = "/usr/bin/chromedriver"
  21. service = Service(PATH)
  22. driver = webdriver.Chrome(service=service, options=options)
  23. except Exception as e:
  24. print(f"Error configurando el navegador: {e}")
  25. exit()
  26. try:
  27. # Parseo de argumentos
  28. parser = argparse.ArgumentParser()
  29. parser.add_argument("clave", help="Clave ULSA argument")
  30. args = parser.parse_args()
  31. clave = args.clave
  32. # Solicitar la contraseña de manera segura
  33. contraseña = getpass.getpass("Contraseña: ")
  34. if not clave or not contraseña:
  35. raise ValueError("Clave y/o contraseña no válidos")
  36. except Exception as e:
  37. print(f"Error en los argumentos: {e}")
  38. driver.quit()
  39. exit()
  40. try:
  41. # Navegar a la URL
  42. url = "sgu.ulsa.edu.mx/psulsa/alumnos/consultainformacionalumnos/consultainformacion.aspx"
  43. formatted_url = f"https://{clave}:{contraseña}@{url}"
  44. driver.get(formatted_url)
  45. driver.get(f'https://{url}')
  46. except Exception as e:
  47. print(f"Error navegando a la URL: {e}")
  48. driver.quit()
  49. exit()
  50. try:
  51. # If dentro del código existe un (case insensitive) Unauthorized
  52. if "Unauthorized" in driver.page_source:
  53. raise Exception("Credenciales inválidas")
  54. elemento = WebDriverWait(driver, 10).until(
  55. EC.presence_of_element_located((By.ID, "ctl00_contenedor_HistorialAlumno1_lblBtnSeccionHAcademico"))
  56. )
  57. elemento.click()
  58. except Exception as e:
  59. print(f"Error interactuando con la página: {e}")
  60. driver.quit()
  61. exit()
  62. try:
  63. # Procesamiento de HTML con BeautifulSoup
  64. html_doc = driver.page_source
  65. soup = BeautifulSoup(html_doc, 'lxml')
  66. table = soup.find('table', attrs={'id': 'ctl00_contenedor_HistorialAlumno1_gvMaterias'})
  67. if table is None:
  68. raise Exception("Tabla no encontrada en la página")
  69. materias_sgu = []
  70. headers = [header.text for header in table.find_all('th')]
  71. def is_cell_empty(cell_content):
  72. return not cell_content.strip() or cell_content == u'\xa0'
  73. for row in table.find_all('tr'):
  74. cols = row.find_all('td')
  75. if cols and not any(is_cell_empty(col.text) for col in cols):
  76. materias_sgu.append({headers[i]: col.text for i, col in enumerate(cols)})
  77. for materia in materias_sgu:
  78. materia['SEMESTRE'] = materia.pop('\xa0')
  79. servicio_social = soup.find('span', attrs={'id': 'ctl00_contenedor_HistorialAlumno1_Header1_lblSS'}).text
  80. # puede ser Realizado, No Realizado
  81. if servicio_social == "No Realizado":
  82. Alumno_serviciosocial = False
  83. elif servicio_social == "Realizado":
  84. Alumno_serviciosocial = True
  85. except Exception as e:
  86. print(f"Error procesando HTML: {e}")
  87. exit()
  88. finally:
  89. driver.quit()
  90. # Conexión a la base de datos y operaciones
  91. conexion = psycopg2.connect(
  92. dbname="sgi",
  93. user="postgres",
  94. password="sys4lci",
  95. host="200.13.89.27",
  96. port="5432"
  97. )
  98. # Ejecutar la consulta para actualizar public."Alumno"."Alumno_serviciosocial"
  99. try:
  100. with conexion.cursor() as cursor:
  101. cursor.execute(f'UPDATE public."Alumno" SET "Alumno_serviciosocial" = {Alumno_serviciosocial} WHERE "Usuario_claveULSA" = \'{clave[2:]}\'')
  102. conexion.commit()
  103. except Exception as e:
  104. print(f"Error al actualizar el servicio social: {e}")
  105. if conexion:
  106. conexion.rollback()
  107. try:
  108. # Ejecutar la consulta para obtener los periodos
  109. with conexion.cursor() as cursor:
  110. cursor.execute('SELECT * FROM "Periodo"')
  111. Periodos = cursor.fetchall()
  112. except Exception as e:
  113. print(f"Error obteniendo los periodos de la base de datos: {e}")
  114. try:
  115. # Ejecutar la consulta para obtener los tipos de calificación
  116. with conexion.cursor() as cursor:
  117. cursor.execute('SELECT * FROM "TipoCalificacion"')
  118. TiposCalificacion = cursor.fetchall()
  119. except Exception as e:
  120. print(f"Error obteniendo los tipos de calificación de la base de datos: {e}")
  121. try:
  122. # Ejecutar la consulta para obtener las materias base
  123. with conexion.cursor() as cursor:
  124. cursor.execute('SELECT * FROM "Materia"')
  125. materias_base = cursor.fetchall()
  126. except Exception as e:
  127. print(f"Error obteniendo las materias de la base de datos: {e}")
  128. try:
  129. # Procesar y preparar las calificaciones para inserción/actualización
  130. calificaciones = []
  131. with conexion.cursor() as cursor:
  132. cursor.execute(f'''
  133. SELECT "Carrera_id", "PlanEstudio_id"
  134. FROM "Alumno_view"
  135. WHERE "Usuario_claveULSA" = {clave[2:]}
  136. ''')
  137. Alumno_base = cursor.fetchone()
  138. for materia_sgu in materias_sgu:
  139. 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)
  140. if not materia_base:
  141. continue
  142. Periodo_base = next((Periodo_base for Periodo_base in Periodos if Periodo_base[-2] == materia_sgu['PERIODO']), None)
  143. if not Periodo_base:
  144. continue
  145. Calificacion_base = next((Calificacion_base for Calificacion_base in TiposCalificacion if Calificacion_base[-2] == materia_sgu['EXAMEN']), None)
  146. if not Calificacion_base:
  147. raise Exception(f"No se encontró el tipo de calificación {materia_sgu['EXAMEN']} en la base de datos")
  148. # 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')
  149. with conexion.cursor() as cursor:
  150. cursor.execute(f'''
  151. SELECT "Grupo_id"
  152. FROM "Grupo_view"
  153. WHERE REGEXP_REPLACE("Grupo_desc", '[^\d]', '', 'g') = '{materia_sgu["GRUPO"]}' AND (("Carrera_id" = {Alumno_base[0]}) OR "Carrera_esComun")
  154. ''')
  155. Grupo = cursor.fetchone()
  156. if Grupo:
  157. cursor.execute(f'''
  158. INSERT INTO public."Alumno_Materia"("Usuario_claveULSA", "Materia_id", "Periodo_id", "Grupo_id")
  159. VALUES ({clave[2:]}, {materia_base[0]}, {Periodo_base[0]}, {Grupo[0]})
  160. ON CONFLICT ("Usuario_claveULSA", "Materia_id", "Periodo_id") DO NOTHING
  161. ;
  162. ''')
  163. conexion.commit()
  164. if not materia_base or not Periodo_base or not Calificacion_base:
  165. 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']}")
  166. continue # Saltar esta iteración si alguna coincidencia falla
  167. calificaciones.append(f"({clave[2:]}, {materia_base[0]}, {Periodo_base[0]}, {Calificacion_base[0]}, {materia_sgu['CALIF']}, CURRENT_DATE, 'SGU')")
  168. if not calificaciones or len(calificaciones) == 0:
  169. raise Exception("No hay calificaciones para insertar o actualizar.")
  170. # Inserción/actualización de calificaciones en la base de datos
  171. with conexion.cursor() as cursor:
  172. cursor.execute(f'''
  173. insert into "Alumno_Materia_Calificacion"
  174. ("Usuario_claveULSA", "Materia_id", "Periodo_id", "TipoCalificacion_id", "Calificacion_calif", "Calificacion_fecha", "Calificacion_comentario")
  175. values
  176. {','.join(calificaciones)}
  177. on conflict ("Usuario_claveULSA", "Materia_id", "Periodo_id", "TipoCalificacion_id")
  178. DO UPDATE SET "Calificacion_calif" = EXCLUDED."Calificacion_calif", "Calificacion_comentario" = EXCLUDED."Calificacion_comentario";
  179. ''')
  180. conexion.commit()
  181. except Exception as e:
  182. print(f"Error al insertar/actualizar las calificaciones: {e} Stack: {e.__traceback__}")
  183. # print the whole stack
  184. if conexion:
  185. conexion.rollback() # Revertir cambios en caso de error
  186. conexion.close()