diff --git a/__pycache__/argument_parser.cpython-39.pyc b/__pycache__/argument_parser.cpython-39.pyc deleted file mode 100644 index 9b76b63..0000000 Binary files a/__pycache__/argument_parser.cpython-39.pyc and /dev/null differ diff --git a/__pycache__/data_processing.cpython-39.pyc b/__pycache__/data_processing.cpython-39.pyc deleted file mode 100644 index 3b18e77..0000000 Binary files a/__pycache__/data_processing.cpython-39.pyc and /dev/null differ diff --git a/__pycache__/database_operations.cpython-39.pyc b/__pycache__/database_operations.cpython-39.pyc deleted file mode 100644 index 6816566..0000000 Binary files a/__pycache__/database_operations.cpython-39.pyc and /dev/null differ diff --git a/__pycache__/main.cpython-39.pyc b/__pycache__/main.cpython-39.pyc deleted file mode 100644 index 4b9a528..0000000 Binary files a/__pycache__/main.cpython-39.pyc and /dev/null differ diff --git a/__pycache__/selenium_setup.cpython-39.pyc b/__pycache__/selenium_setup.cpython-39.pyc deleted file mode 100644 index 9f3675d..0000000 Binary files a/__pycache__/selenium_setup.cpython-39.pyc and /dev/null differ diff --git a/__pycache__/web_navigation.cpython-39.pyc b/__pycache__/web_navigation.cpython-39.pyc deleted file mode 100644 index 6fd635d..0000000 Binary files a/__pycache__/web_navigation.cpython-39.pyc and /dev/null differ diff --git a/extraccion_html.c b/extraccion_html.c deleted file mode 100644 index 2dd81ad..0000000 --- a/extraccion_html.c +++ /dev/null @@ -1,194 +0,0 @@ -#include -#include -#include -#include -#include -#include - -// Define the alumno struct -struct alumno { - char apellido_paterno[100]; - char apellido_materno[100]; - char curp[20]; - char clave_carrera[2]; - char plan[2]; - char clave[6]; - char nombre[100]; - char correo[100]; - char estatus; - char telefono[11]; - int semestre; - char sexo; -}; - -// Function to check for PostgreSQL connection errors -void check_conn_status(PGconn *conn) { - if (PQstatus(conn) != CONNECTION_OK) { - fprintf(stderr, "Connection to database failed: %s", PQerrorMessage(conn)); - PQfinish(conn); - exit(EXIT_FAILURE); - } -} - -// Function to check for PostgreSQL query execution errors -void check_exec_status(PGresult *res, PGconn *conn) { - if (PQresultStatus(res) != PGRES_TUPLES_OK) { - fprintf(stderr, "Query failed: %s", PQerrorMessage(conn)); - PQclear(res); - PQfinish(conn); - exit(EXIT_FAILURE); - } -} - -// Function to extract content from an HTML element by ID -char* get_element_content_by_id(htmlDocPtr doc, const char *id) { - xmlChar xpath[100]; - snprintf((char *)xpath, sizeof(xpath), "//*[@id='%s']", id); - - xmlXPathContextPtr xpathCtx = xmlXPathNewContext(doc); - xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression(xpath, xpathCtx); - - if (xpathObj == NULL || xmlXPathNodeSetIsEmpty(xpathObj->nodesetval)) { - xmlXPathFreeObject(xpathObj); - xmlXPathFreeContext(xpathCtx); - return NULL; - } - - xmlNodePtr node = xpathObj->nodesetval->nodeTab[0]; - xmlChar *content = xmlNodeGetContent(node); - - xmlXPathFreeObject(xpathObj); - xmlXPathFreeContext(xpathCtx); - - return (char *)content; -} - -// Function to parse HTML content using libxml2 and populate the alumno struct -void parse_html(const char *html, struct alumno *alum) { - htmlDocPtr doc = htmlReadMemory(html, strlen(html), NULL, NULL, HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING); - if (doc == NULL) { - fprintf(stderr, "Failed to parse HTML\n"); - return; - } - - char *content; - - content = get_element_content_by_id(doc, "ctl00_contenedor_HistorialAlumno1_lblApPatAlumnoHP"); - if (content) { - strncpy(alum->apellido_paterno, content, 100); - xmlFree(content); - } - - content = get_element_content_by_id(doc, "ctl00_contenedor_HistorialAlumno1_lblApMatAlumnoHP"); - if (content) { - strncpy(alum->apellido_materno, content, 100); - xmlFree(content); - } - - content = get_element_content_by_id(doc, "ctl00_contenedor_HistorialAlumno1_lblCURPAlumnoHP"); - if (content) { - strncpy(alum->curp, content, 20); - xmlFree(content); - } - - content = get_element_content_by_id(doc, "ctl00_contenedor_HistorialAlumno1_Header1_lblCveCarrera"); - if (content) { - strncpy(alum->clave_carrera, content, 2); - xmlFree(content); - } - - content = get_element_content_by_id(doc, "ctl00_contenedor_HistorialAlumno1_Header1_lblAlupla"); - if (content) { - strncpy(alum->plan, content, 4); - xmlFree(content); - } - - content = get_element_content_by_id(doc, "ctl00_contenedor_HistorialAlumno1_Header1_lblCveUlsa"); - if (content) { - strncpy(alum->clave, content, 7); - xmlFree(content); - } - - content = get_element_content_by_id(doc, "ctl00_contenedor_HistorialAlumno1_lblNombreAlumnoHP"); - if (content) { - strncpy(alum->nombre, content, 100); - xmlFree(content); - } - - content = get_element_content_by_id(doc, "ctl00_contenedor_HistorialAlumno1_lblCorreoAlumnoHP"); - if (content) { - strncpy(alum->correo, content, 100); - xmlFree(content); - } - - content = get_element_content_by_id(doc, "ctl00_contenedor_HistorialAlumno1_Header1_lblStat"); - if (content) { - alum->estatus = content[0]; - xmlFree(content); - } - - content = get_element_content_by_id(doc, "ctl00_contenedor_HistorialAlumno1_lblTelefonoAlumnoHP"); - if (content) { - strncpy(alum->telefono, content, 11); - xmlFree(content); - } - - content = get_element_content_by_id(doc, "ctl00_contenedor_HistorialAlumno1_Header1_lblSem"); - if (content) { - alum->semestre = atoi(content); - xmlFree(content); - } - - content = get_element_content_by_id(doc, "ctl00_contenedor_HistorialAlumno1_lblSexoAlumnoHP"); - if (content) { - alum->sexo = content[0]; - xmlFree(content); - } - - xmlFreeDoc(doc); -} - -int main() { - // PostgreSQL connection parameters - const char *conninfo = "dbname=sgi user=postgres password=h3rcul3s#$ hostaddr=200.13.89.8 port=5432"; - PGconn *conn = PQconnectdb(conninfo); - - // Check connection status - check_conn_status(conn); - - // Execute SQL query to retrieve HTML content - PGresult *res = PQexec(conn, "SELECT datos_html FROM public.alumno_extraccion WHERE error_message IS NULL"); - check_exec_status(res, conn); - - // Process each row - int rows = PQntuples(res); - for (int i = 0; i < rows; i++) { - char *html_content = PQgetvalue(res, i, 0); - // printf("HTML Content: %s\n", html_content); - - struct alumno alum; - memset(&alum, 0, sizeof(alum)); // Initialize the struct to zero - - parse_html(html_content, &alum); - - printf("Apellido Paterno: %s\n", alum.apellido_paterno); - printf("Apellido Materno: %s\n", alum.apellido_materno); - printf("CURP: %s\n", alum.curp); - printf("Clave Carrera: %s\n", alum.clave_carrera); - printf("Plan: %s\n", alum.plan); - printf("Clave: %s\n", alum.clave); - printf("Nombre: %s\n", alum.nombre); - printf("Correo: %s\n", alum.correo); - printf("Estatus: %c\n", alum.estatus); - printf("Telefono: %s\n", alum.telefono); - printf("Semestre: %d\n", alum.semestre); - printf("Sexo: %c\n", alum.sexo); - } - - // Clean up - PQclear(res); - PQfinish(conn); - - return 0; -} - diff --git a/lib/__pycache__/argument_parser.cpython-36.pyc b/lib/__pycache__/argument_parser.cpython-36.pyc deleted file mode 100644 index 60e067a..0000000 Binary files a/lib/__pycache__/argument_parser.cpython-36.pyc and /dev/null differ diff --git a/lib/__pycache__/argument_parser.cpython-39.pyc b/lib/__pycache__/argument_parser.cpython-39.pyc deleted file mode 100644 index 90363dc..0000000 Binary files a/lib/__pycache__/argument_parser.cpython-39.pyc and /dev/null differ diff --git a/lib/__pycache__/data_processing.cpython-36.pyc b/lib/__pycache__/data_processing.cpython-36.pyc deleted file mode 100644 index ff108fd..0000000 Binary files a/lib/__pycache__/data_processing.cpython-36.pyc and /dev/null differ diff --git a/lib/__pycache__/data_processing.cpython-39.pyc b/lib/__pycache__/data_processing.cpython-39.pyc deleted file mode 100644 index e146b73..0000000 Binary files a/lib/__pycache__/data_processing.cpython-39.pyc and /dev/null differ diff --git a/lib/__pycache__/database_operations.cpython-36.pyc b/lib/__pycache__/database_operations.cpython-36.pyc deleted file mode 100644 index c1405d4..0000000 Binary files a/lib/__pycache__/database_operations.cpython-36.pyc and /dev/null differ diff --git a/lib/__pycache__/database_operations.cpython-39.pyc b/lib/__pycache__/database_operations.cpython-39.pyc deleted file mode 100644 index ebaf97e..0000000 Binary files a/lib/__pycache__/database_operations.cpython-39.pyc and /dev/null differ diff --git a/lib/__pycache__/funciones.cpython-36.pyc b/lib/__pycache__/funciones.cpython-36.pyc deleted file mode 100644 index cf635e6..0000000 Binary files a/lib/__pycache__/funciones.cpython-36.pyc and /dev/null differ diff --git a/lib/__pycache__/funciones.cpython-39.pyc b/lib/__pycache__/funciones.cpython-39.pyc deleted file mode 100644 index 5ba577d..0000000 Binary files a/lib/__pycache__/funciones.cpython-39.pyc and /dev/null differ diff --git a/lib/__pycache__/selenium_setup.cpython-36.pyc b/lib/__pycache__/selenium_setup.cpython-36.pyc deleted file mode 100644 index 28badc1..0000000 Binary files a/lib/__pycache__/selenium_setup.cpython-36.pyc and /dev/null differ diff --git a/lib/__pycache__/selenium_setup.cpython-39.pyc b/lib/__pycache__/selenium_setup.cpython-39.pyc deleted file mode 100644 index 1ad8b5b..0000000 Binary files a/lib/__pycache__/selenium_setup.cpython-39.pyc and /dev/null differ diff --git a/lib/__pycache__/web_navigation.cpython-36.pyc b/lib/__pycache__/web_navigation.cpython-36.pyc deleted file mode 100644 index 9a3649b..0000000 Binary files a/lib/__pycache__/web_navigation.cpython-36.pyc and /dev/null differ diff --git a/lib/__pycache__/web_navigation.cpython-39.pyc b/lib/__pycache__/web_navigation.cpython-39.pyc deleted file mode 100644 index 1bc024f..0000000 Binary files a/lib/__pycache__/web_navigation.cpython-39.pyc and /dev/null differ diff --git a/lib/argument_parser.py b/lib/argument_parser.py deleted file mode 100644 index f221f8f..0000000 --- a/lib/argument_parser.py +++ /dev/null @@ -1,17 +0,0 @@ -import argparse -import getpass - -def parse_arguments(): - parser = argparse.ArgumentParser() - parser.add_argument("clave", help="Clave ULSA argument") - args = parser.parse_args() - - clave = args.clave - if not clave.startswith('al') or not clave[2:].isdigit() or len(clave) != 8: - raise ValueError("Clave no válida. Debe comenzar con 'al' y tener 6 dígitos.") - - contraseña = getpass.getpass("Contraseña: ") - if not clave or not contraseña: - raise ValueError("Clave y/o contraseña no válidos") - - return clave, contraseña diff --git a/lib/data_processing.py b/lib/data_processing.py deleted file mode 100644 index 3950c41..0000000 --- a/lib/data_processing.py +++ /dev/null @@ -1,53 +0,0 @@ -from bs4 import BeautifulSoup - -def process_html(html_doc): - """ - Procesa el HTML de la página y extrae la información relevante - como materias, estados del servicio social, etc. - """ - soup = BeautifulSoup(html_doc, 'lxml') - table = soup.find('table', attrs={'id': 'ctl00_contenedor_HistorialAlumno1_gvMaterias'}) - - actualmente_cursadas = soup.find('table', attrs={'id': 'ctl00_contenedor_HistorialAlumno1_gvMatOrdinario'}) - - if table is None: - raise Exception("Tabla no encontrada en la página") - elif actualmente_cursadas is None: - raise Exception("Tabla de materias actualmente cursadas no encontrada en la página") - - materias_sgu = [] - headers = [header.text for header in table.find_all('th')] - - for row in table.find_all('tr'): - cols = row.find_all('td') - if cols and not any(col.text == "Promedio:" for col in cols): - materias_sgu.append({ headers[i]: col.text for i, col in enumerate(cols) if not is_cell_empty(col.text) }) - materias_actualmente_cursadas = [] - headers = [header.text for header in actualmente_cursadas.find_all('th')] - for row in actualmente_cursadas.find_all('tr'): - cols = row.find_all('td') - if cols and not any(col.text == "No hay Datos" for col in cols): - materias_actualmente_cursadas.append({ headers[i]: col.text for i, col in enumerate(cols) if not is_cell_empty(col.text) }) - - periodo_actual = soup.find('span', attrs={'id': 'ctl00_contenedor_HistorialAlumno1_Header1_lblPeriodo'}) - grupo_actual = soup.find('span', attrs={'id': 'ctl00_contenedor_HistorialAlumno1_Header1_lblgru'}) - - return clean_materias_sgu(materias_sgu), clean_materias_sgu(materias_actualmente_cursadas), f"'{periodo_actual.text}'", f"'{grupo_actual.text}'" - -def is_cell_empty(cell_content): - """ - Verifica si el contenido de una celda está vacío o contiene solo espacios. - """ - return not cell_content.strip() or cell_content == u'\xa0' - -def clean_materias_sgu(materias_sgu): - """ - Limpia y ajusta las materias obtenidas del SGU para su procesamiento posterior. - """ - for materia in materias_sgu: - if '\xa0' in materia: - materia['SEMESTRE'] = materia.pop('\xa0') - return materias_sgu - -# Puedes agregar más funciones según sea necesario para procesar otros aspectos del HTML - diff --git a/lib/database_operations.py b/lib/database_operations.py deleted file mode 100644 index 70f4bee..0000000 --- a/lib/database_operations.py +++ /dev/null @@ -1,47 +0,0 @@ -import psycopg2 -from psycopg2.extras import DictCursor - -def connect_to_database(dbname="sgi", user="postgres", password="h3rcul3s#$", host="200.13.89.8", port="5432"): - """ - Establece una conexión a la base de datos y la retorna. - """ - try: - connection = psycopg2.connect( - dbname=dbname, - user=user, - password=password, - host=host, - port=port - ) - return connection - except psycopg2.Error as e: - print(f"No se pudo conectar a la base de datos: {e}") - exit() - -def query_all(sql): - with connect_to_database() as conn: - with conn.cursor(cursor_factory=DictCursor) as cur: - cur.execute(sql) - return cur.fetchall() - -def query_single(sql): - with connect_to_database() as conn: - with conn.cursor(cursor_factory=DictCursor) as cur: - cur.execute(sql) - return cur.fetchone() # Returns a dictionary-like object - -def execute_query(sql): - with connect_to_database() as conn: - with conn.cursor() as cur: - cur.execute(sql) - conn.commit() # Commit to save the insert operation - -def log(message, status, error_message=None, clave=None, no_insertadas=[]): - with connect_to_database(dbname="adcfi", user="postgres", password="Ultr4p0d3r0s0##", host="200.13.89.42", port="5432") as conn: - with conn.cursor() as cur: - cur.execute(f""" - INSERT INTO calificaciones.calificaciones_log (response_status, error_message, user_ip, additional_info) - VALUES ({status}, {f"'{error_message}'" if error_message else 'NULL'}, '200.13.89.42', '{{"clave": "{clave}", "message": "{message}", "no insertadas": "[{', '.join(no_insertada for no_insertada in no_insertadas)}]"}}') - """) - conn.commit() - diff --git a/lib/funciones.py b/lib/funciones.py deleted file mode 100644 index 1da1a0f..0000000 --- a/lib/funciones.py +++ /dev/null @@ -1,99 +0,0 @@ -from database_operations import query_all, query_single, execute_query - -def actualizar_servicio(clave, Alumno_serviciosocial): - # Actualizar servicio social - execute_query(f''' - UPDATE "Alumno" SET "Alumno_serviciosocial" = {Alumno_serviciosocial} WHERE "Usuario_claveULSA" = {int(clave[2:])} - ''') - -def get_periodos(materias_sgu): - return query_all(f'''SELECT * FROM "Periodo" WHERE "Periodo_shortname" IN ({",".join(f"'{materia['PERIODO']}'" for materia in materias_sgu)})''') - -def get_materias(materias_sgu): - return query_all('SELECT * FROM "Materia"') -def get_tipo_calificaciones(materias_sgu): - return query_all(f'''SELECT * FROM "TipoCalificacion"''') -def get_alumno(clave): - return query_single(f'SELECT "Carrera_id", "PlanEstudio_id" FROM "Alumno_view" WHERE "Usuario_claveULSA" = {clave[2:]}') -def get_grupo(grupo, carrera_id): - return query_single(f'''SELECT "Grupo_id" FROM "Grupo_view" - WHERE REGEXP_REPLACE("Grupo_desc", '[^\d]', '', 'g') = '{grupo}' AND (("Carrera_id" = {carrera_id}) OR "Carrera_esComun") - ''') -def insert_materia(clave, materia_id, periodo_id, grupo_id): - execute_query(f'''INSERT INTO public."Alumno_Materia"("Usuario_claveULSA", "Materia_id", "Periodo_id", "Grupo_id") - VALUES ({int(clave[2:])}, {materia_id}, {periodo_id}, {grupo_id}) - ON CONFLICT ("Usuario_claveULSA", "Materia_id", "Periodo_id") DO NOTHING; - ''') -def insert_calificaciones(calificaciones): - execute_query(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"; - ''') - - -#alumno structure {apellido_paterno: str, apellido_materno: str, curp: str, clave_carrera: int, plan: int, clave: int, servicio_social: bool, nombre: str, correo: str} -def insert_alumno(alumno): - alumno_base = query_single(f'SELECT "Carrera_id", "PlanEstudio_id" FROM "Alumno_view" WHERE "Usuario_claveULSA" = {alumno["clave"]}') - if alumno_base: - return alumno_base - - usuario_base = query_single(f"""SELECT * FROM "Usuario" WHERE "Usuario_curp" = '{alumno["curp"]}'""") - plan_estudio_base = query_single(f"""SELECT * FROM "PlanEstudio_view" WHERE "PlanEstudio_desc" LIKE '%{alumno["plan"]}' AND "Carrera_clave" = {alumno["clave_carrera"]}""") - - if usuario_base: - execute_query(f''' - INSERT INTO public."Alumno"("Usuario_claveULSA", "Usuario_id", "PlanEstudio_id", "Alumno_fecha_ingreso", "Alumno_generacion", "Alumno_serviciosocial") - VALUES ({alumno["clave"]}, {usuario_base["Usuario_id"]}, {plan_estudio_base["PlanEstudio_id"]}, '{alumno['fecha_ingreso']}', '{alumno['fecha_ingreso']}', {alumno["servicio_social"]}) - ON CONFLICT ("Usuario_claveULSA") DO NOTHING; - ''') - execute_query(f''' - INSERT INTO "Alumno_SubEstadoAlumno" ("SubEstadoAlumno_id", "Usuario_claveULSA", "SEA_fecha", "SEA_actual") - VALUES (3, {alumno["clave"]},'{alumno['fecha_ingreso']}', true) - ''') - - alumno_base = query_single(f'SELECT "Carrera_id", "PlanEstudio_id" FROM "Alumno_view" WHERE "Usuario_claveULSA" = {alumno["clave"]}') - - return alumno_base - - usuario_base = query_single(f''' - INSERT INTO public."Usuario"("Usuario_nombre", "Usuario_apellidos", "Usuario_curp") - VALUES ('{alumno["nombre"]}', '{alumno["apellido_paterno"]} {alumno["apellido_materno"]}', '{alumno["curp"]}') RETURNING "Usuario_id" - ''') - execute_query(f''' - INSERT INTO public."Alumno"("Usuario_claveULSA", "Usuario_id", "PlanEstudio_id", "Alumno_fecha_ingreso", "Alumno_generacion", "Alumno_serviciosocial") - VALUES ({alumno["clave"]}, {usuario_base["Usuario_id"]}, {plan_estudio_base["PlanEstudio_id"]}, '{alumno['fecha_ingreso']}', '{alumno['fecha_ingreso']}', {alumno["servicio_social"]}) - ON CONFLICT ("Usuario_claveULSA") DO NOTHING; - ''') - alumno_base = query_single(f'SELECT "Carrera_id", "PlanEstudio_id" FROM "Alumno_view" WHERE "Usuario_claveULSA" = {alumno["clave"]}') - - return alumno_base - -# insert actualmente_cursadas -def insert_actualmente_cursadas(clave, actualmente_cursadas, periodo_actual, grupo_actual): - grupo_base = query_single(f'SELECT "Grupo_id" FROM "Grupo" WHERE "Grupo_desc" = {grupo_actual}') - periodo_base = query_single(f'SELECT "Periodo_id" FROM "Periodo" WHERE "Periodo_shortname" = {periodo_actual}') - # only where materia["Clave"] is set - for materia in filter(lambda materia: "Clave" in materia, actualmente_cursadas): - # to varchar - materia_clave = f"'{materia['Clave']}'" - materia_base = query_single(f'SELECT "Materia_id" FROM "Materia" WHERE {materia_clave} = ANY("Materia_claves")') - if materia_base: - execute_query(f''' - INSERT INTO public."Alumno_Materia"("Usuario_claveULSA", "Materia_id", "Periodo_id", "Grupo_id") - VALUES ({clave[2:]}, {materia_base["Materia_id"]}, {periodo_base["Periodo_id"]}, {grupo_base["Grupo_id"]}) - ON CONFLICT ("Usuario_claveULSA", "Materia_id", "Periodo_id") DO NOTHING; - ''') - - -def insert_datos(alumno): - execute_query(f''' - INSERT INTO public."pos_ultima_extraccion"("Usuario_claveULSA", "telefono", "correo", "estatus", "promedio", "sexo", "semestre") - VALUES ({alumno["clave"]}, '{alumno["telefono"]}', '{alumno["correo"]}', '{alumno["estatus"]}', {alumno["promedio"]} , '{alumno["sexo"]}', {alumno["semestre"]}) - ON CONFLICT ("Usuario_claveULSA") DO UPDATE SET "telefono" = EXCLUDED."telefono", "correo" = EXCLUDED."correo", "estatus" = EXCLUDED."estatus", "promedio" = EXCLUDED."promedio", - "actualizacion" = now(); - ''') - - \ No newline at end of file diff --git a/lib/log.py b/lib/log.py deleted file mode 100644 index a3c045a..0000000 --- a/lib/log.py +++ /dev/null @@ -1,35 +0,0 @@ -import psycopg2 -from psycopg2.extras import DictCursor - -def connect_to_database(dbname="sgi", user="postgres", password="sys4lci", host="200.13.89.27", port="5432"): - """ - Establece una conexión a la base de datos y la retorna. - """ - connection = psycopg2.connect( - dbname=dbname, - user=user, - password=password, - host=host, - port=port - ) - return connection - - -def query_all(sql): - with connect_to_database() as conn: - with conn.cursor(cursor_factory=DictCursor) as cur: - cur.execute(sql) - return cur.fetchall() - -def query_single(sql): - with connect_to_database() as conn: - with conn.cursor(cursor_factory=DictCursor) as cur: - cur.execute(sql) - return cur.fetchone() # Returns a dictionary-like object - -def execute_query(sql): - with connect_to_database() as conn: - with conn.cursor() as cur: - cur.execute(sql) - conn.commit() # Commit to save the insert operation - diff --git a/lib/selenium_setup.py b/lib/selenium_setup.py deleted file mode 100644 index 58e0279..0000000 --- a/lib/selenium_setup.py +++ /dev/null @@ -1,16 +0,0 @@ -from selenium import webdriver -from selenium.webdriver.chrome.service import Service -from selenium.webdriver.chrome.options import Options - -def configure_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) - return driver diff --git a/lib/web_navigation.py b/lib/web_navigation.py deleted file mode 100644 index 6d1a550..0000000 --- a/lib/web_navigation.py +++ /dev/null @@ -1,54 +0,0 @@ -from selenium.webdriver.common.by import By -from selenium.webdriver.support.ui import WebDriverWait -from selenium.webdriver.support import expected_conditions as EC - -def navigate_to_url(driver, url, clave, contraseña): - formatted_url = f"https://{clave}:{contraseña}@{url}" - driver.get(formatted_url) - driver.get(f'https://{url}') - # If dentro del código no existe un elemento con el id ctl00_lnkHome - if not WebDriverWait(driver, 10).until( - EC.presence_of_element_located((By.ID, "ctl00_lnkHome")) - ): - raise Exception("No se pudo iniciar sesión.") - - # wait until it appears this element.id = ctl00_contenedor_HistorialAlumno1_lblApPatAlumnoHP and get it - - def wait_for_element(driver, element_id): - return WebDriverWait(driver, 10).until( - EC.presence_of_element_located((By.ID, element_id)) - ) - - servicio_social = wait_for_element(driver, "ctl00_contenedor_HistorialAlumno1_Header1_lblSS") - alumno = { - "apellido_paterno": wait_for_element(driver, "ctl00_contenedor_HistorialAlumno1_lblApPatAlumnoHP").text, - "apellido_materno": wait_for_element(driver, "ctl00_contenedor_HistorialAlumno1_lblApMatAlumnoHP").text, - "curp": wait_for_element(driver, "ctl00_contenedor_HistorialAlumno1_lblCURPAlumnoHP").text, - "clave_carrera": int(wait_for_element(driver, "ctl00_contenedor_HistorialAlumno1_Header1_lblCveCarrera").text), - "plan": int(wait_for_element(driver, "ctl00_contenedor_HistorialAlumno1_Header1_lblAlupla").text), - "clave": int(wait_for_element(driver, "ctl00_contenedor_HistorialAlumno1_Header1_lblCveUlsa").text), - "servicio_social": servicio_social.text == "Realizado", - "nombre":wait_for_element(driver, "ctl00_contenedor_HistorialAlumno1_lblNombreAlumnoHP").text, - "correo": wait_for_element(driver, "ctl00_contenedor_HistorialAlumno1_lblCorreoAlumnoHP").text, - 'estatus' : wait_for_element(driver, "ctl00_contenedor_HistorialAlumno1_Header1_lblStat").text, - "telefono": wait_for_element(driver, "ctl00_contenedor_HistorialAlumno1_lblTelefonoAlumnoHP").text, - "semestre": int(wait_for_element(driver, "ctl00_contenedor_HistorialAlumno1_Header1_lblSem").text), - "sexo": wait_for_element(driver, "ctl00_contenedor_HistorialAlumno1_lblSexoAlumnoHP").text, - } - - - click_element(driver, element_id="ctl00_contenedor_HistorialAlumno1_lblBtnSeccionHAcademico") - - historial_academico = driver.page_source - - alumno['promedio'] = float(wait_for_element(driver, "ctl00_contenedor_HistorialAlumno1_lblPromedioAlumnoHA").text) - - return alumno, historial_academico - -def click_element(driver, element_id): - elemento = WebDriverWait(driver, 10).until( - EC.presence_of_element_located((By.ID, element_id)) - ) - elemento.click() - - diff --git a/logs.py b/logs.py deleted file mode 100644 index e8d16fa..0000000 --- a/logs.py +++ /dev/null @@ -1,16 +0,0 @@ -import psycopg2 -from psycopg2.extras import DictCursor - -conn = psycopg2.connect( - dbname='adcfi', - user='postgres', - password='Ultr4p0d3r0s0##', - host='200.13.89.42', - port='5432' -) - -with conn.cursor(cursor_factory=DictCursor) as cur: - cur.execute("SELECT * FROM calificaciones.calificaciones_log ORDER BY log_timestamp DESC LIMIT 10") - - for row in cur: - print(row) \ No newline at end of file diff --git a/main.py b/main.py deleted file mode 100644 index badd435..0000000 --- a/main.py +++ /dev/null @@ -1,105 +0,0 @@ -import sys -from pathlib import Path -from flask import Flask, request, jsonify -from waitress import serve - -app = Flask(__name__) - -# Agregar el directorio lib al path de Python -lib_path = Path(__file__).parent / 'lib' -sys.path.append(str(lib_path)) - -# Ahora puedes importar los módulos desde el directorio lib -from selenium_setup import configure_selenium -from argument_parser import parse_arguments -from web_navigation import navigate_to_url, click_element -from data_processing import process_html -from database_operations import log -from funciones import * - -def main(clave, contraseña): - # Configurar Selenium y navegar a la URL - driver = configure_selenium() - url = "sgu.ulsa.edu.mx/psulsa/alumnos/consultainformacionalumnos/consultainformacion.aspx" - datos_alumno, historial_academico = navigate_to_url(driver, url, clave, contraseña) - materias_sgu, actualmente_cursadas, periodo_actual, grupo_actual = process_html(historial_academico) - insert_actualmente_cursadas(clave, actualmente_cursadas, periodo_actual, grupo_actual) - - # Obtener datos de la base - periodos_base = get_periodos(materias_sgu) - - # obtener la fecha mínima del arreglo de diccionarios de periodos en su campo "Periodo_fecha_inicio" - fecha_mínima = min(periodos_base, key=lambda x: x['Periodo_fecha_inicial'])['Periodo_fecha_inicial'] - # déjalo en el primer día del mes - datos_alumno['fecha_ingreso'] = fecha_mínima.replace(day=1) - - materias_base = get_materias(materias_sgu) - tipo_calificaciones_base = get_tipo_calificaciones(materias_sgu) - alumno_base = insert_alumno(datos_alumno) - - # Actualizar servicio social - actualizar_servicio(clave, datos_alumno['servicio_social']) - - #insert datos - insert_datos(datos_alumno) - - calificaciones = [] - no_insertadas = [] - - 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['Materia_claves'] or (materia_sgu['Cve SEP'] in materia_base["Materia_claves"] and alumno_base['PlanEstudio_id'] == materia_base['PlanEstudio_id'])), None) - periodo_base = next((periodo_base for periodo_base in periodos_base if periodo_base['Periodo_shortname'] == materia_sgu['PERIODO']), None) - tipo_calificacion_base = next((calificacion_base for calificacion_base in tipo_calificaciones_base if calificacion_base['TipoCalificacion_desc_corta'] == materia_sgu['EXAMEN']), None) - if 'GRUPO' in materia_sgu.keys(): - grupo = get_grupo(materia_sgu['GRUPO'], alumno_base['Carrera_id']) - else: - grupo = None - - if materia_base and periodo_base and tipo_calificacion_base: - calificaciones.append(f"({clave[2:]}, {materia_base['Materia_id']}, {periodo_base['Periodo_id']}, {tipo_calificacion_base['TipoCalificacion_id']}, {materia_sgu['CALIF']}, CURRENT_DATE, 'SGU')") - - if not materia_base or not periodo_base or not tipo_calificacion_base or not grupo: - no_insertadas.append(f'''Materia: {materia_base['Materia_id'] if materia_base else materia_sgu['Cve ULSA']} - Periodo_base: {periodo_base['Periodo_id'] if periodo_base else materia_sgu['PERIODO']} Tipo_calificacion_base: {tipo_calificacion_base['TipoCalificacion_id'] if tipo_calificacion_base else materia_sgu['EXAMEN']} Grupo: {grupo['Grupo_id'] if grupo else materia_sgu['GRUPO'] if 'GRUPO' in materia_sgu.keys() else 'None' } ''') - continue - - insert_materia(clave, materia_base['Materia_id'], periodo_base['Periodo_id'], grupo['Grupo_id']) - - - if not calificaciones or len(calificaciones) == 0: - raise Exception("No hay calificaciones para insertar o actualizar.") - - # Insertar calificaciones - insert_calificaciones(calificaciones) - return clave, no_insertadas - -@app.route('/calificaciones', methods=['POST']) -def calificaciones(): - try: - # Obtener la clave y la contraseña de la solicitud POST - clave = request.form.get('clave') - contraseña = request.form.get('contraseña') - - # Verificar si la clave y la contraseña existen - if clave is None or contraseña is None: - return "Error: La clave y/o contraseña no fueron proporcionadas en la solicitud." - - # Procesar los datos (aquí llamamos a la función main) - clave, no_insertadas = main(clave, contraseña) - - # Registro de éxito - log("Proceso terminado con éxito.", 200, None, clave, no_insertadas) - - # Retornar respuesta exitosa como JSON - return jsonify({"mensaje": "Proceso terminado con éxito.", "clave": clave, "no_insertadas": no_insertadas, "success": True}) - - except Exception as e: - # remove all ' from the error message - e = e.replace("'", "") - log(str(e), 500, e, None) - - # Retornar mensaje de error como JSON - return jsonify({"mensaje": str(e), "success": False}) - - -if __name__ == "__main__": - serve(app, host='0.0.0.0', port=5000) diff --git a/proceso b/proceso deleted file mode 100644 index 6b11c04..0000000 Binary files a/proceso and /dev/null differ diff --git a/sgu.py b/sgu.py deleted file mode 100644 index a9e91c8..0000000 --- a/sgu.py +++ /dev/null @@ -1,217 +0,0 @@ -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()